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