diff options
Diffstat (limited to 'autoload/vimwiki/lst.vim')
-rw-r--r-- | autoload/vimwiki/lst.vim | 555 |
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 "}}} |