summaryrefslogtreecommitdiff
path: root/autoload/vimwiki/lst.vim
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/vimwiki/lst.vim')
-rw-r--r--autoload/vimwiki/lst.vim555
1 files changed, 555 insertions, 0 deletions
diff --git a/autoload/vimwiki/lst.vim b/autoload/vimwiki/lst.vim
new file mode 100644
index 0000000..7d42517
--- /dev/null
+++ b/autoload/vimwiki/lst.vim
@@ -0,0 +1,555 @@
+" vim:tabstop=2:shiftwidth=2:expandtab:foldmethod=marker:textwidth=79
+" Vimwiki autoload plugin file
+" Todo lists related stuff here.
+" Author: Maxim Kim <habamax@gmail.com>
+" Home: http://code.google.com/p/vimwiki/
+
+if exists("g:loaded_vimwiki_list_auto") || &cp
+ finish
+endif
+let g:loaded_vimwiki_lst_auto = 1
+
+" Script variables {{{
+let s:rx_li_box = '\[.\?\]'
+" }}}
+
+" Script functions {{{
+
+" Get unicode string symbol at index
+function! s:str_idx(str, idx) "{{{
+ " Unfortunatly vimscript cannot get symbol at index in unicode string such as
+ " '✗○◐●✓'
+ return matchstr(a:str, '\%'.a:idx.'v.')
+endfunction "}}}
+
+" Get checkbox regexp
+function! s:rx_li_symbol(rate) "{{{
+ let result = ''
+ if a:rate == 100
+ let result = s:str_idx(g:vimwiki_listsyms, 5)
+ elseif a:rate == 0
+ let result = s:str_idx(g:vimwiki_listsyms, 1)
+ elseif a:rate >= 67
+ let result = s:str_idx(g:vimwiki_listsyms, 4)
+ elseif a:rate >= 34
+ let result = s:str_idx(g:vimwiki_listsyms, 3)
+ else
+ let result = s:str_idx(g:vimwiki_listsyms, 2)
+ endif
+
+ return '\['.result.'\]'
+endfunction "}}}
+
+" Get blank checkbox
+function! s:blank_checkbox() "{{{
+ return '['.s:str_idx(g:vimwiki_listsyms, 1).'] '
+endfunction "}}}
+
+" Get regexp of the list item.
+function! s:rx_list_item() "{{{
+ return '\('.g:vimwiki_rxListBullet.'\|'.g:vimwiki_rxListNumber.'\)'
+endfunction "}}}
+
+" Get regexp of the list item with checkbox.
+function! s:rx_cb_list_item() "{{{
+ return s:rx_list_item().'\s*\zs\[.\?\]'
+endfunction "}}}
+
+" Get level of the list item.
+function! s:get_level(lnum) "{{{
+ if VimwikiGet('syntax') == 'media'
+ let level = vimwiki#u#count_first_sym(getline(a:lnum))
+ else
+ let level = indent(a:lnum)
+ endif
+ return level
+endfunction "}}}
+
+" Get previous list item.
+" Returns: line number or 0.
+function! s:prev_list_item(lnum) "{{{
+ let c_lnum = a:lnum - 1
+ while c_lnum >= 1
+ let line = getline(c_lnum)
+ if line =~ s:rx_list_item()
+ return c_lnum
+ endif
+ if line =~ '^\s*$'
+ return 0
+ endif
+ let c_lnum -= 1
+ endwhile
+ return 0
+endfunction "}}}
+
+" Get next list item in the list.
+" Returns: line number or 0.
+function! s:next_list_item(lnum) "{{{
+ let c_lnum = a:lnum + 1
+ while c_lnum <= line('$')
+ let line = getline(c_lnum)
+ if line =~ s:rx_list_item()
+ return c_lnum
+ endif
+ if line =~ '^\s*$'
+ return 0
+ endif
+ let c_lnum += 1
+ endwhile
+ return 0
+endfunction "}}}
+
+" Find next list item in the buffer.
+" Returns: line number or 0.
+function! s:find_next_list_item(lnum) "{{{
+ let c_lnum = a:lnum + 1
+ while c_lnum <= line('$')
+ let line = getline(c_lnum)
+ if line =~ s:rx_list_item()
+ return c_lnum
+ endif
+ let c_lnum += 1
+ endwhile
+ return 0
+endfunction "}}}
+
+" Set state of the list item on line number "lnum" to [ ] or [x]
+function! s:set_state(lnum, rate) "{{{
+ let line = getline(a:lnum)
+ let state = s:rx_li_symbol(a:rate)
+ let line = substitute(line, s:rx_li_box, state, '')
+ call setline(a:lnum, line)
+endfunction "}}}
+
+" Get state of the list item on line number "lnum"
+function! s:get_state(lnum) "{{{
+ let state = 0
+ let line = getline(a:lnum)
+ let opt = matchstr(line, s:rx_cb_list_item())
+ if opt =~ s:rx_li_symbol(100)
+ let state = 100
+ elseif opt =~ s:rx_li_symbol(0)
+ let state = 0
+ elseif opt =~ s:rx_li_symbol(25)
+ let state = 25
+ elseif opt =~ s:rx_li_symbol(50)
+ let state = 50
+ elseif opt =~ s:rx_li_symbol(75)
+ let state = 75
+ endif
+ return state
+endfunction "}}}
+
+" Returns 1 if there is checkbox on a list item, 0 otherwise.
+function! s:is_cb_list_item(lnum) "{{{
+ return getline(a:lnum) =~ s:rx_cb_list_item()
+endfunction "}}}
+
+" Returns start line number of list item, 0 if it is not a list.
+function! s:is_list_item(lnum) "{{{
+ let c_lnum = a:lnum
+ while c_lnum >= 1
+ let line = getline(c_lnum)
+ if line =~ s:rx_list_item()
+ return c_lnum
+ endif
+ if line =~ '^\s*$'
+ return 0
+ endif
+ if indent(c_lnum) > indent(a:lnum)
+ return 0
+ endif
+ let c_lnum -= 1
+ endwhile
+ return 0
+endfunction "}}}
+
+" Returns char column of checkbox. Used in parent/child checks.
+function! s:get_li_pos(lnum) "{{{
+ return stridx(getline(a:lnum), '[')
+endfunction "}}}
+
+" Returns list of line numbers of parent and all its child items.
+function! s:get_child_items(lnum) "{{{
+ let result = []
+ let lnum = a:lnum
+ let p_pos = s:get_level(lnum)
+
+ " add parent
+ call add(result, lnum)
+
+ let lnum = s:next_list_item(lnum)
+ while lnum != 0 && s:is_list_item(lnum) && s:get_level(lnum) > p_pos
+ call add(result, lnum)
+ let lnum = s:next_list_item(lnum)
+ endwhile
+
+ return result
+endfunction "}}}
+
+" Returns list of line numbers of all items of the same level.
+function! s:get_sibling_items(lnum) "{{{
+ let result = []
+ let lnum = a:lnum
+ let ind = s:get_level(lnum)
+
+ while lnum != 0 && s:get_level(lnum) >= ind
+ if s:get_level(lnum) == ind && s:is_cb_list_item(lnum)
+ call add(result, lnum)
+ endif
+ let lnum = s:next_list_item(lnum)
+ endwhile
+
+ let lnum = s:prev_list_item(a:lnum)
+ while lnum != 0 && s:get_level(lnum) >= ind
+ if s:get_level(lnum) == ind && s:is_cb_list_item(lnum)
+ call add(result, lnum)
+ endif
+ let lnum = s:prev_list_item(lnum)
+ endwhile
+
+ return result
+endfunction "}}}
+
+" Returns line number of the parent of lnum item
+function! s:get_parent_item(lnum) "{{{
+ let lnum = a:lnum
+ let ind = s:get_level(lnum)
+
+ let lnum = s:prev_list_item(lnum)
+ while lnum != 0 && s:is_list_item(lnum) && s:get_level(lnum) >= ind
+ let lnum = s:prev_list_item(lnum)
+ endwhile
+
+ if s:is_cb_list_item(lnum)
+ return lnum
+ else
+ return a:lnum
+ endif
+endfunction "}}}
+
+" Creates checkbox in a list item.
+function! s:create_cb_list_item(lnum) "{{{
+ let line = getline(a:lnum)
+ let m = matchstr(line, s:rx_list_item())
+ if m != ''
+ let li_content = substitute(strpart(line, len(m)), '^\s*', '', '')
+ let line = substitute(m, '\s*$', ' ', '').s:blank_checkbox().li_content
+ call setline(a:lnum, line)
+ endif
+endfunction "}}}
+
+" Tells if all of the sibling list items are checked or not.
+function! s:all_siblings_checked(lnum) "{{{
+ let result = 0
+ let cnt = 0
+ let siblings = s:get_sibling_items(a:lnum)
+ for lnum in siblings
+ let cnt += s:get_state(lnum)
+ endfor
+ let result = cnt/len(siblings)
+ return result
+endfunction "}}}
+
+" Creates checkbox on a list item if there is no one.
+function! s:TLI_create_checkbox(lnum) "{{{
+ if a:lnum && !s:is_cb_list_item(a:lnum)
+ if g:vimwiki_auto_checkbox
+ call s:create_cb_list_item(a:lnum)
+ endif
+ return 1
+ endif
+ return 0
+endfunction "}}}
+
+" Switch state of the child list items.
+function! s:TLI_switch_child_state(lnum) "{{{
+ let current_state = s:get_state(a:lnum)
+ if current_state == 100
+ let new_state = 0
+ else
+ let new_state = 100
+ endif
+ for lnum in s:get_child_items(a:lnum)
+ call s:set_state(lnum, new_state)
+ endfor
+endfunction "}}}
+
+" Switch state of the parent list items.
+function! s:TLI_switch_parent_state(lnum) "{{{
+ let c_lnum = a:lnum
+ while s:is_cb_list_item(c_lnum)
+ let parent_lnum = s:get_parent_item(c_lnum)
+ if parent_lnum == c_lnum
+ break
+ endif
+ call s:set_state(parent_lnum, s:all_siblings_checked(c_lnum))
+
+ let c_lnum = parent_lnum
+ endwhile
+endfunction "}}}
+
+function! s:TLI_toggle(lnum) "{{{
+ if !s:TLI_create_checkbox(a:lnum)
+ call s:TLI_switch_child_state(a:lnum)
+ endif
+ call s:TLI_switch_parent_state(a:lnum)
+endfunction "}}}
+
+" Script functions }}}
+
+" Toggle list item between [ ] and [X]
+function! vimwiki#lst#ToggleListItem(line1, line2) "{{{
+ let line1 = a:line1
+ let line2 = a:line2
+
+ if line1 != line2 && !s:is_list_item(line1)
+ let line1 = s:find_next_list_item(line1)
+ endif
+
+ let c_lnum = line1
+ while c_lnum != 0 && c_lnum <= line2
+ let li_lnum = s:is_list_item(c_lnum)
+
+ if li_lnum
+ let li_level = s:get_level(li_lnum)
+ if c_lnum == line1
+ let start_li_level = li_level
+ endif
+
+ if li_level <= start_li_level
+ call s:TLI_toggle(li_lnum)
+ let start_li_level = li_level
+ endif
+ endif
+
+ let c_lnum = s:find_next_list_item(c_lnum)
+ endwhile
+
+endfunction "}}}
+
+function! vimwiki#lst#kbd_cr() "{{{
+ " This function is heavily relies on proper 'set comments' option.
+ let cr = "\<CR>"
+ if getline('.') =~ s:rx_cb_list_item()
+ let cr .= s:blank_checkbox()
+ endif
+ return cr
+endfunction "}}}
+
+function! vimwiki#lst#kbd_oO(cmd) "{{{
+ " cmd should be 'o' or 'O'
+
+ let l:count = v:count1
+ while l:count > 0
+
+ let beg_lnum = foldclosed('.')
+ let end_lnum = foldclosedend('.')
+ if end_lnum != -1 && a:cmd ==# 'o'
+ let lnum = end_lnum
+ let line = getline(beg_lnum)
+ else
+ let line = getline('.')
+ let lnum = line('.')
+ endif
+
+ let m = matchstr(line, s:rx_list_item())
+ let res = ''
+ if line =~ s:rx_cb_list_item()
+ let res = substitute(m, '\s*$', ' ', '').s:blank_checkbox()
+ elseif line =~ s:rx_list_item()
+ let res = substitute(m, '\s*$', ' ', '')
+ elseif &autoindent || &smartindent
+ let res = matchstr(line, '^\s*')
+ endif
+
+ if a:cmd ==# 'o'
+ call append(lnum, res)
+ call cursor(lnum + 1, col('$'))
+ else
+ call append(lnum - 1, res)
+ call cursor(lnum, col('$'))
+ endif
+
+ let l:count -= 1
+ endwhile
+
+ startinsert!
+
+endfunction "}}}
+
+function! vimwiki#lst#default_symbol() "{{{
+ " TODO: initialize default symbol from syntax/vimwiki_xxx.vim
+ if VimwikiGet('syntax') == 'default'
+ return '-'
+ else
+ return '*'
+ endif
+endfunction "}}}
+
+function vimwiki#lst#get_list_margin() "{{{
+ if VimwikiGet('list_margin') < 0
+ return &sw
+ else
+ return VimwikiGet('list_margin')
+ endif
+endfunction "}}}
+
+function s:get_list_sw() "{{{
+ if VimwikiGet('syntax') == 'media'
+ return 1
+ else
+ return &sw
+ endif
+endfunction "}}}
+
+function s:get_list_nesting_level(lnum) "{{{
+ if VimwikiGet('syntax') == 'media'
+ if getline(a:lnum) !~ s:rx_list_item()
+ let level = 0
+ else
+ let level = vimwiki#u#count_first_sym(getline(a:lnum)) - 1
+ let level = level < 0 ? 0 : level
+ endif
+ else
+ let level = indent(a:lnum)
+ endif
+ return level
+endfunction "}}}
+
+function s:get_list_indent(lnum) "{{{
+ if VimwikiGet('syntax') == 'media'
+ return indent(a:lnum)
+ else
+ return 0
+ endif
+endfunction "}}}
+
+function! s:compose_list_item(n_indent, n_nesting, sym_nest, sym_bullet, li_content, ...) "{{{
+ if a:0
+ let sep = a:1
+ else
+ let sep = ''
+ endif
+ let li_indent = repeat(' ', max([0,a:n_indent])).sep
+ let li_nesting = repeat(a:sym_nest, max([0,a:n_nesting])).sep
+ if len(a:sym_bullet) > 0
+ let li_bullet = a:sym_bullet.' '.sep
+ else
+ let li_bullet = ''.sep
+ endif
+ return li_indent.li_nesting.li_bullet.a:li_content
+endfunction "}}}
+
+function s:compose_cb_bullet(prev_cb_bullet, sym) "{{{
+ return a:sym.matchstr(a:prev_cb_bullet, '\S*\zs\s\+.*')
+endfunction "}}}
+
+function! vimwiki#lst#change_level(...) "{{{
+ let default_sym = vimwiki#lst#default_symbol()
+ let cmd = '>>'
+ let sym = default_sym
+
+ " parse argument
+ if a:0
+ if a:1 != '<<' && a:1 != '>>'
+ let cmd = '--'
+ let sym = a:1
+ else
+ let cmd = a:1
+ endif
+ endif
+ " is symbol valid
+ if sym.' ' !~ s:rx_cb_list_item() && sym.' ' !~ s:rx_list_item()
+ return
+ endif
+
+ " parsing setup
+ let lnum = line('.')
+ let line = getline('.')
+
+ let list_margin = vimwiki#lst#get_list_margin()
+ let list_sw = s:get_list_sw()
+ let n_nesting = s:get_list_nesting_level(lnum)
+ let n_indent = s:get_list_indent(lnum)
+
+ " remove indent and nesting
+ let li_bullet_and_content = strpart(line, n_nesting + n_indent)
+
+ " list bullet and checkbox
+ let cb_bullet = matchstr(li_bullet_and_content, s:rx_list_item()).
+ \ matchstr(li_bullet_and_content, s:rx_cb_list_item())
+
+ " XXX: it could be not unicode proof --> if checkboxes are set up with unicode syms
+ " content
+ let li_content = strpart(li_bullet_and_content, len(cb_bullet))
+
+ " trim
+ let cb_bullet = vimwiki#u#trim(cb_bullet)
+ let li_content = vimwiki#u#trim(li_content)
+
+ " nesting symbol
+ if VimwikiGet('syntax') == 'media'
+ if len(cb_bullet) > 0
+ let sym_nest = cb_bullet[0]
+ else
+ let sym_nest = sym
+ endif
+ else
+ let sym_nest = ' '
+ endif
+
+ if g:vimwiki_debug
+ echomsg "PARSE: Sw [".list_sw."]"
+ echomsg s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content, '|')
+ endif
+
+ " change level
+ if cmd == '--'
+ let cb_bullet = s:compose_cb_bullet(cb_bullet, sym)
+ if VimwikiGet('syntax') == 'media'
+ let sym_nest = sym
+ endif
+ elseif cmd == '>>'
+ if cb_bullet == ''
+ let cb_bullet = sym
+ else
+ let n_nesting = n_nesting + list_sw
+ endif
+ elseif cmd == '<<'
+ let n_nesting = n_nesting - list_sw
+ if VimwikiGet('syntax') == 'media'
+ if n_nesting < 0
+ let cb_bullet = ''
+ endif
+ else
+ if n_nesting < list_margin
+ let cb_bullet = ''
+ endif
+ endif
+ endif
+
+ let n_nesting = max([0, n_nesting])
+
+ if g:vimwiki_debug
+ echomsg "SHIFT:"
+ echomsg s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content, '|')
+ endif
+
+ " XXX: this is the code that adds the initial indent
+ let add_nesting = VimwikiGet('syntax') != 'media'
+ if n_indent + n_nesting*(add_nesting) < list_margin
+ let n_indent = list_margin - n_nesting*(add_nesting)
+ endif
+
+ if g:vimwiki_debug
+ echomsg "INDENT:"
+ echomsg s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content, '|')
+ endif
+
+ let line = s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content)
+
+ " replace
+ call setline(lnum, line)
+ call cursor(lnum, match(line, '\S') + 1)
+endfunction "}}}