" 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@jukie.net>
" Contributors: Felipe Contreras <felipe.contreras@gmail.com>,
" Peter Hartman <peterjohnhartman@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')