commit 8744a310319ca7b8b8c9b50d714b381f566de714
parent 1c88deeee0682b508a419640f4eb353ed014f755
Author: EinfachToll <istjanichtzufassen@googlemail.de>
Date: Mon, 8 Jul 2013 11:37:35 +0200
support for numbered lists and much other list stuff
Diffstat:
12 files changed, 1289 insertions(+), 484 deletions(-)
diff --git a/autoload/vimwiki/base.vim b/autoload/vimwiki/base.vim
@@ -1424,8 +1424,7 @@ endfunction " }}}
" arguments rxUrl, rxDesc, and rxStyle are copied verbatim, without any
" special character escapes or substitutions.
function! vimwiki#base#apply_template(template, rxUrl, rxDesc, rxStyle) "{{{
- let magic_chars = '.*[\^$'
- let lnk = escape(a:template, magic_chars)
+ let lnk = vimwiki#u#escape(a:template)
if a:rxUrl != ""
let lnk = substitute(lnk, '__LinkUrl__', '\='."'".a:rxUrl."'", '')
endif
diff --git a/autoload/vimwiki/lst.vim b/autoload/vimwiki/lst.vim
@@ -1,555 +1,1292 @@
" 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>
+" Everything concerning bulleted and numbered lists and checkboxes
+" Author: Daniel Schemala <istjanichtzufassen@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
+let g:loaded_vimwiki_list_auto = 1
-" Script variables {{{
-let s:rx_li_box = '\[.\?\]'
-" }}}
+" incrementation functions for the various kinds of numbers {{{
-" Script functions {{{
+function! s:increment_1(value) "{{{
+ return eval(a:value) + 1
+endfunction "}}}
-" 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.')
+function! s:increment_A(value) "{{{
+ let list_of_chars = split(a:value, '.\zs')
+ let done = 0
+ for idx in reverse(range(len(list_of_chars)))
+ let cur_num = char2nr(list_of_chars[idx])
+ if cur_num < 90
+ let list_of_chars[idx] = nr2char(cur_num + 1)
+ let done = 1
+ break
+ else
+ let list_of_chars[idx] = 'A'
+ endif
+ endfor
+ if !done
+ call insert(list_of_chars, 'A')
+ endif
+ return join(list_of_chars, '')
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)
+function! s:increment_a(value) "{{{
+ let list_of_chars = split(a:value, '.\zs')
+ let done = 0
+ for idx in reverse(range(len(list_of_chars)))
+ let cur_num = char2nr(list_of_chars[idx])
+ if cur_num < 122
+ let list_of_chars[idx] = nr2char(cur_num + 1)
+ let done = 1
+ break
+ else
+ let list_of_chars[idx] = 'a'
+ endif
+ endfor
+ if !done
+ call insert(list_of_chars, 'a')
endif
+ return join(list_of_chars, '')
+endfunction "}}}
- return '\['.result.'\]'
+function! s:increment_I(value) "{{{
+ let subst_list = [ ['XLVIII$', 'IL'], ['VIII$', 'IX'], ['III$', 'IV'], ['DCCCXCIX$', 'CM'], ['CCCXCIX$', 'CD'], ['LXXXIX$', 'XC'], ['XXXIX$', 'XL'], ['\(I\{1,2\}\)$', '\1I'], ['CDXCIX$', 'D'], ['CMXCIX$', 'M'], ['XCIX$', 'C'], ['I\([VXLCDM]\)$', '\1'], ['\([VXLCDM]\)$', '\1I'] ]
+ for [regex, subst] in subst_list
+ if a:value =~# regex
+ return substitute(a:value, regex, subst, '')
+ endif
+ endfor
+endfunction "}}}
+
+function! s:increment_i(value) "{{{
+ let subst_list = [ ['xlviii$', 'il'], ['viii$', 'ix'], ['iii$', 'iv'], ['dcccxcix$', 'cm'], ['cccxcix$', 'cd'], ['lxxxix$', 'xc'], ['xxxix$', 'xl'], ['\(i\{1,2\}\)$', '\1i'], ['cdxcix$', 'd'], ['cmxcix$', 'm'], ['xcix$', 'c'], ['i\([vxlcdm]\)$', '\1'], ['\([vxlcdm]\)$', '\1i'] ]
+ for [regex, subst] in subst_list
+ if a:value =~# regex
+ return substitute(a:value, regex, subst, '')
+ endif
+ endfor
endfunction "}}}
-" Get blank checkbox
-function! s:blank_checkbox() "{{{
- return '['.s:str_idx(g:vimwiki_listsyms, 1).'] '
+" incrementation functions for the various kinds of numbers }}}
+
+function! s:substitute_rx_in_line(lnum, pattern, new_string) "{{{
+ call setline(a:lnum, substitute(getline(a:lnum), a:pattern, a:new_string, ''))
endfunction "}}}
-" Get regexp of the list item.
-function! s:rx_list_item() "{{{
- return '\('.g:vimwiki_rxListBullet.'\|'.g:vimwiki_rxListNumber.'\)'
+function! s:substitute_string_in_line(lnum, pattern, new_string) "{{{
+ call s:substitute_rx_in_line(a:lnum, vimwiki#u#escape(a:pattern), a:new_string)
endfunction "}}}
-" Get regexp of the list item with checkbox.
-function! s:rx_cb_list_item() "{{{
- return s:rx_list_item().'\s*\zs\[.\?\]'
+"Returns: the mainly used data structure in this file
+"An item represents a single list item and is a dictionary with the keys
+"lnum - the line number of the list item
+"type - 1 for bulleted item, 2 for numbered item, 0 for a regular line
+"mrkr - the concrete marker, e.g. '**' or 'b)'
+"cb - the char in the checkbox or '' if there is no checkbox
+function! s:get_item(lnum) "{{{
+ let item = {'lnum': a:lnum}
+ if a:lnum == 0 || a:lnum > line('$')
+ let item.type = 0
+ return item
+ endif
+ let matches = matchlist(getline(a:lnum), vimwiki#lst#get_list_item_rx(1))
+ if matches == [] || (matches[1] == '' && matches[2] == '') || (matches[1] != '' && matches[2] != '')
+ let item.type = 0
+ return item
+ endif
+
+ let item.cb = matches[3]
+
+ if matches[1] != ''
+ let item.type = 1
+ let item.mrkr = matches[1]
+ else
+ let item.type = 2
+ let item.mrkr = matches[2]
+ endif
+
+ return item
endfunction "}}}
-" Get level of the list item.
+
+"Returns: level of the line
+"0 is the 'highest' level
function! s:get_level(lnum) "{{{
- if VimwikiGet('syntax') == 'media'
- let level = vimwiki#u#count_first_sym(getline(a:lnum))
+ if VimwikiGet('syntax') != 'media'
+ let level = getline(a:lnum) !~ '^\s*$' ? indent(a:lnum) : 0
else
- let level = indent(a:lnum)
+ let rx_markers = '^[' . vimwiki#u#escape(join(keys(g:vimwiki_bullet_points), "")) . ']\+'
+ let level = s:string_length(matchstr(getline(a:lnum), rx_markers)) - 1
+ if level < 0
+ let level = (indent(a:lnum) == 0) ? 0 : 9999
+ endif
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
+"XXX does not distinguish between letters and romanian numerals
+"Should be no problem as long as the user doesn't mix them
+function! s:regexp_of_marker(item) "{{{
+ if a:item.type == 1
+ return vimwiki#u#escape(a:item.mrkr)
+ elseif a:item.type == 2
+ for ki in ['d', 'u', 'l']
+ let mats = matchstr(a:item.mrkr, '\'.ki.'\+['.vimwiki#u#escape(g:vimwiki_bullet_numbers[1]).']')
+ if mats != ''
+ let [_, divisor] = s:get_chars_and_divisor(mats)
+ return '\'.ki.'\+'.vimwiki#u#escape(divisor)
+ endif
+ endfor
+ else
+ return ''
+ endif
+endfunction "}}}
+
+function! s:empty_item() "{{{
+ return {'type': 0}
+endfunction "}}}
+
+"Returns: the list item after a:item in its list or empty item
+"If a:all is 1, the markers can differ
+function! s:get_next_list_item(item, all) "{{{
+ return s:get_next_or_prev_list_item(a:item, +1, line('$')+1, a:all)
+endfunction "}}}
+
+"Returns: the list item before a:item in its list or empty item
+"If a:all is 1, the markers can differ
+function! s:get_prev_list_item(item, all) "{{{
+ return s:get_next_or_prev_list_item(a:item, -1, 0, a:all)
+endfunction "}}}
+
+function! s:get_next_or_prev_list_item(item, direction, until, all) "{{{
+ let org_lvl = s:get_level(a:item.lnum)
+ let org_regex = s:regexp_of_marker(a:item)
+ let cur_ln = s:get_next_prev_line(a:item.lnum, a:direction)
+ while cur_ln != a:until
+ let cur_lvl = s:get_level(cur_ln)
+ let cur_linecontent = getline(cur_ln)
+
+ if cur_lvl == org_lvl
+ if a:all == 1 || cur_linecontent =~# '^\s*'.org_regex.'\s'
+ return s:get_item(cur_ln)
+ else
+ return s:empty_item()
+ endif
+ elseif cur_lvl < org_lvl
+ return s:empty_item()
+ else
+ let cur_ln = s:get_next_prev_line(cur_ln, a:direction)
endif
- let c_lnum -= 1
endwhile
- return 0
+ return s:empty_item()
+endfunction "}}}
+
+function! s:first_char(string) "{{{
+ return matchstr(a:string, '^.')
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
+"Returns: 'bla)' -> ['bla', ')']
+" must be that complicated, because
+" 'bla)'[:-2] wouldn't work for multibyte chars
+function! s:get_chars_and_divisor(string) "{{{
+ return matchlist(a:string, '^\(.\+\)\(.\)$')[1:2]
+endfunction "}}}
+
+"Returns: 1, a, i, A, I or ''
+"If in doubt if alphanumeric character or romanian
+"numeral, peek in the previous line
+function! s:guess_kind_of_numbered_item(item) "{{{
+ if a:item.type != 2 | return '' | endif
+ let kinds = split(g:vimwiki_bullet_numbers[0], '.\zs')
+ let [chars, divisor] = s:get_chars_and_divisor(a:item.mrkr)
+
+ if chars =~ '\d\+'
+ return '1'
+ endif
+ if chars =~# '\l\+'
+ if chars !~# '^[ivxlcdm]\+' || index(kinds, 'i') == -1
+ return 'a'
+ else
+
+ let item_above = s:get_prev_list_item(a:item, 0)
+ if item_above.type != 0
+ let [chars_above, div_above] = s:get_chars_and_divisor(item_above.mrkr)
+ if index(kinds, 'a') == -1 || (div_above !=# divisor && chars =~# 'i\+') || s:increment_i(chars_above) ==# chars
+ return 'i'
+ else
+ return 'a'
+ endif
+ else
+ if chars =~# 'i\+' || index(kinds, 'a') == -1
+ return 'i'
+ else
+ return 'a'
+ endif
+ endif
+
endif
- if line =~ '^\s*$'
- return 0
+ endif
+ if chars =~# '\u\+'
+ if chars !~# '^[IVXLCDM]\+' || index(kinds, 'I') == -1
+ return 'A'
+ else
+
+ let item_above = s:get_prev_list_item(a:item, 0)
+ if item_above.type != 0
+ let [chars_above, div_above] = s:get_chars_and_divisor(item_above.mrkr)
+ if index(kinds, 'A') == -1 || (div_above !=# divisor && chars =~# 'I\+') || s:increment_i(chars_above) ==# chars
+ return 'I'
+ else
+ return 'A'
+ endif
+ else
+ if chars =~# 'I\+' || index(kinds, 'A') == -1
+ return 'I'
+ else
+ return 'A'
+ endif
+ endif
+
endif
- let c_lnum += 1
- endwhile
- return 0
+ endif
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
+
+function! s:get_first_item_in_list(item, all) "{{{
+ let first_item = a:item
+ while 1
+ let prev_item = s:get_prev_list_item(first_item, a:all)
+ if prev_item.type == 0
+ break
+ else
+ let first_item = prev_item
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
+ return first_item
+endfunction "}}}
+
+"Returns: lnum+1 in most cases, but skips blank lines and preformatted text
+"0 in case of nonvalid line
+"If there is no second argument, 0 is returned at a header, otherwise the
+"header is skipped
+function! s:get_next_line(lnum, ...) "{{{
+ if getline(a:lnum) =~# '^\s*'.g:vimwiki_rxPreStart
+ let cur_ln = a:lnum + 1
+ while cur_ln <= line('$') && getline(cur_ln) !~# '^\s*'.g:vimwiki_rxPreEnd.'\s*$'
+ let cur_ln += 1
+ endwhile
+ let next_line = nextnonblank(cur_ln+1)
+ else
+ let next_line = nextnonblank(a:lnum+1)
endif
- return state
+
+ if a:0 > 0 && getline(next_line) =~# g:vimwiki_rxHeader
+ let next_line = s:get_next_line(next_line, 1)
+ endif
+
+ if next_line < 0 || next_line > line('$') || (getline(next_line) =~# g:vimwiki_rxHeader && a:0 == 0)
+ return 0
+ endif
+
+ return next_line
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()
+"Returns: lnum-1 in most cases, but skips blank lines and preformatted text
+"0 in case of nonvalid line and a header, because a header ends every list
+function! s:get_prev_line(lnum) "{{{
+ let prev_line = prevnonblank(a:lnum-1)
+
+ if getline(prev_line) =~# '^\s*'.g:vimwiki_rxPreEnd.'\s*$'
+ let cur_ln = a:lnum - 1
+ while 1
+ if cur_ln == 0 || getline(cur_ln) =~# '^\s*'.g:vimwiki_rxPreStart
+ break
+ endif
+ let cur_ln -= 1
+ endwhile
+ let prev_line = cur_ln
+ endif
+
+ if prev_line < 0 || prev_line > line('$') || getline(prev_line) =~# g:vimwiki_rxHeader
+ return 0
+ endif
+
+ return prev_line
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
+function! s:get_next_prev_line(lnum, dir) "{{{
+ if a:dir == 1
+ return s:get_next_line(a:lnum)
+ else
+ return s:get_prev_line(a:lnum)
+ endif
+endfunction "}}}
+
+function! s:get_first_child(item) "{{{
+ if a:item.lnum >= line('$')
+ return s:empty_item()
+ endif
+ let org_lvl = s:get_level(a:item.lnum)
+ let cur_item = s:get_item(s:get_next_line(a:item.lnum))
+ while 1
+ if cur_item.type != 0 && s:get_level(cur_item.lnum) > org_lvl
+ return cur_item
endif
- if indent(c_lnum) > indent(a:lnum)
- return 0
+ if cur_item.lnum > line('$') || cur_item.lnum <= 0 || s:get_level(cur_item.lnum) <= org_lvl
+ return s:empty_item()
endif
- let c_lnum -= 1
+ let cur_item = s:get_item(s:get_next_line(cur_item.lnum))
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), '[')
+
+"Returns: the next sibling of a:child, given the parent item
+"Used for iterating over children
+"Note: child items do not necessarily have the same indent, i.e. level
+function! s:get_next_child_item(parent, child) "{{{
+ if a:parent.type == 0 | return s:empty_item() | endif
+ let parent_lvl = s:get_level(a:parent.lnum)
+ let cur_ln = s:get_last_line_of_item_incl_children(a:child)
+ while 1
+ let next_line = s:get_next_line(cur_ln)
+ if next_line == 0 || s:get_level(next_line) <= parent_lvl
+ break
+ endif
+ let cur_ln = next_line
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type > 0
+ return cur_item
+ endif
+ endwhile
+ return s:empty_item()
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)
+"Renumbers the current list from a:item on downwards
+"Returns: the last item that was adjusted
+function! s:adjust_numbered_list_below(item, recursive) "{{{
+ if !(a:item.type == 2 || (a:item.type == 1 && a:recursive))
+ return a:item
+ endif
- " add parent
- call add(result, lnum)
+ let kind = s:guess_kind_of_numbered_item(a:item)
- 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)
+ let cur_item = a:item
+ while 1
+ if a:recursive
+ call s:adjust_items_recursively(cur_item)
+ endif
+
+ let next_item = s:get_next_list_item(cur_item, 0)
+ if next_item.type == 0
+ break
+ endif
+
+ if cur_item.type == 2
+ let [chars, divisor] = s:get_chars_and_divisor(cur_item.mrkr)
+ let new_val = s:increment_{kind}(chars) . divisor
+ call s:substitute_string_in_line(next_item.lnum, next_item.mrkr, new_val)
+ let next_item.mrkr = new_val
+ endif
+
+ let cur_item = next_item
endwhile
-
- return result
+ return cur_item
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)
+function! s:adjust_items_recursively(parent) "{{{
+ if a:parent.type == 0
+ return s:empty_item()
+ end
- while lnum != 0 && s:get_level(lnum) >= ind
- if s:get_level(lnum) == ind && s:is_cb_list_item(lnum)
- call add(result, lnum)
+ let child_item = s:get_first_child(a:parent)
+ if child_item.type == 0
+ return child_item
+ endif
+ while 1
+ let last_item = s:adjust_numbered_list(child_item, 1, 1)
+
+ let child_item = s:get_next_child_item(a:parent, last_item)
+ if child_item.type == 0
+ return last_item
endif
- let lnum = s:next_list_item(lnum)
endwhile
+endfunction "}}}
- 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)
+"Renumbers the list a:item is in.
+"If a:all == 0, only the items which have the same kind of marker as a:item are
+"considered, otherwise all items.
+"Returns: the last item that was adjusted
+function! s:adjust_numbered_list(item, all, recursive) "{{{
+ if !(a:item.type == 2 || (a:item.type == 1 && (a:all || a:recursive)))
+ return s:empty_item()
+ end
+
+ let first_item = s:get_first_item_in_list(a:item, a:all)
+
+ while 1
+ if first_item.type == 2
+ let [_, divisor] = s:get_chars_and_divisor(first_item.mrkr)
+ let new_mrkr = s:guess_kind_of_numbered_item(first_item) . divisor
+ call s:substitute_string_in_line(first_item.lnum, first_item.mrkr, new_mrkr)
+ let first_item.mrkr = new_mrkr
endif
- let lnum = s:prev_list_item(lnum)
+
+ let last_item = s:adjust_numbered_list_below(first_item, a:recursive)
+
+ let next_first_item = s:get_next_list_item(last_item, 1)
+ if a:all == 0 || next_first_item.type == 0
+ return last_item
+ endif
+ let first_item = next_first_item
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)
+"Returns: the (rounded) rate of [X] checked child items in percent
+function! s:get_rate(item) "{{{
+ let rate = -1
+ if a:item.type == 0 || a:item.cb == ''
+ return rate
+ endif
+ let state = a:item.cb
+ return index(g:vimwiki_listsyms, state) * 25
+endfunction "}}}
+
+"Set state of the list item to [ ] or [o] or whatever
+"Returns: 1 if the state changed, 0 otherwise
+function! s:set_state(item, new_rate) "{{{
+ let new_state = s:rate_to_state(a:new_rate)
+ let old_state = s:rate_to_state(s:get_rate(a:item))
+ if new_state !=# old_state
+ call s:substitute_rx_in_line(a:item.lnum, '\[.]', '['.new_state.']')
+ return 1
+ else
+ return 0
+ endif
+endfunction "}}}
- 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)
+"Set state of the list item to [ ] or [o] or whatever
+"Updates the states of its child items
+function! s:set_state_plus_children(item, new_rate) "{{{
+ call s:set_state(a:item, a:new_rate)
+
+ let child_item = s:get_first_child(a:item)
+ while 1
+ if child_item.type == 0
+ break
+ endif
+ if child_item.cb != ''
+ call s:set_state_plus_children(child_item, a:new_rate)
+ endif
+ let child_item = s:get_next_child_item(a:item, child_item)
endwhile
+endfunction "}}}
- if s:is_cb_list_item(lnum)
- return lnum
+function! s:rate_to_state(rate) "{{{
+ let state = ''
+ if a:rate == 100
+ let state = g:vimwiki_listsyms[4]
+ elseif a:rate == 0
+ let state = g:vimwiki_listsyms[0]
+ elseif a:rate >= 67
+ let state = g:vimwiki_listsyms[3]
+ elseif a:rate >= 34
+ let state = g:vimwiki_listsyms[2]
else
- return a:lnum
+ let state = g:vimwiki_listsyms[1]
endif
+ return state
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)
+function! s:get_parent(item) "{{{
+ let parent_line = 0
+
+ let cur_ln = prevnonblank(a:item.lnum)
+ let child_lvl = s:get_level(cur_ln)
+ if child_lvl == 0
+ return s:empty_item()
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
+ while 1
+ let cur_ln = s:get_prev_line(cur_ln)
+ if cur_ln == 0 | break | endif
+ let cur_lvl = s:get_level(cur_ln)
+ if cur_lvl < child_lvl
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type == 0
+ let child_lvl = cur_lvl
+ continue
+ endif
+ let parent_line = cur_ln
+ break
+ endif
+ endwhile
+ return s:get_item(parent_line)
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)
+function! s:update_state(item) "{{{
+ if a:item.type == 0 || a:item.cb == ''
+ return
+ endif
+
+ let sum_siblings = 0
+ let count_siblings = 0
+
+ let child_item = s:get_first_child(a:item)
+
+ while 1
+ if child_item.type == 0
+ break
endif
- return 1
+ if child_item.cb != ''
+ let count_siblings += 1
+ let sum_siblings += s:get_rate(child_item)
+ endif
+ let child_item = s:get_next_child_item(a:item, child_item)
+ endwhile
+
+ let new_rate = count_siblings > 0 ? sum_siblings / count_siblings : 0
+
+ "set state and set the parents states recursively
+ let state_changed = s:set_state(a:item, new_rate)
+ if state_changed
+ call s:update_state(s:get_parent(a:item))
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
+
+"Creates checkbox in a list item.
+"Returns: 1 if successful
+function! s:create_cb(item) "{{{
+ if a:item.type == 0 || a:item.cb != ''
+ return 0
endif
- for lnum in s:get_child_items(a:lnum)
- call s:set_state(lnum, new_state)
- endfor
+
+ let new_item = a:item
+ let new_item.cb = g:vimwiki_listsyms[0]
+ call s:substitute_rx_in_line(new_item.lnum, vimwiki#u#escape(new_item.mrkr) . '\zs\ze', ' [' . new_item.cb . ']')
+
+ call s:update_state(new_item)
+ return 1
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
+"Returns: the item if there is one in a:lnum
+"else the multiline item a:lnum belongs to
+function! s:get_corresponding_item(lnum) "{{{
+ let item = s:get_item(a:lnum)
+ if item.type != 0
+ return item
+ endif
+ let org_lvl = s:get_level(a:lnum)
+ let cur_ln = a:lnum
+ while cur_ln > 0
+ let cur_lvl = s:get_level(cur_ln)
+ let cur_item = s:get_item(cur_ln)
+ if cur_lvl < org_lvl && cur_item.type != 0
+ return cur_item
+ endif
+ if cur_lvl < org_lvl
+ let org_lvl = cur_lvl
+ endif
+ let cur_ln = s:get_prev_line(cur_ln)
+ endwhile
+ return s:empty_item()
+endfunction "}}}
+
+"Returns: the last line of a (possibly multiline) item, including all children
+function! s:get_last_line_of_item_incl_children(item) "{{{
+ let cur_ln = a:item.lnum
+ let org_lvl = s:get_level(a:item.lnum)
+ while 1
+ let next_line = s:get_next_line(cur_ln)
+ if next_line == 0 || s:get_level(next_line) <= org_lvl
+ return cur_ln
endif
- call s:set_state(parent_lnum, s:all_siblings_checked(c_lnum))
+ let cur_ln = next_line
+ endwhile
+endfunction "}}}
+
+"Returns: the last line of a (possibly multiline) item
+"Note: there can be other list items inbetween these lines
+function! s:get_last_line_of_item(item) "{{{
+ if a:item.type == 0 | return 0 | endif
+ let org_lvl = s:get_level(a:item.lnum)
+ let last_corresponding_line = a:item.lnum
- let c_lnum = parent_lnum
+ let cur_ln = s:get_next_line(a:item.lnum)
+ while 1
+ if cur_ln == 0 || s:get_level(cur_ln) <= org_lvl
+ break
+ endif
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type == 0
+ let last_corresponding_line = cur_ln
+ let cur_ln = s:get_next_line(cur_ln)
+ else
+ let cur_ln = s:get_next_line(s:get_last_line_of_item_incl_children(cur_item))
+ endif
endwhile
+
+ return last_corresponding_line
+endfunction "}}}
+
+"Returns: the column where the text of a line starts (possible list item
+"markers and checkboxes are skipped)
+function! s:text_begin(lnum) "{{{
+ return s:string_length(matchstr(getline(a:lnum), vimwiki#lst#get_list_item_rx(1)))
endfunction "}}}
-function! s:TLI_toggle(lnum) "{{{
- if !s:TLI_create_checkbox(a:lnum)
- call s:TLI_switch_child_state(a:lnum)
+if exists("*strdisplaywidth")
+ fu! s:string_length(str)
+ return strdisplaywidth(a:str)
+ endfu
+else
+ fu! s:string_length(str)
+ return strlen(substitute(a:str, '.', 'x', 'g'))
+ endfu
+endif
+
+"Returns: 2 if there is a marker and text
+" 1 for a marker and no text
+" 0 for no marker at all (empty line or only text)
+function! s:line_has_marker(lnum) "{{{
+ if getline(a:lnum) =~# vimwiki#lst#get_list_item_rx(1).'\s*$'
+ return 1
+ elseif getline(a:lnum) =~# vimwiki#lst#get_list_item_rx(1).'\s*\S'
+ return 2
+ else
+ return 0
endif
- call s:TLI_switch_parent_state(a:lnum)
endfunction "}}}
-" Script functions }}}
+"Renumbers the list the cursor is in
+"also update its parents state
+function! vimwiki#lst#adjust_numbered_list() "{{{
+ let cur_item = s:get_corresponding_item(line('.'))
+ if cur_item.type == 0 | return | endif
+ call s:adjust_numbered_list(cur_item, 1, 0)
+ call s:update_state(s:get_parent(cur_item))
+endfunction "}}}
-" Toggle list item between [ ] and [X]
-function! vimwiki#lst#ToggleListItem(line1, line2) "{{{
- let line1 = a:line1
- let line2 = a:line2
+"Renumbers all lists of the buffer
+"of course, this might take some seconds
+function! vimwiki#lst#adjust_whole_buffer() "{{{
+ let cur_ln = 1
+ while 1
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type != 0
+ let cur_item = s:adjust_numbered_list(cur_item, 0, 1)
+ endif
+ let cur_ln = s:get_next_line(cur_item.lnum, 1)
+ if cur_ln <= 0 || cur_ln > line('$')
+ return
+ endif
+ endwhile
+endfunction "}}}
- if line1 != line2 && !s:is_list_item(line1)
- let line1 = s:find_next_list_item(line1)
+"Toggle checkbox between [ ] and [X] or creates one
+function! vimwiki#lst#toggle_cb(line1, line2) "{{{
+ let from_item = s:get_corresponding_item(a:line1)
+ let to_item = s:get_corresponding_item(a:line2)
+ if from_item.type == 0
+ return
endif
- let c_lnum = line1
- while c_lnum != 0 && c_lnum <= line2
- let li_lnum = s:is_list_item(c_lnum)
+ let parent_items_of_lines = []
- if li_lnum
- let li_level = s:get_level(li_lnum)
- if c_lnum == line1
- let start_li_level = li_level
+ if from_item.cb == ''
+
+ "if line1 has no CB, make a CB in every selected line
+ let parent_items_of_lines = []
+ for cur_ln in range(from_item.lnum, to_item.lnum)
+ let cur_item = s:get_item(cur_ln)
+ let success = s:create_cb(cur_item)
+
+ if success
+ let cur_parent_item = s:get_parent(cur_item)
+ if index(parent_items_of_lines, cur_parent_item) == -1
+ call insert(parent_items_of_lines, cur_parent_item)
+ endif
endif
+ endfor
- if li_level <= start_li_level
- call s:TLI_toggle(li_lnum)
- let start_li_level = li_level
+ else
+
+ "if line1 has CB, toggle it and set all siblings to the same new state
+ let rate_first_line = s:get_rate(from_item)
+ let new_rate = rate_first_line==100 ? 0 : 100
+
+ for cur_ln in range(from_item.lnum, to_item.lnum)
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type != 0 && cur_item.cb != ''
+ call s:set_state_plus_children(cur_item, new_rate)
+ let cur_parent_item = s:get_parent(cur_item)
+ if index(parent_items_of_lines, cur_parent_item) == -1
+ call insert(parent_items_of_lines, cur_parent_item)
+ endif
endif
+ endfor
+
+ endif
+
+ for parent_item in parent_items_of_lines
+ call s:update_state(parent_item)
+ endfor
+
+endfunction "}}}
+
+function! s:get_idx_list_markers(item) "{{{
+ if a:item.type == 1
+ let m = s:first_char(a:item.mrkr)
+ else
+ let [_, divisor] = s:get_chars_and_divisor(a:item.mrkr)
+ let m = s:guess_kind_of_numbered_item(a:item) . divisor
+ endif
+ return index(g:vimwiki_list_markers, m)
+endfunction "}}}
+
+function! s:get_next_mrkr(item) "{{{
+ if a:item.type == 0
+ let new_mrkr = g:vimwiki_list_markers[0]
+ else
+ let idx = s:get_idx_list_markers(a:item)
+ let new_mrkr = g:vimwiki_list_markers[(idx + 1) % len(g:vimwiki_list_markers)]
+ endif
+ return new_mrkr
+endfunction "}}}
+
+function! s:get_prev_mrkr(item) "{{{
+ if a:item.type == 0
+ return g:vimwiki_list_markers[-1]
+ endif
+ let idx = s:get_idx_list_markers(a:item)
+ if idx == -1
+ return g:vimwiki_list_markers[-1]
+ else
+ return g:vimwiki_list_markers[(idx - 1 + len(g:vimwiki_list_markers)) % len(g:vimwiki_list_markers)]
+ endif
+endfunction "}}}
+
+function! s:set_new_mrkr(item, new_mrkr) "{{{
+ if a:item.type == 0
+ call s:substitute_rx_in_line(a:item.lnum, '^\s*\zs\ze', a:new_mrkr.' ')
+ if indent(a:item.lnum) == 0 && VimwikiGet('syntax') != 'media'
+ call s:set_indent(a:item.lnum, vimwiki#lst#get_list_margin())
+ endif
+ else
+ call s:substitute_string_in_line(a:item.lnum, a:item.mrkr, a:new_mrkr)
+ endif
+endfunction "}}}
+
+function! vimwiki#lst#change_marker(line1, line2, new_mrkr) "{{{
+ let cur_col_from_eol = col("$") - col("'^")
+ let new_mrkr = a:new_mrkr
+ let cur_ln = a:line1
+ while 1
+ let cur_item = s:get_item(cur_ln)
+
+ if new_mrkr ==# "next"
+ let new_mrkr = s:get_next_mrkr(cur_item)
+ elseif new_mrkr ==# "prev"
+ let new_mrkr = s:get_prev_mrkr(cur_item)
endif
- let c_lnum = s:find_next_list_item(c_lnum)
+ "handle markers like ***
+ if has_key(g:vimwiki_bullet_points, s:first_char(new_mrkr)) && g:vimwiki_bullet_points[s:first_char(new_mrkr)] == 1
+ "use *** if the item above has *** too
+ let item_above = s:get_prev_list_item(cur_item, 1)
+ if item_above.type == 1 && s:first_char(item_above.mrkr) == s:first_char(new_mrkr)
+ let new_mrkr = item_above.mrkr
+ else
+ "use *** if the item below has *** too
+ let item_below = s:get_next_list_item(cur_item, 1)
+ if item_below.type == 1 && s:first_char(item_below.mrkr) == s:first_char(new_mrkr)
+ let new_mrkr = item_below.mrkr
+ else
+ "if the old is ### and the new is * use ***
+ if cur_item.type == 1 && has_key(g:vimwiki_bullet_points, s:first_char(cur_item.mrkr)) && g:vimwiki_bullet_points[s:first_char(cur_item.mrkr)] == 1
+ let new_mrkr = repeat(new_mrkr, s:string_length(cur_item.mrkr))
+ else
+ "use *** if the parent item has **
+ let parent_item = s:get_parent(cur_item)
+ if parent_item.type == 1 && s:first_char(parent_item.mrkr) == s:first_char(new_mrkr)
+ let new_mrkr = repeat(s:first_char(parent_item.mrkr), s:string_length(parent_item.mrkr)+1)
+ endif
+ endif
+ endif
+ endif
+
+ endif
+
+ call s:set_new_mrkr(cur_item, new_mrkr)
+ call s:adjust_numbered_list(s:get_item(cur_ln), 1, 0)
+
+ if cur_ln >= a:line2 | break | endif
+ let cur_ln = s:get_next_line(cur_ln, 1)
endwhile
+ call cursor('.', col('$') - cur_col_from_eol)
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()
+function! s:remove_cb(item) "{{{
+ let item = a:item
+ if item.type != 0 && item.cb != ''
+ let item.cb = ''
+ call s:substitute_rx_in_line(item.lnum, '\s\+\[.\]', '')
endif
- return cr
+ return item
endfunction "}}}
-function! vimwiki#lst#kbd_oO(cmd) "{{{
- " cmd should be 'o' or 'O'
+function! vimwiki#lst#remove_cb(first_line, last_line) "{{{
+ let first_item = s:get_corresponding_item(a:first_line)
+ let last_item = s:get_corresponding_item(a:last_line)
- let l:count = v:count1
- while l:count > 0
+ if first_item.type == 0 || last_item.type == 0
+ return
+ endif
- 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('.')
+ let parent_items_of_lines = []
+ let cur_ln = first_item.lnum
+ while 1
+ if cur_ln <= 0 || cur_ln > last_item.lnum | break | endif
+ let cur_item = s:get_item(cur_ln)
+ if cur_item.type != 0
+ let cur_item = s:remove_cb(cur_item)
+ let cur_parent_item = s:get_parent(cur_item)
+ if index(parent_items_of_lines, cur_parent_item) == -1
+ call insert(parent_items_of_lines, cur_parent_item)
+ endif
endif
+ let cur_ln = s:get_next_line(cur_ln)
+ endwhile
+ for parent_item in parent_items_of_lines
+ call s:update_state(parent_item)
+ endfor
+endfunction "}}}
- 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
+function! vimwiki#lst#remove_cb_in_list() "{{{
+ let first_item = s:get_first_item_in_list(s:get_corresponding_item(line('.')), 0)
- if a:cmd ==# 'o'
- call append(lnum, res)
- call cursor(lnum + 1, col('$'))
+ let cur_item = first_item
+ while 1
+ let next_item = s:get_next_list_item(cur_item, 0)
+ let cur_item = s:remove_cb(cur_item)
+ if next_item.type == 0
+ break
else
- call append(lnum - 1, res)
- call cursor(lnum, col('$'))
+ let cur_item = next_item
endif
-
- let l:count -= 1
endwhile
- startinsert!
+ call s:update_state(s:get_parent(first_item))
+endfunction "}}}
+
+function! vimwiki#lst#change_marker_in_list(new_mrkr) "{{{
+ let first_item = s:get_first_item_in_list(s:get_corresponding_item(line('.')), 0)
+ if first_item.type == 0 | return | endif
+ let first_item_line = first_item.lnum
+
+ let cur_item = first_item
+ while 1
+ let next_item = s:get_next_list_item(cur_item, 0)
+ call s:set_new_mrkr(cur_item, a:new_mrkr)
+ if next_item.type == 0
+ break
+ else
+ let cur_item = next_item
+ endif
+ endwhile
+
+ call s:adjust_numbered_list(s:get_item(first_item_line), 0, 0)
endfunction "}}}
-function! vimwiki#lst#default_symbol() "{{{
- " TODO: initialize default symbol from syntax/vimwiki_xxx.vim
- if VimwikiGet('syntax') == 'default'
- return '-'
+function! s:set_indent(lnum, new_indent) "{{{
+ if &expandtab
+ let indentstring = repeat(' ', a:new_indent)
else
- return '*'
+ let indentstring = repeat('\t', a:new_indent / &tabstop) . repeat(' ', a:new_indent % &tabstop)
endif
+ call s:substitute_rx_in_line(a:lnum, '^\s*', indentstring)
endfunction "}}}
-function vimwiki#lst#get_list_margin() "{{{
- if VimwikiGet('list_margin') < 0
- return &sw
+function! s:decrease_level(item) "{{{
+ let removed_indent = 0
+ if VimwikiGet('syntax') == 'media' && a:item.type == 1 && g:vimwiki_bullet_points[s:first_char(a:item.mrkr)]
+ if s:string_length(a:item.mrkr) >= 2
+ call s:substitute_string_in_line(a:item.lnum, s:first_char(a:item.mrkr), '')
+ let removed_indent = -1
+ endif
else
- return VimwikiGet('list_margin')
+ let old_indent = indent(a:item.lnum)
+ if &shiftround
+ let new_indent = (old_indent - 1) / &shiftwidth * &shiftwidth
+ else
+ let new_indent = old_indent - &shiftwidth
+ endif
+ call s:set_indent(a:item.lnum, new_indent)
+ let removed_indent = new_indent - old_indent
endif
+ return removed_indent
endfunction "}}}
-function s:get_list_sw() "{{{
- if VimwikiGet('syntax') == 'media'
- return 1
+function! s:increase_level(item) "{{{
+ let additional_space = 0
+ if VimwikiGet('syntax') == 'media' && a:item.type == 1 && g:vimwiki_bullet_points[s:first_char(a:item.mrkr)]
+ call s:substitute_string_in_line(a:item.lnum, a:item.mrkr, a:item.mrkr . s:first_char(a:item.mrkr))
+ let additional_indent = 1
else
- return &sw
+ let old_indent = indent(a:item.lnum)
+ if &shiftround
+ let new_indent = (old_indent / &shiftwidth + 1) * &shiftwidth
+ else
+ let new_indent = old_indent + &shiftwidth
+ endif
+ call s:set_indent(a:item.lnum, new_indent)
+ let additional_indent = new_indent - old_indent
endif
-endfunction "}}}
+ return additional_indent
+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
+"adds a:indent_by to the current indent
+"a:indent_by can be negative
+function! s:indent_line_by(lnum, indent_by) "{{{
+ let item = s:get_item(a:lnum)
+ if VimwikiGet('syntax') == 'media' && item.type == 1 && g:vimwiki_bullet_points[s:first_char(item.mrkr)]
+ if a:indent_by > 0
+ call s:substitute_string_in_line(a:lnum, item.mrkr, item.mrkr . s:first_char(item.mrkr))
+ elseif a:indent_by < 0
+ call s:substitute_string_in_line(a:lnum, s:first_char(item.mrkr), '')
endif
else
- let level = indent(a:lnum)
+ call s:set_indent(a:lnum, indent(a:lnum) + a:indent_by)
endif
- return level
-endfunction "}}}
+endfunction "}}}
-function s:get_list_indent(lnum) "{{{
- if VimwikiGet('syntax') == 'media'
- return indent(a:lnum)
+"Returns: the item above or the item below or an empty item
+function! s:get_a_neighbor_item(item) "{{{
+ let prev_item = s:get_prev_list_item(a:item, 1)
+ if prev_item.type != 0
+ return prev_item
else
- return 0
+ let next_item = s:get_next_list_item(a:item, 1)
+ if next_item.type != 0
+ return next_item
+ endif
endif
-endfunction "}}}
+ return s:empty_item()
+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 = ''
+"sets kind of the item depending on neighbor items and the parent item
+function! s:adjust_mrkr(item, look_above_and_below) "{{{
+ if a:item.type == 0 || VimwikiGet('syntax') == 'media'
+ return
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
+
+ let new_mrkr = a:item.mrkr
+ if a:look_above_and_below
+ let neighbor_item = s:get_a_neighbor_item(a:item)
else
- let li_bullet = ''.sep
+ let neighbor_item = s:get_prev_list_item(a:item, 1)
+ endif
+ if neighbor_item.type != 0
+ let new_mrkr = neighbor_item.mrkr
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 "}}}
+ "if possible, set e.g. *** if parent has ** as marker
+ if neighbor_item.type == 0 && a:item.type == 1 && has_key(g:vimwiki_bullet_points, s:first_char(a:item.mrkr)) && g:vimwiki_bullet_points[s:first_char(a:item.mrkr)] == 1
+ let parent_item = s:get_parent(a:item)
+ if parent_item.type == 1 && s:first_char(parent_item.mrkr) == s:first_char(a:item.mrkr)
+ let new_mrkr = repeat(s:first_char(parent_item.mrkr), s:string_length(parent_item.mrkr)+1)
+ endif
+ endif
-function! vimwiki#lst#change_level(...) "{{{
- let default_sym = vimwiki#lst#default_symbol()
- let cmd = '>>'
- let sym = default_sym
+ call s:substitute_string_in_line(a:item.lnum, a:item.mrkr, new_mrkr)
+ call s:adjust_numbered_list(a:item, 0, 1)
+endfunction "}}}
- " parse argument
- if a:0
- if a:1 != '<<' && a:1 != '>>'
- let cmd = '--'
- let sym = a:1
+"changes lvl of lines in selection
+function! s:change_level(from_line, to_line, direction, plus_children) "{{{
+ let from_item = s:get_corresponding_item(a:from_line)
+ if from_item.type == 0 | return | endif
+ if a:from_line == a:to_line
+ if a:plus_children
+ let to_line = s:get_last_line_of_item_incl_children(from_item)
else
- let cmd = a:1
+ let to_line = s:get_last_line_of_item(from_item)
endif
+ else
+ let to_line = a:to_line
endif
- " is symbol valid
- if sym.' ' !~ s:rx_cb_list_item() && sym.' ' !~ s:rx_list_item()
+
+ if from_item.type == 0 || to_line == 0
return
endif
- " parsing setup
- let lnum = line('.')
- let line = getline('.')
+ let to_be_adjusted = s:get_a_neighbor_item(from_item)
+ let old_parent = s:get_parent(from_item)
+ let first_line_level = s:get_level(from_item.lnum)
+ let more_than_one_level_concerned = 0
- 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)
+ let first_line_indented_by = (a:direction == 'increase') ? s:increase_level(from_item) : s:decrease_level(from_item)
- " remove indent and nesting
- let li_bullet_and_content = strpart(line, n_nesting + n_indent)
+ let cur_ln = s:get_next_line(from_item.lnum)
+ while cur_ln > 0 && cur_ln <= to_line
+ if !more_than_one_level_concerned && s:get_level(cur_ln) != first_line_level && s:get_item(cur_ln).type != 0
+ let more_than_one_level_concerned = 1
+ endif
+ call s:indent_line_by(cur_ln, first_line_indented_by)
+ let cur_ln = s:get_next_line(cur_ln, 1)
+ endwhile
- " 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())
+ call s:adjust_mrkr(from_item, (a:from_line == a:to_line))
+ call s:update_state(old_parent)
+ let from_item = s:get_item(from_item.lnum)
+ if s:get_rate(from_item) < 100
+ call s:update_state(from_item)
+ else
+ call s:update_state(s:get_parent(from_item))
+ endif
- " 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))
+ if more_than_one_level_concerned
+ call vimwiki#lst#adjust_whole_buffer()
+ else
+ call s:adjust_numbered_list(to_be_adjusted, 0, 0)
+ endif
+endfunction "}}}
- " trim
- let cb_bullet = vimwiki#u#trim(cb_bullet)
- let li_content = vimwiki#u#trim(li_content)
+function! vimwiki#lst#change_level(from_line, to_line, direction, plus_children) "{{{
+ let cur_col = col('$') - col('.')
+ call s:change_level(a:from_line, a:to_line, a:direction, a:plus_children)
+ call cursor('.', col('$') - cur_col)
+endfunction "}}}
- " nesting symbol
- if VimwikiGet('syntax') == 'media'
- if len(cb_bullet) > 0
- let sym_nest = cb_bullet[0]
- else
- let sym_nest = sym
- endif
+function! s:clone_marker_from_to(from, to) "{{{
+ let item_from = s:get_item(a:from)
+ if item_from.type == 0 | return | endif
+ let new_mrkr = item_from.mrkr . ' '
+ call s:substitute_rx_in_line(a:to, '^\s*', new_mrkr)
+ let new_indent = ( VimwikiGet('syntax') != 'media' ? indent(a:from) : 0 )
+ call s:set_indent(a:to, new_indent)
+ if item_from.cb != ''
+ call s:create_cb(s:get_item(a:to))
+ call s:update_state(s:get_parent(s:get_item(a:to)))
+ endif
+ if item_from.type == 2
+ let adjust_from = ( a:from < a:to ? a:from : a:to )
+ call s:adjust_numbered_list_below(s:get_item(adjust_from), 0)
+ endif
+endfunction "}}}
+
+"indent line a:lnum to be the continuation of a:prev_item
+function! s:indent_multiline(prev_item, lnum) "{{{
+ if a:prev_item.type != 0
+ call s:set_indent(a:lnum, s:text_begin(a:prev_item.lnum))
+ endif
+endfunction "}}}
+
+
+function! vimwiki#lst#kbd_o() "{{{
+ let fold_end = foldclosedend('.')
+ let lnum = (fold_end == -1) ? line('.') : fold_end
+ let cur_item = s:get_item(lnum)
+ "inserting and deleting the x is necessary
+ "because otherwise the indent is lost
+ normal! ox
+ if cur_item.lnum < s:get_last_line_of_item(cur_item)
+ call s:indent_multiline(cur_item, cur_item.lnum+1)
else
- let sym_nest = ' '
+ call s:clone_marker_from_to(cur_item.lnum, cur_item.lnum+1)
endif
+ startinsert!
+endfunction "}}}
- if g:vimwiki_debug
- echomsg "PARSE: Sw [".list_sw."]"
- echomsg s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content, '|')
+function! vimwiki#lst#kbd_O() "{{{
+ normal! Ox
+ let cur_ln = line('.')
+ if getline(cur_ln+1) !~ '^\s*$'
+ call s:clone_marker_from_to(cur_ln+1, cur_ln)
+ else
+ call s:clone_marker_from_to(cur_ln-1, cur_ln)
endif
+ startinsert!
+endfunction "}}}
- " 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
+function! s:remove_mrkr(item) "{{{
+ let item = a:item
+ if item.cb != ''
+ let item = s:remove_cb(item)
+ let parent_item = s:get_parent(item)
+ else
+ let parent_item = s:empty_item()
+ endif
+ call s:substitute_rx_in_line(item.lnum, vimwiki#u#escape(item.mrkr).'\s*', '')
+ call remove(item, 'mrkr')
+ call remove(item, 'cb')
+ let item.type = 0
+ call s:update_state(parent_item)
+ return item
+endfunction "}}}
+
+function! s:create_marker(lnum) "{{{
+ let new_sibling = s:get_corresponding_item(a:lnum)
+ if new_sibling.type == 0
+ let new_sibling = s:get_a_neighbor_item(s:get_item(a:lnum))
+ endif
+ if new_sibling.type != 0
+ call s:clone_marker_from_to(new_sibling.lnum, a:lnum)
+ else
+ let cur_item = s:get_item(a:lnum)
+ call s:set_new_mrkr(cur_item, g:vimwiki_list_markers[0])
+ call s:adjust_numbered_list(cur_item, 0, 0)
+ endif
+endfunction "}}}
+
+"XXX: this is a bit messy
+function! vimwiki#lst#kbd_cr(normal, just_mrkr) "{{{
+ let lnum = line('.')
+ let has_bp = s:line_has_marker(lnum)
+
+ "special behaviour if there is only a marker with no text
+ if (has_bp == 1 && a:just_mrkr == 2) || (has_bp != 0 && virtcol('.') < s:text_begin(lnum))
+ "insert new Mark but remove Mark in old line
+ call append(lnum-1, '')
+ startinsert!
+ return
+ elseif has_bp == 1 && a:just_mrkr == 3
+ "list is finished, but cursor stays in current line
+ let item = s:get_item(lnum)
+ let neighbor_item = s:get_a_neighbor_item(item)
+ let child_item = s:get_first_child(item)
+ let parent_item = (item.cb != '') ? s:get_parent(item) : s:empty_item()
+ normal! "_cc
+ call s:adjust_numbered_list(neighbor_item, 0, 0)
+ call s:adjust_numbered_list(child_item, 0, 0)
+ call s:update_state(parent_item)
+ startinsert
+ return
+ elseif has_bp == 1 && a:just_mrkr == 4
+ "list is finished, but cursor goes to next line
+ let item = s:get_item(lnum)
+ let neighbor_item = s:get_a_neighbor_item(item)
+ let child_item = s:get_first_child(item)
+ let parent_item = (item.cb != '') ? s:get_parent(item) : s:empty_item()
+ normal! "_cc
+ call s:adjust_numbered_list(neighbor_item, 0, 0)
+ call s:adjust_numbered_list(child_item, 0, 0)
+ call s:update_state(parent_item)
+ startinsert
+ return
+ elseif has_bp == 1 && a:just_mrkr == 5
+ "successively decrease level
+ if s:get_level(lnum) > 0
+ call s:change_level(lnum, lnum, 'decrease', 0)
+ startinsert!
else
- let n_nesting = n_nesting + list_sw
+ let item = s:get_item(lnum)
+ let neighbor_item = s:get_a_neighbor_item(item)
+ let child_item = s:get_first_child(item)
+ let parent_item = (item.cb != '') ? s:get_parent(item) : s:empty_item()
+ normal! "_cc
+ call s:adjust_numbered_list(neighbor_item, 0, 0)
+ call s:adjust_numbered_list(child_item, 0, 0)
+ call s:update_state(parent_item)
+ startinsert
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
+ return
+ endif
+
+
+ let cur_col = col("$") - col("'^")
+
+
+ if has_bp == 0 && (a:normal == 2 || a:normal == 3)
+ normal! gi
+ call s:create_marker(lnum+1)
+ endif
+
+ if has_bp == 0
+ normal! gix
+ endif
+
+ "make multilined list item
+ if (has_bp == 2 && (a:normal == 2 || a:normal == 4 || (cur_col == 0 && getline(lnum) =~ '\s$'))) " || (cur_item.lnum < s:get_last_line_of_item(cur_item))
+ normal! gi
+ let prev_line = s:get_corresponding_item(s:get_prev_line(lnum+1))
+ call s:indent_multiline(prev_line, lnum+1)
+
+ "the ultimate feature of this script: make new marker on <CR>
+ elseif (has_bp == 2 && (a:normal == 1 || a:normal == 3)) || (has_bp == 1 && a:just_mrkr == 1)
+ normal! gi
+ call s:clone_marker_from_to(lnum, lnum+1)
+ "tiny sweet extra feature: indent next line if current line ends with :
+ if getline(lnum) =~ ':$'
+ call s:change_level(lnum+1, lnum+1, 'increase', 0)
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, '|')
+ call cursor(lnum+1, col("$") - cur_col)
+ if cur_col == 0
+ startinsert!
+ else
+ startinsert
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)
+endfunction "}}}
+
+"creates a list item in the current line or removes it
+function! vimwiki#lst#toggle_list_item() "{{{
+ let cur_col_from_eol = col("$") - col("'^")
+ let cur_item = s:get_item(line('.'))
+
+ if cur_item.type == 0
+ call s:create_marker(cur_item.lnum)
+ else
+ let prev_item = s:get_prev_list_item(cur_item, 1)
+ if prev_item.type == 0
+ let prev_item = s:get_corresponding_item(s:get_prev_line(cur_item.lnum))
+ endif
+ let cur_item = s:remove_mrkr(cur_item)
+ let adjust_prev_item = (prev_item.type == 2 && s:get_level(cur_item.lnum) <= s:get_level(prev_item.lnum)) ? 1 : 0
+ call s:indent_multiline(prev_item, cur_item.lnum)
+ if adjust_prev_item
+ call s:adjust_numbered_list_below(prev_item, 0)
+ endif
endif
- if g:vimwiki_debug
- echomsg "INDENT:"
- echomsg s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content, '|')
+ "set cursor position s.t. it's on the same char as before
+ let new_cur_col = col("$") - cur_col_from_eol
+ call cursor(cur_item.lnum, new_cur_col >= 1 ? new_cur_col : 1)
+
+ if cur_col_from_eol == 0 || getline(cur_item.lnum) =~ '^\s*$'
+ startinsert!
+ else
+ startinsert
endif
+endfunction "}}}
+
+function! vimwiki#lst#default_symbol() "{{{
+ return g:vimwiki_list_markers[0]
+endfunction "}}}
- let line = s:compose_list_item(n_indent, n_nesting, sym_nest, cb_bullet, li_content)
+function! vimwiki#lst#get_list_margin() "{{{
+ if VimwikiGet('list_margin') < 0
+ return &sw
+ else
+ return VimwikiGet('list_margin')
+ endif
+endfunction "}}}
- " replace
- call setline(lnum, line)
- call cursor(lnum, match(line, '\S') + 1)
+function! vimwiki#lst#get_list_item_rx(with_cb) "{{{
+ let rx_without_cb = '^\s*\%(\('.g:vimwiki_rxListBullet.'\)\|\('.g:vimwiki_rxListNumber.'\)\)'
+ if a:with_cb
+ return rx_without_cb . '\s\+\%(\[\(['.join(g:vimwiki_listsyms, '').']\)\]\s\)\?'
+ else
+ return rx_without_cb . '\s'
+ endif
endfunction "}}}
+
diff --git a/autoload/vimwiki/tbl.vim b/autoload/vimwiki/tbl.vim
@@ -447,7 +447,7 @@ endfunction "}}}
function! vimwiki#tbl#kbd_cr() "{{{
let lnum = line('.')
if !s:is_table(getline(lnum))
- return "\<CR>"
+ return ""
endif
if s:is_separator(getline(lnum+1)) || !s:is_table(getline(lnum+1))
@@ -507,9 +507,14 @@ function! vimwiki#tbl#format(lnum, ...) "{{{
endif
let indent = s:get_indent(a:lnum)
+ if &expandtab
+ let indentstring = repeat(' ', indent)
+ else
+ let indentstring = repeat(' ', indent / &tabstop) . repeat(' ', indent % &tabstop)
+ endif
for [lnum, row] in s:get_aligned_rows(a:lnum, col1, col2)
- let row = repeat(' ', indent).row
+ let row = indentstring.row
call setline(lnum, row)
endfor
diff --git a/autoload/vimwiki/u.vim b/autoload/vimwiki/u.vim
@@ -75,3 +75,6 @@ function! vimwiki#u#path_common_pfx(path1, path2) "{{{
endif
endfunction "}}}
+function! vimwiki#u#escape(string) "{{{
+ return escape(a:string, '.*[]\^$')
+endfunction "}}}
diff --git a/doc/vimwiki.txt b/doc/vimwiki.txt
@@ -1702,14 +1702,23 @@ Default: 0
Checked list items can be highlighted with a color:
* [X] the whole line can be highlighted with the option set to 1.
+ * this line is highlighted as well with the option set to 2
* [ ] I wish vim could use strikethru.
Value Description~
-1 Highlight checked [X] check box with |group-name| "Comment".
-0 Don't.
+0 Don't highlight anything.
+1 Highlight checked [X] list item with |group-name| "Comment".
+2 Highlight checked [X] list item and all its child items.
Default: 0
+Note: Option 2 does not work perfectly. Specifically, it might break on
+preformatted text or if you mix tabs and spaces for indenting, and indented
+headers will erroneously be highlighted.
+Furthermore, if your list is long, Vim's highlight can break. Consider putting
+>
+ au BufEnter file.wiki :syntax sync fromstart
+in your .vimrc
------------------------------------------------------------------------------
*g:vimwiki_global_ext*
@@ -1786,12 +1795,15 @@ Default: 'Vimwiki'
------------------------------------------------------------------------------
*g:vimwiki_listsyms*
-String of 5 symbols for list items with checkboxes.
-Default value is ' .oOX'.
+List of 5 symbols for list items with checkboxes.
+Default value is [' ', '.', 'o', 'O', 'X'].
g:vimwiki_listsyms[0] is for 0% done items.
g:vimwiki_listsyms[4] is for 100% done items.
+You can set it to some more fancy symbols like this:
+
+ let g:vimwiki_listsyms = ['✗', '○', '◐', '●', '✓']
------------------------------------------------------------------------------
*g:vimwiki_use_mouse*
diff --git a/ftplugin/vimwiki.vim b/ftplugin/vimwiki.vim
@@ -11,7 +11,7 @@ let b:did_ftplugin = 1 " Don't load another plugin for this buffer
" UNDO list {{{
" Reset the following options to undo this plugin.
let b:undo_ftplugin = "setlocal ".
- \ "suffixesadd< isfname< comments< ".
+ \ "suffixesadd< isfname< formatlistpat< ".
\ "formatoptions< foldtext< ".
\ "foldmethod< foldexpr< commentstring< "
" UNDO }}}
@@ -32,29 +32,69 @@ setlocal isfname-=[,]
" gf}}}
" Autocreate list items {{{
-" for list items, and list items with checkboxes
-setlocal formatoptions+=tnro
-setlocal formatoptions-=cq
+" for bulleted and numbered list items, and list items with checkboxes
+setlocal autoindent
+setlocal nosmartindent
+setlocal nocindent
+setlocal comments=""
+setlocal formatoptions-=c
+setlocal formatoptions-=r
+setlocal formatoptions-=o
+setlocal formatoptions-=2
+setlocal formatoptions+=n
+
+
if VimwikiGet('syntax') == 'default'
- setl comments=b:*,b:#,b:-
- setl formatlistpat=^\\s*[*#-]\\s*
+ "1 means multiple bullets, like * ** ***
+ let g:vimwiki_bullet_points = { '-':0, '*':1, '#':1 , '◆':1}
+ let g:vimwiki_bullet_numbers = ['1iIaA', '.)]']
+ "this should contain at least one element
+ "it is used for i_<C-A> among other things
+ let g:vimwiki_list_markers = ['-', '#', '◆', '1.', 'i)', 'a)']
elseif VimwikiGet('syntax') == 'markdown'
- setlocal comments=fb:*,fb:-,fb:+,nb:> commentstring=\ >\ %s
- setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^[-*+]\\s\\+j
+ let g:vimwiki_bullet_points = { '-':0, '*':0, '+':0 }
+ let g:vimwiki_bullet_numbers = ['1', '.']
+ let g:vimwiki_list_markers = ['-', '*', '+', '1.']
+else "media
+ "better leave this as it is, because media syntax is special
+ let g:vimwiki_bullet_points = { '*':1, '#':1 }
+ let g:vimwiki_bullet_numbers = ['', '']
+ let g:vimwiki_list_markers = ['*', '#']
+endif
+
+let g:vimwiki_rxListBullet = join( map(keys(g:vimwiki_bullet_points), 'vimwiki#u#escape(v:val) . repeat("\\+", g:vimwiki_bullet_points[v:val])') , '\|')
+
+"create regex for numbered list
+if g:vimwiki_bullet_numbers[0] == ''
+ "regex that matches nothing
+ let g:vimwiki_rxListNumber = '$^'
else
- setl comments=n:*,n:#
+ let s:char_to_rx = {'1': '\d\+', 'i': '[ivxlcdm]\+', 'I': '[IVXLCDM]\+', 'a': '\l\{1,3}', 'A': '\u\{1,3}'}
+ let g:vimwiki_rxListNumber = '\C\%(' . join( map(split(g:vimwiki_bullet_numbers[0], '.\zs'), "s:char_to_rx[v:val]"), '\|').'\)'
+ let g:vimwiki_rxListNumber .= '['.vimwiki#u#escape(g:vimwiki_bullet_numbers[1]).']'
endif
+if VimwikiGet('syntax') == 'default' || VimwikiGet('syntax') == 'markdown'
+ let g:vimwiki_rxListItemAndChildren = '^\(\s*\)\%('.g:vimwiki_rxListBullet.'\|'.g:vimwiki_rxListNumber.'\)\s\+\['.g:vimwiki_listsyms[4].'\]\s.*\%(\n\%(\1\s.*\|^$\)\)*'
+else
+ let g:vimwiki_rxListItemAndChildren = '^\('.g:vimwiki_rxListBullet.'\)\s\+\['.g:vimwiki_listsyms[4].'\]\s.*\%(\n\%(\1\%('.g:vimwiki_rxListBullet.'\).*\|^$\|^\s.*\)\)*'
+endif
+
+"Create 'formatlistpat'
+let &formatlistpat = vimwiki#lst#get_list_item_rx(1)
+
+
+
if !empty(&langmap)
" Valid only if langmap is a comma separated pairs of chars
let l_o = matchstr(&langmap, '\C,\zs.\zeo,')
if l_o
- exe 'nnoremap <buffer> '.l_o.' :call vimwiki#lst#kbd_oO("o")<CR>a'
+ exe 'nnoremap <buffer> '.l_o.' :call vimwiki#lst#kbd_o()<CR>a'
endif
let l_O = matchstr(&langmap, '\C,\zs.\zeO,')
if l_O
- exe 'nnoremap <buffer> '.l_O.' :call vimwiki#lst#kbd_oO("O")<CR>a'
+ exe 'nnoremap <buffer> '.l_O.' :call vimwiki#lst#kbd_O()<CR>a'
endif
endif
@@ -277,7 +317,7 @@ command! -buffer -nargs=? VimwikiNormalizeLink call vimwiki#base#normalize_link(
command! -buffer VimwikiTabnewLink call vimwiki#base#follow_link('tabnew')
-command! -buffer -range VimwikiToggleListItem call vimwiki#lst#ToggleListItem(<line1>, <line2>)
+command! -buffer -range VimwikiToggleCheckbox call vimwiki#lst#toggle_cb(<line1>, <line2>)
command! -buffer VimwikiGenerateLinks call vimwiki#base#generate_links()
@@ -294,7 +334,12 @@ command! -buffer -nargs=1 VimwikiGoto call vimwiki#base#goto("<args>")
" list commands
-command! -buffer -nargs=* VimwikiListChangeLevel call vimwiki#lst#change_level(<f-args>)
+command! -buffer -range -nargs=1 VimwikiListChangeMarker call vimwiki#lst#change_marker(<line1>, <line2>, <f-args>)
+command! -buffer -nargs=1 VimwikiListChangeMarkerInList call vimwiki#lst#change_marker_in_list(<f-args>)
+command! -buffer -nargs=+ VimwikiListLineBreak call <SID>CR(<f-args>)
+command! -buffer -range -nargs=1 VimwikiListIncreaseLvl call vimwiki#lst#change_level(<line1>, <line2>, 'increase', <f-args>)
+command! -buffer -range -nargs=1 VimwikiListDecreaseLvl call vimwiki#lst#change_level(<line1>, <line2>, 'decrease', <f-args>)
+command! -buffer -range VimwikiListRemoveCB call vimwiki#lst#remove_cb(<line1>, <line2>)
" table commands
command! -buffer -nargs=* VimwikiTable call vimwiki#tbl#create(<f-args>)
@@ -405,15 +450,18 @@ endif
nnoremap <silent><script><buffer>
\ <Plug>VimwikiRenameLink :VimwikiRenameLink<CR>
-if !hasmapto('<Plug>VimwikiToggleListItem')
- nmap <silent><buffer> <C-Space> <Plug>VimwikiToggleListItem
- vmap <silent><buffer> <C-Space> <Plug>VimwikiToggleListItem
+if !hasmapto('<Plug>VimwikiToggleCheckbox')
+ nmap <silent><buffer> <C-Space> <Plug>VimwikiToggleCheckbox
+ vmap <silent><buffer> <C-Space> <Plug>VimwikiToggleCheckbox
if has("unix")
- nmap <silent><buffer> <C-@> <Plug>VimwikiToggleListItem
+ nmap <silent><buffer> <C-@> <Plug>VimwikiToggleCheckbox
+ vmap <silent><buffer> <C-@> <Plug>VimwikiToggleCheckbox
endif
endif
nnoremap <silent><script><buffer>
- \ <Plug>VimwikiToggleListItem :VimwikiToggleListItem<CR>
+ \ <Plug>VimwikiToggleCheckbox :VimwikiToggleCheckbox<CR>
+vnoremap <silent><script><buffer>
+ \ <Plug>VimwikiToggleCheckbox :VimwikiToggleCheckbox<CR>
if !hasmapto('<Plug>VimwikiDiaryNextDay')
nmap <silent><buffer> <C-Down> <Plug>VimwikiDiaryNextDay
@@ -427,42 +475,67 @@ endif
nnoremap <silent><script><buffer>
\ <Plug>VimwikiDiaryPrevDay :VimwikiDiaryPrevDay<CR>
-function! s:CR() "{{{
- let res = vimwiki#lst#kbd_cr()
- if res == "\<CR>" && g:vimwiki_table_mappings
+function! s:CR(normal, just_mrkr) "{{{
+ if g:vimwiki_table_mappings
let res = vimwiki#tbl#kbd_cr()
+ if res != ""
+ exe "normal! " . res . "\<Right>"
+ return
+ endif
endif
- return res
+ call vimwiki#lst#kbd_cr(a:normal, a:just_mrkr)
endfunction "}}}
-" List and Table <CR> mapping
-inoremap <buffer> <expr> <CR> <SID>CR()
-
" List mappings
-nnoremap <buffer> o :<C-U>call vimwiki#lst#kbd_oO('o')<CR>
-nnoremap <buffer> O :<C-U>call vimwiki#lst#kbd_oO('O')<CR>
-nnoremap <buffer> gll :VimwikiListChangeLevel <<<CR>
-nnoremap <buffer> glm :VimwikiListChangeLevel >><CR>
-nnoremap <buffer> gl* :VimwikiListChangeLevel *<CR>
-nnoremap <buffer> gl8 :VimwikiListChangeLevel *<CR>
-if VimwikiGet('syntax') == 'default'
- nnoremap <buffer> gl- :VimwikiListChangeLevel -<CR>
- nnoremap <buffer> gl# :VimwikiListChangeLevel #<CR>
- nnoremap <buffer> gl3 :VimwikiListChangeLevel #<CR>
-elseif VimwikiGet('syntax') == 'markdown'
- nnoremap <buffer> gl- :VimwikiListChangeLevel -<CR>
- nnoremap <buffer> gl1 :VimwikiListChangeLevel 1.<CR>
-elseif VimwikiGet('syntax') == 'media'
- nnoremap <buffer> gl# :VimwikiListChangeLevel #<CR>
- nnoremap <buffer> gl3 :VimwikiListChangeLevel #<CR>
-endif
+inoremap <buffer> <CR> <Esc>:VimwikiListLineBreak 1 5<CR>
+inoremap <buffer> <S-CR> <Esc>:VimwikiListLineBreak 2 2<CR>
+nnoremap <silent> <buffer> o :call vimwiki#lst#kbd_o()<CR>
+nnoremap <silent> <buffer> O :call vimwiki#lst#kbd_O()<CR>
+map <silent> <buffer> glh :VimwikiListDecreaseLvl 0<CR>
+map <silent> <buffer> gll :VimwikiListIncreaseLvl 0<CR>
+map <silent> <buffer> gLh :VimwikiListDecreaseLvl 1<CR>
+map <silent> <buffer> gLl :VimwikiListIncreaseLvl 1<CR>
+map <silent> <buffer> gLH glH
+map <silent> <buffer> gLL gLl
+inoremap <buffer> <C-D> <C-O>:VimwikiListDecreaseLvl 0<CR>
+inoremap <buffer> <C-T> <C-O>:VimwikiListIncreaseLvl 0<CR>
+inoremap <buffer> <C-A> <C-O>:VimwikiListChangeMarker next<CR>
+inoremap <buffer> <C-S> <C-O>:VimwikiListChangeMarker prev<CR>
+nmap <silent> <buffer> glr :call vimwiki#lst#adjust_numbered_list()<CR>
+nmap <silent> <buffer> gLr :call vimwiki#lst#adjust_whole_buffer()<CR>
+nmap <silent> <buffer> gLR gLr
+noremap <silent> <buffer> gl<Space> :VimwikiListRemoveCB<CR>
+map <silent> <buffer> gL<Space> :call vimwiki#lst#remove_cb_in_list()<CR>
+inoremap <silent> <buffer> <C-B> <Esc>:call vimwiki#lst#toggle_list_item()<CR>
+
+for s:k in keys(g:vimwiki_bullet_points)
+ exe 'noremap <silent> <buffer> gl'.s:k.' :VimwikiListChangeMarker '.s:k.'<CR>'
+ exe 'noremap <silent> <buffer> gL'.s:k.' :VimwikiListChangeMarkerInList '.s:k.'<CR>'
+endfor
+for s:a in split(g:vimwiki_bullet_numbers[0], '.\zs')
+ let chars = split(g:vimwiki_bullet_numbers[1], '.\zs')
+ if len(chars) == 0
+ exe 'noremap <silent> <buffer> gl'.s:a.' :VimwikiListChangeMarker '.s:a.'<CR>'
+ exe 'noremap <silent> <buffer> gL'.s:a.' :VimwikiListChangeMarkerInList '.s:a.'<CR>'
+ elseif len(chars) == 1
+ exe 'noremap <silent> <buffer> gl'.s:a.' :VimwikiListChangeMarker '.s:a.chars[0].'<CR>'
+ exe 'noremap <silent> <buffer> gL'.s:a.' :VimwikiListChangeMarkerInList '.s:a.chars[0].'<CR>'
+ else
+ for s:b in chars
+ exe 'noremap <silent> <buffer> gl'.s:a.s:b.' :VimwikiListChangeMarker '.s:a.s:b.'<CR>'
+ exe 'noremap <silent> <buffer> gL'.s:a.s:b.' :VimwikiListChangeMarkerInList '.s:a.s:b.'<CR>'
+ endfor
+ endif
+endfor
+
+
+"Table mappings
+ if g:vimwiki_table_mappings
+ inoremap <expr> <buffer> <Tab> vimwiki#tbl#kbd_tab()
+ inoremap <expr> <buffer> <S-Tab> vimwiki#tbl#kbd_shift_tab()
+ endif
-" Table mappings
-if g:vimwiki_table_mappings
- inoremap <expr> <buffer> <Tab> vimwiki#tbl#kbd_tab()
- inoremap <expr> <buffer> <S-Tab> vimwiki#tbl#kbd_shift_tab()
-endif
nnoremap <buffer> gqq :VimwikiTableAlignQ<CR>
nnoremap <buffer> gww :VimwikiTableAlignW<CR>
diff --git a/plugin/vimwiki.vim b/plugin/vimwiki.vim
@@ -392,7 +392,7 @@ call s:default('ext2syntax', {}) " syntax map keyed on extension
call s:default('hl_headers', 0)
call s:default('hl_cb_checked', 0)
call s:default('list_ignore_newline', 1)
-call s:default('listsyms', ' .oOX')
+call s:default('listsyms', [' ', '.', 'o', 'O', 'X'])
call s:default('use_calendar', 1)
call s:default('table_mappings', 1)
call s:default('table_auto_fmt', 1)
diff --git a/syntax/vimwiki.vim b/syntax/vimwiki.vim
@@ -49,9 +49,6 @@ execute 'runtime! syntax/vimwiki_'.VimwikiGet('syntax').'.vim'
" -------------------------------------------------------------------------
let time0 = vimwiki#u#time(starttime) "XXX
-let g:vimwiki_rxListItem = '\('.
- \ g:vimwiki_rxListBullet.'\|'.g:vimwiki_rxListNumber.
- \ '\)'
" LINKS: setup of larger regexes {{{
@@ -67,12 +64,11 @@ let g:vimwiki_WikiLinkTemplate2 = g:vimwiki_rxWikiLinkPrefix . '__LinkUrl__'.
\ g:vimwiki_rxWikiLinkSeparator. '__LinkDescription__'.
\ g:vimwiki_rxWikiLinkSuffix
"
-let magic_chars = '.*[]\^$'
let valid_chars = '[^\\\]]'
-let g:vimwiki_rxWikiLinkPrefix = escape(g:vimwiki_rxWikiLinkPrefix, magic_chars)
-let g:vimwiki_rxWikiLinkSuffix = escape(g:vimwiki_rxWikiLinkSuffix, magic_chars)
-let g:vimwiki_rxWikiLinkSeparator = escape(g:vimwiki_rxWikiLinkSeparator, magic_chars)
+let g:vimwiki_rxWikiLinkPrefix = vimwiki#u#escape(g:vimwiki_rxWikiLinkPrefix)
+let g:vimwiki_rxWikiLinkSuffix = vimwiki#u#escape(g:vimwiki_rxWikiLinkSuffix)
+let g:vimwiki_rxWikiLinkSeparator = vimwiki#u#escape(g:vimwiki_rxWikiLinkSeparator)
let g:vimwiki_rxWikiLinkUrl = valid_chars.'\{-}'
let g:vimwiki_rxWikiLinkDescr = valid_chars.'\{-}'
@@ -116,9 +112,9 @@ let g:vimwiki_WikiInclTemplate2 = g:vimwiki_rxWikiInclPrefix . '__LinkUrl__'.
let valid_chars = '[^\\\}]'
-let g:vimwiki_rxWikiInclPrefix = escape(g:vimwiki_rxWikiInclPrefix, magic_chars)
-let g:vimwiki_rxWikiInclSuffix = escape(g:vimwiki_rxWikiInclSuffix, magic_chars)
-let g:vimwiki_rxWikiInclSeparator = escape(g:vimwiki_rxWikiInclSeparator, magic_chars)
+let g:vimwiki_rxWikiInclPrefix = vimwiki#u#escape(g:vimwiki_rxWikiInclPrefix)
+let g:vimwiki_rxWikiInclSuffix = vimwiki#u#escape(g:vimwiki_rxWikiInclSuffix)
+let g:vimwiki_rxWikiInclSeparator = vimwiki#u#escape(g:vimwiki_rxWikiInclSeparator)
let g:vimwiki_rxWikiInclUrl = valid_chars.'\{-}'
let g:vimwiki_rxWikiInclArg = valid_chars.'\{-}'
let g:vimwiki_rxWikiInclArgs = '\%('. g:vimwiki_rxWikiInclSeparator. g:vimwiki_rxWikiInclArg. '\)'.'\{-}'
@@ -406,24 +402,19 @@ syntax match VimwikiCellSeparator
\ /\%(|\)\|\%(-\@<=+\-\@=\)\|\%([|+]\@<=-\+\)/ contained
" List items
-execute 'syntax match VimwikiList /'.g:vimwiki_rxListBullet.'/'
-execute 'syntax match VimwikiList /'.g:vimwiki_rxListNumber.'/'
+execute 'syntax match VimwikiList /'.vimwiki#lst#get_list_item_rx(0).'/'
execute 'syntax match VimwikiList /'.g:vimwiki_rxListDefine.'/'
-" List item checkbox
-"syntax match VimwikiCheckBox /\[.\?\]/
-let g:vimwiki_rxCheckBox = '\s*\[['.g:vimwiki_listsyms.']\?\]\s'
-" Todo lists have a checkbox
-execute 'syntax match VimwikiListTodo /'.g:vimwiki_rxListBullet.g:vimwiki_rxCheckBox.'/'
-execute 'syntax match VimwikiListTodo /'.g:vimwiki_rxListNumber.g:vimwiki_rxCheckBox.'/'
-if g:vimwiki_hl_cb_checked
- execute 'syntax match VimwikiCheckBoxDone /'.
- \ g:vimwiki_rxListBullet.'\s*\['.g:vimwiki_listsyms[4].'\]\s.*$/'.
- \ ' contains=VimwikiNoExistsLink,VimwikiLink'
- execute 'syntax match VimwikiCheckBoxDone /'.
- \ g:vimwiki_rxListNumber.'\s*\['.g:vimwiki_listsyms[4].'\]\s.*$/'.
- \ ' contains=VimwikiNoExistsLink,VimwikiLink'
+execute 'syntax match VimwikiListTodo /'.vimwiki#lst#get_list_item_rx(1).'/'
+
+if g:vimwiki_hl_cb_checked == 1
+ execute 'syntax match VimwikiCheckBoxDone /'.vimwiki#lst#get_list_item_rx(0).'\s*\['.g:vimwiki_listsyms[4].'\]\s.*$/ '.
+ \ 'contains=VimwikiNoExistsLink,VimwikiLink,@Spell'
+
+elseif g:vimwiki_hl_cb_checked == 2
+ execute 'syntax match VimwikiCheckBoxDone /'.g:vimwiki_rxListItemAndChildren.'/ contains=VimwikiNoExistsLink,VimwikiLink,@Spell'
endif
+
execute 'syntax match VimwikiEqIn /'.g:vimwiki_rxEqIn.'/ contains=VimwikiEqInChar'
execute 'syntax match VimwikiEqInT /'.g:vimwiki_rxEqIn.'/ contained contains=VimwikiEqInCharT'
diff --git a/syntax/vimwiki_default.vim b/syntax/vimwiki_default.vim
@@ -72,10 +72,6 @@ let g:vimwiki_rxHR = '^-----*$'
" Tables. Each line starts and ends with '|'; each cell is separated by '|'
let g:vimwiki_rxTableSep = '|'
-" List items start with optional whitespace(s) then '* ' or '# '
-let g:vimwiki_rxListBullet = '^\s*[*-]\s'
-let g:vimwiki_rxListNumber = '^\s*#\s'
-
let g:vimwiki_rxListDefine = '::\(\s\|$\)'
" Preformatted text
diff --git a/syntax/vimwiki_markdown.vim b/syntax/vimwiki_markdown.vim
@@ -72,10 +72,6 @@ let g:vimwiki_rxHR = '^-----*$'
" Tables. Each line starts and ends with '|'; each cell is separated by '|'
let g:vimwiki_rxTableSep = '|'
-" List items start with optional whitespace(s) then '* ' or '1. ', '2. ', etc.
-let g:vimwiki_rxListBullet = '^\s*[*+-]\s'
-let g:vimwiki_rxListNumber = '^\s*[0-9]\+\.\s'
-
let g:vimwiki_rxListDefine = '::\%(\s\|$\)'
" Preformatted text
diff --git a/syntax/vimwiki_markdown_custom.vim b/syntax/vimwiki_markdown_custom.vim
@@ -42,12 +42,11 @@ let g:vimwiki_WikiLink1Template2 = g:vimwiki_rxWikiLink1Prefix . '__LinkDescript
\ g:vimwiki_rxWikiLink1Separator. '__LinkUrl__'.
\ g:vimwiki_rxWikiLink1Suffix
"
-let magic_chars = '.*[]\^$'
let valid_chars = '[^\\\[\]]'
-let g:vimwiki_rxWikiLink1Prefix = escape(g:vimwiki_rxWikiLink1Prefix, magic_chars)
-let g:vimwiki_rxWikiLink1Suffix = escape(g:vimwiki_rxWikiLink1Suffix, magic_chars)
-let g:vimwiki_rxWikiLink1Separator = escape(g:vimwiki_rxWikiLink1Separator, magic_chars)
+let g:vimwiki_rxWikiLink1Prefix = vimwiki#u#escape(g:vimwiki_rxWikiLink1Prefix)
+let g:vimwiki_rxWikiLink1Suffix = vimwiki#u#escape(g:vimwiki_rxWikiLink1Suffix)
+let g:vimwiki_rxWikiLink1Separator = vimwiki#u#escape(g:vimwiki_rxWikiLink1Separator)
let g:vimwiki_rxWikiLink1Url = valid_chars.'\{-}'
let g:vimwiki_rxWikiLink1Descr = valid_chars.'\{-}'
@@ -125,12 +124,11 @@ let g:vimwiki_Weblink1Template = g:vimwiki_rxWeblink1Prefix . '__LinkDescription
\ g:vimwiki_rxWeblink1Separator. '__LinkUrl__'.
\ g:vimwiki_rxWeblink1Suffix
-let magic_chars = '.*[]\^$'
let valid_chars = '[^\\]'
-let g:vimwiki_rxWeblink1Prefix = escape(g:vimwiki_rxWeblink1Prefix, magic_chars)
-let g:vimwiki_rxWeblink1Suffix = escape(g:vimwiki_rxWeblink1Suffix, magic_chars)
-let g:vimwiki_rxWeblink1Separator = escape(g:vimwiki_rxWeblink1Separator, magic_chars)
+let g:vimwiki_rxWeblink1Prefix = vimwiki#u#escape(g:vimwiki_rxWeblink1Prefix)
+let g:vimwiki_rxWeblink1Suffix = vimwiki#u#escape(g:vimwiki_rxWeblink1Suffix)
+let g:vimwiki_rxWeblink1Separator = vimwiki#u#escape(g:vimwiki_rxWeblink1Separator)
let g:vimwiki_rxWeblink1Url = valid_chars.'\{-}'
let g:vimwiki_rxWeblink1Descr = valid_chars.'\{-}'
diff --git a/syntax/vimwiki_media.vim b/syntax/vimwiki_media.vim
@@ -53,11 +53,6 @@ let g:vimwiki_rxHR = '^-----*$'
" Tables. Each line starts and ends with '|'; each cell is separated by '|'
let g:vimwiki_rxTableSep = '|'
-" Bulleted list items start with whitespace(s), then '*'
-" highlight only bullets and digits.
-let g:vimwiki_rxListBullet = '^\s*\*\+\s\%([^*]*$\)\@='
-let g:vimwiki_rxListNumber = '^\s*#\+\s'
-
let g:vimwiki_rxListDefine = '^\%(;\|:\)\s'
" Preformatted text