X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=vim%2Fplugin%2Fnotmuch.vim;h=968b9aa61fd68330fb78ff54113bfb1de78fcf67;hp=a15f4df5960c5b11e01f5f3b4bc9fe4d2279f361;hb=5a2d760067559b3fabf8718d189e815e7e6402da;hpb=5e7df3c17e0d07d5252820beb5ef1e0bfea86498 diff --git a/vim/plugin/notmuch.vim b/vim/plugin/notmuch.vim index a15f4df5..968b9aa6 100644 --- a/vim/plugin/notmuch.vim +++ b/vim/plugin/notmuch.vim @@ -25,10 +25,45 @@ if !exists('g:notmuch_cmd') let g:notmuch_cmd = 'notmuch' endif +if !exists('g:notmuch_search_reverse') + let g:notmuch_search_reverse = 1 +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\\)[{}].*$' + +let s:notmuch_show_message_parse_regexp = '\(id:[^ ]*\) depth:\([0-9]*\) filename:\(.*\)$' +let s:notmuch_show_tags_regexp = '(\([^)]*\))$' + +let s:notmuch_show_signature_regexp = '^\(-- \?\|_\+\)$' +let s:notmuch_show_signature_lines_max = 12 + +let s:notmuch_show_citation_regexp = '^\s*>' + +let s:notmuch_show_headers = [ 'Subject', 'From' ] + +let g:notmuch_show_fold_signatures = 1 +let g:notmuch_show_fold_citations = 1 + " --- implement search screen function! s:NM_cmd_search(words) - let data = s:NM_run(['search'] + a:words) + let cmd = ['search'] + if g:notmuch_search_reverse + let cmd = cmd + ['--reverse'] + 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") @@ -36,19 +71,20 @@ 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:'))) setlocal cursorline setlocal nowrap endfunction function! s:NM_search_display() - let line = line('.') - 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 info = b:nm_raw_data[line] + let line = line('.') + let info = b:nm_raw_lines[line-1] let what = split(info, '\s\+')[0] call s:NM_cmd_show([what]) endif @@ -58,18 +94,272 @@ endfunction " --- implement show screen 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") - call s:NM_newBuffer('show', data) + let info = s:NM_cmd_show_parse(lines) + + 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=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() + 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, s:notmuch_show_part_end_regexp) != -1 + let part_end = len(info['disp']) + else + call add(info['disp'], line) + endif + + if in_part == 'text/plain' + if !part_end && mode_type == '' + if match(line, s:notmuch_show_signature_regexp) != -1 + let mode_type = 'sig' + let mode_start = len(info['disp']) + elseif match(line, s: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, s: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) > s: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 + + 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, s: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, s: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, s: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(s:notmuch_show_headers, m[1]) != -1 + call add(info['disp'], line) + endif + endif + endif + endif + + elseif in_message + if match(line, s: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, s:notmuch_show_header_begin_regexp) != -1 + let in_header = 1 + continue + + elseif match(line, s:notmuch_show_body_begin_regexp) != -1 + let body_start = len(info['disp']) + 1 + let in_body = 1 + continue + endif + + else + if match(line, s:notmuch_show_message_begin_regexp) != -1 + let msg['start'] = len(info['disp']) + 1 + + let m = matchlist(line, s: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 + + 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 - exec printf("nnoremap q :b %d", bufnr) +function! NM_cmd_show_foldtext() + let foldtext = b:nm_raw_info['foldtext'] + return foldtext[v:foldstart] endfunction -" --- helper function +" --- helper functions function! s:NM_newBuffer(ft, content) enew