X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=vim%2Fplugin%2Fnotmuch.vim;h=9e5ee39a9fa1daaa817b52bcfd71ad04bcca32f4;hp=0151160268a3bdf2015d0835710853ced9cf5a1a;hb=75ae11ebd222cbbc19817be0cc6998cb6b1456d4;hpb=5c7ebe31d58d649bfb68627d642dde1cc6cf6493 diff --git a/vim/plugin/notmuch.vim b/vim/plugin/notmuch.vim index 01511602..9e5ee39a 100644 --- a/vim/plugin/notmuch.vim +++ b/vim/plugin/notmuch.vim @@ -23,9 +23,14 @@ let s:notmuch_defaults = { \ 'g:notmuch_cmd': 'notmuch' , - \ 'g:notmuch_search_reverse': 1 , + \ + \ 'g:notmuch_search_newest_first': 1 , + \ 'g:notmuch_search_from_column_width': 20 , + \ \ 'g:notmuch_show_fold_signatures': 1 , \ 'g:notmuch_show_fold_citations': 1 , + \ 'g:notmuch_show_fold_bodies': 0 , + \ 'g:notmuch_show_fold_headers': 1 , \ \ 'g:notmuch_show_message_begin_regexp': '^ message{' , \ 'g:notmuch_show_message_end_regexp': '^ message}' , @@ -48,69 +53,241 @@ let s:notmuch_defaults = { \ 'g:notmuch_show_citation_regexp': '^\s*>' , \ } -" for some reason NM_set_defaults() didn't work for arrays... -if !exists('g:notmuch_show_headers') - let g:notmuch_show_headers = [ 'Subject', 'From' ] -endif +" defaults for g:notmuch_initial_search_words +" override with: let g:notmuch_initial_search_words = [ ... ] +let s:notmuch_initial_search_words_defaults = [ + \ 'tag:inbox and tag:unread', + \ ] + +" defaults for g:notmuch_show_headers +" override with: let g:notmuch_show_headers = [ ... ] +let s:notmuch_show_headers_defaults = [ + \ 'Subject', + \ 'To', + \ 'Cc', + \ 'Bcc', + \ 'Date', + \ ] + +" --- keyboard mapping definitions {{{1 + +" --- --- bindings for search screen {{{2 +let g:notmuch_search_maps = { + \ '': ':call NM_search_show_thread()', + \ 'a': ':call NM_search_archive_thread()', + \ 'f': ':call NM_search_filter()', + \ 'm': ':call NM_new_mail()', + \ 'o': ':call NM_search_toggle_order()', + \ 'r': ':call NM_search_reply_to_thread()', + \ 's': ':call NM_search_prompt()', + \ 'S': ':call NM_search_edit()', + \ 't': ':call NM_search_filter_by_tag()', + \ 'q': ':call NM_kill_this_buffer()', + \ '+': ':call NM_search_add_tags([])', + \ '-': ':call NM_search_remove_tags([])', + \ '=': ':call NM_search_refresh_view()', + \ } -" --- process and set the defaults {{{1 +" --- --- bindings for show screen {{{2 +let g:notmuch_show_maps = { + \ '': ':call NM_show_prev()', + \ '': ':call NM_show_next()', + \ 'q': ':call NM_kill_this_buffer()', + \ + \ 'b': ':call NM_show_fold_toggle(''b'', ''bdy'', !g:notmuch_show_fold_bodies)', + \ 'c': ':call NM_show_fold_toggle(''c'', ''cit'', !g:notmuch_show_fold_citations)', + \ 'h': ':call NM_show_fold_toggle(''h'', ''hdr'', !g:notmuch_show_fold_headers)', + \ 's': ':call NM_show_fold_toggle(''s'', ''sig'', !g:notmuch_show_fold_signatures)', + \ + \ 'a': ':call NM_show_archive_thread()', + \ 'A': ':call NM_show_mark_read_then_archive_thread()', + \ 'N': ':call NM_show_mark_read_then_next_open_message()', + \ 'v': ':call NM_show_view_all_mime_parts()', + \ '+': ':call NM_show_add_tag()', + \ '-': ':call NM_show_remove_tag()', + \ '': ':call NM_show_advance_marking_read_and_archiving()', + \ '\|': ':call NM_show_pipe_message()', + \ + \ 'r': ':call NM_show_reply()', + \ 'm': ':call NM_new_mail()', + \ } -function! NM_set_defaults(force) - 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) - "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 - echoe cmd - exec cmd - endfor -endfunction -call NM_set_defaults(0) " --- implement search screen {{{1 function! s:NM_cmd_search(words) let cmd = ['search'] - if g:notmuch_search_reverse - let cmd = cmd + ['--reverse'] + if g:notmuch_search_newest_first + let cmd = cmd + ['--sort=newest-first'] + else + let cmd = cmd + ['--sort=oldest-first'] endif let data = s:NM_run(cmd + a:words) "let data = substitute(data, '27/27', '25/27', '') "let data = substitute(data, '\[4/4\]', '[0/4]', '') let lines = split(data, "\n") let disp = copy(lines) - call map(disp, 'substitute(v:val, "^thread:\\S* ", "", "")' ) + "call map(disp, 'substitute(v:val, "^thread:\\S* ", "", "")' ) + call map(disp, 's:NM_cmd_search_fmtline(v:val)') - call s:NM_newBuffer('search', join(disp, "\n")) + call NM_newBuffer('search', join(disp, "\n")) let b:nm_raw_lines = lines + let b:nm_search_words = a:words - nnoremap :call NM_search_display() - nnoremap s :call NM_cmd_search(split(input('NotMuch Search:'))) + call NM_cmd_search_mksyntax() + call NM_set_map(g:notmuch_search_maps) setlocal cursorline setlocal nowrap endfunction +function! s:NM_cmd_search_fmtline(line) + let m = matchlist(a:line, '^\(thread:\S\+\)\s\([^]]\+\]\) \([^;]\+\); \(.*\) (\([^(]*\))$') + if !len(m) + return 'ERROR PARSING: ' . a:line + endif + let max = g:notmuch_search_from_column_width + let from = m[3] + if strlen(from) >= max + let from = m[3][0:max-4] . '...' + endif + return printf('%s %-20s | %s (%s)', m[2], from, m[4], m[5]) +endfunction +function! s:NM_cmd_search_mksyntax() + syntax clear nmSearchFrom + "syntax region nmSearchFrom start='\]\@<=' end='.'me=e+5,he=e+5,re=e+5 oneline contained + "syntax match nmSearchFrom /\]\@<=.\{10\}/ oneline contained + exec printf('syntax match nmSearchFrom /\(\] \)\@<=.\{%d\}/ oneline contained', g:notmuch_search_from_column_width) + "exec printf('syntax region nmSearchFrom start=''\%%%dv'' end=''\%%%dv'' oneline contained', 20, 30) +endfunction + +" --- --- search screen action functions {{{2 -function! s:NM_search_display() +function! s:NM_search_show_thread() + let id = NM_search_find_thread_id() + if id != '' + call NM_cmd_show([id]) + endif +endfunction + +function! s:NM_search_prompt() + " TODO: input() can support completion + let text = input('NotMuch Search: ') + if strlen(text) + let tags = split(text) + else + let tags = s:notmuch_initial_search_words_defaults + endif + setlocal bufhidden=delete + call NM_cmd_search(tags) +endfunction + +function! s:NM_search_edit() + " TODO: input() can support completion + let text = input('NotMuch Search: ', join(b:nm_search_words, ' ')) + if strlen(text) + call NM_cmd_search(split(text)) + endif +endfunction + +function! s:NM_search_archive_thread() + call NM_add_remove_tags('-', ['inbox']) + " TODO: this could be made better and more generic + setlocal modifiable + s/(\([^)]*\)\\([^)]*\))$/(\1\2)/ + setlocal nomodifiable + norm j +endfunction + +function! s:NM_search_filter() + call NM_search_filter_helper('Filter: ', '') +endfunction + +function! s:NM_search_filter_by_tag() + call NM_search_filter_helper('Filter Tag(s): ', 'tag:') +endfunction + +function! s:NM_search_filter_helper(prompt, prefix) + " TODO: input() can support completion + let text = input(a:prompt) + if !strlen(text) + return + endif + + let tags = split(text) + map(tags, 'and a:prefix . v:val') + let tags = b:nm_search_words + tags + echo tags + + let prev_bufnr = bufnr('%') + setlocal bufhidden=hide + call NM_cmd_search(tags) + setlocal bufhidden=delete + let b:nm_prev_bufnr = prev_bufnr +endfunction + +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 NM_search_refresh_view() +endfunction + +function! s:NM_search_reply_to_thread() + echo 'not implemented' +endfunction + +function! s:NM_search_add_tags(tags) + call NM_search_add_remove_tags('Add Tag(s): ', '+', a:tags) +endfunction + +function! s:NM_search_remove_tags(tags) + call NM_search_add_remove_tags('Remove Tag(s): ', '-', a:tags) +endfunction + +function! s:NM_search_refresh_view() + let lno = line('.') + setlocal bufhidden=delete + call NM_cmd_search(b:nm_search_words) + " 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_find_thread_id() if !exists('b:nm_raw_lines') - echo 'no b:nm_raw_lines' + echoe 'no b:nm_raw_lines' + return '' else let line = line('.') let info = b:nm_raw_lines[line-1] let what = split(info, '\s\+')[0] - call s:NM_cmd_show([what]) + return what endif 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 + call NM_add_remove_tags(a:prefix, split(text, ' ')) + else + call NM_add_remove_tags(a:prefix, a:intags) + endif + call NM_search_refresh_view() +endfunction + +function! s:NM_add_remove_tags(prefix, tags) + let id = NM_search_find_thread_id() + if id == '' + echoe 'Eeek! I couldn''t find the thead id!' + endif + call map(a:tags, 'a:prefix . v:val') + " TODO: handle errors + call NM_run(['tag'] + a:tags + ['--', id]) +endfunction " --- implement show screen {{{1 @@ -121,24 +298,26 @@ function! s:NM_cmd_show(words) let info = s:NM_cmd_show_parse(lines) - call s:NM_newBuffer('show', join(info['disp'], "\n")) + setlocal bufhidden=hide + call NM_newBuffer('show', join(info['disp'], "\n")) setlocal bufhidden=delete let b:nm_raw_info = info let b:nm_prev_bufnr = prev_bufnr - call s:NM_cmd_show_mkfolds() - call s:NM_cmd_show_mksyntax() + call NM_cmd_show_mkfolds() + call NM_cmd_show_mksyntax() + call NM_set_map(g:notmuch_show_maps) setlocal foldtext=NM_cmd_show_foldtext() setlocal fillchars= setlocal foldcolumn=6 - exec printf("nnoremap q :b %d", b:nm_prev_bufnr) - nnoremap :call NM_cmd_show_next() - nnoremap c :call NM_cmd_show_fold_toggle('c', 'cit', !g:notmuch_show_fold_citations) - nnoremap s :call NM_cmd_show_fold_toggle('s', 'sig', !g:notmuch_show_fold_signatures) endfunction -function! s:NM_cmd_show_next() +function! s:NM_show_prev() + echoe "not implemented" +endfunction + +function! s:NM_show_next() let info = b:nm_raw_info let lnum = line('.') let cnt = 0 @@ -153,10 +332,60 @@ function! s:NM_cmd_show_next() return endfor norm qj - call NM_search_display() + call NM_search_show_thread() +endfunction + +function! s:NM_show_archive_thread() + echo 'not implemented' +endfunction + +function! s:NM_show_mark_read_then_archive_thread() + echo 'not implemented' +endfunction + +function! s:NM_show_next_message() + echo 'not implemented' endfunction -function! s:NM_cmd_show_fold_toggle(key, type, fold) +function! s:NM_show_mark_read_then_next_open_message() + echo 'not implemented' +endfunction + +function! s:NM_show_previous_message() + echo 'not implemented' +endfunction + +function! s:NM_show_reply() + echo 'not implemented' +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_marking_read_and_archiving() + echo 'not implemented' +endfunction + +function! s:NM_show_pipe_message() + echo 'not implemented' +endfunction + +" --- --- search screen helper functions {{{2 + +function! s:NM_show_fold_toggle(key, type, fold) let info = b:nm_raw_info let act = 'open' if a:fold @@ -167,7 +396,7 @@ function! s:NM_cmd_show_fold_toggle(key, type, fold) exec printf('%dfold%s', fld[1], act) endif endfor - exec printf('nnoremap %s :call NM_cmd_show_fold_toggle(''%s'', ''%s'', %d)', a:key, a:key, a:type, !a:fold) + exec printf('nnoremap %s :call NM_show_fold_toggle(''%s'', ''%s'', %d)', a:key, a:key, a:type, !a:fold) endfunction @@ -221,14 +450,13 @@ function! s:NM_cmd_show_parse(inlines) elseif mode_type == 'cit' if part_end || match(line, g:notmuch_show_citation_regexp) == -1 let outlnum = len(info['disp']) - let foldinfo = [ mode_type, mode_start, outlnum, + let foldinfo = [ mode_type, mode_start, outlnum-1, \ printf('[ %d-line citation. Press "c" to show. ]', outlnum - mode_start) ] let mode_type = '' endif elseif mode_type == 'sig' let outlnum = len(info['disp']) if (outlnum - mode_start) > g:notmuch_show_signature_lines_max - echoe 'line ' . outlnum . ' stopped matching' let mode_type = '' elseif part_end let foldinfo = [ mode_type, mode_start, outlnum, @@ -258,7 +486,7 @@ function! s:NM_cmd_show_parse(inlines) endif if match(line, g:notmuch_show_body_end_regexp) != -1 let body_end = len(info['disp']) - let foldinfo = [ 'body', body_start, body_end, + let foldinfo = [ 'bdy', body_start, body_end, \ printf('[ BODY %d - %d lines ]', len(info['msgs']), body_end - body_start) ] let in_body = 0 @@ -283,6 +511,10 @@ function! s:NM_cmd_show_parse(inlines) else if match(line, g:notmuch_show_header_end_regexp) != -1 + let hdr_start = msg['hdr_start']+1 + let hdr_end = len(info['disp']) + let foldinfo = [ 'hdr', hdr_start, hdr_end, + \ printf('[ %d-line headers. Press "h" to show. ]', hdr_end - hdr_start) ] let msg['header'] = hdr let in_header = 0 let hdr = {} @@ -302,7 +534,7 @@ function! s:NM_cmd_show_parse(inlines) let msg['end'] = len(info['disp']) call add(info['disp'], '') - let foldinfo = [ 'match', msg['start'], msg['end'], + let foldinfo = [ 'msg', msg['start'], msg['end'], \ printf('[ MSG %d - %s ]', len(info['msgs']), msg['descr']) ] call add(info['msgs'], msg) @@ -352,6 +584,8 @@ function! s:NM_cmd_show_mkfolds() exec printf('%d,%dfold', afold[1], afold[2]) if (afold[0] == 'sig' && g:notmuch_show_fold_signatures) \ || (afold[0] == 'cit' && g:notmuch_show_fold_citations) + \ || (afold[0] == 'bdy' && g:notmuch_show_fold_bodies) + \ || (afold[0] == 'hdr' && g:notmuch_show_fold_headers) exec printf('%dfoldclose', afold[1]) else exec printf('%dfoldopen', afold[1]) @@ -380,7 +614,7 @@ function! NM_cmd_show_foldtext() endfunction -" --- helper functions {{{1 +" --- notmuch helper functions {{{1 function! s:NM_newBuffer(ft, content) enew @@ -405,12 +639,73 @@ function! s:NM_run(args) endif endfunction +" --- external mail handling helpers {{{1 + +function! s:NM_new_mail() + echo 'not implemented' +endfunction + +" --- other helpers {{{1 + +function! s:NM_kill_this_buffer() + if exists('b:nm_prev_bufnr') + setlocal bufhidden=delete + exec printf(":buffer %d", b:nm_prev_bufnr) + else + echo "Nothing to kill." + endif +endfunction + +" --- process and set the defaults {{{1 + +function! NM_set_defaults(force) + 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) + "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_show_headers') + let g:notmuch_show_headers = s:notmuch_show_headers_defaults +endif +if !exists('g:notmuch_initial_search_words') + let g:notmuch_initial_search_words = s:notmuch_initial_search_words_defaults +endif + + +" --- assign keymaps {{{1 + +function! s:NM_set_map(maps) + for [key, code] in items(a:maps) + exec printf('nnoremap %s %s', key, code) + endfor +endfunction " --- command handler {{{1 function! NotMuch(args) if !strlen(a:args) - call s:NM_cmd_search(['tag:inbox']) + if exists('b:nm_search_words') + let words = b:nm_search_words + else + let words = g:notmuch_initial_search_words + endif + call NM_cmd_search(words) return endif