]> git.notmuchmail.org Git - notmuch/blob - vim/notmuch.vim
vim: don't execute search if it's cancelled
[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_reply(orig)
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                 reply = orig.reply do |m|
514                         # fix headers
515                         if not m[:reply_to]
516                                 m.to = [orig[:from].to_s, orig[:to].to_s]
517                         end
518                         m.cc = orig[:cc]
519                         m.from = $email
520                         m.message_id = generate_message_id
521                         m.charset = 'utf-8'
522                         m.content_transfer_encoding = '7bit'
523                 end
524
525                 dir = File.expand_path('~/.notmuch/compose')
526                 FileUtils.mkdir_p(dir)
527                 Tempfile.open(['nm-', '.mail'], dir) do |f|
528                         lines = []
529
530                         lines += help_lines
531                         lines << ''
532
533                         body_lines = []
534                         if $mail_installed
535                                 addr = Mail::Address.new(orig[:from].value)
536                                 name = addr.name
537                                 name = addr.local + "@" if name.nil? && !addr.local.nil?
538                         else
539                                 name = orig[:from]
540                         end
541                         name = "somebody" if name.nil?
542
543                         body_lines << "%s wrote:" % name
544                         part = orig.find_first_text
545                         part.convert.each_line do |l|
546                                 body_lines << "> %s" % l.chomp
547                         end
548                         body_lines << ""
549                         body_lines << ""
550                         body_lines << ""
551
552                         reply.body = body_lines.join("\n")
553
554                         lines += reply.to_s.lines.map { |e| e.chomp }
555                         lines << ""
556
557                         old_count = lines.count - 1
558
559                         f.puts(lines)
560
561                         sig_file = File.expand_path('~/.signature')
562                         if File.exists?(sig_file)
563                                 f.puts("-- ")
564                                 f.write(File.read(sig_file))
565                         end
566
567                         f.flush
568
569                         VIM::command("let s:reply_from='%s'" % reply.from.first.to_s)
570                         VIM::command("call s:new_file_buffer('compose', '#{f.path}')")
571                         VIM::command("call cursor(#{old_count}, 0)")
572                 end
573         end
574
575         def folders_render()
576                 $curbuf.render do |b|
577                         folders = VIM::evaluate('g:notmuch_rb_folders')
578                         count_threads = VIM::evaluate('g:notmuch_rb_folders_count_threads')
579                         $searches.clear
580                         folders.each do |name, search|
581                                 q = $curbuf.query(search)
582                                 $searches << search
583                                 count = count_threads ? q.search_threads.count : q.search_messages.count
584                                 b << "%9d %-20s (%s)" % [count, name, search]
585                         end
586                 end
587         end
588
589         def search_render(search)
590                 date_fmt = VIM::evaluate('g:notmuch_rb_date_format')
591                 q = $curbuf.query(search)
592                 q.sort = Notmuch::SORT_NEWEST_FIRST
593                 $threads.clear
594                 t = q.search_threads
595
596                 $render = $curbuf.render_staged(t) do |b, items|
597                         items.each do |e|
598                                 authors = e.authors.to_utf8.split(/[,|]/).map { |a| author_filter(a) }.join(",")
599                                 date = Time.at(e.newest_date).strftime(date_fmt)
600                                 subject = e.messages.first['subject']
601                                 if $mail_installed
602                                         subject = Mail::Field.new("Subject: " + subject).to_s
603                                 else
604                                         subject = subject.force_encoding('utf-8')
605                                 end
606                                 b << "%-12s %3s %-20.20s | %s (%s)" % [date, e.matched_messages, authors, subject, e.tags]
607                                 $threads << e.thread_id
608                         end
609                 end
610         end
611
612         def do_tag(filter, tags)
613                 $curbuf.do_write do |db|
614                         q = db.query(filter)
615                         q.search_messages.each do |e|
616                                 e.freeze
617                                 tags.split.each do |t|
618                                         case t
619                                         when /^-(.*)/
620                                                 e.remove_tag($1)
621                                         when /^\+(.*)/
622                                                 e.add_tag($1)
623                                         when /^([^\+^-].*)/
624                                                 e.add_tag($1)
625                                         end
626                                 end
627                                 e.thaw
628                                 e.tags_to_maildir_flags
629                         end
630                         q.destroy!
631                 end
632         end
633
634         module DbHelper
635                 def init(name)
636                         @name = name
637                         @db = Notmuch::Database.new($db_name)
638                         @queries = []
639                 end
640
641                 def query(*args)
642                         q = @db.query(*args)
643                         @queries << q
644                         q
645                 end
646
647                 def close
648                         @queries.delete_if { |q| ! q.destroy! }
649                         @db.close
650                 end
651
652                 def reopen
653                         close if @db
654                         @db = Notmuch::Database.new($db_name)
655                 end
656
657                 def do_write
658                         db = Notmuch::Database.new($db_name, :mode => Notmuch::MODE_READ_WRITE)
659                         begin
660                                 yield db
661                         ensure
662                                 db.close
663                         end
664                 end
665         end
666
667         class Message
668                 attr_accessor :start, :body_start, :end
669                 attr_reader :message_id, :filename, :mail
670
671                 def initialize(msg, mail)
672                         @message_id = msg.message_id
673                         @filename = msg.filename
674                         @mail = mail
675                         @start = 0
676                         @end = 0
677                         mail.import_headers(msg) if not $mail_installed
678                 end
679
680                 def to_s
681                         "id:%s" % @message_id
682                 end
683
684                 def inspect
685                         "id:%s, file:%s" % [@message_id, @filename]
686                 end
687         end
688
689         class StagedRender
690                 def initialize(buffer, enumerable, block)
691                         @b = buffer
692                         @enumerable = enumerable
693                         @block = block
694                         @last_render = 0
695
696                         @b.render { do_next }
697                 end
698
699                 def is_ready?
700                         @last_render - @b.line_number <= $curwin.height
701                 end
702
703                 def do_next
704                         items = @enumerable.take($curwin.height * 2)
705                         return if items.empty?
706                         @block.call @b, items
707                         @last_render = @b.count
708                 end
709         end
710
711         class VIM::Buffer
712                 include DbHelper
713
714                 def <<(a)
715                         append(count(), a)
716                 end
717
718                 def render_staged(enumerable, &block)
719                         StagedRender.new(self, enumerable, block)
720                 end
721
722                 def render
723                         old_count = count
724                         yield self
725                         (1..old_count).each do
726                                 delete(1)
727                         end
728                 end
729         end
730
731         class Notmuch::Tags
732                 def to_s
733                         to_a.join(" ")
734                 end
735         end
736
737         class Notmuch::Message
738                 def to_s
739                         "id:%s" % message_id
740                 end
741         end
742
743         # workaround for bug in vim's ruby
744         class Object
745                 def flush
746                 end
747         end
748
749         module SimpleMessage
750                 class Header < Array
751                         def self.parse(string)
752                                 return nil if string.empty?
753                                 return Header.new(string.split(/,\s+/))
754                         end
755
756                         def to_s
757                                 self.join(', ')
758                         end
759                 end
760
761                 def initialize(string = nil)
762                         @raw_source = string
763                         @body = nil
764                         @headers = {}
765
766                         return if not string
767
768                         if string =~ /(.*?(\r\n|\n))\2/m
769                                 head, body = $1, $' || '', $2
770                         else
771                                 head, body = string, ''
772                         end
773                         @body = body
774                 end
775
776                 def [](name)
777                         @headers[name.to_sym]
778                 end
779
780                 def []=(name, value)
781                         @headers[name.to_sym] = value
782                 end
783
784                 def format_header(value)
785                         value.to_s.tr('_', '-').gsub(/(\w+)/) { $1.capitalize }
786                 end
787
788                 def to_s
789                         buffer = ''
790                         @headers.each do |key, value|
791                                 buffer << "%s: %s\r\n" %
792                                         [format_header(key), value]
793                         end
794                         buffer << "\r\n"
795                         buffer << @body
796                         buffer
797                 end
798
799                 def body=(value)
800                         @body = value
801                 end
802
803                 def from
804                         @headers[:from]
805                 end
806
807                 def decoded
808                         @body
809                 end
810
811                 def mime_type
812                         'text/plain'
813                 end
814
815                 def multipart?
816                         false
817                 end
818
819                 def reply
820                         r = Mail::Message.new
821                         r[:from] = self[:to]
822                         r[:to] = self[:from]
823                         r[:cc] = self[:cc]
824                         r[:in_reply_to] = self[:message_id]
825                         r[:references] = self[:references]
826                         r
827                 end
828
829                 HEADERS = [ :from, :to, :cc, :references, :in_reply_to, :reply_to, :message_id ]
830
831                 def import_headers(m)
832                         HEADERS.each do |e|
833                                 dashed = format_header(e)
834                                 @headers[e] = Header.parse(m[dashed])
835                         end
836                 end
837         end
838
839         module Mail
840
841                 if not $mail_installed
842                         puts "WARNING: Install the 'mail' gem, without it support is limited"
843
844                         def self.read(filename)
845                                 Message.new(File.open(filename, 'rb') { |f| f.read })
846                         end
847
848                         class Message
849                                 include SimpleMessage
850                         end
851                 end
852
853                 class Message
854
855                         def find_first_text
856                                 return self if not multipart?
857                                 return text_part || html_part
858                         end
859
860                         def convert
861                                 if mime_type != "text/html"
862                                         text = decoded
863                                 else
864                                         IO.popen("elinks --dump", "w+") do |pipe|
865                                                 pipe.write(decode_body)
866                                                 pipe.close_write
867                                                 text = pipe.read
868                                         end
869                                 end
870                                 text
871                         end
872                 end
873         end
874
875         class String
876                 def to_utf8
877                         RUBY_VERSION >= "1.9" ? force_encoding('utf-8') : self
878                 end
879         end
880
881         get_config
882 EOF
883         if a:0
884           call s:search(join(a:000))
885         else
886           call s:folders()
887         endif
888 endfunction
889
890 command -nargs=* NotMuch call s:NotMuch(<f-args>)
891
892 " vim: set noexpandtab: