diff options
Diffstat (limited to 'vim/plugin/notmuch-vimpy.vim')
-rw-r--r-- | vim/plugin/notmuch-vimpy.vim | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/vim/plugin/notmuch-vimpy.vim b/vim/plugin/notmuch-vimpy.vim new file mode 100644 index 0000000..8d80a0e --- /dev/null +++ b/vim/plugin/notmuch-vimpy.vim @@ -0,0 +1,863 @@ +" nm-vimpy.vim plugin --- run notmuch within vim +" +" 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/>. +" +" Based on the notmuch.vim plugin by: +" Authors: Bart Trojanowski <bart@jukie.net> +" Contributors: Felipe Contreras <felipe.contreras@gmail.com>, +" Peter Hartman <peterjohnhartman@gmail.com> +" +" Mostly rewritten using python by Anton Khirnov <anton@khirnov.net> +" + + +" --- initialization +if exists('s:nm_vimpy_loaded') || &cp + finish +endif + +" init the python layer +let s:python_path = expand('<sfile>:p:h') +python import sys +exec "python sys.path += [r'" . s:python_path . "']" +python import vim, nm_vim + +command! NMVimpy call NMVimpy() + +" --- configuration defaults + +let s:nm_vimpy_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:nm_vimpy_fcc_maildir': 'sent' , + \ 'g:nm_vimpy_user_agent': 'notmuch-vimpy' , + \ } + +" defaults for g:nm_vimpy_folders +" override with: let g:nm_vimpy_folders = [ ... ] +let s:nm_vimpy_folders_defaults = [ + \ [ 'new', 'tag:inbox and tag:unread' ], + \ [ 'inbox', 'tag:inbox' ], + \ [ 'unread', 'tag:unread' ], + \ ] + +let s:nm_vimpy_show_headers_defaults = [ + \ 'From', + \ 'To', + \ 'Cc', + \ 'Subject', + \ 'Date', + \ 'Reply-To', + \ 'Message-Id', + \] + +" defaults for g:nm_vimpy_compose_headers +" override with: let g:nm_vimpy_compose_headers = [ ... ] +let s:nm_vimpy_compose_headers_defaults = [ + \ 'From', + \ 'To', + \ 'Cc', + \ 'Bcc', + \ 'Subject' + \ ] + +" --- keyboard mapping definitions + +" --- --- bindings for folders mode {{{2 + +let g:nm_vimpy_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:nm_vimpy_search_maps = { + \ '<Enter>': ':call <SID>NM_search_show_thread()<CR>', + \ '<Space>': ':call <SID>NM_search_show_thread_unread()<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:nm_vimpy_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>', + \ + \ '<Enter>': ':call <SID>NM_show_view_attachment()<CR>', + \ 'S': ':call <SID>NM_show_save_attachment()<CR>', + \ + \ 'r': ':call <SID>NM_show_reply()<CR>', + \ 'R': ':call <SID>NM_show_view_raw_message()<CR>', + \ 'm': ':call <SID>NM_new_mail()<CR>', + \ } + +" --- --- bindings for compose screen {{{2 +let g:nm_vimpy_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:nm_vimpy_raw_message_nmaps = { + \ 'q': ':call <SID>NM_kill_this_buffer()<CR>', + \ } + +" --- implement folders screen {{{1 + +" Create the folders buffer. +" Takes a list of [ folder name, query string] +function! s:NM_cmd_folders(folders) + call <SID>NM_create_buffer('folders') + silent 0put!='<Enter>:view <Space>:view unread s:search =:refresh tt:all tags m:new mail' + python nm_vim.SavedSearches(vim.eval("a:folders")) + call <SID>NM_finalize_menu_buffer() + call <SID>NM_set_map('n', g:nm_vimpy_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') + silent 0put!=printf(' Query results: %s', a:querystr) + if a:relative + python nm_vim.Search(querystr = vim.eval("a:querystr"), parent = nm_vim.nm_buffers[vim.eval('cur_buf')]) + else + python nm_vim.Search(querystr = vim.eval("a:querystr")) + endif + call <SID>NM_finalize_menu_buffer() + call <SID>NM_set_map('n', g:nm_vimpy_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 + +" Same as NM_search_show_thread, except jump to first unread +function! s:NM_search_show_thread_unread() + call <SID>NM_search_show_thread() + python nm_vim.vim_get_message_for_tag('unread') + if start > 0 + exec printf('norm %dGzt', start) + silent! norm zo + 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,NM_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,NM_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 3ggdG + 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 + let text = input(a:prompt, '', 'custom,NM_tag_name_completion') + 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') + python nm_vim.ShowThread(vim.eval('a:querystr')) + + call <SID>NM_set_map('n', g:nm_vimpy_show_maps) + setlocal fillchars= + setlocal foldtext=NM_show_foldtext() + setlocal foldcolumn=6 + setlocal foldmethod=syntax + setlocal nomodifiable + setlocal nowrap + call <SID>NM_jump_message(1) +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 + +"Show the raw message for current line in a new buffer +function! s:NM_show_view_raw_message() + exec printf('python nm_vim.vim_get_object(%d, 0)', line('.')) + if !exists('obj') + return + endif + + call <SID>NM_create_buffer('rawmessage') + python nm_vim.RawMessage(vim.eval("obj['id']")) + call <SID>NM_set_map('n', g:nm_vimpy_raw_message_nmaps) +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 + +function! s:NM_show_save_attachment() + let filename = input('Where to save the attachment: ', './', 'file') + if len(filename) + try + python nm_vim.vim_save_attachment(int(vim.eval('line(".")')), vim.eval('filename')) + echo 'Attachment saved successfully.' + endtry + endif +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:nm_vimpy_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:nm_vimpy_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) + python nm_vim.get_current_buffer().attach(vim.eval('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 + setlocal bufhidden=hide + execute printf('set filetype=nm_vimpy-%s', a:type) + execute printf('set syntax=nm_vimpy-%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:nm_vimpy_signature') + call extend(lines, ['', '-- ']) + call extend(lines, g:nm_vimpy_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:nm_vimpy_compose_nmaps) + + 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 + python nm_vim.get_current_buffer().tag(tags = vim.eval("a:tags"), querystr = vim.eval('join(filter)')) +endfunction + +" --- process and set the defaults {{{1 + +function! s:NM_set_defaults(force) + setlocal bufhidden=hide + for [key, dflt] in items(s:nm_vimpy_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 <SID>NM_set_defaults(0) + +" for some reason NM_set_defaults() didn't work for arrays... +if !exists('g:nm_vimpy_folders') + let g:nm_vimpy_folders = s:nm_vimpy_folders_defaults +endif + +if !exists('g:nm_vimpy_show_headers') + let g:nm_vimpy_show_headers = s:nm_vimpy_show_headers_defaults +endif + +if !exists('g:nm_vimpy_signature') + if filereadable(glob('~/.signature')) + let g:nm_vimpy_signature = readfile(glob('~/.signature')) + endif +endif +if !exists('g:nm_vimpy_compose_headers') + let g:nm_vimpy_compose_headers = s:nm_vimpy_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! NMVimpy() + call <SID>NM_cmd_folders(g:nm_vimpy_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 indentlevel = matchstr(getline(v:foldstart), '^[0-9]\+') + return repeat(' ', indentlevel) . getline(v:foldstart + 1) +endfunction + +"Completion of search prompt +function! NM_search_type_completion(arg_lead, cmd_line, cursor_pos) + let keywords = 'from:' . "\n" . + \ 'to:' . "\n" . + \ 'subject:' . "\n" . + \ 'attachment:' . "\n" . + \ 'tag:' . "\n" . + \ 'id:' . "\n" . + \ 'thread:' . "\n" . + \ 'folder:' . "\n" . + \ 'and' . "\n" . + \ 'or' + let s_idx = strridx(a:arg_lead, " ") + let col_idx = stridx(a:arg_lead, ":", s_idx) + let prefix = strpart(a:arg_lead, 0, s_idx + 1) + if col_idx < 0 + return prefix . substitute(keywords, "\n", "\n" . prefix, "g") + endif + if stridx(a:arg_lead, 'tag:', s_idx) >= 0 + python nm_vim.vim_get_tags() + return prefix . 'tag:' . substitute(taglist, "\n", "\n" . prefix . 'tag:', "g") + endif + return '' +endfunction + +function! NM_tag_name_completion(arg_lead, cmd_line, cursor_pos) + let s_idx = strridx(a:arg_lead, " ") + let prefix = strpart(a:arg_lead, 0, s_idx + 1) + python nm_vim.vim_get_tags() + return prefix . substitute(taglist, "\n", "\n" . prefix, "g") +endfunction + +let s:notmuch_loaded = 1 |