import vim plugin

This commit is contained in:
Clément DOUIN 2021-04-03 12:12:26 +02:00
parent cbc74916a9
commit e23e363e0a
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
14 changed files with 1120 additions and 0 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target
/vim/doc/tags

125
vim/README.md Normal file
View file

@ -0,0 +1,125 @@
# 📫 Himalaya.vim
Vim plugin for [Himalaya](https://github.com/soywod/himalaya) CLI email client.
![image](https://user-images.githubusercontent.com/10437171/104848096-aee51000-58e3-11eb-8d99-bcfab5ca28ba.png)
## Table of contents
* [Motivation](#motivation)
* [Installation](#installation)
* [Usage](#usage)
* [List messages view](#list-messages-view)
* [Read message view](#read-message-view)
* [Write message view](#write-message-view)
* [License](https://github.com/soywod/himalaya.vim/blob/master/LICENSE)
* [Credits](#credits)
## Motivation
Bringing emails to the terminal is a pain. The mainstream TUI, (neo)mutt, takes
time to configure. The default mapping is not intuitive when coming from the
Vim environment. It is even scary to use at the beginning, since you are
dealing with sensitive data!
The aim of Himalaya is to extract the email logic into a simple (yet solid) CLI
API that can be used either directly from the terminal or UIs. It gives users
more flexibility.
This Vim plugin is a TUI implementation for Himalaya CLI.
## Installation
First you need to install and configure the [himalaya
CLI](https://github.com/soywod/himalaya#installation). Then you can install
this plugin with your favorite plugin manager. For eg with
[vim-plug](https://github.com/junegunn/vim-plug) add to your `.vimrc`:
```viml
Plug "soywod/himalaya.vim"
```
Then:
```viml
:PlugInstall
```
## Usage
### List messages view
```vim
:Himalaya
```
![gif](https://user-images.githubusercontent.com/10437171/110707014-f9ef1580-81f8-11eb-93ad-233010733ca3.gif)
| Function | Default binding |
| --- | --- |
| Change the current mbox | `gm` |
| Show previous page | `gp` |
| Show next page | `gn` |
| Read focused msg | `<Enter>` |
| Write a new msg | `gw` |
| Reply to the focused msg | `gr` |
| Reply all to the focused msg | `gR` |
| Forward the focused message | `gf` |
| Download all focused msg attachments | `ga` |
They can be customized:
```vim
nmap gm <plug>(himalaya-mbox-input)
nmap gp <plug>(himalaya-mbox-prev-page)
nmap gn <plug>(himalaya-mbox-next-page)
nmap <cr> <plug>(himalaya-msg-read)
nmap gw <plug>(himalaya-msg-write)
nmap gr <plug>(himalaya-msg-reply)
nmap gR <plug>(himalaya-msg-reply-all)
nmap gf <plug>(himalaya-msg-forward)
nmap ga <plug>(himalaya-msg-attachments)
```
### Read message view
![gif](https://user-images.githubusercontent.com/10437171/110708073-7b937300-81fa-11eb-9f4c-5472cea22e21.gif)
| Function | Default binding |
| --- | --- |
| Write a new msg | `gw` |
| Reply to the msg | `gr` |
| Reply all to the msg | `gR` |
| Forward the message | `gf` |
| Download all msg attachments | `ga` |
They can be customized:
```vim
nmap gw <plug>(himalaya-msg-write)
nmap gr <plug>(himalaya-msg-reply)
nmap gR <plug>(himalaya-msg-reply-all)
nmap gf <plug>(himalaya-msg-forward)
nmap ga <plug>(himalaya-msg-attachments)
```
### Write message view
![gif](https://user-images.githubusercontent.com/10437171/110708795-84387900-81fb-11eb-8f8a-f7e7862e816d.gif)
When you exit this special buffer, you will be prompted 4 choices:
- `Send`: sends the message
- `Draft`: saves the message into the `Drafts` mailbox
- `Quit`: quits the buffer without saving
- `Cancel`: goes back to the message edition
## Credits
- [IMAP RFC3501](https://tools.ietf.org/html/rfc3501)
- [Iris](https://github.com/soywod/iris.vim), the himalaya predecessor
- [isync](https://isync.sourceforge.io/), an email synchronizer for offline usage
- [NeoMutt](https://neomutt.org/), an email terminal user interface
- [Alpine](http://alpine.x10host.com/alpine/alpine-info/), an other email terminal user interface
- [mutt-wizard](https://github.com/LukeSmithxyz/mutt-wizard), a tool over NeoMutt and isync
- [rust-imap](https://github.com/jonhoo/rust-imap), a rust IMAP lib

View file

@ -0,0 +1,57 @@
let s:print_info = function("himalaya#utils#print_msg")
let s:print_err = function("himalaya#utils#print_err")
let s:cli = function("himalaya#shared#cli")
" Pagination
let s:curr_page = 0
function! himalaya#mbox#curr_page()
return s:curr_page
endfunction
function! himalaya#mbox#prev_page()
let s:curr_page = max([0, s:curr_page - 1])
call himalaya#msg#list()
endfunction
function! himalaya#mbox#next_page()
let s:curr_page = s:curr_page + 1
call himalaya#msg#list()
endfunction
" Mailbox
let s:curr_mbox = "INBOX"
function! himalaya#mbox#curr_mbox()
return s:curr_mbox
endfunction
function! himalaya#mbox#input()
try
call s:print_info("Fetching mailboxes…")
let mboxes = map(s:cli("mailboxes", []), "v:val.name")
" if &rtp =~ "fzf.vim"
" call fzf#run({
" \"source": mboxes,
" \"sink": function("himalaya#mbox#post_input"),
" \"down": "25%",
" \})
" else
let choice = map(copy(mboxes), "printf('%s (%d)', v:val, v:key)")
redraw | echo
let choice = input(join(choice, ", ") . ": ")
redraw | echo
call himalaya#mbox#post_input(mboxes[choice])
" endif
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#mbox#post_input(mbox)
let s:curr_mbox = a:mbox
let s:curr_page = 0
call himalaya#msg#list()
endfunction

View file

@ -0,0 +1,107 @@
let s:trim = function("himalaya#utils#trim")
let s:print_err = function("himalaya#utils#print_err")
let s:print_info = function("himalaya#utils#print_msg")
let s:buff_name = "Himalaya"
" Exec utils
function! s:exec(cmd, args)
let cmd = call("printf", [a:cmd] + a:args)
let res = system(cmd)
try
return eval(res)
catch
throw res
endtry
endfunction
" Render utils
function! s:render(type, lines)
let s:max_widths = s:get_max_widths(a:lines, s:config[a:type].columns)
let header = [s:render_line(s:config.labels, s:max_widths, a:type)]
let line = map(copy(a:lines), "s:render_line(v:val, s:max_widths, a:type)")
return header + line
endfunction
function! s:render_line(line, max_widths, type)
return "|" . join(map(
\copy(s:config[a:type].columns),
\"s:render_cell(a:line[v:val], a:max_widths[v:key])",
\), "")
endfunction
function! s:render_cell(cell, max_width)
let cell_width = strdisplaywidth(a:cell[:a:max_width])
return a:cell[:a:max_width] . repeat(" ", a:max_width - cell_width) . " |"
endfunction
function! s:get_max_widths(msgs, columns)
let max_widths = map(copy(a:columns), "strlen(s:config.labels[v:val])")
for msg in a:msgs
let widths = map(copy(a:columns), "strlen(msg[v:val])")
call map(max_widths, "max([widths[v:key], v:val])")
endfor
return max_widths
endfunction
" List
let s:config = {
\"list": {
\"columns": ["delim", "name", "attributes"],
\},
\"labels": {
\"delim": "DELIM",
\"name": "NAME",
\"attributes": "ATTRIBUTES",
\},
\}
function! himalaya#mbx#format_for_list(mbx)
let mbx = copy(a:mbx)
let mbx.attributes = join(mbx.attributes, ", ")
return mbx
endfunction
function! s:get_focused_mbx()
try
return s:trim(split(getline("."), "|")[1])
catch
throw "mailbox not found"
endtry
endfunction
function! himalaya#mbx#list()
try
call s:print_info("Fetching mailboxes…")
let prev_pos = getpos(".")
let mbxs = s:exec("himalaya --output json mailboxes", [])
let mbxs = map(copy(mbxs), 'himalaya#mbx#format_for_list(v:val)')
silent! bwipeout "Mailboxes"
silent! edit Mailboxes
call append(0, s:render("list", mbxs))
execute "$d"
call setpos(".", prev_pos)
setlocal filetype=himalaya-mbx-list
let &modified = 0
call s:print_info("Done!")
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#mbx#select()
let mbx = s:get_focused_mbx()
call himalaya#msg#list(mbx)
endfunction

View file

@ -0,0 +1,241 @@
let s:print_info = function("himalaya#utils#print_msg")
let s:print_err = function("himalaya#utils#print_err")
let s:trim = function("himalaya#utils#trim")
let s:cli = function("himalaya#shared#cli")
let s:msg_id = 0
let s:draft = ""
" Message
function! s:format_msg_for_list(msg)
let msg = copy(a:msg)
let flag_unseen = index(msg.flags, "Seen") == -1 ? "🟓" : " "
let flag_replied = index(msg.flags, "Answered") == -1 ? " " : "↩"
let flag_flagged = index(msg.flags, "Flagged") == -1 ? " " : "!"
let msg.flags = printf("%s%s%s", flag_unseen, flag_replied, flag_flagged)
return msg
endfunction
function! himalaya#msg#list()
try
let mbox = himalaya#mbox#curr_mbox()
let page = himalaya#mbox#curr_page()
call s:print_info(printf("Fetching %s messages…", tolower(mbox)))
let msgs = s:cli("--mailbox %s list --page %d", [shellescape(mbox), page])
let msgs = map(copy(msgs), "s:format_msg_for_list(v:val)")
call s:print_info("Done!")
let buftype = stridx(bufname("%"), "Himalaya messages") == 0 ? "file" : "edit"
execute printf("silent! %s Himalaya messages [%s] [page %d]", buftype, tolower(mbox), page + 1)
setlocal modifiable
execute "%d"
call append(0, s:render("list", msgs))
execute "$d"
setlocal filetype=himalaya-msg-list
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#read()
try
let s:msg_id = s:get_focused_msg_id()
let mbox = himalaya#mbox#curr_mbox()
call s:print_info(printf("Fetching message %d…", s:msg_id))
let msg = s:cli("read %d --mailbox %s", [s:msg_id, shellescape(mbox)])
call s:print_info("Done!")
let attachment = msg.hasAttachment ? " []" : ""
execute printf("silent! edit Himalaya read message [%d]%s", s:msg_id, attachment)
setlocal modifiable
execute "%d"
call append(0, split(substitute(msg.content, "\r", "", "g"), "\n"))
execute "$d"
setlocal filetype=himalaya-msg-read
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#write()
try
call s:print_info("Fetching new template…")
let msg = s:cli("template new", [])
call s:print_info("Done!")
silent! edit Himalaya write
call append(0, split(substitute(msg.template, "\r", "", "g"), "\n"))
execute "$d"
setlocal filetype=himalaya-msg-write
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#reply()
try
let mbox = himalaya#mbox#curr_mbox()
let msg_id = stridx(bufname("%"), "Himalaya messages") == 0 ? s:get_focused_msg_id() : s:msg_id
call s:print_info("Fetching reply template…")
let msg = s:cli("template reply %d --mailbox %s", [msg_id, shellescape(mbox)])
call s:print_info("Done!")
execute printf("silent! edit Himalaya reply [%d]", msg_id)
call append(0, split(substitute(msg.template, "\r", "", "g"), "\n"))
execute "$d"
setlocal filetype=himalaya-msg-write
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#reply_all()
try
let mbox = himalaya#mbox#curr_mbox()
let msg_id = stridx(bufname("%"), "Himalaya messages") == 0 ? s:get_focused_msg_id() : s:msg_id
call s:print_info("Fetching reply all template…")
let msg = s:cli("template reply %d --mailbox %s --all", [msg_id, shellescape(mbox)])
call s:print_info("Done!")
execute printf("silent! edit Himalaya reply all [%d]", msg_id)
call append(0, split(substitute(msg.template, "\r", "", "g"), "\n"))
execute "$d"
setlocal filetype=himalaya-msg-write
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#forward()
try
let mbox = himalaya#mbox#curr_mbox()
let msg_id = stridx(bufname("%"), "Himalaya messages") == 0 ? s:get_focused_msg_id() : s:msg_id
call s:print_info("Fetching forward template…")
let msg = s:cli("template forward %d --mailbox %s", [msg_id, shellescape(mbox)])
call s:print_info("Done!")
execute printf("silent! edit Himalaya forward [%d]", msg_id)
call append(0, split(substitute(msg.template, "\r", "", "g"), "\n"))
execute "$d"
setlocal filetype=himalaya-msg-write
let &modified = 0
execute 0
catch
call s:print_err(v:exception)
endtry
endfunction
function! himalaya#msg#draft_save()
let s:draft = join(getline(1, "$"), "\r\n")
call s:print_info("Draft saved!")
let &modified = 0
endfunction
function! himalaya#msg#draft_handle()
while 1
let choice = input("(s)end, (d)raft, (q)uit or (c)ancel? ")
let choice = tolower(choice)[0]
redraw | echo
if choice == "s"
call s:print_info("Sending message…")
call s:cli("send -- %s", [shellescape(s:draft)])
call s:print_info("Done!")
return
elseif choice == "d"
call s:print_info("Saving draft…")
call s:cli("save --mailbox Drafts -- %s", [shellescape(s:draft)])
call s:print_info("Done!")
return
elseif choice == "q"
return
elseif choice == "c"
throw "Action canceled"
endif
endwhile
endfunction
function! himalaya#msg#attachments()
try
let mbox = himalaya#mbox#curr_mbox()
let msg_id = stridx(bufname("%"), "Himalaya messages") == 0 ? s:get_focused_msg_id() : s:msg_id
call s:print_info("Downloading attachments…")
let msg = s:cli("attachments %d --mailbox %s", [msg_id, shellescape(mbox)])
call s:print_info("Done!")
catch
call s:print_err(v:exception)
endtry
endfunction
" Render utils
let s:config = {
\"list": {
\"columns": ["uid", "flags", "subject", "sender", "date"],
\},
\"labels": {
\"uid": "UID",
\"flags": "FLAGS",
\"subject": "SUBJECT",
\"sender": "SENDER",
\"date": "DATE",
\},
\}
function! s:render(type, lines)
let s:max_widths = s:get_max_widths(a:lines, s:config[a:type].columns)
let header = [s:render_line(s:config.labels, s:max_widths, a:type)]
let line = map(copy(a:lines), "s:render_line(v:val, s:max_widths, a:type)")
return header + line
endfunction
function! s:render_line(line, max_widths, type)
return "|" . join(map(
\copy(s:config[a:type].columns),
\"s:render_cell(a:line[v:val], a:max_widths[v:key])",
\), "")
endfunction
function! s:render_cell(cell, max_width)
let cell_width = strdisplaywidth(a:cell[:a:max_width])
return a:cell[:a:max_width] . repeat(" ", a:max_width - cell_width) . " |"
endfunction
function! s:get_max_widths(msgs, columns)
let max_widths = map(copy(a:columns), "strlen(s:config.labels[v:val])")
for msg in a:msgs
let widths = map(copy(a:columns), "strlen(msg[v:val])")
call map(max_widths, "max([widths[v:key], v:val])")
endfor
return max_widths
endfunction
function! s:get_focused_msg_id()
try
return s:trim(split(getline("."), "|")[0])
catch
throw "message not found"
endtry
endfunction

View file

@ -0,0 +1,28 @@
function! himalaya#shared#define_bindings(bindings)
for [mode, key, name] in a:bindings
let plug = substitute(name, "[#_]", "-", "g")
let plug = printf("<plug>(himalaya-%s)", plug)
execute printf("%snoremap <silent>%s :call himalaya#%s()<cr>", mode, plug, name)
if !hasmapto(plug, mode)
execute printf("%smap <nowait><buffer>%s %s", mode, key, plug)
endif
endfor
endfunction
function! himalaya#shared#cli(cmd, args)
let cmd = call("printf", ["himalaya --output json " . a:cmd] + a:args)
let res = system(cmd)
if !empty(res)
try
return eval(res)
catch
throw res
endtry
endif
endfunction
function! himalaya#shared#thread_fold(lnum)
return getline(a:lnum)[0] == ">"
endfunction

View file

@ -0,0 +1,238 @@
let s:compose = function('himalaya#utils#compose')
let s:trim = function('himalaya#utils#trim')
let s:print_msg = function('himalaya#utils#print_msg')
let s:print_err = function('himalaya#utils#print_err')
let s:max_widths = []
let s:buff_name = 'Himalaya'
let s:msgs = []
let s:config = {
\'list': {
\'columns': ['uid', 'subject', 'sender', 'date'],
\},
\'labels': {
\'uid': 'ID',
\'subject': 'SUBJECT',
\'sender': 'SENDER',
\'date': 'DATE',
\},
\}
function! himalaya#ui#list()
try
let prev_pos = getpos('.')
let s:msgs = himalaya#msg#list()
let lines = map(copy(s:msgs), 'himalaya#msg#format_for_list(v:val)')
redir => buf_list | silent! ls | redir END
execute 'silent! edit ' . s:buff_name
if match(buf_list, '"Himalaya') > -1
execute '0,$d'
endif
call append(0, s:render('list', lines))
execute '$d'
call setpos('.', prev_pos)
setlocal filetype=himalaya-list
let &modified = 0
echo
catch
call s:print_err(v:exception)
endtry
endfunction
" Cell management
function! himalaya#ui#select_next_cell()
normal! f|l
if col('.') == col('$') - 1
if line('.') == line('$')
normal! T|
else
normal! j0l
endif
endif
endfunction
function! himalaya#ui#select_prev_cell()
if col('.') == 2 && line('.') > 2
normal! k$T|
else
normal! 2T|
endif
endfunction
function! himalaya#ui#delete_in_cell()
execute printf('normal! %sdt|', col('.') == 1 ? '' : 'T|')
endfunction
function! himalaya#ui#change_in_cell()
call himalaya#ui#delete_in_cell()
startinsert
endfunction
function! himalaya#ui#visual_in_cell()
execute printf('normal! %svt|', col('.') == 1 ? '' : 'T|')
endfunction
" Parse utils
function! himalaya#ui#parse_buffer()
" try
" let lines = filter(getline(2, "$"), "!empty(s:trim(v:val))")
" let prev_msgs = copy(s:msgs)
" let next_msgs = map(lines, "s:parse_buffer_line(v:key, v:val)")
" let msgs_to_add = filter(copy(next_msgs), "empty(v:val.id)")
" let msgs_to_edit = []
" let msgs_to_do = []
" let msgs = []
" for prev_msg in prev_msgs
" let next_msg = filter(copy(next_msgs), "v:val.id == prev_msg.id")
" if empty(next_msg)
" let msgs_to_do += [prev_msg.id]
" elseif prev_msg.desc != next_msg[0].desc || prev_msg.project != next_msg[0].project || prev_msg.due.approx != next_msg[0].due
" let msgs_to_edit += [next_msg[0]]
" endif
" endfor
" for msg in msgs_to_add | let msgs += [himalaya#msg#add(msg)] | endfor
" for msg in msgs_to_edit | let msgs += [himalaya#msg#edit(msg)] | endfor
" for id in msgs_to_do | let msgs += [himalaya#msg#do(id)] | endfor
" call himalaya#ui#list()
" let &modified = 0
" for msg in msgs | call s:print_msg(msg) | endfor
" catch
" call s:print_err(v:exception)
" endtry
endfunction
function! s:parse_buffer_line(index, line)
if match(a:line, '^|[0-9a-f\-]\{-} *|.* *|.\{-} *|.\{-} *|.\{-} *|$') != -1
let cells = split(a:line, "|")
let id = s:trim(cells[0])
let desc = s:trim(join(cells[1:-4], ""))
let project = s:trim(cells[-3])
let due = s:trim(cells[-1])
return {
\"id": id,
\"desc": desc,
\"project": project,
\"due": due,
\}
else
let [desc, project, due] = s:parse_args(s:trim(a:line))
return {
\"id": "",
\"desc": desc,
\"project": project,
\"due": due,
\}
endif
endfunction
function! s:uniq_by_id(a, b)
if a:a.id > a:b.id | return 1
elseif a:a.id < a:b.id | return -1
else | return 0 | endif
endfunction
function! s:parse_args(args)
let args = split(a:args, ' ')
let idx = 0
let desc = []
let project = ""
let due = ""
while idx < len(args)
let arg = args[idx]
if arg == "-p" || arg == "--project"
let project = get(args, idx + 1, "")
let idx = idx + 1
elseif arg == "-d" || arg == "--due"
let due = get(args, idx + 1, "")
let idx = idx + 1
else
call add(desc, arg)
endif
let idx = idx + 1
endwhile
return [join(desc, ' '), project, due]
endfunction
" ------------------------------------------------------------------ # Renders #
function! s:render(type, lines)
let s:max_widths = s:get_max_widths(a:lines, s:config[a:type].columns)
let header = [s:render_line(s:config.labels, s:max_widths, a:type)]
let line = map(copy(a:lines), 's:render_line(v:val, s:max_widths, a:type)')
return header + line
endfunction
function! s:render_line(line, max_widths, type)
return '|' . join(map(
\copy(s:config[a:type].columns),
\'s:render_cell(a:line[v:val], a:max_widths[v:key])',
\), '')
endfunction
function! s:render_cell(cell, max_width)
let cell_width = strdisplaywidth(a:cell[:a:max_width])
return a:cell[:a:max_width] . repeat(' ', a:max_width - cell_width) . ' |'
endfunction
" -------------------------------------------------------------------- # Utils #
function! s:get_max_widths(msgs, columns)
let max_widths = map(copy(a:columns), 'strlen(s:config.labels[v:val])')
for msg in a:msgs
let widths = map(copy(a:columns), 'strlen(msg[v:val])')
call map(max_widths, 'max([widths[v:key], v:val])')
endfor
return max_widths
endfunction
function! s:get_focused_msg_id()
try
return s:trim(split(getline("."), "|")[0])
catch
throw "msg not found"
endtry
endfunction
function! s:refresh_buff_name()
let buff_name = 'Himalaya'
if !g:himalaya_hide_done
let buff_name .= '*'
endif
if len(g:himalaya_context) > 0
let tags = map(copy(g:himalaya_context), 'printf(" +%s", v:val)')
let buff_name .= join(tags, '')
endif
if buff_name != s:buff_name
execute 'silent! enew'
execute 'silent! bwipeout ' . s:buff_name
let s:buff_name = buff_name
endif
endfunction
function! s:exists_in(list, item)
return index(a:list, a:item) > -1
endfunction

View file

@ -0,0 +1,87 @@
" ------------------------------------------------------------------ # Compose #
function! himalaya#utils#compose(...)
let funcs = map(reverse(copy(a:000)), 'function(v:val)')
return function('s:compose', [funcs])
endfunction
function! s:compose(funcs, arg)
let data = a:arg
for Func in a:funcs
let data = Func(data)
endfor
return data
endfunction
" --------------------------------------------------------------------- # Trim #
function! himalaya#utils#trim(str)
return himalaya#utils#compose('s:trim_left', 's:trim_right')(a:str)
endfunction
function! s:trim_left(str)
return substitute(a:str, '^\s*', '', 'g')
endfunction
function! s:trim_right(str)
return substitute(a:str, '\s*$', '', 'g')
endfunction
" ------------------------------------------------------------------- # Assign #
function! himalaya#utils#assign(...)
let overrides = copy(a:000)
let base = remove(overrides, 0)
for override in overrides
for [key, val] in items(override)
let base[key] = val
unlet key val
endfor
endfor
return base
endfunction
" ---------------------------------------------------------------------- # Sum #
function! himalaya#utils#sum(array)
let total = 0
for item in a:array
let total += item
endfor
return total
endfunction
" ----------------------------------------------------------- # Match one item #
function! himalaya#utils#match_one(list_src, list_dest)
if empty(a:list_dest)
return 1
endif
for item in a:list_src
if index(a:list_dest, item) > -1 | return 1 | endif
endfor
return 0
endfunction
" --------------------------------------------------------------------- # Logs #
function! himalaya#utils#print_msg(msg)
echohl None
echom a:msg
endfunction
function! himalaya#utils#print_err(err)
redraw
echohl ErrorMsg
echom a:err
echohl None
endfunction

149
vim/doc/himalaya.txt Normal file
View file

@ -0,0 +1,149 @@
*himalaya.txt* - ⏱ Minimalist task & time manager.
_/ _/ _/ _/ _/_/_/_/ _/_/ _/_/_/
_/ _/ _/_/ _/ _/ _/ _/ _/
_/ _/ _/ _/ _/ _/_/_/ _/ _/ _/ _/_/
_/ _/ _/ _/_/ _/ _/ _/ _/ _/
_/_/ _/ _/ _/ _/_/ _/_/_/
==============================================================================
TABLE OF CONTENTS *himalaya-contents*
Requirements .......................................... |himalaya-requirements|
Usage ........................................................ |himalaya-usage|
Mappings ................................................. |himalaya-mappings|
License .................................................... |himalaya-license|
Contributing .......................................... |himalaya-contributing|
Changelog ................................................ |himalaya-changelog|
Credits .................................................... |himalaya-credits|
==============================================================================
REQUIREMENTS *himalaya-requirements*
- Vim or Neovim
- Himalaya CLI {1}
https://github.com/soywod/himalaya#installation {1}
==============================================================================
USAGE *himalaya-usage*
It is recommanded to first read the Himalaya CLI documentation {1} to understand
the concept.
To list tasks:
>
:Himalaya
<
Then you can manage tasks using Vim mapping. The table will automatically
readjust on buffer save (`:w`). See https://github.com/soywod/himalaya.vim.
https://github.com/soywod/himalaya#readme {1}
==============================================================================
MAPPINGS *himalaya-mappings*
Here the default mappings:
List done tasks gd
List deleted tasks gD
Toggle task <CR>
Show task infos {K}
Set context {gc}
Show worktime {gw}
Jump to the next cell <C-n>
Jump to the prev cell <C-p>
Delete in cell {dic}
Change in cell {cic}
Visual in cell {vic}
You can customize them:
>
nmap gd <plug>(himalaya-list-done)
nmap gD <plug>(himalaya-list-deleted)
nmap <cr> <plug>(himalaya-toggle)
nmap K <plug>(himalaya-info)
nmap gc <plug>(himalaya-context)
nmap gw <plug>(himalaya-worktime)
nmap <c-n> <plug>(himalaya-next-cell)
nmap <c-p> <plug>(himalaya-prev-cell)
nmap dic <plug>(himalaya-delete-in-cell)
nmap cic <plug>(himalaya-change-in-cell)
nmap vic <plug>(himalaya-visual-in-cell)
<
==============================================================================
LICENSE *himalaya-license*
Copyright © 2019-2020 Clément DOUIN <clement.douin@posteo.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Author name here nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================================================
CONTRIBUTING *himalaya-contributing*
Report any bugs here: https://github.com/soywod/himalaya.vim/issues
Feel free to submit pull requests: https://github.com/soywod/himalaya.vim/pulls
1) Git commit messages follow the `Angular Convention` {1}, but contain only a
subject.
>
- Use imperative, present tense: “change” not “changed” nor “changes”
- Don't capitalize first letter
- No dot (.) at the end
<
2) Vim code should be as clean as possible, variables and functions use the
snake case convention. A line should never contain more than `80` characters.
3) Tests should be added for each new functionality. Be sure to run tests
before proposing a pull request.
https://gist.github.com/stephenparish/9941e89d80e2bc58a153 {1}
==============================================================================
CHANGELOG *himalaya-changelog*
See https://github.com/soywod/himalaya.vim/blob/master/CHANGELOG.md.
==============================================================================
CREDITS *himalaya-credits*
- Taskwarrior, a task manager {1}
- Timewarrior, a time tracker {2}
- vim-taskwarrior, a Taskwarrior wrapper for vim {3}
- vimwiki, for the idea of managing tasks inside a buffer {4}
- Kronos, the Himalaya predecessor {5}
https://taskwarrior.org {1}
https://taskwarrior.org/docs/timewarrior {2}
https://github.com/blindFS/vim-taskwarrior {3}
https://github.com/vimwiki/vimwiki {4}
https://github.com/soywod/kronos.vim {5}
==============================================================================
vim:tw=78:ts=4:ft=help:norl:

View file

@ -0,0 +1,21 @@
setlocal buftype=nofile
setlocal cursorline
setlocal nomodifiable
setlocal nowrap
setlocal startofline
nnoremap <buffer><silent>q :bwipeout<cr>
nnoremap <buffer><silent><cr> :bwipeout<cr>
nnoremap <buffer><silent><esc> :bwipeout<cr>
call himalaya#shared#define_bindings([
\["n", "gm" , "mbox#input" ],
\["n", "gp" , "mbox#prev_page" ],
\["n", "gn" , "mbox#next_page" ],
\["n", "<cr>", "msg#read" ],
\["n", "gw" , "msg#write" ],
\["n", "gr" , "msg#reply" ],
\["n", "gR" , "msg#reply_all" ],
\["n", "gf" , "msg#forward" ],
\["n", "ga" , "msg#attachments"],
\])

View file

@ -0,0 +1,18 @@
setlocal bufhidden=wipe
setlocal buftype=nofile
setlocal cursorline
setlocal foldexpr=himalaya#shared#thread_fold(v:lnum)
setlocal foldlevel=0
setlocal foldlevelstart=0
setlocal foldmethod=expr
setlocal nomodifiable
setlocal nowrap
setlocal startofline
call himalaya#shared#define_bindings([
\["n", "gw", "msg#write" ],
\["n", "gr", "msg#reply" ],
\["n", "gR", "msg#reply_all" ],
\["n", "gf", "msg#forward" ],
\["n", "ga", "msg#attachments"],
\])

View file

@ -0,0 +1,13 @@
setlocal cursorline
setlocal foldexpr=himalaya#shared#thread_fold(v:lnum)
setlocal foldlevel=0
setlocal foldlevelstart=0
setlocal foldmethod=expr
setlocal nowrap
setlocal startofline
augroup himalaya
autocmd! * <buffer>
autocmd BufWriteCmd <buffer> call himalaya#msg#draft_save()
autocmd BufUnload <buffer> call himalaya#msg#draft_handle()
augroup end

11
vim/plugin/himalaya.vim Normal file
View file

@ -0,0 +1,11 @@
if exists("g:himalaya_loaded")
finish
endif
let g:himalaya_loaded = 1
if !executable("himalaya")
throw "Himalaya CLI not found, see https://github.com/soywod/himalaya#installation"
endif
command! Himalaya call himalaya#msg#list()

View file

@ -0,0 +1,24 @@
if exists("b:current_syntax")
finish
endif
syntax match hya_sep /|/
syntax match hya_uid /^|.\{-}|/ contains=hya_sep
syntax match hya_flags /^|.\{-}|.\{-}|/ contains=hya_uid,hya_sep
syntax match hya_subject /^|.\{-}|.\{-}|.\{-}|/ contains=hya_uid,hya_flags,hya_sep
syntax match hya_sender /^|.\{-}|.\{-}|.\{-}|.\{-}|/ contains=hya_uid,hya_flags,hya_subject,hya_sep
syntax match hya_date /^|.\{-}|.\{-}|.\{-}|.\{-}|.\{-}|/ contains=hya_uid,hya_flags,hya_subject,hya_sender,hya_sep
syntax match hya_head /.*\%1l/ contains=hya_sep
syntax match hya_unseen /^|.\{-}|🟓.*$/ contains=hya_sep
highlight default link hya_sep VertSplit
highlight default link hya_uid Identifier
highlight default link hya_flags Special
highlight default link hya_subject String
highlight default link hya_sender Structure
highlight default link hya_date Constant
highlight hya_head term=bold,underline cterm=bold,underline gui=bold,underline
highlight hya_unseen term=bold cterm=bold gui=bold
let b:current_syntax = "himalaya-msg-list"