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