Hi, my attempts to make the vim client more usable somehow spiraled out of control and turned into a huge rewrite. The intermediate results I hereby present for your amusement and comments. (attached as whole files, since the patch would be unreadable)
The main point of the rewrite is splitting of a large part of the code into Python. This should have the following advantages: 1) python-notmuch bindings can be used, which should allow for cleaner and more reliable code than running the binary and parsing its output with regexps. (also provides a nice use case for python-notmuch) 2) Python's huge standard library makes implementing some features MUCH easier. 3) More people know Python than vimscript, thus making the client development easier The code is ? quality, but should be close to usable. It already has some features not present in the mainline vim client, like attachments (viewing and sending, saving to file should be trivial to add, will be done when I have some time), better support for unicode and more. Some UI features from the mainline versions that I didn't use were removed and customization options are somewhat lacking atm. This is of course to be improved later, depending on the responses. Comments, bugreports and fixes very much welcome. -- Anton Khirnov -------------- next part -------------- " notmuch.vim plugin --- run notmuch within vim " " Copyright ? Carl Worth " " This file is part of Notmuch. " " Notmuch is free software: you can redistribute it and/or modify it " under the terms of the GNU General Public License as published by " the Free Software Foundation, either version 3 of the License, or " (at your option) any later version. " " Notmuch is distributed in the hope that it will be useful, but " WITHOUT ANY WARRANTY; without even the implied warranty of " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU " General Public License for more details. " " You should have received a copy of the GNU General Public License " along with Notmuch. If not, see <http://www.gnu.org/licenses/>. " " Authors: Bart Trojanowski <bart at jukie.net> " Contributors: Felipe Contreras <felipe.contreras at gmail.com>, " Peter Hartman <peterjohnhartman at gmail.com> " if exists('s:notmuch_loaded') || &cp finish endif let s:notmuch_loaded = 1 " --- configuration defaults {{{1 let s:notmuch_defaults = { \ 'g:notmuch_cmd': 'notmuch' , \ \ 'g:notmuch_search_newest_first': 1 , \ \ 'g:notmuch_compose_insert_mode_start': 1 , \ 'g:notmuch_compose_header_help': 1 , \ 'g:notmuch_compose_temp_file_dir': '~/.notmuch/compose/' , \ 'g:notmuch_fcc_maildir': 'sent' , \ } " defaults for g:notmuch_folders " override with: let g:notmuch_folders = [ ... ] let s:notmuch_folders_defaults = [ \ [ 'new', 'tag:inbox and tag:unread' ], \ [ 'inbox', 'tag:inbox' ], \ [ 'unread', 'tag:unread' ], \ ] let s:notmuch_show_headers_defaults = [ \ 'From', \ 'To', \ 'Cc', \ 'Subject', \ 'Date', \ 'Reply-To', \ 'Message-Id', \] " defaults for g:notmuch_compose_headers " override with: let g:notmuch_compose_headers = [ ... ] let s:notmuch_compose_headers_defaults = [ \ 'From', \ 'To', \ 'Cc', \ 'Bcc', \ 'Subject' \ ] " --- keyboard mapping definitions {{{1 " --- --- bindings for folders mode {{{2 let g:notmuch_folders_maps = { \ 'm': ':call <SID>NM_new_mail()<CR>', \ 's': ':call <SID>NM_search_prompt(0)<CR>', \ 'q': ':call <SID>NM_kill_this_buffer()<CR>', \ '=': ':call <SID>NM_folders_refresh_view()<CR>', \ '<Enter>': ':call <SID>NM_folders_show_search('''')<CR>', \ '<Space>': ':call <SID>NM_folders_show_search(''tag:unread'')<CR>', \ 'tt': ':call <SID>NM_folders_from_tags()<CR>', \ } " --- --- bindings for search screen {{{2 let g:notmuch_search_maps = { \ '<Space>': ':call <SID>NM_search_show_thread()<CR>', \ '<Enter>': ':call <SID>NM_search_show_thread()<CR>', \ '<C-]>': ':call <SID>NM_search_expand(''<cword>'')<CR>', \ 'a': ':call <SID>NM_search_archive_thread()<CR>', \ 'A': ':call <SID>NM_search_mark_read_then_archive_thread()<CR>', \ 'D': ':call <SID>NM_search_delete_thread()<CR>', \ 'f': ':call <SID>NM_search_filter()<CR>', \ 'm': ':call <SID>NM_new_mail()<CR>', \ 'o': ':call <SID>NM_search_toggle_order()<CR>', \ 'r': ':call <SID>NM_search_reply_to_thread()<CR>', \ 's': ':call <SID>NM_search_prompt(0)<CR>', \ ',s': ':call <SID>NM_search_prompt(1)<CR>', \ 'q': ':call <SID>NM_kill_this_buffer()<CR>', \ '+': ':call <SID>NM_search_add_tags([])<CR>', \ '-': ':call <SID>NM_search_remove_tags([])<CR>', \ '=': ':call <SID>NM_search_refresh_view()<CR>', \ } " --- --- bindings for show screen {{{2 let g:notmuch_show_maps = { \ '<C-P>': ':call <SID>NM_jump_message(-1)<CR>', \ '<C-N>': ':call <SID>NM_jump_message(+1)<CR>', \ '<C-]>': ':call <SID>NM_search_expand(''<cword>'')<CR>', \ 'q': ':call <SID>NM_kill_this_buffer()<CR>', \ 's': ':call <SID>NM_search_prompt(0)<CR>', \ \ \ 'a': ':call <SID>NM_show_archive_thread()<CR>', \ 'A': ':call <SID>NM_show_mark_read_then_archive_thread()<CR>', \ 'N': ':call <SID>NM_show_mark_read_then_next_open_message()<CR>', \ 'v': ':call <SID>NM_show_view_all_mime_parts()<CR>', \ '+': ':call <SID>NM_show_add_tag()<CR>', \ '-': ':call <SID>NM_show_remove_tag()<CR>', \ '<Space>': ':call <SID>NM_show_advance()<CR>', \ '\|': ':call <SID>NM_show_pipe_message()<CR>', \ \ '<S-Tab>': ':call <SID>NM_show_previous_fold()<CR>', \ '<Tab>': ':call <SID>NM_show_next_fold()<CR>', \ '<Enter>': ':call <SID>NM_show_view_attachment()<CR>', \ \ 'r': ':call <SID>NM_show_reply()<CR>', \ 'm': ':call <SID>NM_new_mail()<CR>', \ } " --- --- bindings for compose screen {{{2 let g:notmuch_compose_nmaps = { \ ',s': ':call <SID>NM_compose_send()<CR>', \ ',a': ':call <SID>NM_compose_attach()<CR>', \ ',q': ':call <SID>NM_kill_this_buffer()<CR>', \ '<Tab>': ':call <SID>NM_compose_next_entry_area()<CR>', \ } let g:notmuch_compose_imaps = { \ '<Tab>': '<C-r>=<SID>NM_compose_next_entry_area()<CR>', \ } " --- implement folders screen {{{1 " Create the folders buffer. " Takes a list of [ folder name, query string] " TODO decorate (help on the first line?) function! s:NM_cmd_folders(folders) call <SID>NM_create_buffer('folders') silent 0put!=' Notmuch plugin.' python nm_vim.SavedSearches(vim.eval("a:folders")) call <SID>NM_finalize_menu_buffer() call <SID>NM_set_map('n', g:notmuch_folders_maps) endfunction " Show a folder for each existing tag. function! s:NM_folders_from_tags() let folders = [] python nm_vim.vim_get_tags() for tag in split(taglist, '\n') call add(folders, [tag, 'tag:' . tag ]) endfor call <SID>NM_cmd_folders(folders) endfunction " --- --- folders screen action functions {{{2 " Refresh the folders screen function! s:NM_folders_refresh_view() let lno = line('.') setlocal modifiable silent norm 3GdG python nm_vim.get_current_buffer().refresh() setlocal nomodifiable exec printf('norm %dG', lno) endfunction " Show contents of the folder corresponding to current line AND query function! s:NM_folders_show_search(query) exec printf('python nm_vim.vim_get_object(%d, 0)', line('.')) if exists('obj') if len(a:query) let querystr = '(' . obj['id'] . ') and ' . a:query else let querystr = obj['id'] endif call <SID>NM_cmd_search(querystr, 0) endif endfunction " Create the search buffer corresponding to querystr. " If relative is 1, the search is relative to current buffer function! s:NM_cmd_search(querystr, relative) let cur_buf = bufnr('%') call <SID>NM_create_buffer('search') if a:relative exec printf('python nm_vim.Search(querystr = "%s", parent = nm_vim.nm_buffers["%d"])', a:querystr, cur_buf) else exec printf('python nm_vim.Search(querystr = "%s")', a:querystr) endif call <SID>NM_finalize_menu_buffer() call <SID>NM_set_map('n', g:notmuch_search_maps) endfunction " --- --- search screen action functions {{{2 " Show the thread corresponding to current line function! s:NM_search_show_thread() let querystr = <SID>NM_search_thread_id() if len(querystr) call <SID>NM_cmd_show(querystr) endif endfunction " Search according to input from user. " If edit is 1, current query string is inserted to prompt for editing. function! s:NM_search_prompt(edit) if a:edit python nm_vim.vim_get_id() else let buf_id = '' endif let querystr = input('Search: ', buf_id, 'custom,Search_type_completion') if len(querystr) call <SID>NM_cmd_search(querystr, 0) endif endfunction " Filter current search, i.e. search for " (current querystr) AND (user input) function! s:NM_search_filter() let querystr = input('Filter: ', '', 'custom,Search_type_completion') if len(querystr) call <SID>NM_cmd_search(querystr, 1) endif endfunction """"""""""""""""""""""'' TODO function! s:NM_search_archive_thread() call <SID>NM_tag([], ['-inbox']) norm j endfunction function! s:NM_search_mark_read_then_archive_thread() call <SID>NM_tag([], ['-unread', '-inbox']) norm j endfunction function! s:NM_search_delete_thread() call <SID>NM_tag([], ['+junk','-inbox','-unread']) norm j endfunction """"""""""""""""""""""""""""""""""""""""""""""""""""" " XXX This function is broken function! s:NM_search_toggle_order() let g:notmuch_search_newest_first = !g:notmuch_search_newest_first " FIXME: maybe this would be better done w/o reading re-reading the lines " reversing the b:nm_raw_lines and the buffer lines would be better call <SID>NM_search_refresh_view() endfunction "XXX this function is broken function! s:NM_search_reply_to_thread() python vim.command('let querystr = "%s"'%nm_vim.get_current_buffer().id) let cmd = ['reply'] call add(cmd, <SID>NM_search_thread_id()) call add(cmd, 'AND') call extend(cmd, [querystr]) let data = <SID>NM_run(cmd) let lines = split(data, "\n") call <SID>NM_newComposeBuffer(lines, 0) endfunction function! s:NM_search_add_tags(tags) call <SID>NM_search_add_remove_tags('Add Tag(s): ', '+', a:tags) endfunction function! s:NM_search_remove_tags(tags) call <SID>NM_search_add_remove_tags('Remove Tag(s): ', '-', a:tags) endfunction function! s:NM_search_refresh_view() let lno = line('.') setlocal modifiable norm ggdG python nm_vim.get_current_buffer().refresh() setlocal nomodifiable " FIXME: should find the line of the thread we were on if possible exec printf('norm %dG', lno) endfunction " --- --- search screen helper functions {{{2 function! s:NM_search_thread_id() exec printf('python nm_vim.vim_get_object(%d, 0)', line('.')) if exists('obj') return 'thread:' . obj['id'] endif return '' endfunction function! s:NM_search_add_remove_tags(prompt, prefix, intags) if type(a:intags) != type([]) || len(a:intags) == 0 " TODO: input() can support completion let text = input(a:prompt) if !strlen(text) return endif let tags = split(text, ' ') else let tags = a:intags endif call map(tags, 'a:prefix . v:val') call <SID>NM_tag([], tags) endfunction " --- implement show screen {{{1 function! s:NM_cmd_show(querystr) "TODO: folding, syntax call <SID>NM_create_buffer('show') exec printf('python nm_vim.ShowThread("%s")', a:querystr) call <SID>NM_set_map('n', g:notmuch_show_maps) setlocal fillchars= setlocal foldtext=NM_show_foldtext() setlocal foldcolumn=6 setlocal foldmethod=syntax endfunction function! s:NM_jump_message(offset) "TODO implement can_change_thread and find_matching, nicer positioning exec printf('python nm_vim.vim_get_object(%d, %d)', line('.'), a:offset) if exists('obj') silent norm zc exec printf('norm %dGzt', obj['start']) silent norm zo endif endfunction function! s:NM_show_next_thread() call <SID>NM_kill_this_buffer() if line('.') != line('$') norm j call <SID>NM_search_show_thread() else echo 'No more messages.' endif endfunction function! s:NM_show_archive_thread() call <SID>NM_tag('', ['-inbox']) call <SID>NM_show_next_thread() endfunction function! s:NM_show_mark_read_then_archive_thread() call <SID>NM_tag('', ['-unread', '-inbox']) call <SID>NM_show_next_thread() endfunction function! s:NM_show_mark_read_then_next_open_message() echo 'not implemented' endfunction function! s:NM_show_previous_message() echo 'not implemented' endfunction "XXX pythonise function! s:NM_show_reply() let cmd = ['reply'] call add(cmd, 'id:' . <SID>NM_show_message_id()) let data = <SID>NM_run(cmd) let lines = split(data, "\n") call <SID>NM_newComposeBuffer(lines, 0) endfunction function! s:NM_show_view_all_mime_parts() echo 'not implemented' endfunction function! s:NM_show_view_raw_message() echo 'not implemented' endfunction function! s:NM_show_add_tag() echo 'not implemented' endfunction function! s:NM_show_remove_tag() echo 'not implemented' endfunction function! s:NM_show_advance() let advance_tags = ['-unread'] exec printf('python nm_vim.vim_get_object(%d, 0)', line('.')) if !exists('obj') return endif call <SID>NM_tag(['id:' . obj['id']], advance_tags) if obj['end'] == line('$') call <SID>NM_kill_this_buffer() else call <SID>NM_jump_message(1) endif endfunction function! s:NM_show_pipe_message() echo 'not implemented' endfunction function! s:NM_show_view_attachment() exec printf('python nm_vim.vim_view_attachment(%d)', line('.')) endfunction " --- --- show screen helper functions {{{2 function! s:NM_show_message_id() exec printf('python nm_vim.vim_get_object(%d, 0)', line('.')) if exists('obj') return obj['id'] else return '' endfunction " --- implement compose screen {{{1 function! s:NM_cmd_compose(words, body_lines) let lines = [] let start_on_line = 0 let hdrs = { } if !has_key(hdrs, 'From') || !len(hdrs['From']) let me = <SID>NM_compose_get_user_email() let hdrs['From'] = [ me ] endif for key in g:notmuch_compose_headers let text = has_key(hdrs, key) ? join(hdrs[key], ', ') : '' call add(lines, key . ': ' . text) if !start_on_line && !strlen(text) let start_on_line = len(lines) endif endfor for [key,val] in items(hdrs) if match(g:notmuch_compose_headers, key) == -1 let line = key . ': ' . join(val, ', ') call add(lines, line) endif endfor call add(lines, '') if !start_on_line let start_on_line = len(lines) + 1 endif call extend(lines, [ '', '' ]) call <SID>NM_newComposeBuffer(lines, start_on_line) endfunction function! s:NM_compose_send() let fname = expand('%') try python nm_vim.get_current_buffer().send() call <SID>NM_kill_this_buffer() call delete(fname) echo 'Mail sent successfully.' endtry endfunction function! s:NM_compose_attach() let attachment = input('Enter attachment filename: ', '', 'file') if len(attachment) exec printf('python nm_vim.get_current_buffer().attach("%s")', attachment) endif endfunction function! s:NM_compose_next_entry_area() let lnum = line('.') let hdr_end = <SID>NM_compose_find_line_match(1,'^$',1) if lnum < hdr_end let lnum = lnum + 1 let line = getline(lnum) if match(line, '^\([^:]\+\):\s*$') == -1 call cursor(lnum, strlen(line) + 1) return '' endif while match(getline(lnum+1), '^\s') != -1 let lnum = lnum + 1 endwhile call cursor(lnum, strlen(getline(lnum)) + 1) return '' elseif lnum == hdr_end call cursor(lnum+1, strlen(getline(lnum+1)) + 1) return '' endif if mode() == 'i' if !getbufvar(bufnr('.'), '&et') return "\t" endif let space = '' let shiftwidth = a:shiftwidth let shiftwidth = shiftwidth - ((virtcol('.')-1) % shiftwidth) " we assume no one has shiftwidth set to more than 40 :) return ' '[0:shiftwidth] endif endfunction " --- --- compose screen helper functions {{{2 function! s:NM_compose_get_user_email() " TODO: do this properly (still), i.e., allow for multiple email accounts let email = substitute(system('notmuch config get user.primary_email'), '\v(^\s*|\s*$|\n)', '', 'g') return email endfunction function! s:NM_compose_find_line_match(start, pattern, failure) let lnum = a:start let lend = line('$') while lnum < lend if match(getline(lnum), a:pattern) != -1 return lnum endif let lnum = lnum + 1 endwhile return a:failure endfunction " --- notmuch helper functions {{{1 function! s:NM_create_buffer(type) let prev_bufnr = bufnr('%') enew setlocal buftype=nofile execute printf('set filetype=notmuch-%s', a:type) execute printf('set syntax=notmuch-%s', a:type) "XXX this should probably go let b:nm_prev_bufnr = prev_bufnr endfunction "set some options for "menu"-like buffers -- folders/searches function! s:NM_finalize_menu_buffer() setlocal nomodifiable setlocal cursorline setlocal nowrap endfunction function! s:NM_newBuffer(how, type, content) if strlen(a:how) exec a:how else enew endif setlocal buftype=nofile readonly modifiable scrolloff=0 sidescrolloff=0 silent put=a:content keepjumps 0d setlocal nomodifiable execute printf('set filetype=notmuch-%s', a:type) execute printf('set syntax=notmuch-%s', a:type) endfunction function! s:NM_newFileBuffer(fdir, fname, type, lines) let fdir = expand(a:fdir) if !isdirectory(fdir) call mkdir(fdir, 'p') endif let file_name = <SID>NM_mktemp(fdir, a:fname) if writefile(a:lines, file_name) throw 'Eeek! couldn''t write to temporary file ' . file_name endif exec printf('edit %s', file_name) setlocal buftype= noreadonly modifiable scrolloff=0 sidescrolloff=0 execute printf('set filetype=notmuch-%s', a:type) execute printf('set syntax=notmuch-%s', a:type) endfunction function! s:NM_newComposeBuffer(lines, start_on_line) let lines = a:lines let start_on_line = a:start_on_line let real_hdr_start = 1 if g:notmuch_compose_header_help let help_lines = [ \ 'Notmuch-Help: Type in your message here; to help you use these bindings:', \ 'Notmuch-Help: ,a - attach a file', \ 'Notmuch-Help: ,s - send the message (Notmuch-Help lines will be removed)', \ 'Notmuch-Help: ,q - abort the message', \ 'Notmuch-Help: <Tab> - skip through header lines', \ ] call extend(lines, help_lines, 0) let real_hdr_start = len(help_lines) if start_on_line > 0 let start_on_line = start_on_line + len(help_lines) endif endif if exists('g:notmuch_signature') call extend(lines, ['', '--']) call extend(lines, g:notmuch_signature) endif let prev_bufnr = bufnr('%') call <SID>NM_newFileBuffer(g:notmuch_compose_temp_file_dir, '%s.mail', \ 'compose', lines) let b:nm_prev_bufnr = prev_bufnr call <SID>NM_set_map('n', g:notmuch_compose_nmaps) call <SID>NM_set_map('i', g:notmuch_compose_imaps) if start_on_line > 0 && start_on_line <= len(lines) call cursor(start_on_line, strlen(getline(start_on_line)) + 1) else call cursor(real_hdr_start, strlen(getline(real_hdr_start)) + 1) call <SID>NM_compose_next_entry_area() endif if g:notmuch_compose_insert_mode_start startinsert! endif python nm_vim.Compose() endfunction function! s:NM_mktemp(dir, name) let time_stamp = strftime('%Y%m%d-%H%M%S') let file_name = substitute(a:dir,'/*$','/','') . printf(a:name, time_stamp) " TODO: check if it exists, try again return file_name endfunction function! s:NM_shell_escape(word) " TODO: use shellescape() let word = substitute(a:word, '''', '\\''', 'g') return '''' . word . '''' endfunction function! s:NM_run(args) let words = a:args call map(words, 's:NM_shell_escape(v:val)') let cmd = g:notmuch_cmd . ' ' . join(words) . '< /dev/null' let out = system(cmd) let err = v:shell_error if err echohl Error echo substitute(out, '\n*$', '', '') echohl None return '' else return out endif endfunction " --- external mail handling helpers {{{1 function! s:NM_new_mail() call <SID>NM_cmd_compose([], []) endfunction " --- tag manipulation helpers {{{1 " used to combine an array of words with prefixes and separators " example: " NM_combine_tags('tag:', ['one', 'two', 'three'], 'OR', '()') " -> ['(', 'tag:one', 'OR', 'tag:two', 'OR', 'tag:three', ')'] function! s:NM_combine_tags(word_prefix, words, separator, brackets) let res = [] for word in a:words if len(res) && strlen(a:separator) call add(res, a:separator) endif call add(res, a:word_prefix . word) endfor if len(res) > 1 && strlen(a:brackets) if strlen(a:brackets) != 2 throw 'Eeek! brackets arg to NM_combine_tags must be 2 chars' endif call insert(res, a:brackets[0]) call add(res, a:brackets[1]) endif return res endfunction " --- other helpers {{{1 function! s:NM_kill_this_buffer() let prev_bufnr = b:nm_prev_bufnr python nm_vim.delete_current_buffer() bdelete! exec printf("buffer %d", prev_bufnr) endfunction function! s:NM_search_expand(arg) let word = expand(a:arg) let prev_bufnr = bufnr('%') call <SID>NM_cmd_search(word, 0) let b:nm_prev_bufnr = prev_bufnr endfunction function! s:NM_tag(filter, tags) let filter = len(a:filter) ? a:filter : [<SID>NM_search_thread_id()] if !len(filter) throw 'Eeek! I couldn''t find the thead id!' endif exec printf('python nm_vim.get_current_buffer().tag(tags = vim.eval("a:tags"), querystr = "%s")', join(filter)) endfunction " --- process and set the defaults {{{1 function! NM_set_defaults(force) setlocal bufhidden=hide for [key, dflt] in items(s:notmuch_defaults) let cmd = '' if !a:force && exists(key) && type(dflt) == type(eval(key)) continue elseif type(dflt) == type(0) let cmd = printf('let %s = %d', key, dflt) elseif type(dflt) == type('') let cmd = printf('let %s = ''%s''', key, dflt) " FIXME: not sure why this didn't work when dflt is an array "elseif type(dflt) == type([]) " let cmd = printf('let %s = %s', key, string(dflt)) else echoe printf('E: Unknown type in NM_set_defaults(%d) using [%s,%s]', \ a:force, key, string(dflt)) continue endif exec cmd endfor endfunction call NM_set_defaults(0) " for some reason NM_set_defaults() didn't work for arrays... if !exists('g:notmuch_folders') let g:notmuch_folders = s:notmuch_folders_defaults endif if !exists('g:notmuch_show_headers') let g:notmuch_show_headers = s:notmuch_show_headers_defaults endif if !exists('g:notmuch_signature') if filereadable(glob('~/.signature')) let g:notmuch_signature = readfile(glob('~/.signature')) endif endif if !exists('g:notmuch_compose_headers') let g:notmuch_compose_headers = s:notmuch_compose_headers_defaults endif " --- assign keymaps {{{1 function! s:NM_set_map(type, maps) for [key, code] in items(a:maps) exec printf('%snoremap <buffer> %s %s', a:type, key, code) endfor endfunction " --- command handler {{{1 function! NotMuch() if !exists('s:notmuch_inited') " init the python layer python import sys exec "python sys.path += [r'" . s:python_path . "']" python import vim, nm_vim let s:notmuch_inited = 1 endif call <SID>NM_cmd_folders(g:notmuch_folders) endfunction "Custom foldtext() for show buffers, which indents folds to "represent thread structure function! NM_show_foldtext() if v:foldlevel != 1 return foldtext() endif let numlines = v:foldend - v:foldstart + 1 let indentlevel = matchstr(getline(v:foldstart), '^[0-9]\+') return repeat(' ', indentlevel) . getline(v:foldstart + 1) endfunction "Completion of search prompt "TODO properly deal with complex queries function! Search_type_completion(arg_lead, cmd_line, cursor_pos) let idx = stridx(a:arg_lead, ':') if idx < 0 return 'from:' . "\n" . \ 'to:' . "\n" . \ 'subject:' . "\n" . \ 'attachment:' . "\n" . \ 'tag:' . "\n" . \ 'id:' . "\n" . \ 'thread:' . "\n" . \ 'folder:' endif if stridx(a:arg_lead, 'tag:') >= 0 python nm_vim.vim_get_tags() return 'tag:' . substitute(taglist, "\n", "\ntag:", "g") endif return '' endfunction " --- glue {{{1 command! NotMuch call NotMuch() let s:python_path = expand('<sfile>:p:h') -------------- next part -------------- A non-text attachment was scrubbed... Name: nm_vim.py Type: text/x-python Size: 19445 bytes Desc: not available URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20110515/6e9c6542/attachment-0001.py> -------------- next part -------------- " notmuch folders mode syntax file syntax region nmFolfers start=/^/ end=/$/ oneline contains=nmFoldersMessageCount syntax match nmFoldersMessageCount /^ *[0-9]\+ */ contained nextgroup=nmFoldersUnreadCount syntax match nmFoldersUnreadCount /(.\{-}) */ contained nextgroup=nmFoldersName syntax match nmFoldersName /.*\ze(/ contained nextgroup=nmFoldersSearch syntax match nmFoldersSearch /([^()]\+)$/ highlight link nmFoldersMessageCount Statement highlight link nmFoldersUnreadCount Underlined highlight link nmFoldersName Type highlight link nmFoldersSearch String highlight CursorLine term=reverse cterm=reverse gui=reverse -------------- next part -------------- syntax region nmSearch start=/^/ end=/$/ oneline contains=nmSearchDate keepend syntax match nmSearchDate /^.\{-13}/ contained nextgroup=nmSearchNum skipwhite syntax match nmSearchNum "[0-9]\+\/" contained nextgroup=nmSearchTotal skipwhite syntax match nmSearchTotal /[0-9]\+/ contained nextgroup=nmSearchFrom skipwhite syntax match nmSearchFrom /.\{-}\ze|/ contained nextgroup=nmSearchSubject skipwhite "XXX this fails on some messages with multiple authors syntax match nmSearchSubject /.*\ze(/ contained nextgroup=nmSearchTags syntax match nmSearchTags /.\+$/ contained syntax match nmUnread /^.*(.*\<unread\>.*)$/ highlight link nmSearchDate Statement highlight link nmSearchNum Number highlight link nmSearchTotal Type highlight link nmSearchFrom Include highlight link nmSearchSubject Normal highlight link nmSearchTags String highlight link nmUnread Underlined -------------- next part -------------- " notmuch show mode syntax file setlocal conceallevel=2 setlocal concealcursor=vinc syntax region nmMessage matchgroup=Ignore concealends start='[0-9]\+\/-*message start-*\\' end='\\-*message end-*\/' fold contains=@nmShowMsgBody keepend "TODO what about those syntax cluster nmShowMsgDesc contains=nmShowMsgDescWho,nmShowMsgDescDate,nmShowMsgDescTags syntax match nmShowMsgDescWho /[^)]\+)/ contained syntax match nmShowMsgDescDate / ([^)]\+[0-9]) / contained syntax match nmShowMsgDescTags /([^)]\+)$/ contained syntax cluster nmShowMsgBody contains=@nmShowMsgBodyMail, at nmShowMsgBodyGit syntax include @nmShowMsgBodyMail syntax/mail.vim silent! syntax include @nmShowMsgBodyGit syntax/notmuch-git-diff.vim highlight nmShowMsgDescWho term=reverse cterm=reverse gui=reverse highlight link nmShowMsgDescDate Type highlight link nmShowMsgDescTags String "TODO what about this? highlight Folded term=reverse ctermfg=LightGrey ctermbg=Black guifg=LightGray guibg=Black