let s:mailbox = '' let s:num_msgs = 0 " number of messages let s:query = '' let s:drb_uri = $DRB_URI let s:client_script = "vmail_client " . s:drb_uri . " " let s:window_width_command = s:client_script . "window_width= " let s:list_mailboxes_command = s:client_script . "list_mailboxes " let s:lookup_command = s:client_script . "lookup " 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:move_to_command = s:client_script . "move_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_draft_command = s:client_script . "save_draft " let s:save_attachments_command = s:client_script . "save_attachments " let s:open_html_command = s:client_script . "open_html_part " let s:message_bufname = "MessageWindow" let s:list_bufname = "MessageListWindow" function! VmailStatusLine() return "%<%f\ " . s:mailbox . "%r%=%-14.(%l,%c%V%)\ %P" endfunction function! s:create_list_window() "setlocal bufhidden=delete "setlocal buftype=nofile setlocal nomodifiable setlocal noswapfile "setlocal nomodifiable setlocal nowrap setlocal nonumber setlocal foldcolumn=0 setlocal nospell " setlocal nobuflisted setlocal textwidth=0 setlocal noreadonly " hi CursorLine cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white setlocal cursorline " we need the bufnr to find the window later let s:listbufnr = bufnr('%') setlocal statusline=%!VmailStatusLine() endfunction " the message display buffer window function! s:create_message_window() exec "split " . s:message_bufname setlocal buftype=nofile " setlocal noswapfile " setlocal nobuflisted let s:message_window_bufnr = bufnr('%') " message window bindings noremap :call focus_list_window() noremap q :call focus_list_window() noremap q q noremap r :call compose_reply(0) noremap a :call compose_reply(1) noremap R :call show_raw() noremap R :call show_raw() noremap f :call compose_forward() " TODO improve this noremap o yE :!open '"' noremap j :call show_next_message() noremap k :call show_previous_message() noremap c :call compose_message() noremap h :call open_html_part() nnoremap q :close nnoremap d :call focus_list_window():call delete_messages("Deleted") nnoremap s :call focus_list_window():call toggle_star() nnoremap m :call focus_list_window():call mailbox_window() nnoremap A :call save_attachments() " go fullscreen nnoremap :call toggle_fullscreen() close endfunction function! s:show_message() let line = getline(line(".")) if match(line, '^> Load') != -1 setlocal modifiable delete call s:more_messages() return endif " remove the unread flag [+] let newline = substitute(line, "\\[+\]\\s*", "", '') 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(":\") redraw let selected_uid = matchstr(line, '^\d\+') let s:current_uid = selected_uid let command = s:lookup_command . s:current_uid echo "Loading message. Please wait..." let res = system(command) call s:focus_message_window() setlocal modifiable 1,$delete put =res " critical: don't call execute 'normal \' " call feedkeys("") 1delete normal 1 normal jk setlocal nomodifiable redraw endfunction function! s:show_next_message() call s:focus_list_window() execute "normal j" execute "normal \" endfunction function! s:show_previous_message() call s:focus_list_window() execute "normal k" if line('.') != 1 execute "normal \" endif endfunction " invoked from withint message window function! s:show_raw() let command = s:lookup_command . s:current_uid . ' raw' echo command setlocal modifiable 1,$delete let res = system(command) put =res 1delete normal 1G setlocal nomodifiable endfunction function! s:focus_list_window() let winnr = bufwinnr(s:listbufnr) if winnr == -1 " create window split exec "buffer" . s:listbufnr else exec winnr . "wincmd w" endif " set up syntax highlighting if has("syntax") syn clear syn match BufferFlagged /^.*[*].*$/hs=s hi def BufferFlagged ctermfg=red ctermbg=black endif " vertically center the cursor line normal z. call feedkeys("\") " prevents screen artifacts when user presses return too fast endfunction function! s:focus_message_window() let winnr = bufwinnr(s:message_window_bufnr) if winnr == -1 " create window exec "botright split " . s:message_bufname else exec winnr . "wincmd w" endif endfunction " gets new messages since last update function! s:update() let command = s:update_command echo command let res = system(command) if match(res, '^\d\+') != -1 setlocal modifiable let line = line('$') $put =res setlocal nomodifiable let num = len(split(res, '\n', '')) redraw call cursor(line + 1, 0) normal z. redraw echo "you have " . num . " new message" . (num == 1 ? '' : 's') . "!" else redraw echo "no new messages" endif endfunction function! s:toggle_star() range let lnum = a:firstline let n = 0 let uids = [] while lnum <= a:lastline let line = getline(lnum) let message_uid = matchstr(line, '^\d\+') call add(uids, message_uid) let lnum = lnum + 1 endwhile let uid_set = join(uids, ",") let flag_symbol = "[*]" " check if starred already let action = " +FLAGS" if (match(line, flag_symbol) != -1) let action = " -FLAGS" endif let command = s:flag_command . uid_set . action . " Flagged" echo command " toggle [*] on lines let res = system(command) setlocal modifiable exec a:firstline . "," . a:lastline . "delete" exec (a:firstline - 1). "put =res" setlocal nomodifiable " if more than 2 lines change, vim forces us to look at a message. " dismiss it. if len(split(res, "\n")) > 2 call feedkeys("\") endif endfunction " flag can be Deleted or [Gmail]/Spam func! s:delete_messages(flag) range let lnum = a:firstline let n = 0 let uids = [] while lnum <= a:lastline let line = getline(lnum) let message_uid = matchstr(line, '^\d\+') call add(uids, message_uid) let lnum = lnum + 1 endwhile let uid_set = join(uids, ",") let command = s:flag_command . uid_set . " +FLAGS " . a:flag echo command let res = system(command) setlocal modifiable exec a:firstline . "," . a:lastline . "delete" setlocal nomodifiable " if more than 2 lines change, vim forces us to look at a message. " dismiss it. if len(uids) > 2 call feedkeys("\") endif endfunc " -------------------------------------------------------------------------------- " move to another mailbox function! s:move_to_mailbox() range let lnum = a:firstline let n = 0 let uids = [] while lnum <= a:lastline let line = getline(lnum) let message_uid = matchstr(line, '^\d\+') call add(uids, message_uid) let lnum = lnum + 1 endwhile let s:uid_set = join(uids, ",") " now prompt use to select mailbox if !exists("s:mailboxes") call s:get_mailbox_list() endif topleft split MailboxSelect setlocal buftype=nofile setlocal noswapfile setlocal modifiable resize 1 inoremap :call complete_move_to_mailbox() inoremap :q set completefunc=CompleteMoveMailbox " c-p clears the line let s:firstline = a:firstline let s:lastline = a:lastline call feedkeys("i\\\", 't') " save these in script scope to delete the lines when move completes endfunction " Open command window to choose a mailbox to move a message to. " Very similar to mailbox_window() function function! s:complete_move_to_mailbox() let mailbox = getline(line('.')) close " check if mailbox is a real mailbox if (index(s:mailboxes, mailbox) == -1) return endif let command = s:move_to_command . s:uid_set . ' ' . shellescape(mailbox) echo command let res = system(command) setlocal modifiable exec s:firstline . "," . s:lastline . "delete" setlocal nomodifiable endfunction function! CompleteMoveMailbox(findstart, base) if !exists("s:mailboxes") call s:get_mailbox_list() endif if a:findstart " locate the start of the word return 0 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 = system(command) let s:mailboxes = split(res, "\n", '') endfunction function! CompleteMailbox(findstart, base) if !exists("s:mailboxes") call s:get_mailbox_list() endif if a:findstart " locate the start of the word return 0 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 " ------------------------------------------------------------------------------- " select mailbox function! s:mailbox_window() if !exists("s:mailboxes") call s:get_mailbox_list() endif topleft split MailboxSelect setlocal buftype=nofile setlocal noswapfile setlocal modifiable resize 1 inoremap :call select_mailbox() inoremap :q set completefunc=CompleteMailbox " c-p clears the line call feedkeys("i\\\", 't') endfunction function! s:select_mailbox() let mailbox = getline(line('.')) 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 command = s:select_mailbox_command . shellescape(s:mailbox) echo command call system(command) redraw " now get latest 100 messages call s:focus_list_window() setlocal modifiable let command = s:search_command . "100 all" echo "Please wait. Loading messages..." let res = system(command) 1,$delete put! =res execute "normal Gdd\" normal G setlocal nomodifiable normal z. endfunction function! s:search_window() topleft split SearchWindow setlocal buftype=nofile setlocal noswapfile resize 1 setlocal modifiable noremap! :call do_search() call feedkeys("i") endfunction function! s:do_search() let s:query = getline(line('.')) close " empty query if match(s:query, '^\s*$') != -1 return endif " close message window if open call s:focus_message_window() close " TODO should we really hardcode 100 as the quantity? let command = s:search_command . "100 " . shellescape(s:query) echo command call s:focus_list_window() let res = system(command) setlocal modifiable 1,$delete put! =res execute "normal Gdd\" normal z. setlocal nomodifiable endfunction function! s:more_messages() let line = getline(line('.')) let uid = matchstr(line, '^\d\+') let command = s:more_messages_command . uid echo command let res = system(command) setlocal modifiable let lines = split(res, "\n") call append(0, lines) " execute "normal Gdd\" setlocal nomodifiable endfunction " -------------------------------------------------------------------------------- " compose reply, compose, forward, save draft function! s:compose_reply(all) let command = s:reply_template_command . s:current_uid if a:all let command = command . ' 1' endif call s:open_compose_window(command) " cursor after headers normal } normal o endfunction function! s:compose_message() write let command = s:new_message_template_command call s:open_compose_window(command) " position cursor after to: call search("^to:") normal $ call feedkeys("a") endfunction function! s:compose_forward() write let command = s:forward_template_command . s:current_uid call s:open_compose_window(command) call search("^to:") normal $ call feedkeys("a") endfunction func! s:open_compose_window(command) redraw echo a:command let res = system(a:command) split ComposeMessage wincmd p close setlocal modifiable 1,$delete put! =res normal 1G noremap d :call deliver_message() nnoremap q :call cancel_compose() nnoremap q :call cancel_compose() nnoremap s :call save_draft() set 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 matching with "a:base" let matches = system("grep " . shellescape(a:base) . " contacts.txt") return split(matches, "\n") endif endfun function! s:cancel_compose() call s:focus_list_window() wincmd p close! endfunction function! s:deliver_message() write let mail = join(getline(1,'$'), "\n") exec ":!" . s:deliver_command . " < ComposeMessage" redraw call s:focus_list_window() wincmd p close! endfunction func! s:save_draft() write let mail = join(getline(1,'$'), "\n") exec ":!" . s:save_draft_command . " < ComposeMessage" redraw call s:focus_list_window() wincmd p close! endfunc " -------------------------------------------------------------------------------- " call from inside message window with h func! s:open_html_part() let command = s:open_html_command . s:current_uid let outfile = system(command) " todo: allow user to change open in browser command? exec "!open " . 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) let command = s:save_attachments_command . s:savedir echo command let res = system(command) endfunc " -------------------------------------------------------------------------------- func! s:toggle_fullscreen() if winnr('$') > 1 only normal z. else call feedkeys("\") endif endfunc call s:create_list_window() call s:create_message_window() call s:focus_list_window() " to go list window " this are list window bindings noremap :call show_message() noremap q :qal! noremap s :call toggle_star() noremap d :call delete_messages("Deleted") " TODO the range doesn't quite work as expect, need " trying to make user defined commands that work from : prompt " command -buffer -range VmailDelete call s:toggle_star("Deleted") " command -buffer -range VmailStar call s:toggle_star("Flagged") noremap ! :call delete_messages("[Gmail]/Spam") "open a link browser (os x) "autocmd CursorMoved call show_message() noremap u :call update() noremap s :call search_window() noremap m :call mailbox_window() noremap v :call move_to_mailbox() noremap c :call compose_message() noremap r :call show_message():call compose_reply(0) noremap a :call show_message():call compose_reply(1) " go fullscreen nnoremap :call toggle_fullscreen() " press double return in list view to go full screen on a message; then " return? again to restore the list view " go to bottom and center cursorline normal G normal z. " send window width " system(s:set_window_width_command . winwidth(1))