]> git.notmuchmail.org Git - notmuch/blob - vim/notmuch.vim
64038eb5d498cd1b0c4ff31de8f8a88a5dd6367b
[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         setlocal modifiable
212 ruby << EOF
213         $cur_search = VIM::evaluate('text')
214         $curbuf.reopen
215         search_render($cur_search)
216 EOF
217         setlocal nomodifiable
218 endfunction
219
220 function! s:search_info()
221         ruby vim_puts get_thread_id
222 endfunction
223
224 function! s:search_refresh()
225         setlocal modifiable
226         ruby $curbuf.reopen
227         ruby search_render($cur_search)
228         setlocal nomodifiable
229 endfunction
230
231 function! s:search_tag(intags)
232         if empty(a:intags)
233                 let tags = input('tags: ')
234         else
235                 let tags = a:intags
236         endif
237         ruby do_tag(get_thread_id, VIM::evaluate('l:tags'))
238         norm j
239 endfunction
240
241 function! s:folders_search_prompt()
242         let text = input('Search: ')
243         call s:search(text)
244 endfunction
245
246 function! s:folders_refresh()
247         setlocal modifiable
248         ruby $curbuf.reopen
249         ruby folders_render()
250         setlocal nomodifiable
251 endfunction
252
253 "" basic
254
255 function! s:show_cursor_moved()
256 ruby << EOF
257         if $render.is_ready?
258                 VIM::command('setlocal modifiable')
259                 $render.do_next
260                 VIM::command('setlocal nomodifiable')
261         end
262 EOF
263 endfunction
264
265 function! s:show_next_thread()
266         call s:kill_this_buffer()
267         if line('.') != line('$')
268                 norm j
269                 call s:search_show_thread(0)
270         else
271                 echo 'No more messages.'
272         endif
273 endfunction
274
275 function! s:kill_this_buffer()
276 ruby << EOF
277         if $buf_queue.size > 1
278                 $curbuf.close
279                 VIM::command("bdelete!")
280                 $buf_queue.pop
281                 b = $buf_queue.last
282                 VIM::command("buffer #{b}") if b
283         end
284 EOF
285 endfunction
286
287 function! s:set_map(maps)
288         nmapclear <buffer>
289         for [key, code] in items(a:maps)
290                 let cmd = printf(":call <SID>%s<CR>", code)
291                 exec printf('nnoremap <buffer> %s %s', key, cmd)
292         endfor
293 endfunction
294
295 function! s:new_buffer(type)
296         enew
297         setlocal buftype=nofile bufhidden=hide
298         keepjumps 0d
299         execute printf('set filetype=notmuch-%s', a:type)
300         execute printf('set syntax=notmuch-%s', a:type)
301         ruby $curbuf.init(VIM::evaluate('a:type'))
302         ruby $buf_queue.push($curbuf.number)
303 endfunction
304
305 function! s:set_menu_buffer()
306         setlocal nomodifiable
307         setlocal cursorline
308         setlocal nowrap
309 endfunction
310
311 "" main
312
313 function! s:show(thread_id)
314         call s:new_buffer('show')
315         setlocal modifiable
316 ruby << EOF
317         thread_id = VIM::evaluate('a:thread_id')
318         $cur_thread = thread_id
319         $messages.clear
320         $curbuf.render do |b|
321                 q = $curbuf.query(get_cur_view)
322                 q.sort = Notmuch::SORT_OLDEST_FIRST
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_rb_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
352 EOF
353         setlocal nomodifiable
354         call s:set_map(g:notmuch_rb_show_maps)
355 endfunction
356
357 function! s:search_show_thread(mode)
358 ruby << 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}')")
367 EOF
368 endfunction
369
370 function! s:search(search)
371         call s:new_buffer('search')
372 ruby << EOF
373         $cur_search = VIM::evaluate('a:search')
374         search_render($cur_search)
375 EOF
376         call s:set_menu_buffer()
377         call s:set_map(g:notmuch_rb_search_maps)
378         autocmd CursorMoved <buffer> call s:show_cursor_moved()
379 endfunction
380
381 function! s:folders_show_search()
382 ruby << EOF
383         n = $curbuf.line_number
384         s = $searches[n - 1]
385         VIM::command("call s:search('#{s}')")
386 EOF
387 endfunction
388
389 function! 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_rb_folders_maps)
394 endfunction
395
396 "" root
397
398 function! s:set_defaults()
399         if exists('g:notmuch_rb_custom_search_maps')
400                 call extend(g:notmuch_rb_search_maps, g:notmuch_rb_custom_search_maps)
401         endif
402
403         if exists('g:notmuch_rb_custom_show_maps')
404                 call extend(g:notmuch_rb_show_maps, g:notmuch_rb_custom_show_maps)
405         endif
406
407         " TODO for now lets check the old folders too
408         if !exists('g:notmuch_rb_folders')
409                 if exists('g:notmuch_folders')
410                         let g:notmuch_rb_folders = g:notmuch_folders
411                 else
412                         let g:notmuch_rb_folders = s:notmuch_rb_folders_default
413                 endif
414         endif
415 endfunction
416
417 function! s:NotMuch(...)
418         call s:set_defaults()
419
420 ruby << EOF
421         require 'notmuch'
422         require 'rubygems'
423         require 'tempfile'
424         require 'socket'
425         begin
426                 require 'mail'
427         rescue LoadError
428         end
429
430         $db_name = nil
431         $email = $email_name = $email_address = nil
432         $searches = []
433         $buf_queue = []
434         $threads = []
435         $messages = []
436         $config = {}
437         $mail_installed = defined?(Mail)
438
439         def get_config
440                 group = nil
441                 config = ENV['NOTMUCH_CONFIG'] || '~/.notmuch-config'
442                 File.open(File.expand_path(config)).each do |l|
443                         l.chomp!
444                         case l
445                         when /^\[(.*)\]$/
446                                 group = $1
447                         when ''
448                         when /^(.*)=(.*)$/
449                                 key = "%s.%s" % [group, $1]
450                                 value = $2
451                                 $config[key] = value
452                         end
453                 end
454
455                 $db_name = $config['database.path']
456                 $email_name = $config['user.name']
457                 $email_address = $config['user.primary_email']
458                 $email = "%s <%s>" % [$email_name, $email_address]
459         end
460
461         def vim_puts(s)
462                 VIM::command("echo '#{s.to_s}'")
463         end
464
465         def vim_p(s)
466                 VIM::command("echo '#{s.inspect}'")
467         end
468
469         def author_filter(a)
470                 # TODO email format, aliases
471                 a.strip!
472                 a.gsub!(/[\.@].*/, '')
473                 a.gsub!(/^ext /, '')
474                 a.gsub!(/ \(.*\)/, '')
475                 a
476         end
477
478         def get_thread_id
479                 n = $curbuf.line_number - 1
480                 return "thread:%s" % $threads[n]
481         end
482
483         def get_message
484                 n = $curbuf.line_number
485                 return $messages.find { |m| n >= m.start && n <= m.end }
486         end
487
488         def get_cur_view
489                 if $cur_filter
490                         return "#{$cur_thread} and (#{$cur_filter})"
491                 else
492                         return $cur_thread
493                 end
494         end
495
496         def generate_message_id
497                 t = Time.now
498                 random_tag = sprintf('%x%x_%x%x%x',
499                         t.to_i, t.tv_usec,
500                         $$, Thread.current.object_id.abs, rand(255))
501                 return "<#{random_tag}@#{Socket.gethostname}.notmuch>"
502         end
503
504         def open_reply(orig)
505                 help_lines = [
506                         'Notmuch-Help: Type in your message here; to help you use these bindings:',
507                         'Notmuch-Help:   ,s    - send the message (Notmuch-Help lines will be removed)',
508                         'Notmuch-Help:   ,q    - abort the message',
509                         ]
510                 reply = orig.reply do |m|
511                         # fix headers
512                         if not m[:reply_to]
513                                 m.to = [orig[:from].to_s, orig[:to].to_s]
514                         end
515                         m.cc = orig[:cc]
516                         m.from = $email
517                         m.message_id = generate_message_id
518                         m.charset = 'utf-8'
519                         m.content_transfer_encoding = '7bit'
520                 end
521
522                 dir = File.expand_path('~/.notmuch/compose')
523                 FileUtils.mkdir_p(dir)
524                 Tempfile.open(['nm-', '.mail'], dir) do |f|
525                         lines = []
526
527                         lines += help_lines
528                         lines << ''
529
530                         body_lines = []
531                         if $mail_installed
532                                 addr = Mail::Address.new(orig[:from].value)
533                                 name = addr.name
534                                 name = addr.local + "@" if name.nil? && !addr.local.nil?
535                         else
536                                 name = orig[:from]
537                         end
538                         name = "somebody" if name.nil?
539
540                         body_lines << "%s wrote:" % name
541                         part = orig.find_first_text
542                         part.convert.each_line do |l|
543                                 body_lines << "> %s" % l.chomp
544                         end
545                         body_lines << ""
546                         body_lines << ""
547                         body_lines << ""
548
549                         reply.body = body_lines.join("\n")
550
551                         lines += reply.to_s.lines.map { |e| e.chomp }
552                         lines << ""
553
554                         old_count = lines.count - 1
555
556                         f.puts(lines)
557
558                         sig_file = File.expand_path('~/.signature')
559                         if File.exists?(sig_file)
560                                 f.puts("-- ")
561                                 f.write(File.read(sig_file))
562                         end
563
564                         f.flush
565
566                         VIM::command("let s:reply_from='%s'" % reply.from.first.to_s)
567                         VIM::command("call s:new_file_buffer('compose', '#{f.path}')")
568                         VIM::command("call cursor(#{old_count}, 0)")
569                 end
570         end
571
572         def folders_render()
573                 $curbuf.render do |b|
574                         folders = VIM::evaluate('g:notmuch_rb_folders')
575                         count_threads = VIM::evaluate('g:notmuch_rb_folders_count_threads')
576                         $searches.clear
577                         folders.each do |name, search|
578                                 q = $curbuf.query(search)
579                                 $searches << search
580                                 count = count_threads ? q.search_threads.count : q.search_messages.count
581                                 b << "%9d %-20s (%s)" % [count, name, search]
582                         end
583                 end
584         end
585
586         def search_render(search)
587                 date_fmt = VIM::evaluate('g:notmuch_rb_date_format')
588                 q = $curbuf.query(search)
589                 q.sort = Notmuch::SORT_NEWEST_FIRST
590                 $threads.clear
591                 t = q.search_threads
592
593                 $render = $curbuf.render_staged(t) do |b, items|
594                         items.each do |e|
595                                 authors = e.authors.to_utf8.split(/[,|]/).map { |a| author_filter(a) }.join(",")
596                                 date = Time.at(e.newest_date).strftime(date_fmt)
597                                 subject = e.messages.first['subject']
598                                 if $mail_installed
599                                         subject = Mail::Field.new("Subject: " + subject).to_s
600                                 else
601                                         subject = subject.force_encoding('utf-8')
602                                 end
603                                 b << "%-12s %3s %-20.20s | %s (%s)" % [date, e.matched_messages, authors, subject, e.tags]
604                                 $threads << e.thread_id
605                         end
606                 end
607         end
608
609         def do_tag(filter, tags)
610                 $curbuf.do_write do |db|
611                         q = db.query(filter)
612                         q.search_messages.each do |e|
613                                 e.freeze
614                                 tags.split.each do |t|
615                                         case t
616                                         when /^-(.*)/
617                                                 e.remove_tag($1)
618                                         when /^\+(.*)/
619                                                 e.add_tag($1)
620                                         when /^([^\+^-].*)/
621                                                 e.add_tag($1)
622                                         end
623                                 end
624                                 e.thaw
625                                 e.tags_to_maildir_flags
626                         end
627                         q.destroy!
628                 end
629         end
630
631         module DbHelper
632                 def init(name)
633                         @name = name
634                         @db = Notmuch::Database.new($db_name)
635                         @queries = []
636                 end
637
638                 def query(*args)
639                         q = @db.query(*args)
640                         @queries << q
641                         q
642                 end
643
644                 def close
645                         @queries.delete_if { |q| ! q.destroy! }
646                         @db.close
647                 end
648
649                 def reopen
650                         close if @db
651                         @db = Notmuch::Database.new($db_name)
652                 end
653
654                 def do_write
655                         db = Notmuch::Database.new($db_name, :mode => Notmuch::MODE_READ_WRITE)
656                         begin
657                                 yield db
658                         ensure
659                                 db.close
660                         end
661                 end
662         end
663
664         class Message
665                 attr_accessor :start, :body_start, :end
666                 attr_reader :message_id, :filename, :mail
667
668                 def initialize(msg, mail)
669                         @message_id = msg.message_id
670                         @filename = msg.filename
671                         @mail = mail
672                         @start = 0
673                         @end = 0
674                         mail.import_headers(msg) if not $mail_installed
675                 end
676
677                 def to_s
678                         "id:%s" % @message_id
679                 end
680
681                 def inspect
682                         "id:%s, file:%s" % [@message_id, @filename]
683                 end
684         end
685
686         class StagedRender
687                 def initialize(buffer, enumerable, block)
688                         @b = buffer
689                         @enumerable = enumerable
690                         @block = block
691                         @last_render = 0
692
693                         @b.render { do_next }
694                 end
695
696                 def is_ready?
697                         @last_render - @b.line_number <= $curwin.height
698                 end
699
700                 def do_next
701                         items = @enumerable.take($curwin.height * 2)
702                         return if items.empty?
703                         @block.call @b, items
704                         @last_render = @b.count
705                 end
706         end
707
708         class VIM::Buffer
709                 include DbHelper
710
711                 def <<(a)
712                         append(count(), a)
713                 end
714
715                 def render_staged(enumerable, &block)
716                         StagedRender.new(self, enumerable, block)
717                 end
718
719                 def render
720                         old_count = count
721                         yield self
722                         (1..old_count).each do
723                                 delete(1)
724                         end
725                 end
726         end
727
728         class Notmuch::Tags
729                 def to_s
730                         to_a.join(" ")
731                 end
732         end
733
734         class Notmuch::Message
735                 def to_s
736                         "id:%s" % message_id
737                 end
738         end
739
740         # workaround for bug in vim's ruby
741         class Object
742                 def flush
743                 end
744         end
745
746         module SimpleMessage
747                 class Header < Array
748                         def self.parse(string)
749                                 return nil if string.empty?
750                                 return Header.new(string.split(/,\s+/))
751                         end
752
753                         def to_s
754                                 self.join(', ')
755                         end
756                 end
757
758                 def initialize(string = nil)
759                         @raw_source = string
760                         @body = nil
761                         @headers = {}
762
763                         return if not string
764
765                         if string =~ /(.*?(\r\n|\n))\2/m
766                                 head, body = $1, $' || '', $2
767                         else
768                                 head, body = string, ''
769                         end
770                         @body = body
771                 end
772
773                 def [](name)
774                         @headers[name.to_sym]
775                 end
776
777                 def []=(name, value)
778                         @headers[name.to_sym] = value
779                 end
780
781                 def format_header(value)
782                         value.to_s.tr('_', '-').gsub(/(\w+)/) { $1.capitalize }
783                 end
784
785                 def to_s
786                         buffer = ''
787                         @headers.each do |key, value|
788                                 buffer << "%s: %s\r\n" %
789                                         [format_header(key), value]
790                         end
791                         buffer << "\r\n"
792                         buffer << @body
793                         buffer
794                 end
795
796                 def body=(value)
797                         @body = value
798                 end
799
800                 def from
801                         @headers[:from]
802                 end
803
804                 def decoded
805                         @body
806                 end
807
808                 def mime_type
809                         'text/plain'
810                 end
811
812                 def multipart?
813                         false
814                 end
815
816                 def reply
817                         r = Mail::Message.new
818                         r[:from] = self[:to]
819                         r[:to] = self[:from]
820                         r[:cc] = self[:cc]
821                         r[:in_reply_to] = self[:message_id]
822                         r[:references] = self[:references]
823                         r
824                 end
825
826                 HEADERS = [ :from, :to, :cc, :references, :in_reply_to, :reply_to, :message_id ]
827
828                 def import_headers(m)
829                         HEADERS.each do |e|
830                                 dashed = format_header(e)
831                                 @headers[e] = Header.parse(m[dashed])
832                         end
833                 end
834         end
835
836         module Mail
837
838                 if not $mail_installed
839                         puts "WARNING: Install the 'mail' gem, without it support is limited"
840
841                         def self.read(filename)
842                                 Message.new(File.open(filename, 'rb') { |f| f.read })
843                         end
844
845                         class Message
846                                 include SimpleMessage
847                         end
848                 end
849
850                 class Message
851
852                         def find_first_text
853                                 return self if not multipart?
854                                 return text_part || html_part
855                         end
856
857                         def convert
858                                 if mime_type != "text/html"
859                                         text = decoded
860                                 else
861                                         IO.popen("elinks --dump", "w+") do |pipe|
862                                                 pipe.write(decode_body)
863                                                 pipe.close_write
864                                                 text = pipe.read
865                                         end
866                                 end
867                                 text
868                         end
869                 end
870         end
871
872         class String
873                 def to_utf8
874                         RUBY_VERSION >= "1.9" ? force_encoding('utf-8') : self
875                 end
876         end
877
878         get_config
879 EOF
880         if a:0
881           call s:search(join(a:000))
882         else
883           call s:folders()
884         endif
885 endfunction
886
887 command -nargs=* NotMuch call s:NotMuch(<f-args>)
888
889 " vim: set noexpandtab: