[PATCH v4 1/2] VIM: Add better attachment support

Subject: [PATCH v4 1/2] VIM: Add better attachment support

Date: Fri, 24 Oct 2014 09:38:39 -0700

To: notmuch@notmuchmail.org

Cc:

From: Ian Main


Change how the notmuch vim client supports attachments:

- For each message part a 'Part <number>: <filename>' is added to the
  header.
- You can then use 'e' to extract the attachment under the cursor or
  use it elsewhere to extract all attachments (the prior behavior)
- You can use 'v' to 'view' the attachment/part using xdg-open by
  default.
- If the message is 'text/html' we include a 'Part:' for the body of
  the message so you can easily view it in a web browser if you so
  choose.

    Ian
---

- Fixed commit message
- Fixed documentation

 vim/notmuch.txt |  8 +++++-
 vim/notmuch.vim | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/vim/notmuch.txt b/vim/notmuch.txt
index 4374102..d5e1ad2 100644
--- a/vim/notmuch.txt
+++ b/vim/notmuch.txt
@@ -72,6 +72,9 @@ q	Quit view
 A	Archive (-inbox -unread)
 I	Mark as read (-unread)
 t	Tag (prompted)
+e       Extract attachment on the current 'Part' line or all
+	attachments if the cursor is elsewhere.
+<enter> View attachment on the current 'Part' line.
 s	Search
 p	Save patches
 r	Reply
@@ -148,6 +151,9 @@ You can also configure your externail mail reader and sendemail program:
 >
 	let g:notmuch_reader = 'mutt -f %s'
 	let g:notmuch_sendmail = 'sendmail'
-<
+
+You can also configure what probram is used to view attachments:
+
+	let g:notmuch_view_attachment = 'xdg-open'
 
 vim:tw=78:ts=8:noet:ft=help:
diff --git a/vim/notmuch.vim b/vim/notmuch.vim
index cad9517..1466e50 100644
--- a/vim/notmuch.vim
+++ b/vim/notmuch.vim
@@ -35,6 +35,7 @@ let g:notmuch_show_maps = {
 	\ 't':		'show_tag("")',
 	\ 'o':		'show_open_msg()',
 	\ 'e':		'show_extract_msg()',
+	\ '<Enter>':	'show_view_attachment()',
 	\ 's':		'show_save_msg()',
 	\ 'p':		'show_save_patches()',
 	\ 'r':		'show_reply()',
@@ -58,6 +59,8 @@ let s:notmuch_date_format_default = '%d.%m.%y'
 let s:notmuch_datetime_format_default = '%d.%m.%y %H:%M:%S'
 let s:notmuch_reader_default = 'mutt -f %s'
 let s:notmuch_sendmail_default = 'sendmail'
+let s:notmuch_view_attachment_default = 'xdg-open'
+let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp'
 let s:notmuch_folders_count_threads_default = 0
 let s:notmuch_compose_start_insert_default = 1
 
@@ -152,13 +155,72 @@ function! s:show_info()
 	ruby vim_puts get_message.inspect
 endfunction
 
+function! s:show_view_attachment()
+	let line = getline(".")
+ruby << EOF
+	m = get_message
+	line = VIM::evaluate('line')
+
+	match = line.match(/^Part (\d*):/)
+	if match and match.length == 2
+		# Set up the tmpdir
+		tmpdir = VIM::evaluate('g:notmuch_attachment_tmpdir')
+		tmpdir = File.expand_path(tmpdir)
+		Dir.mkdir(tmpdir) unless Dir.exists?(tmpdir)
+
+		p = m.mail.parts[match[1].to_i - 1]
+		if p == nil
+			# Not a multipart message, use the message itself.
+			p = m.mail
+		end
+		if p.filename and p.filename.length > 0
+			filename = p.filename
+		else
+			suffix = ''
+			if p.mime_type == 'text/html'
+				suffix = '.html'
+			end
+			filename = "part-#{match[1]}#{suffix}"
+		end
+
+		# Sanitize just in case..
+		filename.gsub!(/[^0-9A-Za-z.\-]/, '_')
+
+		fullpath = File.expand_path("#{tmpdir}/#{filename}")
+		vim_puts "Viewing attachment #{fullpath}"
+		File.open(fullpath, 'w') do |f|
+			f.write p.body.decoded
+			cmd = VIM::evaluate('g:notmuch_view_attachment')
+			system(cmd, fullpath)
+		end
+	else
+		vim_puts "No attachment on this line."
+	end
+EOF
+endfunction
+
 function! s:show_extract_msg()
+	let line = getline(".")
 ruby << EOF
 	m = get_message
-	m.mail.attachments.each do |a|
+	line = VIM::evaluate('line')
+
+	# If the user is on a line that has an 'Part'
+	# line, we just extract the one attachment.
+	match = line.match(/^Part (\d*):/)
+	if match and match.length == 2
+		a = m.mail.parts[match[1].to_i - 1]
 		File.open(a.filename, 'w') do |f|
 			f.write a.body.decoded
-			print "Extracted '#{a.filename}'"
+			vim_puts "Extracted #{a.filename}"
+		end
+	else
+		# Extract them all..
+		m.mail.attachments.each do |a|
+			File.open(a.filename, 'w') do |f|
+				f.write a.body.decoded
+				vim_puts "Extracted #{a.filename}"
+			end
 		end
 	end
 EOF
@@ -331,6 +393,16 @@ ruby << EOF
 			b << "To: %s" % msg['to']
 			b << "Cc: %s" % msg['cc']
 			b << "Date: %s" % msg['date']
+			cnt = 0
+			m.parts.each do |p|
+				cnt += 1
+				b << "Part %d: %s (%s)" % [cnt, p.mime_type, p.filename]
+			end
+			# Add a special case for text/html messages.  Here we show the
+			# only 'part' so that we can view it in a web browser if we want.
+			if m.parts.length == 0 and part.mime_type == 'text/html'
+				b << "Part 1: text/html"
+			end
 			nm_m.body_start = b.count
 			b << "--- %s ---" % part.mime_type
 			part.convert.each_line do |l|
@@ -425,6 +497,14 @@ function! s:set_defaults()
 		endif
 	endif
 
+	if !exists('g:notmuch_attachment_tmpdir')
+		let g:notmuch_attachment_tmpdir = s:notmuch_attachment_tmpdir_default
+	endif
+
+	if !exists('g:notmuch_view_attachment')
+		let g:notmuch_view_attachment = s:notmuch_view_attachment_default
+	endif
+
 	if !exists('g:notmuch_folders_count_threads')
 		if exists('g:notmuch_rb_count_threads')
 			let g:notmuch_count_threads = g:notmuch_rb_count_threads
-- 
1.9.3


Thread: