1if exists("g:loaded_notmuch")
2	finish
3endif
4
5if !has("ruby") || version < 700
6	finish
7endif
8
9let g:loaded_notmuch = "yep"
10
11let g:notmuch_folders_maps = {
12	\ '<Enter>':	'folders_show_search()',
13	\ 's':		'folders_search_prompt()',
14	\ '=':		'folders_refresh()',
15	\ 'c':		'compose()',
16	\ }
17
18let g:notmuch_search_maps = {
19	\ 'q':		'kill_this_buffer()',
20	\ '<Enter>':	'search_show_thread(1)',
21	\ '<Space>':	'search_show_thread(2)',
22	\ 'A':		'search_tag("-inbox -unread")',
23	\ 'I':		'search_tag("-unread")',
24	\ 't':		'search_tag("")',
25	\ 's':		'search_search_prompt()',
26	\ '=':		'search_refresh()',
27	\ '?':		'search_info()',
28	\ 'c':		'compose()',
29	\ }
30
31let g:notmuch_show_maps = {
32	\ 'q':		'kill_this_buffer()',
33	\ 'A':		'show_tag("-inbox -unread")',
34	\ 'I':		'show_tag("-unread")',
35	\ 't':		'show_tag("")',
36	\ 'o':		'show_open_msg()',
37	\ 'e':		'show_extract_msg()',
38	\ 's':		'show_save_msg()',
39	\ 'p':		'show_save_patches()',
40	\ 'r':		'show_reply()',
41	\ '?':		'show_info()',
42	\ '<Tab>':	'show_next_msg()',
43	\ 'c':		'compose()',
44	\ }
45
46let g:notmuch_compose_maps = {
47	\ ',s':		'compose_send()',
48	\ ',q':		'compose_quit()',
49	\ }
50
51let s:notmuch_folders_default = [
52	\ [ 'new', 'tag:inbox and tag:unread' ],
53	\ [ 'inbox', 'tag:inbox' ],
54	\ [ 'unread', 'tag:unread' ],
55	\ ]
56
57let s:notmuch_date_format_default = '%d.%m.%y'
58let s:notmuch_datetime_format_default = '%d.%m.%y %H:%M:%S'
59let s:notmuch_reader_default = 'mutt -f %s'
60let s:notmuch_sendmail_default = 'sendmail'
61let s:notmuch_folders_count_threads_default = 0
62let s:notmuch_compose_start_insert_default = 1
63
64function! s:new_file_buffer(type, fname)
65	exec printf('edit %s', a:fname)
66	execute printf('set filetype=notmuch-%s', a:type)
67	execute printf('set syntax=notmuch-%s', a:type)
68	ruby $curbuf.init(VIM::evaluate('a:type'))
69endfunction
70
71function! s:on_compose_delete()
72	if b:compose_done
73		return
74	endif
75	if input('[s]end/[q]uit? ') =~ '^s'
76		call s:compose_send()
77	endif
78endfunction
79
80"" actions
81
82function! s:compose_quit()
83	let b:compose_done = 1
84	call s:kill_this_buffer()
85endfunction
86
87function! s:compose_send()
88	let b:compose_done = 1
89	let fname = expand('%')
90	let lines = getline(5, '$')
91
92ruby << EOF
93	# Generate proper mail to send
94	text = VIM::evaluate('lines').join("\n")
95	fname = VIM::evaluate('fname')
96	transport = Mail.new(text)
97	transport.message_id = generate_message_id
98	transport.charset = 'utf-8'
99	File.write(fname, transport.to_s)
100EOF
101
102	let cmdtxt = g:notmuch_sendmail . ' -t -f ' . s:reply_from . ' < ' . fname
103	let out = system(cmdtxt)
104	let err = v:shell_error
105	if err
106		echohl Error
107		echo 'Eeek! unable to send mail'
108		echo out
109		echohl None
110		return
111	endif
112	call delete(fname)
113	echo 'Mail sent successfully.'
114	call s:kill_this_buffer()
115endfunction
116
117function! s:show_next_msg()
118ruby << EOF
119	r, c = $curwin.cursor
120	n = $curbuf.line_number
121	i = $messages.index { |m| n >= m.start && n <= m.end }
122	m = $messages[i + 1]
123	if m
124		r = m.body_start + 1
125		VIM::command("normal #{m.start}zt")
126		$curwin.cursor = r, c
127	end
128EOF
129endfunction
130
131function! s:show_reply()
132	ruby open_reply get_message.mail
133	let b:compose_done = 0
134	call s:set_map(g:notmuch_compose_maps)
135	autocmd BufDelete <buffer> call s:on_compose_delete()
136	if g:notmuch_compose_start_insert
137		startinsert!
138	end
139endfunction
140
141function! s:compose()
142	ruby open_compose
143	let b:compose_done = 0
144	call s:set_map(g:notmuch_compose_maps)
145	autocmd BufDelete <buffer> call s:on_compose_delete()
146	if g:notmuch_compose_start_insert
147		startinsert!
148	end
149endfunction
150
151function! s:show_info()
152	ruby vim_puts get_message.inspect
153endfunction
154
155function! s:show_extract_msg()
156ruby << EOF
157	m = get_message
158	m.mail.attachments.each do |a|
159		File.open(a.filename, 'w') do |f|
160			f.write a.body.decoded
161			print "Extracted '#{a.filename}'"
162		end
163	end
164EOF
165endfunction
166
167function! s:show_open_msg()
168ruby << EOF
169	m = get_message
170	mbox = File.expand_path('~/.notmuch/vim_mbox')
171	cmd = VIM::evaluate('g:notmuch_reader') % mbox
172	system "notmuch show --format=mbox id:#{m.message_id} > #{mbox} && #{cmd}"
173EOF
174endfunction
175
176function! s:show_save_msg()
177	let file = input('File name: ')
178ruby << EOF
179	file = VIM::evaluate('file')
180	m = get_message
181	system "notmuch show --format=mbox id:#{m.message_id} > #{file}"
182EOF
183endfunction
184
185function! s:show_save_patches()
186ruby << EOF
187	q = $curbuf.query($cur_thread)
188	t = q.search_threads.first
189	n = 0
190	t.toplevel_messages.first.replies.each do |m|
191		next if not m['subject'] =~ /^\[PATCH.*\]/
192		file = "%04d.patch" % [n += 1]
193		system "notmuch show --format=mbox id:#{m.message_id} > #{file}"
194	end
195	vim_puts "Saved #{n} patches"
196EOF
197endfunction
198
199function! s:show_tag(intags)
200	if empty(a:intags)
201		let tags = input('tags: ')
202	else
203		let tags = a:intags
204	endif
205	ruby do_tag(get_cur_view, VIM::evaluate('l:tags'))
206	call s:show_next_thread()
207endfunction
208
209function! s:search_search_prompt()
210	let text = input('Search: ')
211	if text == ""
212	  return
213	endif
214	setlocal modifiable
215ruby << EOF
216	$cur_search = VIM::evaluate('text')
217	$curbuf.reopen
218	search_render($cur_search)
219EOF
220	setlocal nomodifiable
221endfunction
222
223function! s:search_info()
224	ruby vim_puts get_thread_id
225endfunction
226
227function! s:search_refresh()
228	setlocal modifiable
229	ruby $curbuf.reopen
230	ruby search_render($cur_search)
231	setlocal nomodifiable
232endfunction
233
234function! s:search_tag(intags)
235	if empty(a:intags)
236		let tags = input('tags: ')
237	else
238		let tags = a:intags
239	endif
240	ruby do_tag(get_thread_id, VIM::evaluate('l:tags'))
241	norm j
242endfunction
243
244function! s:folders_search_prompt()
245	let text = input('Search: ')
246	call s:search(text)
247endfunction
248
249function! s:folders_refresh()
250	setlocal modifiable
251	ruby $curbuf.reopen
252	ruby folders_render()
253	setlocal nomodifiable
254endfunction
255
256"" basic
257
258function! s:show_cursor_moved()
259ruby << EOF
260	if $render.is_ready?
261		VIM::command('setlocal modifiable')
262		$render.do_next
263		VIM::command('setlocal nomodifiable')
264	end
265EOF
266endfunction
267
268function! s:show_next_thread()
269	call s:kill_this_buffer()
270	if line('.') != line('$')
271		norm j
272		call s:search_show_thread(0)
273	else
274		echo 'No more messages.'
275	endif
276endfunction
277
278function! s:kill_this_buffer()
279ruby << EOF
280	$curbuf.close
281	VIM::command("bdelete!")
282EOF
283endfunction
284
285function! s:set_map(maps)
286	nmapclear <buffer>
287	for [key, code] in items(a:maps)
288		let cmd = printf(":call <SID>%s<CR>", code)
289		exec printf('nnoremap <buffer> %s %s', key, cmd)
290	endfor
291endfunction
292
293function! s:new_buffer(type)
294	enew
295	setlocal buftype=nofile bufhidden=hide
296	keepjumps 0d
297	execute printf('set filetype=notmuch-%s', a:type)
298	execute printf('set syntax=notmuch-%s', a:type)
299	ruby $curbuf.init(VIM::evaluate('a:type'))
300endfunction
301
302function! s:set_menu_buffer()
303	setlocal nomodifiable
304	setlocal cursorline
305	setlocal nowrap
306endfunction
307
308"" main
309
310function! s:show(thread_id)
311	call s:new_buffer('show')
312	setlocal modifiable
313ruby << EOF
314	thread_id = VIM::evaluate('a:thread_id')
315	$cur_thread = thread_id
316	$messages.clear
317	$curbuf.render do |b|
318		q = $curbuf.query(get_cur_view)
319		q.sort = Notmuch::SORT_OLDEST_FIRST
320		$exclude_tags.each { |t|
321			q.add_tag_exclude(t)
322		}
323		msgs = q.search_messages
324		msgs.each do |msg|
325			m = Mail.read(msg.filename)
326			part = m.find_first_text
327			nm_m = Message.new(msg, m)
328			$messages << nm_m
329			date_fmt = VIM::evaluate('g:notmuch_datetime_format')
330			date = Time.at(msg.date).strftime(date_fmt)
331			nm_m.start = b.count
332			b << "%s %s (%s)" % [msg['from'], date, msg.tags]
333			b << "Subject: %s" % [msg['subject']]
334			b << "To: %s" % msg['to']
335			b << "Cc: %s" % msg['cc']
336			b << "Date: %s" % msg['date']
337			nm_m.body_start = b.count
338			b << "--- %s ---" % part.mime_type
339			part.convert.each_line do |l|
340				b << l.chomp
341			end
342			b << ""
343			nm_m.end = b.count
344		end
345		b.delete(b.count)
346	end
347	$messages.each_with_index do |msg, i|
348		VIM::command("syntax region nmShowMsg#{i}Desc start='\\%%%il' end='\\%%%il' contains=@nmShowMsgDesc" % [msg.start, msg.start + 1])
349		VIM::command("syntax region nmShowMsg#{i}Head start='\\%%%il' end='\\%%%il' contains=@nmShowMsgHead" % [msg.start + 1, msg.body_start])
350		VIM::command("syntax region nmShowMsg#{i}Body start='\\%%%il' end='\\%%%dl' contains=@nmShowMsgBody" % [msg.body_start, msg.end])
351	end
352EOF
353	setlocal nomodifiable
354	call s:set_map(g:notmuch_show_maps)
355endfunction
356
357function! s:search_show_thread(mode)
358ruby << EOF
359	mode = VIM::evaluate('a:mode')
360	id = get_thread_id
361	case mode
362	when 0;
363	when 1; $cur_filter = nil
364	when 2; $cur_filter = $cur_search
365	end
366	VIM::command("call s:show('#{id}')")
367EOF
368endfunction
369
370function! s:search(search)
371	call s:new_buffer('search')
372ruby << EOF
373	$cur_search = VIM::evaluate('a:search')
374	search_render($cur_search)
375EOF
376	call s:set_menu_buffer()
377	call s:set_map(g:notmuch_search_maps)
378	autocmd CursorMoved <buffer> call s:show_cursor_moved()
379endfunction
380
381function! s:folders_show_search()
382ruby << EOF
383	n = $curbuf.line_number
384	s = $searches[n - 1]
385	VIM::command("call s:search('#{s}')")
386EOF
387endfunction
388
389function! s:folders()
390	call s:new_buffer('folders')
391	ruby folders_render()
392	call s:set_menu_buffer()
393	call s:set_map(g:notmuch_folders_maps)
394endfunction
395
396"" root
397
398function! s:set_defaults()
399	if !exists('g:notmuch_date_format')
400		if exists('g:notmuch_rb_date_format')
401			let g:notmuch_date_format = g:notmuch_rb_date_format
402		else
403			let g:notmuch_date_format = s:notmuch_date_format_default
404		endif
405	endif
406
407	if !exists('g:notmuch_datetime_format')
408		if exists('g:notmuch_rb_datetime_format')
409			let g:notmuch_datetime_format = g:notmuch_rb_datetime_format
410		else
411			let g:notmuch_datetime_format = s:notmuch_datetime_format_default
412		endif
413	endif
414
415	if !exists('g:notmuch_reader')
416		if exists('g:notmuch_rb_reader')
417			let g:notmuch_reader = g:notmuch_rb_reader
418		else
419			let g:notmuch_reader = s:notmuch_reader_default
420		endif
421	endif
422
423	if !exists('g:notmuch_sendmail')
424		if exists('g:notmuch_rb_sendmail')
425			let g:notmuch_sendmail = g:notmuch_rb_sendmail
426		else
427			let g:notmuch_sendmail = s:notmuch_sendmail_default
428		endif
429	endif
430
431	if !exists('g:notmuch_folders_count_threads')
432		if exists('g:notmuch_rb_count_threads')
433			let g:notmuch_count_threads = g:notmuch_rb_count_threads
434		else
435			let g:notmuch_folders_count_threads = s:notmuch_folders_count_threads_default
436		endif
437	endif
438
439	if !exists('g:notmuch_compose_start_insert')
440		let g:notmuch_compose_start_insert = s:notmuch_compose_start_insert_default
441	endif
442
443	if !exists('g:notmuch_custom_search_maps') && exists('g:notmuch_rb_custom_search_maps')
444		let g:notmuch_custom_search_maps = g:notmuch_rb_custom_search_maps
445	endif
446
447	if !exists('g:notmuch_custom_show_maps') && exists('g:notmuch_rb_custom_show_maps')
448		let g:notmuch_custom_show_maps = g:notmuch_rb_custom_show_maps
449	endif
450
451	if exists('g:notmuch_custom_search_maps')
452		call extend(g:notmuch_search_maps, g:notmuch_custom_search_maps)
453	endif
454
455	if exists('g:notmuch_custom_show_maps')
456		call extend(g:notmuch_show_maps, g:notmuch_custom_show_maps)
457	endif
458
459	if !exists('g:notmuch_folders')
460		if exists('g:notmuch_rb_folders')
461			let g:notmuch_folders = g:notmuch_rb_folders
462		else
463			let g:notmuch_folders = s:notmuch_folders_default
464		endif
465	endif
466endfunction
467
468function! s:NotMuch(...)
469	call s:set_defaults()
470
471ruby << EOF
472	require 'notmuch'
473	require 'rubygems'
474	require 'tempfile'
475	require 'socket'
476	begin
477		require 'mail'
478	rescue LoadError
479	end
480
481	$db_name = nil
482	$email = $email_name = $email_address = nil
483	$exclude_tags = []
484	$searches = []
485	$threads = []
486	$messages = []
487	$mail_installed = defined?(Mail)
488
489	def get_config_item(item)
490		result = ''
491		IO.popen(['notmuch', 'config', 'get', item]) { |out|
492			result = out.read
493		}
494		return result.rstrip
495	end
496
497	def get_config
498		$db_name = get_config_item('database.path')
499		$email_name = get_config_item('user.name')
500		$email_address = get_config_item('user.primary_email')
501		$email_name = get_config_item('user.name')
502		$email = "%s <%s>" % [$email_name, $email_address]
503		ignore_tags = get_config_item('search.exclude_tags')
504		$exclude_tags = ignore_tags.split("\n")
505	end
506
507	def vim_puts(s)
508		VIM::command("echo '#{s.to_s}'")
509	end
510
511	def vim_p(s)
512		VIM::command("echo '#{s.inspect}'")
513	end
514
515	def author_filter(a)
516		# TODO email format, aliases
517		a.strip!
518		a.gsub!(/[\.@].*/, '')
519		a.gsub!(/^ext /, '')
520		a.gsub!(/ \(.*\)/, '')
521		a
522	end
523
524	def get_thread_id
525		n = $curbuf.line_number - 1
526		return "thread:%s" % $threads[n]
527	end
528
529	def get_message
530		n = $curbuf.line_number
531		return $messages.find { |m| n >= m.start && n <= m.end }
532	end
533
534	def get_cur_view
535		if $cur_filter
536			return "#{$cur_thread} and (#{$cur_filter})"
537		else
538			return $cur_thread
539		end
540	end
541
542	def generate_message_id
543		t = Time.now
544		random_tag = sprintf('%x%x_%x%x%x',
545			t.to_i, t.tv_usec,
546			$$, Thread.current.object_id.abs, rand(255))
547		return "<#{random_tag}@#{Socket.gethostname}.notmuch>"
548	end
549
550	def open_compose_helper(lines, cur)
551		help_lines = [
552			'Notmuch-Help: Type in your message here; to help you use these bindings:',
553			'Notmuch-Help:   ,s    - send the message (Notmuch-Help lines will be removed)',
554			'Notmuch-Help:   ,q    - abort the message',
555			]
556
557		dir = File.expand_path('~/.notmuch/compose')
558		FileUtils.mkdir_p(dir)
559		Tempfile.open(['nm-', '.mail'], dir) do |f|
560			f.puts(help_lines)
561			f.puts
562			f.puts(lines)
563
564			sig_file = File.expand_path('~/.signature')
565			if File.exists?(sig_file)
566				f.puts("-- ")
567				f.write(File.read(sig_file))
568			end
569
570			f.flush
571
572			cur += help_lines.size + 1
573
574			VIM::command("let s:reply_from='%s'" % $email_address)
575			VIM::command("call s:new_file_buffer('compose', '#{f.path}')")
576			VIM::command("call cursor(#{cur}, 0)")
577		end
578	end
579
580	def open_reply(orig)
581		reply = orig.reply do |m|
582			# fix headers
583			if not m[:reply_to]
584				m.to = [orig[:from].to_s, orig[:to].to_s]
585			end
586			m.cc = orig[:cc]
587			m.from = $email
588			m.charset = 'utf-8'
589		end
590
591		lines = []
592
593		body_lines = []
594		if $mail_installed
595			addr = Mail::Address.new(orig[:from].value)
596			name = addr.name
597			name = addr.local + "@" if name.nil? && !addr.local.nil?
598		else
599			name = orig[:from]
600		end
601		name = "somebody" if name.nil?
602
603		body_lines << "%s wrote:" % name
604		part = orig.find_first_text
605		part.convert.each_line do |l|
606			body_lines << "> %s" % l.chomp
607		end
608		body_lines << ""
609		body_lines << ""
610		body_lines << ""
611
612		reply.body = body_lines.join("\n")
613
614		lines += reply.present.lines.map { |e| e.chomp }
615		lines << ""
616
617		cur = lines.count - 1
618
619		open_compose_helper(lines, cur)
620	end
621
622	def open_compose()
623		lines = []
624
625		lines << "From: #{$email}"
626		lines << "To: "
627		cur = lines.count
628
629		lines << "Cc: "
630		lines << "Bcc: "
631		lines << "Subject: "
632		lines << ""
633		lines << ""
634		lines << ""
635
636		open_compose_helper(lines, cur)
637	end
638
639	def folders_render()
640		$curbuf.render do |b|
641			folders = VIM::evaluate('g:notmuch_folders')
642			count_threads = VIM::evaluate('g:notmuch_folders_count_threads') == 1
643			$searches.clear
644			folders.each do |name, search|
645				q = $curbuf.query(search)
646				$exclude_tags.each { |t|
647					q.add_tag_exclude(t)
648				}
649				$searches << search
650				count = count_threads ? q.count_threads : q.count_messages
651				b << "%9d %-20s (%s)" % [count, name, search]
652			end
653		end
654	end
655
656	def search_render(search)
657		date_fmt = VIM::evaluate('g:notmuch_date_format')
658		q = $curbuf.query(search)
659		q.sort = Notmuch::SORT_NEWEST_FIRST
660		$exclude_tags.each { |t|
661			q.add_tag_exclude(t)
662		}
663		$threads.clear
664		t = q.search_threads
665
666		$render = $curbuf.render_staged(t) do |b, items|
667			items.each do |e|
668				authors = e.authors.to_utf8.split(/[,|]/).map { |a| author_filter(a) }.join(",")
669				date = Time.at(e.newest_date).strftime(date_fmt)
670				subject = e.messages.first['subject']
671				if $mail_installed
672					subject = Mail::Field.parse("Subject: " + subject).to_s
673				else
674					subject = subject.force_encoding('utf-8')
675				end
676				b << "%-12s %3s %-20.20s | %s (%s)" % [date, e.matched_messages, authors, subject, e.tags]
677				$threads << e.thread_id
678			end
679		end
680	end
681
682	def do_tag(filter, tags)
683		$curbuf.do_write do |db|
684			q = db.query(filter)
685			q.search_messages.each do |e|
686				e.freeze
687				tags.split.each do |t|
688					case t
689					when /^-(.*)/
690						e.remove_tag($1)
691					when /^\+(.*)/
692						e.add_tag($1)
693					when /^([^\+^-].*)/
694						e.add_tag($1)
695					end
696				end
697				e.thaw
698				e.tags_to_maildir_flags
699			end
700			q.destroy!
701		end
702	end
703
704	module DbHelper
705		def init(name)
706			@name = name
707			@db = Notmuch::Database.new($db_name)
708			@queries = []
709		end
710
711		def query(*args)
712			q = @db.query(*args)
713			@queries << q
714			q
715		end
716
717		def close
718			@queries.delete_if { |q| ! q.destroy! }
719			@db.close
720		end
721
722		def reopen
723			close if @db
724			@db = Notmuch::Database.new($db_name)
725		end
726
727		def do_write
728			db = Notmuch::Database.new($db_name, :mode => Notmuch::MODE_READ_WRITE)
729			begin
730				yield db
731			ensure
732				db.close
733			end
734		end
735	end
736
737	class Message
738		attr_accessor :start, :body_start, :end
739		attr_reader :message_id, :filename, :mail
740
741		def initialize(msg, mail)
742			@message_id = msg.message_id
743			@filename = msg.filename
744			@mail = mail
745			@start = 0
746			@end = 0
747			mail.import_headers(msg) if not $mail_installed
748		end
749
750		def to_s
751			"id:%s" % @message_id
752		end
753
754		def inspect
755			"id:%s, file:%s" % [@message_id, @filename]
756		end
757	end
758
759	class StagedRender
760		def initialize(buffer, enumerable, block)
761			@b = buffer
762			@enumerable = enumerable
763			@block = block
764			@last_render = 0
765
766			@b.render { do_next }
767		end
768
769		def is_ready?
770			@last_render - @b.line_number <= $curwin.height
771		end
772
773		def do_next
774			items = @enumerable.take($curwin.height * 2)
775			return if items.empty?
776			@block.call @b, items
777			@last_render = @b.count
778		end
779	end
780
781	class VIM::Buffer
782		include DbHelper
783
784		def <<(a)
785			append(count(), a)
786		end
787
788		def render_staged(enumerable, &block)
789			StagedRender.new(self, enumerable, block)
790		end
791
792		def render
793			old_count = count
794			yield self
795			(1..old_count).each do
796				delete(1)
797			end
798		end
799	end
800
801	class Notmuch::Tags
802		def to_s
803			to_a.join(" ")
804		end
805	end
806
807	class Notmuch::Message
808		def to_s
809			"id:%s" % message_id
810		end
811	end
812
813	# workaround for bug in vim's ruby
814	class Object
815		def flush
816		end
817	end
818
819	module SimpleMessage
820		class Header < Array
821			def self.parse(string)
822				return nil if string.empty?
823				return Header.new(string.split(/,\s+/))
824			end
825
826			def to_s
827				self.join(', ')
828			end
829		end
830
831		def initialize(string = nil)
832			@raw_source = string
833			@body = nil
834			@headers = {}
835
836			return if not string
837
838			if string =~ /(.*?(\r\n|\n))\2/m
839				head, body = $1, $' || '', $2
840			else
841				head, body = string, ''
842			end
843			@body = body
844		end
845
846		def [](name)
847			@headers[name.to_sym]
848		end
849
850		def []=(name, value)
851			@headers[name.to_sym] = value
852		end
853
854		def format_header(value)
855			value.to_s.tr('_', '-').gsub(/(\w+)/) { $1.capitalize }
856		end
857
858		def to_s
859			buffer = ''
860			@headers.each do |key, value|
861				buffer << "%s: %s\r\n" %
862					[format_header(key), value]
863			end
864			buffer << "\r\n"
865			buffer << @body
866			buffer
867		end
868
869		def body=(value)
870			@body = value
871		end
872
873		def from
874			@headers[:from]
875		end
876
877		def decoded
878			@body
879		end
880
881		def mime_type
882			'text/plain'
883		end
884
885		def multipart?
886			false
887		end
888
889		def reply
890			r = Mail::Message.new
891			r[:from] = self[:to]
892			r[:to] = self[:from]
893			r[:cc] = self[:cc]
894			r[:in_reply_to] = self[:message_id]
895			r[:references] = self[:references]
896			r
897		end
898
899		HEADERS = [ :from, :to, :cc, :references, :in_reply_to, :reply_to, :message_id ]
900
901		def import_headers(m)
902			HEADERS.each do |e|
903				dashed = format_header(e)
904				@headers[e] = Header.parse(m[dashed])
905			end
906		end
907	end
908
909	module Mail
910
911		if not $mail_installed
912			puts "WARNING: Install the 'mail' gem, without it support is limited"
913
914			def self.read(filename)
915				Message.new(File.open(filename, 'rb') { |f| f.read })
916			end
917
918			class Message
919				include SimpleMessage
920			end
921		end
922
923		class Message
924
925			def find_first_text
926				return self if not multipart?
927				return text_part || html_part
928			end
929
930			def convert
931				if mime_type != "text/html"
932					text = decoded
933				else
934					IO.popen(VIM::evaluate('exists("g:notmuch_html_converter") ? ' +
935							'g:notmuch_html_converter : "elinks --dump"'), "w+") do |pipe|
936						pipe.write(decode_body)
937						pipe.close_write
938						text = pipe.read
939					end
940				end
941				text
942			end
943
944			def present
945				buffer = ''
946				header.fields.each do |f|
947					buffer << "%s: %s\r\n" % [f.name, f.to_s]
948				end
949				buffer << "\r\n"
950				buffer << body.to_s
951				buffer
952			end
953		end
954	end
955
956	class String
957		def to_utf8
958			RUBY_VERSION >= "1.9" ? force_encoding('utf-8') : self
959		end
960	end
961
962	get_config
963EOF
964	if a:0
965	  call s:search(join(a:000))
966	else
967	  call s:folders()
968	endif
969endfunction
970
971command -nargs=* NotMuch call s:NotMuch(<f-args>)
972
973" vim: set noexpandtab:
974