"================================================= " File: vmail.vim " Description: Vmail is a Vim interface to Gmail. " Author: Daniel Choi " ================================================ if exists("g:loaded_vmail") || &cp finish endif let g:loaded_vmail = 1 if !exists("g:vmail_flagged_color") let g:vmail_flagged_color = "ctermfg=green guifg=green guibg=grey" endif let s:mailbox = $VMAIL_MAILBOX let s:query = $VMAIL_QUERY let s:browser_command = $VMAIL_BROWSER let s:append_file = '' let s:drb_uri = $DRB_URI let s:client_script = "vmail_client " . shellescape(s:drb_uri) . " " let s:set_window_width_command = s:client_script . "window_width= " let s:list_mailboxes_command = s:client_script . "list_mailboxes " let s:show_message_command = s:client_script . "show_message " let s:update_command = s:client_script . "update" let s:fetch_headers_command = s:client_script . "fetch_headers " let s:select_mailbox_command = s:client_script . "select_mailbox " let s:search_command = s:client_script . "search " let s:more_messages_command = s:client_script . "more_messages " let s:flag_command = s:client_script . "flag " let s:append_to_file_command = s:client_script . "append_to_file " let s:move_to_command = s:client_script . "move_to " let s:copy_to_command = s:client_script . "copy_to " let s:new_message_template_command = s:client_script . "new_message_template " let s:reply_template_command = s:client_script . "reply_template " let s:forward_template_command = s:client_script . "forward_template " let s:deliver_command = s:client_script . "deliver " let s:save_attachments_command = s:client_script . "save_attachments " let s:open_html_part_command = s:client_script . "open_html_part " let s:show_help_command = s:client_script . "show_help" let s:message_bufname = "current_message.txt" function! VmailStatusLine() return "%<%f\ " . s:mailbox . " " . s:query . "%r%=%-14.(%l,%c%V%)\ %Y %P" endfunction function! s:create_list_window() setlocal nomodifiable setlocal noswapfile setlocal nowrap setlocal nonumber setlocal foldcolumn=0 setlocal nospell setlocal textwidth=0 setlocal noreadonly setlocal ft=vmailMessageList " let user set this " setlocal cursorline " we need the bufnr to find the window later let s:listbufnr = bufnr('%') let s:listbufname = bufname('%') setlocal statusline=%!VmailStatusLine() call s:message_list_window_mappings() autocmd BufNewFile,BufRead *.txt setlocal modifiable endfunction " the message display buffer window function! s:create_message_window() exec "split " . s:message_bufname setlocal modifiable setlocal buftype=nofile let s:message_window_bufnr = bufnr('%') call s:message_window_mappings() setlocal ft=mail close endfunction function! s:system_with_error_handling(command) let res = system(a:command) if res =~ 'VMAIL_ERROR' echoe "ERROR" res return "" else return res end endfunction function! s:show_message(stay_in_message_list) let line = getline(line(".")) if match(line, '^> Load') != -1 setlocal modifiable delete call s:more_messages() return endif let s:uid = shellescape(matchstr(line, '\S\+$')) if s:uid == "" return end " mark as read let newline = substitute(line, '^\V*+', '* ', '') let newline = substitute(newline, '^\V+ ', ' ', '') setlocal modifiable call setline(line('.'), newline) setlocal nomodifiable write " this just clears the command line and prevents the screen from " moving up when the next echo statement executes: " call feedkeys(":\<cr>") " redraw let command = s:show_message_command . s:uid echom "Loading message ". s:uid .". Please wait..." redrawstatus let res = s:system_with_error_handling(command) call s:focus_message_window() set modifiable 1,$delete put =res " critical: don't call execute 'normal \<cr>' " call feedkeys("<cr>") 1delete normal 1Gjk set nomodifiable if a:stay_in_message_list call s:focus_list_window() end redraw endfunction " from message window function! s:show_next_message() let fullscreen = (bufwinnr(s:listbufnr) == -1) " we're in full screen message mode if fullscreen 3split exec 'b'. s:listbufnr else call s:focus_list_window() end normal j call s:show_message(1) normal zz wincmd p redraw endfunction function! s:show_previous_message() let fullscreen = (bufwinnr(s:listbufnr) == -1) " we're in full screen message mode if fullscreen 3split exec 'b'. s:listbufnr else call s:focus_list_window() end normal k if line('.') != line('$') call s:show_message(1) endif normal zz wincmd p redraw endfunction " from message list window function! s:show_next_message_in_list() if line('.') != line('$') normal j call s:show_message(1) endif endfunction function! s:show_previous_message_in_list() if line('.') != 1 normal k call s:show_message(1) endif endfunction " invoked from withint message window function! s:show_raw() let command = s:show_message_command . s:uid . ' raw' echo command setlocal modifiable 1,$delete let res = s:system_with_error_handling(command) put =res 1delete normal 1G setlocal nomodifiable endfunction function! s:focus_list_window() if bufwinnr(s:listbufnr) == winnr() return end let winnr = bufwinnr(s:listbufnr) if winnr == -1 " create window split exec "buffer" . s:listbufnr else exec winnr . "wincmd w" endif call s:set_list_colors() " call feedkeys("\<c-l>") " prevents screen artifacts when user presses return too fast " turn this off though, because it causes an annoying flash endfunction function! s:focus_message_window() let winnr = bufwinnr(s:message_window_bufnr) if winnr == -1 " create window exec "rightbelow split " . s:message_bufname else exec winnr . "wincmd w" endif endfunction func! s:close_message_window() if winnr('$') > 1 close! else call s:focus_list_window() wincmd p close! normal z. endif endfunc " gets new messages since last update function! s:update() let command = s:update_command echo "Checking for new messages. Please wait..." let res = s:system_with_error_handling(command) let lines = split(res, '\n') if len(lines) > 0 setlocal modifiable call append(0, lines) setlocal nomodifiable write! let num = len(lines) call cursor(num, 0) normal z. redraw echom "You have " . num . " new message" . (num == 1 ? '' : 's') . "!" else redraw echom "No new messages" endif endfunction " function argument a:read: Represents the next state " 0 means unread, 1 means read. function! s:mark_as_read_unread(read) range let uid_set = s:collect_uids(a:firstline, a:lastline) let nummsgs = len(uid_set) " decide whether to set messages to SEEN or UNSEEN let action = (a:read ? " +" : " -") . "FLAGS" " construct the imap command let command = s:flag_command . shellescape(join(uid_set, ',')) . action . " SEEN" " do the real imap flagging let res = s:system_with_error_handling(command) setlocal modifiable let lnum = a:firstline while lnum <= a:lastline let line = getline(lnum) if action ==# " +FLAGS" let newline = substitute(line, '^*+', '* ', '') let newline = substitute(newline, '^+ ', ' ', '') else let newline = substitute(line, '^ ', '+', '') let newline = substitute(newline, '^\* ', '*+', '') endif call setline(lnum, newline) let lnum += 1 endwhile setlocal nomodifiable write redraw echom nummsgs ." conversation(s) have been marked as " . (a:read ? "read" : "unread") . "." endfunction function! s:toggle_star() range let uid_set = s:collect_uids(a:firstline, a:lastline) let nummsgs = len(uid_set) let flag_symbol = "^*" " check if starred already let action = " +FLAGS" if (match(getline(a:firstline), flag_symbol) != -1) let action = " -FLAGS" endif let command = s:flag_command . shellescape(join(uid_set, ',')) . action . " Flagged" if nummsgs == 1 echom "Toggling flag on message" else echom "Toggling flags on " . nummsgs . " messages" endif " toggle * on lines let res = s:system_with_error_handling(command) setlocal modifiable let lnum = a:firstline while lnum <= a:lastline let line = getline(lnum) if action == " +FLAGS" let newline = substitute(line, '^ ', '*', '') let newline = substitute(newline, '^+ ', '*+', '') else let newline = substitute(line, '^*+', '+ ', '') let newline = substitute(newline, '^* ', ' ', '') endif call setline(lnum, newline) let lnum += 1 endwhile setlocal nomodifiable write redraw if nummsgs == 1 echom "Toggled flag on message" else echom "Toggled flags on " . nummsgs . " messages" endif endfunction " flag can be Deleted or spam func! s:delete_messages(flag) range let uid_set = s:collect_uids(a:firstline, a:lastline) let nummsgs = len(uid_set) let command = s:flag_command . shellescape(join(uid_set, ',')) . " +FLAGS " . a:flag if nummsgs == 1 echom "Deleting message" else echom "Deleting " . nummsgs . " messages" endif let res = s:system_with_error_handling(command) setlocal modifiable exec "silent " . a:firstline . "," . a:lastline . "delete" setlocal nomodifiable write redraw echo nummsgs . " message" . (nummsgs == 1 ? '' : 's') . " marked " . a:flag endfunc func! s:archive_messages() range let uid_set = s:collect_uids(a:firstline, a:lastline) let nummsgs = len(uid_set) let command = s:move_to_command . shellescape(join(uid_set, ',')) . ' ' . "all" echo "Archiving message" . (nummsgs == 1 ? '' : 's') let res = s:system_with_error_handling(command) setlocal modifiable exec "silent " . a:firstline . "," . a:lastline . "delete" setlocal nomodifiable write redraw echo nummsgs . " message" . (nummsgs == 1 ? '' : 's') . " archived" endfunc " -------------------------------------------------------------------------------- " append text bodies of a set of messages to a file func! s:append_messages_to_file() range let uid_set = s:collect_uids(a:firstline, a:lastline) let nummsgs = len(uid_set) let append_file = input("print messages to file: ", s:append_file) if append_file == '' echom "Canceled" return endif let s:append_file = append_file let command = s:append_to_file_command . shellescape(join(uid_set, ',')) . ' ' . s:append_file echo "Appending " . nummsgs . " message" . (nummsgs == 1 ? '' : 's') . " to " . s:append_file . ". Please wait..." let res = s:system_with_error_handling(command) echo res redraw endfunc " -------------------------------------------------------------------------------- " move to another mailbox function! s:move_to_mailbox(copy) range let s:copy_to_mailbox = a:copy let uid_set = s:collect_uids(a:firstline, a:lastline) let s:nummsgs = len(uid_set) let s:uid_set = shellescape(join(uid_set, ',')) " now prompt use to select mailbox if !exists("s:mailboxes") call s:get_mailbox_list() endif leftabove split MailboxSelect setlocal buftype=nofile setlocal noswapfile setlocal modifiable resize 1 let prompt = "select mailbox to " . (a:copy ? 'copy' : 'move') . " to: " call setline(1, prompt) normal $ inoremap <silent> <buffer> <cr> <Esc>:call <SID>complete_move_to_mailbox()<CR> inoremap <silent> <buffer> <esc> <Esc>:q<cr> setlocal completefunc=CompleteMoveMailbox " c-p clears the line let s:firstline = a:firstline let s:lastline = a:lastline call feedkeys("a\<c-x>\<c-u>\<c-p>", 't') " save these in script scope to delete the lines when move completes endfunction function! s:complete_move_to_mailbox() let mailbox = get(split(getline(line('.')), ": "), 1) close if s:copy_to_mailbox let command = s:copy_to_command . s:uid_set . ' ' . shellescape(mailbox) else let command = s:move_to_command . s:uid_set . ' ' . shellescape(mailbox) endif redraw echo "Moving uids ". s:uid_set . " to mailbox " . mailbox let res = s:system_with_error_handling(command) setlocal modifiable if !s:copy_to_mailbox exec "silent " . s:firstline . "," . s:lastline . "delete" end setlocal nomodifiable write redraw echo s:nummsgs . " message" . (s:nummsgs == 1 ? '' : 's') . ' ' . (s:copy_to_mailbox ? 'copied' : 'moved') . ' to ' . mailbox endfunction function! CompleteMoveMailbox(findstart, base) if !exists("s:mailboxes") call s:get_mailbox_list() endif if a:findstart " locate the start of the word let line = getline('.') let start = col('.') - 1 while start > 0 && line[start - 1] =~ '\a' let start -= 1 endwhile return start else " find months matching with "a:base" let res = [] for m in s:mailboxes if m == s:mailbox continue endif if m =~ '^' . a:base call add(res, m) endif endfor return res endif endfun " -------------------------------------------------------------------------------- function! s:get_mailbox_list() let command = s:list_mailboxes_command redraw echo command let res = s:system_with_error_handling(command) let s:mailboxes = split(res, "\n", '') endfunction " ------------------------------------------------------------------------------- " select mailbox function! s:mailbox_window() call s:get_mailbox_list() leftabove split MailboxSelect setlocal buftype=nofile setlocal noswapfile setlocal modifiable resize 1 inoremap <silent> <buffer> <cr> <Esc>:call <SID>select_mailbox()<CR> inoremap <silent> <buffer> <esc> <Esc>:q<cr> setlocal completefunc=CompleteMailbox " c-p clears the line call setline(1, "select mailbox to switch to: ") normal $ call feedkeys("a\<c-x>\<c-u>\<c-p>", 't') endfunction function! CompleteMailbox(findstart, base) if !exists("s:mailboxes") call s:get_mailbox_list() endif if a:findstart " locate the start of the word let line = getline('.') let start = col('.') - 1 while start > 0 && line[start - 1] =~ '\a' let start -= 1 endwhile return start else " find mailboxes matching with "a:base" let res = [] for m in s:mailboxes if m =~ '^' . a:base call add(res, m) endif endfor return res endif endfun function! s:select_mailbox() let mailbox = get(split(getline(line('.')), ": "), 1) close call s:focus_message_window() close " check if mailbox is a real mailbox if (index(s:mailboxes, mailbox) == -1) return endif let s:mailbox = mailbox let s:query = "all" let command = s:select_mailbox_command . shellescape(s:mailbox) redraw echom "Selecting mailbox: ". s:mailbox . ". Please wait..." call s:system_with_error_handling(command) redraw " reset window width now call s:system_with_error_handling(s:set_window_width_command . winwidth(1)) " now get latest 100 messages call s:focus_list_window() setlocal modifiable let command = s:search_command . shellescape("all") echo "Loading messages..." let res = s:system_with_error_handling(command) silent 1,$delete silent! put! =res execute "normal Gdd\<c-y>" setlocal nomodifiable write normal gg redraw echom "Current mailbox: ". s:mailbox endfunction func! s:search_query() if !exists("s:query") let s:query = "" endif let s:query = input("search query: ", s:query) call s:do_search() endfunc function! s:do_search() " empty query if match(s:query, '^\s*$') != -1 return endif " close message window if open call s:focus_message_window() close let command = s:search_command . shellescape(s:query) redraw call s:focus_list_window() setlocal modifiable echo "Running query on " . s:mailbox . ": " . s:query . ". Please wait..." let res = s:system_with_error_handling(command) silent! 1,$delete silent! put! =res execute "silent normal Gdd\<c-y>" setlocal nomodifiable write normal gg endfunction function! s:more_messages() let command = s:more_messages_command echo "Fetching more messages. Please wait..." let res = s:system_with_error_handling(command) setlocal modifiable let lines = split(res, "\n") call append(line('$'), lines) " execute "normal Gdd\<c-y>" setlocal nomodifiable normal j redraw echo "Done" endfunction " -------------------------------------------------------------------------------- " compose reply, compose, forward, save draft function! s:compose_reply(all) let command = s:reply_template_command if a:all let command = command . ' 1' endif call s:open_compose_window(command) " cursor after headers normal 1G} endfunction function! s:compose_message() let command = s:new_message_template_command call s:open_compose_window(command) " position cursor after to: " call search("^to:") " normal A endfunction function! s:compose_forward() let command = s:forward_template_command call s:open_compose_window(command) " call search("^to:") " normal A endfunction func! s:open_compose_window(command) redraw echo a:command let res = s:system_with_error_handling(a:command) let previous_winnr = winnr() only split compose_message.txt setlocal modifiable wincmd p close! silent! 1,$delete silent! put! =res redraw "call feedkeys("\<cr>") call s:compose_window_mappings() setlocal completefunc=CompleteContact setlocal ft=mail normal 1G endfunc func! s:turn_into_compose_window() call s:compose_window_mappings() setlocal completefunc=CompleteContact endfunc " contacts.txt file should be generated. " grep works well, does partial matches function! CompleteContact(findstart, base) if !exists("s:mailboxes") call s:get_mailbox_list() endif if a:findstart " locate the start of the word let line = getline('.') let start = col('.') - 1 while start > 0 && line[start - 1] =~ '\a' let start -= 1 endwhile return start else " find contacts " model regex: match at beginning of line, or inside < > wrapping " email addr " '\(^ho\|<ho\)' " let regex = shellescape('\(^' . a:base . '\|<' . a:base . '\)') let regex = shellescape(a:base) let matches = s:system_with_error_handling("grep -i " . regex . " " . $VMAIL_CONTACTS_FILE) return split(matches, "\n") endif endfun func! s:close_and_focus_list_window() call s:focus_list_window() wincmd p close! normal z. endfunc function! s:send_message() let mail = join(getline(1,'$'), "\n") echo "Sending message" let res = system(s:deliver_command, mail) if match(res, '^Failed') == -1 write! call s:close_and_focus_list_window() endif echom substitute(res, '[\s\r\n]\+$', '', '') redraw endfunction " -------------------------------------------------------------------------------- " Update message list with new emails. func! UPDATE_MESSAGE_LIST(filename) if s:mailbox == 'INBOX' if bufnr('%') == s:listbufnr call s:update_and_redraw_message_list(a:filename) " If current_message is open with message list in another split. elseif bufname('%') == s:message_bufname && index(tabpagebuflist(), s:listbufnr) >= 0 let message_bufnr = bufnr('%') " Switch to message list window and update it. while bufnr('%') != s:listbufnr | wincmd w | endwhile call s:update_and_redraw_message_list(a:filename) while bufnr('%') != message_bufnr | wincmd w | endwhile else call s:update_message_list(a:filename) endif endif endfunc func! s:update_and_redraw_message_list(filename) call s:update_message_list(a:filename) edit! redraw! endfunc func! s:update_message_list(filename) let newer_contents = readfile(a:filename) let older_contents = readfile(s:listbufname) let updated_contents = extend(newer_contents, older_contents) call writefile(updated_contents, s:listbufname) endfunc " -------------------------------------------------------------------------------- " call from inside message window with <Leader>h func! s:open_html_part() let command = s:open_html_part_command " the command saves the html part to a local file let outfile = s:system_with_error_handling(command) " todo: allow user to change open in browser command? exec "!" . s:browser_command . ' ' . outfile endfunc func! s:save_attachments() if !exists("s:savedir") let s:savedir = getcwd() . "/attachments" end let s:savedir = input("save attachments to directory: ", s:savedir, "dir") let command = s:save_attachments_command . s:savedir let res = s:system_with_error_handling(command) echo res endfunc func! s:attach_file(file) normal gg normal } let attach = "attach: " . a:file put =attach endfunc " -------------------------------------------------------------------------------- func! s:toggle_maximize_window() if winnr('$') > 1 only " normal z. elseif bufwinnr(s:listbufnr) == winnr() call s:show_message(1) else " we're in the message window call s:focus_list_window() wincmd p endif endfunc " maybe not DRY enough, but fix that later " also, come up with a more precise regex pattern for matching hyperlinks func! s:open_href(all) range let pattern = 'https\?:[^ >)\]]\+' let n = 0 " range version if a:firstline < a:lastline let lnum = a:firstline while lnum <= a:lastline let href = matchstr(getline(lnum), pattern) if href != "" let command = s:browser_command ." ".shellescape(href)." &" call s:system_with_error_handling(command) let n += 1 endif let lnum += 1 endwhile echom 'opened '.n.' links' return end let line = search(pattern, 'cw') if line && a:all while line let href = matchstr(getline(line('.')), pattern) let command = s:browser_command ." ".shellescape(href)." &" call s:system_with_error_handling(command) let n += 1 let line = search('https\?:', 'W') endwhile echom 'opened '.n.' links' else let href = matchstr(getline(line('.')), pattern) let command = s:browser_command ." ".shellescape(href)." &" call s:system_with_error_handling(command) echom 'opened '.href endif endfunc " -------------------------------------------------------------------------------- " HELP func! s:show_help() let command = s:browser_command . ' ' . shellescape('http://danielchoi.com/software/vmail.html') call s:system_with_error_handling(command) "let helpfile = s:system_with_error_handling(s:show_help_command) "exec "split " . helpfile endfunc " -------------------------------------------------------------------------------- " CONVENIENCE FUNCS function! s:collect_uids(startline, endline) let uid_set = [] let lnum = a:startline while lnum <= a:endline let uid = matchstr(getline(lnum), '\S\+$') call add(uid_set, uid) let lnum += 1 endwhile return uid_set endfunc " -------------------------------------------------------------------------------- " MAPPINGS func! s:message_window_mappings() if !hasmapto('<Plug>VmailMessageWindow_FocusListWindow') nmap <buffer> <CR> <Plug>VmailMessageWindow_FocusListWindow endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_FocusListWindow <C-W>=:call <SID>focus_list_window()<CR> if !hasmapto('<Plug>VmailMessageWindow_Reply') nmap <buffer> <leader>r <Plug>VmailMessageWindow_Reply endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_Reply :call <SID>compose_reply(0)<CR> if !hasmapto('<Plug>VmailMessageWindow_ReplyToAll') nmap <buffer> <leader>a <Plug>VmailMessageWindow_ReplyToAll endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ReplyToAll :call <SID>compose_reply(1)<CR> if !hasmapto('<Plug>VmailMessageWindow_ShowRaw') nmap <buffer> <leader>R <Plug>VmailMessageWindow_ShowRaw endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ShowRaw :call <SID>show_raw()<CR> if !hasmapto('<Plug>VmailMessageWindow_Forward') nmap <buffer> <leader>f <Plug>VmailMessageWindow_Forward endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_Forward :call <SID>compose_forward()<CR> if !hasmapto('<Plug>VmailMessageWindow_ShowNext') nmap <buffer> <C-j> <Plug>VmailMessageWindow_ShowNext nmap <buffer> <leader>j <Plug>VmailMessageWindow_ShowNext endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ShowNext :call <SID>show_next_message()<CR> if !hasmapto('<Plug>VmailMessageWindow_ShowPrev') nmap <buffer> <C-k> <Plug>VmailMessageWindow_ShowPrev nmap <buffer> <leader>k <Plug>VmailMessageWindow_ShowPrev endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ShowPrev :call <SID>show_previous_message()<CR> if !hasmapto('<Plug>VmailMessageWindow_ComposeMessage') nmap <buffer> <leader>c <Plug>VmailMessageWindow_ComposeMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ComposeMessage :call <SID>compose_message()<CR> if !hasmapto('<Plug>VmailMessageWindow_OpenHTML') nmap <buffer> <leader>h <Plug>VmailMessageWindow_OpenHTML endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_OpenHTML :call <SID>open_html_part()<CR> if !hasmapto('<Plug>VmailMessageWindow_CloseWindow') nmap <buffer> <leader>q <Plug>VmailMessageWindow_CloseWindow endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_CloseWindow :call <SID>close_message_window()<CR> if !hasmapto('<Plug>VmailMessageWindow_DeleteMessage') nmap <buffer> <leader># <Plug>VmailMessageWindow_DeleteMessage nmap <buffer> <leader>3 <Plug>VmailMessageWindow_DeleteMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_DeleteMessage :close<cr>:call <SID>focus_list_window()<CR>:call <SID>delete_messages("Deleted")<CR> if !hasmapto('<Plug>VmailMessageWindow_ToggleStar') nmap <buffer> <leader>* <Plug>VmailMessageWindow_ToggleStar nmap <buffer> <leader>8 <Plug>VmailMessageWindow_ToggleStar endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ToggleStar :call <SID>focus_list_window()<cr>:call <SID>toggle_star()<CR> if !hasmapto('<Plug>VmailMessageWindow_MarkAsRead') nmap <buffer> I <Plug>VmailMessageWindow_MarkAsRead endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_MarkAsRead :call <SID>focus_list_window()<cr>:call <SID>mark_as_read_unread(1)<CR> if !hasmapto('<Plug>VmailMessageWindow_MarkAsUnread') nmap <buffer> U <Plug>VmailMessageWindow_MarkAsUnread endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_MarkAsUnread :call <SID>focus_list_window()<cr>:call <SID>mark_as_read_unread(0)<CR> if !hasmapto('<Plug>VmailMessageWindow_MarkAsSpam') nmap <buffer> <leader>! <Plug>VmailMessageWindow_MarkAsSpam nmap <buffer> <leader>1 <Plug>VmailMessageWindow_MarkAsSpam endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_MarkAsSpam :close<cr>:call <SID>focus_list_window()<cr>:call <SID>delete_messages("spam")<CR> if !hasmapto('<Plug>VmailMessageWindow_ArchiveMessage') nmap <buffer> <leader>e <Plug>VmailMessageWindow_ArchiveMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ArchiveMessage :call <SID>focus_list_window()<cr>:call <SID>archive_messages()<CR> if !hasmapto('<Plug>VmailMessageWindow_MoveToMailbox') nmap <buffer> <leader>b <Plug>VmailMessageWindow_MoveToMailbox endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_MoveToMailbox :call <SID>focus_list_window()<cr>:call <SID>move_to_mailbox(0)<CR> if !hasmapto('<Plug>VmailMessageWindow_CopyToMailbox') nmap <buffer> <leader>B <Plug>VmailMessageWindow_CopyToMailbox endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_CopyToMailbox :call <SID>focus_list_window()<cr>:call <SID>move_to_mailbox(1)<CR> if !hasmapto('<Plug>VmailMessageWindow_Update') nmap <buffer> <leader>u <Plug>VmailMessageWindow_Update endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_Update :call <SID>focus_list_window()<cr>:call <SID>update()<CR> if !hasmapto('<Plug>VmailMessageWindow_SwitchMailBox') nmap <buffer> <leader>m <Plug>VmailMessageWindow_SwitchMailBox endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_SwitchMailBox :call <SID>focus_list_window()<cr>:call <SID>mailbox_window()<CR> if !hasmapto('<Plug>VmailMessageWindow_SaveAttachment') nmap <buffer> <leader>A <Plug>VmailMessageWindow_SaveAttachment endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_SaveAttachment :call <SID>save_attachments()<CR> if !hasmapto('<Plug>VmailMessageWindow_ToggleWindow') nmap <buffer> <Space> <Plug>VmailMessageWindow_ToggleWindow endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_ToggleWindow :call <SID>toggle_maximize_window()<cr> if !hasmapto('<Plug>VmailMessageWindow_AppendMessagesToFile') nmap <buffer> <leader>vp <Plug>VmailMessageWindow_AppendMessagesToFile endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_AppendMessagesToFile :call <SID>focus_list_window()<cr>:call <SID>append_messages_to_file()<CR> if !hasmapto('<Plug>VmailMessageWindow_Search') nmap <buffer> <leader>s <Plug>VmailMessageWindow_Search endif nnoremap <buffer> <unique> <script> <Plug>VmailMessageWindow_Search :call <SID>focus_list_window()<cr>:call <SID>search_query()<cr> endfunc func! s:message_list_window_mappings() if !hasmapto('<Plug>VmailOpenMessage') nmap <buffer> <CR> <Plug>VmailOpenMessage nmap <buffer> <LeftMouse> <Plug>VmailOpenMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailOpenMessage :call <SID>show_message(0)<CR> if !hasmapto('<Plug>VmailPreviewMessage') nmap <buffer> l <Plug>VmailPreviewMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailPreviewMessage :call <SID>show_message(1)<CR> if !hasmapto('<Plug>VmailExit') nmap <buffer> <leader>q <Plug>VmailExit endif nnoremap <buffer> <unique> <script> <Plug>VmailExit :qall!<CR> if !hasmapto('<Plug>VmailToggleStar') nmap <buffer> <leader>* <Plug>VmailToggleStar xmap <buffer> <leader>* <Plug>VmailToggleStar nmap <buffer> <leader>8 <Plug>VmailToggleStar xmap <buffer> <leader>8 <Plug>VmailToggleStar endif nnoremap <buffer> <unique> <script> <Plug>VmailToggleStar :call <SID>toggle_star()<CR> xnoremap <buffer> <unique> <script> <Plug>VmailToggleStar :call <SID>toggle_star()<CR> if !hasmapto('<Plug>VmailMarkAsUnread') nmap <buffer> U <Plug>VmailMarkAsUnread xmap <buffer> U <Plug>VmailMarkAsUnread endif nnoremap <buffer> <unique> <script> <Plug>VmailMarkAsUnread :call <SID>mark_as_read_unread(0)<CR> xnoremap <buffer> <unique> <script> <Plug>VmailMarkAsUnread :call <SID>mark_as_read_unread(0)<CR> if !hasmapto('<Plug>VmailMarkAsRead') nmap <buffer> I <Plug>VmailMarkAsRead xmap <buffer> I <Plug>VmailMarkAsRead endif nnoremap <buffer> <unique> <script> <Plug>VmailMarkAsRead :call <SID>mark_as_read_unread(1)<CR> xnoremap <buffer> <unique> <script> <Plug>VmailMarkAsRead :call <SID>mark_as_read_unread(1)<CR> if !hasmapto('<Plug>VmailDelete') nmap <buffer> <leader># <Plug>VmailDelete xmap <buffer> <leader># <Plug>VmailDelete nmap <buffer> <leader>3 <Plug>VmailDelete xmap <buffer> <leader>3 <Plug>VmailDelete endif nnoremap <buffer> <unique> <script> <Plug>VmailDelete :call <SID>delete_messages("Deleted")<CR> xnoremap <buffer> <unique> <script> <Plug>VmailDelete :call <SID>delete_messages("Deleted")<CR> if !hasmapto('<Plug>VmailMarkAsSpam') nmap <buffer> <leader>! <Plug>VmailMarkAsSpam xmap <buffer> <leader>! <Plug>VmailMarkAsSpam nmap <buffer> <leader>1 <Plug>VmailMarkAsSpam xmap <buffer> <leader>1 <Plug>VmailMarkAsSpam endif nnoremap <buffer> <unique> <script> <Plug>VmailMarkAsSpam :call <SID>delete_messages("spam")<CR> xnoremap <buffer> <unique> <script> <Plug>VmailMarkAsSpam :call <SID>delete_messages("spam")<CR> if !hasmapto('<Plug>VmailArchiveMessage') nmap <buffer> <leader>e <Plug>VmailArchiveMessage xmap <buffer> <leader>e <Plug>VmailArchiveMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailArchiveMessage :call <SID>archive_messages()<CR> xnoremap <buffer> <unique> <script> <Plug>VmailArchiveMessage :call <SID>archive_messages()<CR> if !hasmapto('<Plug>VmailAppendMessagesToFile') nmap <buffer> <leader>vp <Plug>VmailAppendMessagesToFile xmap <buffer> <leader>vp <Plug>VmailAppendMessagesToFile endif nnoremap <buffer> <unique> <script> <Plug>VmailAppendMessagesToFile :call <SID>append_messages_to_file()<CR> xnoremap <buffer> <unique> <script> <Plug>VmailAppendMessagesToFile :call <SID>append_messages_to_file()<CR> if !hasmapto('<Plug>VmailUpdate') nmap <buffer> u <Plug>VmailUpdate nmap <buffer> <leader>u <Plug>VmailUpdate endif nnoremap <buffer> <unique> <script> <Plug>VmailUpdate :call <SID>update()<CR> if !hasmapto('<Plug>VmailSearch') nmap <buffer> <leader>s <Plug>VmailSearch endif nnoremap <buffer> <unique> <script> <Plug>VmailSearch :call <SID>search_query()<CR> if !hasmapto('<Plug>VmailSwitchMailbox') nmap <buffer> <leader>m <Plug>VmailSwitchMailbox endif nnoremap <buffer> <unique> <script> <Plug>VmailSwitchMailbox :call <SID>mailbox_window()<CR> if !hasmapto('<Plug>VmailMoveToMailbox') nmap <buffer> <leader>b <Plug>VmailMoveToMailbox xmap <buffer> <leader>b <Plug>VmailMoveToMailbox endif nnoremap <buffer> <unique> <script> <Plug>VmailMoveToMailbox :call <SID>move_to_mailbox(0)<CR> xnoremap <buffer> <unique> <script> <Plug>VmailMoveToMailbox :call <SID>move_to_mailbox(0)<CR> if !hasmapto('<Plug>VmailCopyToMailbox') nmap <buffer> <leader>B <Plug>VmailCopyToMailbox xmap <buffer> <leader>B <Plug>VmailCopyToMailbox endif nnoremap <buffer> <unique> <script> <Plug>VmailCopyToMailbox :call <SID>move_to_mailbox(1)<CR> xnoremap <buffer> <unique> <script> <Plug>VmailCopyToMailbox :call <SID>move_to_mailbox(1)<CR> if !hasmapto('<Plug>VmailComposeNew') nmap <buffer> <leader>c <Plug>VmailComposeNew endif nnoremap <buffer> <unique> <script> <Plug>VmailComposeNew :call <SID>compose_message()<CR> if !hasmapto('<Plug>VmailComposeReply') nmap <buffer> <Leader>r <Plug>VmailComposeReply endif nnoremap <buffer> <unique> <script> <Plug>VmailComposeReply :call <SID>show_message(0)<CR>:call <SID>compose_reply(0)<CR> if !hasmapto('<Plug>VmailComposeReplyAll') nmap <buffer> <Leader>a <Plug>VmailComposeReplyAll endif nnoremap <buffer> <unique> <script> <Plug>VmailComposeReplyAll :call <SID>show_message(0)<CR>:call <SID>compose_reply(1)<CR> if !hasmapto('<Plug>VmailForward') nmap <buffer> <Leader>f <Plug>VmailForward endif nnoremap <buffer> <unique> <script> <Plug>VmailForward :call <SID>show_message(0)<CR>:call <SID>compose_forward()<CR> if !hasmapto('<Plug>VmailShowNextMessage') nmap <buffer> <C-j> <Plug>VmailShowNextMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailShowNextMessage :call <SID>show_next_message_in_list()<CR> if !hasmapto('<Plug>VmailShowPrevMessage') nmap <buffer> <C-k> <Plug>VmailShowPrevMessage endif nnoremap <buffer> <unique> <script> <Plug>VmailShowPrevMessage :call <SID>show_previous_message_in_list()<CR> if !hasmapto('<Plug>VmailToggleWindow') nmap <buffer> <Space> <Plug>VmailToggleWindow endif nnoremap <buffer> <unique> <script> <Plug>VmailToggleWindow :call <SID>toggle_maximize_window()<cr> autocmd CursorMoved <buffer> :redraw autocmd BufEnter,BufWinEnter <buffer> :call <SID>set_list_colors() endfunc func! s:compose_window_mappings() if !hasmapto('<Plug>VmailComposeWinClose') nmap <buffer> <leader>q <Plug>VmailComposeWinClose endif nnoremap <buffer> <script> <Plug>VmailComposeWinClose :call <SID>close_and_focus_list_window()<CR> setlocal ai command! -bar -nargs=1 -complete=file VMAttach call s:attach_file(<f-args>) endfunc func! s:global_mappings() " NOTE send_message is a global mapping, so user can load a saved " message from a file and send it nnoremap <silent> <leader>vs :call <SID>send_message()<CR> noremap <silent> <leader>o :call <SID>open_href(0)<cr> noremap <silent> <leader>O :call <SID>open_href(1)<cr> noremap <silent> <leader>? :call <SID>show_help()<cr> noremap <silent> <leader>qq :qal!<cr> endfunc func! s:set_list_colors() if !exists("g:syntax_on") return endif syn clear syn match vmailSizeCol /|\s\+\(< 1k\|\d*\(b\|k\|M\|G\)\)\s\+|/ contains=vmailSeperator contained syn match vmailFirstCol /^.\{-}|/ nextgroup=vmailDateCol syn match vmailFirstColAnswered /An/ contained containedin=vmailFirstCol syn match vmailFirstColForward /\$F/ contained containedin=vmailFirstCol syn match vmailFirstColNotJunk /No/ contained containedin=vmailFirstCol " Examples matched by vmailDateCol: " | Dec 15 11:59pm | " | Dec 15 2008 | " | 15.12 23:59 | " | 15.12 2008 | syn match vmailDateCol /\s\+\(... \d\d \(\(\d\d:\d\d..\)\|\(\d\{4}\)\)\)\|\(\d\d\.\d\d \(\(\d\d:\d\d\)\|\(\d\{4}\)\)\)\s\+|/ nextgroup=vmailFromCol contains=vmailSeperator syn match vmailFromCol /\s.\{-}|\@=/ contained nextgroup=vmailFromSeperator syn match vmailFromColEmail /<[^ ]*/ contained containedin=vmailFromCol syn match vmailFromSeperator /|/ contained nextgroup=vmailSubject syn match vmailSubject /.*\s\+/ contained contains=vmailSizeCol syn match vmailSubjectRe /\cre:\|fwd\?:/ contained containedin=vmailSubject syn match vmailSeperator /|/ contained syn match vmailNewMessage /^\s*+.*/ syn match vmailStarredMessage /^\s*\*.*/ hi def link vmailFirstCol Comment hi def link vmailDateCol Statement hi def link vmailFromCol Identifier hi def link vmailSizeCol Constant hi def link vmailSeperator Comment hi def link vmailFromSeperator vmailSeperator hi def link vmailFromColEmail Comment hi def link vmailSubjectRe Type hi def link vmailFirstColSpec Number hi def link vmailFirstColAnswered vmailFirstColSpec hi def link vmailFirstColForward vmailFirstColSpec hi def link vmailFirstColNotJunk vmailFirstColSpec hi def link vmailSpecialMsg Special hi def link vmailNewMessage vmailSpecialMsg hi def link vmailStarredMessage vmailSpecialMsg syn match VmailBufferFlagged /^*.*/hs=s exec "hi def VmailBufferFlagged " . g:vmail_flagged_color endfunc "TODO see if using LocalLeader and maplocalleader makes more sense let mapleader = "," call s:global_mappings() call s:create_list_window() call s:create_message_window() call s:focus_list_window() " to go list window " send window width call s:system_with_error_handling(s:set_window_width_command . winwidth(1)) autocmd bufreadpost *.txt call <SID>turn_into_compose_window() normal G call s:system_with_error_handling(s:select_mailbox_command . shellescape(s:mailbox)) call s:do_search()