X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=vim%2Fplugin%2Fnotmuch.vim;h=b9fc8d220288d7968bd2385c39fb32942a200e00;hp=70f38e2ac5a1a42768eafd5b7efab08401c8e295;hb=fe2a905110a9527e77e9c40ba8dd9aab369af6b9;hpb=89dc64726f9a396b0a0912788e1736141968c8b3 diff --git a/vim/plugin/notmuch.vim b/vim/plugin/notmuch.vim index 70f38e2a..b9fc8d22 100644 --- a/vim/plugin/notmuch.vim +++ b/vim/plugin/notmuch.vim @@ -19,41 +19,81 @@ " " Authors: Bart Trojanowski -" --- defaults - -if !exists('g:notmuch_cmd') - let g:notmuch_cmd = 'notmuch' -endif - -if !exists('g:notmuch_search_reverse') - let g:notmuch_search_reverse = 1 +" --- configuration defaults {{{1 + +let s:notmuch_defaults = { + \ 'g:notmuch_cmd': 'notmuch' , + \ 'g:notmuch_search_reverse': 1 , + \ 'g:notmuch_show_fold_signatures': 1 , + \ 'g:notmuch_show_fold_citations': 1 , + \ + \ 'g:notmuch_show_message_begin_regexp': '^ message{' , + \ 'g:notmuch_show_message_end_regexp': '^ message}' , + \ 'g:notmuch_show_header_begin_regexp': '^ header{' , + \ 'g:notmuch_show_header_end_regexp': '^ header}' , + \ 'g:notmuch_show_body_begin_regexp': '^ body{' , + \ 'g:notmuch_show_body_end_regexp': '^ body}' , + \ 'g:notmuch_show_attachment_begin_regexp': '^ attachment{' , + \ 'g:notmuch_show_attachment_end_regexp': '^ attachment}' , + \ 'g:notmuch_show_part_begin_regexp': '^ part{' , + \ 'g:notmuch_show_part_end_regexp': '^ part}' , + \ 'g:notmuch_show_marker_regexp': '^ \\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$', + \ + \ 'g:notmuch_show_message_parse_regexp': '\(id:[^ ]*\) depth:\([0-9]*\) filename:\(.*\)$', + \ 'g:notmuch_show_tags_regexp': '(\([^)]*\))$' , + \ + \ 'g:notmuch_show_signature_regexp': '^\(-- \?\|_\+\)$' , + \ 'g:notmuch_show_signature_lines_max': 12 , + \ + \ '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 -" --- used to match output of notmuch - -let s:notmuch_show_message_begin_regexp = '^ message{' -let s:notmuch_show_message_end_regexp = '^ message}' -let s:notmuch_show_header_begin_regexp = '^ header{' -let s:notmuch_show_header_end_regexp = '^ header}' -let s:notmuch_show_body_begin_regexp = '^ body{' -let s:notmuch_show_body_end_regexp = '^ body}' -let s:notmuch_show_attachment_begin_regexp = '^ attachment{' -let s:notmuch_show_attachment_end_regexp = '^ attachment}' -let s:notmuch_show_part_begin_regexp = '^ part{' -let s:notmuch_show_part_end_regexp = '^ part}' -let s:notmuch_show_marker_regexp = '^ \\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$' +" --- keyboard mapping definitions {{{1 + +let g:notmuch_search_maps = { + \ '': ':call NM_search_display()', + \ 's': ':call NM_cmd_search(split(input(''NotMuch Search:'')))', + \ } + +" --- 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 + echoe cmd + exec cmd + endfor +endfunction +call NM_set_defaults(0) -let s:notmuch_show_id_regexp = '\(id:[^ ]*\)' -let s:notmuch_show_depth_regexp = ' depth:\([0-9]*\) ' -let s:notmuch_show_filename_regexp = 'filename:\(.*\)$' -let s:notmuch_show_tags_regexp = '(\([^)]*\))$' +" --- assign keymaps {{{1 -let s:notmuch_show_signature_regexp = '^\(-- \?\|_\+\)$' -let s:notmuch_show_signature_lines_max = 12 +function! s:NM_set_map(maps) + for [key, code] in items(a:maps) + exec printf('nnoremap %s %s', key, code) + endfor +endfunction -let s:notmuch_show_citation_regexp = '^\s*>' -" --- implement search screen +" --- implement search screen {{{1 function! s:NM_cmd_search(words) let cmd = ['search'] @@ -68,118 +108,294 @@ function! s:NM_cmd_search(words) call map(disp, 'substitute(v:val, "^thread:\\S* ", "", "")' ) call s:NM_newBuffer('search', join(disp, "\n")) - let b:nm_raw_data = lines + let b:nm_raw_lines = lines - nnoremap :call NM_search_display() - nnoremap s :call NM_cmd_search(split(input('NotMuch Search:'))) + call NM_set_map(g:notmuch_search_maps) setlocal cursorline setlocal nowrap endfunction function! s:NM_search_display() - if !exists('b:nm_raw_data') - echo 'no b:nm_raw_data' + if !exists('b:nm_raw_lines') + echo 'no b:nm_raw_lines' else let line = line('.') - let info = b:nm_raw_data[line-1] + let info = b:nm_raw_lines[line-1] let what = split(info, '\s\+')[0] call s:NM_cmd_show([what]) endif endfunction -" --- implement show screen +" --- implement show screen {{{1 function! s:NM_cmd_show(words) - let bufnr = bufnr('%') + let prev_bufnr = bufnr('%') let data = s:NM_run(['show'] + a:words) + let lines = split(data, "\n") + + let info = s:NM_cmd_show_parse(lines) - call s:NM_newBuffer('show', data) + call s:NM_newBuffer('show', join(info['disp'], "\n")) setlocal bufhidden=delete - let b:nm_raw_data = data + 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() setlocal foldtext=NM_cmd_show_foldtext() setlocal fillchars= - setlocal foldcolumn=5 + setlocal foldcolumn=6 - exec printf("nnoremap q :b %d", bufnr) + 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_mkfolds() - let msg_start = -1 - let hdr_start = -1 - let bdy_start = -1 - let prt_start = -1 - let modetype = '' - let modeline = -1 - let lnum = 1 - let b:nm_fold_data = {} - while lnum <= line('$') - let line = getline(lnum) - if match(line, s:notmuch_show_message_begin_regexp) != -1 - let msg_start = lnum - elseif match(line, s:notmuch_show_message_end_regexp) != -1 - exec printf('%d,%dfold', msg_start, lnum) - exec printf('%dfoldopen', msg_start) - let b:nm_fold_data[msg_start] = ['msg', getline(msg_start)] - - elseif match(line, s:notmuch_show_header_begin_regexp) != -1 - let hdr_start = lnum - elseif match(line, s:notmuch_show_header_end_regexp) != -1 - exec printf('%d,%dfold', hdr_start, lnum) - exec printf('%dfoldclose', hdr_start) - let b:nm_fold_data[hdr_start] = ['hdr', '* ' . getline(hdr_start+1) . ' [ Press "h" for full header. ]'] - - elseif match(line, s:notmuch_show_body_begin_regexp) != -1 - let bdy_start = lnum - elseif match(line, s:notmuch_show_body_end_regexp) != -1 - exec printf('%d,%dfold', bdy_start, lnum) - exec printf('%dfoldopen', bdy_start) - let b:nm_fold_data[bdy_start] = ['bdy', getline(bdy_start)] - - elseif match(line, s:notmuch_show_part_begin_regexp) != -1 - let prt_start = lnum - elseif match(line, s:notmuch_show_part_end_regexp) != -1 - exec printf('%d,%dfold', prt_start, lnum) - exec printf('%dfoldopen', prt_start) - let b:nm_fold_data[msg_start] = ['msg', getline(prt_start)] - - elseif modetype == '' - if match(line, s:notmuch_show_signature_regexp) != -1 - let modetype = 'sig' - let modeline = lnum - elseif match(line, s:notmuch_show_citation_regexp) != -1 - let modetype = 'cit' - let modeline = lnum +function! s:NM_cmd_show_next() + let info = b:nm_raw_info + let lnum = line('.') + let cnt = 0 + for msg in info['msgs'] + let cnt = cnt + 1 + if lnum >= msg['start'] + continue + endif + + exec printf('norm %dG', msg['start']) + norm zz + return + endfor + norm qj + call NM_search_display() +endfunction + +function! s:NM_cmd_show_fold_toggle(key, type, fold) + let info = b:nm_raw_info + let act = 'open' + if a:fold + let act = 'close' + endif + for fld in info['folds'] + if fld[0] == a:type + 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) +endfunction + + +" s:NM_cmd_show_parse returns the following dictionary: +" 'disp': lines to display +" 'msgs': message info dicts { start, end, id, depth, filename, descr, header } +" 'folds': fold info arrays [ type, start, end ] +" 'foldtext': fold text indexed by start line +function! s:NM_cmd_show_parse(inlines) + let info = { 'disp': [], + \ 'msgs': [], + \ 'folds': [], + \ 'foldtext': {} } + let msg = {} + let hdr = {} + + let in_message = 0 + let in_header = 0 + let in_body = 0 + let in_part = '' + + let body_start = -1 + let part_start = -1 + + let mode_type = '' + let mode_start = -1 + + let inlnum = 0 + for line in a:inlines + let inlnum = inlnum + 1 + let foldinfo = [] + + if strlen(in_part) + let part_end = 0 + + if match(line, g:notmuch_show_part_end_regexp) != -1 + let part_end = len(info['disp']) + else + call add(info['disp'], line) endif - elseif modetype == 'cit' - if match(line, s:notmuch_show_citation_regexp) == -1 - exec printf('%d,%dfold', modeline, lnum) - let b:nm_fold_data[modeline] = [modetype, printf('[ %d-line citation. Press "c" to show. ]', lnum - modeline)] - let modetype = '' + + if in_part == 'text/plain' + if !part_end && mode_type == '' + if match(line, g:notmuch_show_signature_regexp) != -1 + let mode_type = 'sig' + let mode_start = len(info['disp']) + elseif match(line, g:notmuch_show_citation_regexp) != -1 + let mode_type = 'cit' + let mode_start = len(info['disp']) + endif + 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, + \ 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, + \ printf('[ %d-line signature. Press "s" to show. ]', outlnum - mode_start) ] + let mode_type = '' + endif + endif endif - elseif modetype == 'sig' - if (lnum - modeline) > s:notmuch_show_signature_lines_max - let modetype = '' - elseif match(line, s:notmuch_show_part_end_regexp) != -1 - let modeline2 = lnum - 1 - exec printf('%d,%dfold', modeline, modeline2) - let b:nm_fold_data[modeline] = [modetype, printf('[ %d-line signature. Press "s" to show. ]', modeline2 - modeline)] - let modetype = '' + + if part_end + " FIXME: this is a hack for handling two folds being added for one line + " we should handle addinga fold in a function + if len(foldinfo) + call add(info['folds'], foldinfo[0:2]) + let info['foldtext'][foldinfo[1]] = foldinfo[3] + endif + + let foldinfo = [ 'text', part_start, part_end, + \ printf('[ %d-line %s. Press "p" to show. ]', part_end - part_start, in_part) ] + let in_part = '' + call add(info['disp'], '') + endif + + elseif in_body + if !has_key(msg,'body_start') + let msg['body_start'] = len(info['disp']) + 1 + endif + if match(line, g:notmuch_show_body_end_regexp) != -1 + let body_end = len(info['disp']) + let foldinfo = [ 'body', body_start, body_end, + \ printf('[ BODY %d - %d lines ]', len(info['msgs']), body_end - body_start) ] + + let in_body = 0 + + elseif match(line, g:notmuch_show_part_begin_regexp) != -1 + let m = matchlist(line, 'ID: \(\d\+\), Content-type: \(\S\+\)') + let in_part = 'unknown' + if len(m) + let in_part = m[2] + endif + call add(info['disp'], + \ printf('--- %s ---', in_part)) + let part_start = len(info['disp']) + 1 + endif + + elseif in_header + if in_header == 1 + let msg['descr'] = line + call add(info['disp'], line) + let in_header = 2 + let msg['hdr_start'] = len(info['disp']) + 1 + + else + if match(line, g:notmuch_show_header_end_regexp) != -1 + let msg['header'] = hdr + let in_header = 0 + let hdr = {} + else + let m = matchlist(line, '^\(\w\+\):\s*\(.*\)$') + if len(m) + let hdr[m[1]] = m[2] + if match(g:notmuch_show_headers, m[1]) != -1 + call add(info['disp'], line) + endif + endif + endif + endif + + elseif in_message + if match(line, g:notmuch_show_message_end_regexp) != -1 + let msg['end'] = len(info['disp']) + call add(info['disp'], '') + + let foldinfo = [ 'match', msg['start'], msg['end'], + \ printf('[ MSG %d - %s ]', len(info['msgs']), msg['descr']) ] + + call add(info['msgs'], msg) + let msg = {} + let in_message = 0 + let in_header = 0 + let in_body = 0 + let in_part = '' + + elseif match(line, g:notmuch_show_header_begin_regexp) != -1 + let in_header = 1 + continue + + elseif match(line, g:notmuch_show_body_begin_regexp) != -1 + let body_start = len(info['disp']) + 1 + let in_body = 1 + continue + endif + + else + if match(line, g:notmuch_show_message_begin_regexp) != -1 + let msg['start'] = len(info['disp']) + 1 + + let m = matchlist(line, g:notmuch_show_message_parse_regexp) + if len(m) + let msg['id'] = m[1] + let msg['depth'] = m[2] + let msg['filename'] = m[3] + endif + + let in_message = 1 endif endif - let lnum = lnum + 1 - endwhile + if len(foldinfo) + call add(info['folds'], foldinfo[0:2]) + let info['foldtext'][foldinfo[1]] = foldinfo[3] + endif + endfor + return info +endfunction + +function! s:NM_cmd_show_mkfolds() + let info = b:nm_raw_info + + for afold in info['folds'] + 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) + exec printf('%dfoldclose', afold[1]) + else + exec printf('%dfoldopen', afold[1]) + endif + endfor +endfunction + +function! s:NM_cmd_show_mksyntax() + let info = b:nm_raw_info + let cnt = 0 + for msg in info['msgs'] + let cnt = cnt + 1 + let start = msg['start'] + let hdr_start = msg['hdr_start'] + let body_start = msg['body_start'] + let end = msg['end'] + exec printf('syntax region nmShowMsg%dDesc start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgDesc', cnt, start, start+1) + exec printf('syntax region nmShowMsg%dHead start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgHead', cnt, hdr_start, body_start) + exec printf('syntax region nmShowMsg%dBody start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgBody', cnt, body_start, end) + endfor endfunction function! NM_cmd_show_foldtext() - return b:nm_fold_data[v:foldstart][1] + let foldtext = b:nm_raw_info['foldtext'] + return foldtext[v:foldstart] endfunction -" --- helper functions +" --- helper functions {{{1 function! s:NM_newBuffer(ft, content) enew @@ -205,7 +421,7 @@ function! s:NM_run(args) endfunction -" --- command handler +" --- command handler {{{1 function! NotMuch(args) if !strlen(a:args) @@ -223,11 +439,13 @@ function! CompleteNotMuch(arg_lead, cmd_line, cursor_pos) endfunction -" --- glue +" --- glue {{{1 command! -nargs=* -complete=customlist,CompleteNotMuch NotMuch call NotMuch() cabbrev notmuch =(getcmdtype()==':' && getcmdpos()==1 ? 'NotMuch' : 'notmuch') -" --- hacks, only for development :) +" --- hacks, only for development :) {{{1 nnoremap ,nmr :source ~/.vim/plugin/notmuch.vim:call NotMuch('') + +" vim: set ft=vim ts=8 sw=8 et foldmethod=marker :