@@ -72,6 +72,12 @@ while s:power >= 0
7272 let s: power -= 1
7373endwhile
7474
75+ if ! exists (' g:bullets_list_item_styles' )
76+ " A list of regex patterns that are recognized as bullet points for
77+ " bullet items.
78+ let g: bullets_list_item_styles = [' -' , ' \*+' , ' \.+' , ' #\.' , ' \+' , ' \\item' ]
79+ endif
80+
7581if ! exists (' g:bullets_outline_levels' )
7682 " Capitalization matters: all caps will make the symbol caps, lower = lower
7783 " Standard bullets should include the marker symbol after 'std'
@@ -161,7 +167,7 @@ fun! s:parse_bullet_text(line_text)
161167 if s: bullet_cache isnot v: null
162168 let l: cached = get (s: bullet_cache , a: line_text , v: null )
163169 if l: cached isnot v: null
164- " Return a copy so as not to break the referene
170+ " Return a copy so as not to break the reference
165171 return copy (l: cached )
166172 endif
167173 endif
@@ -177,11 +183,11 @@ fun! s:parse_bullet_text(line_text)
177183 let l: roman = empty (l: bullet ) && empty (l: num ) ? s: match_roman_list_item (a: line_text ) : {}
178184
179185 let l: kinds = s: filter ([l: bullet , l: check , l: num , l: alpha , l: roman ], ' !empty(v:val)' )
180-
186+
181187 if s: bullet_cache isnot v: null
182188 let s: bullet_cache [a: line_text ] = l: kinds
183189 endif
184-
190+
185191 return l: kinds
186192endfun
187193
@@ -324,7 +330,9 @@ fun! s:match_checkbox_bullet_item(input_text)
324330endfun
325331
326332fun ! s: match_bullet_list_item (input_text)
327- let l: std_bullet_regex = ' \v(^(\s*)(-|\*+|\.+|#\.|\+|\\item)(\s+))(.*)'
333+ let l: std_bullet_regex = ' \v(^(\s*)('
334+ \ . join (g: bullets_list_item_styles , ' |' )
335+ \ . ' )(\s+))(.*)'
328336 let l: matches = matchlist (a: input_text , l: std_bullet_regex )
329337
330338 if empty (l: matches )
@@ -574,7 +582,11 @@ fun! s:insert_new_bullet()
574582 " We don't want to create a new bullet if the previous one was not used,
575583 " instead we want to delete the empty bullet - like word processors do
576584 if g: bullets_delete_last_bullet_if_empty
577- call setline (l: curr_line_num , ' ' )
585+ if g: bullets_delete_last_bullet_if_empty == 1
586+ call setline (l: curr_line_num , ' ' )
587+ elseif g: bullets_delete_last_bullet_if_empty == 2
588+ call <SID> change_bullet_level (1 , 0 )
589+ endif
578590 let l: send_return = 0
579591 endif
580592 elseif ! (l: bullet .bullet_type == # ' abc' && s: abc2dec (l: bullet .bullet) + 1 > s: abc_max )
@@ -775,6 +787,85 @@ fun! s:set_child_checkboxes(lnum, checked)
775787 endif
776788endfun
777789
790+ " Recompute partial checkboxes of a full checkbox tree given the root lnum
791+ fun ! s: recompute_checkbox_tree (lnum)
792+ if ! g: bullets_nested_checkboxes
793+ return
794+ endif
795+
796+ let l: indent = indent (a: lnum )
797+ let l: bullet = s: closest_bullet_types (a: lnum , l: indent )
798+ let l: bullet = s: resolve_bullet_type (l: bullet )
799+
800+ if l: bullet .bullet_type !=# ' chk'
801+ return
802+ endif
803+
804+ " recursively recompute checkbox tree for all children, then finally self
805+
806+ let l: children = s: get_children_line_numbers (a: lnum )
807+ for l: child_nr in l: children
808+ " nb: this skips 'grandchildren' checkboxes (i.e., children who aren't
809+ " checkboxes but have checkbox children themselves), but those grandkids
810+ " will be targeted by s:recompute_checkboxes_in_range anyway
811+ call s: recompute_checkbox_tree (l: child_nr )
812+ endfor
813+
814+
815+ if empty (l: children )
816+ " if no children, preserve previous checked state
817+ " partially completed checkboxes become unchecked
818+ if empty (l: bullet ) || ! has_key (l: bullet , ' checkbox_marker' )
819+ return
820+ endif
821+
822+ let l: checkbox_markers = split (g: bullets_checkbox_markers , ' \zs' )
823+ let l: partial_markers = join (l: checkbox_markers [1 :-2 ], ' ' )
824+
825+ if l: bullet .checkbox_marker = ~# ' \v[' . l: partial_markers . ' ]'
826+ call s: set_checkbox (a: lnum , l: checkbox_markers [0 ])
827+ endif
828+ else
829+ " if children exist, recompute this checkbox status
830+ let l: first_child = l: children [0 ]
831+ let l: completion_marker = s: sibling_checkbox_status (l: first_child )
832+ call s: set_checkbox (a: lnum , l: completion_marker )
833+ endif
834+ endfun
835+
836+ fun ! s: recompute_checkboxes_in_range (start , end )
837+ if ! g: bullets_nested_checkboxes
838+ return
839+ endif
840+
841+ call s: enable_bullet_cache ()
842+ for l: nr in range (a: start , a: end )
843+ " find all bullets who do not have a checkbox parent
844+ let l: parent = s: get_parent (l: nr )
845+ if ! empty (l: parent ) && l: parent .bullet_type == # ' chk'
846+ continue
847+ end
848+
849+ call s: recompute_checkbox_tree (l: nr )
850+ endfor
851+ call s: disable_bullet_cache ()
852+ endfun
853+
854+ " Recomputes checkboxes for the whole list containing the cursor.
855+ fun ! s: recompute_checkboxes ()
856+ if ! g: bullets_nested_checkboxes
857+ return
858+ endif
859+
860+ call s: enable_bullet_cache ()
861+ let l: first_line = s: first_bullet_line (line (' .' ))
862+ let l: last_line = s: last_bullet_line (line (' .' ))
863+ if l: first_line > 0 && l: last_line > 0
864+ call s: recompute_checkboxes_in_range (l: first_line , l: last_line )
865+ endif
866+ call s: disable_bullet_cache ()
867+ endfun
868+
778869command ! SelectCheckboxInside call <SID> select_checkbox (1 )
779870command ! SelectCheckbox call <SID> select_checkbox (0 )
780871command ! ToggleCheckbox call <SID> toggle_checkboxes_nested ()
@@ -957,6 +1048,7 @@ endfun
9571048
9581049command ! -range =% RenumberSelection call <SID> renumber_selection ()
9591050command ! RenumberList call <SID> renumber_whole_list ()
1051+ command ! RecomputeCheckboxes call <SID> recompute_checkboxes ()
9601052
9611053" --------------------------------------------------------- }}}
9621054
@@ -967,7 +1059,7 @@ fun! s:change_line_bullet_level(direction, lnum)
9671059 if a: direction == 1
9681060 if l: curr_line != [] && indent (a: lnum ) == 0
9691061 " Promoting a bullet at the highest level will delete the bullet
970- call setline (a: lnum , l: curr_line [0 ].text_after_bullet)
1062+ call setline (a: lnum , l: curr_line [-1 ].text_after_bullet)
9711063 return
9721064 else
9731065 execute a: lnum . ' normal! <<'
@@ -1103,6 +1195,9 @@ nnoremap <silent> <Plug>(bullets-renumber) :RenumberList<cr>
11031195" Toggle checkbox
11041196nnoremap <silent> <Plug> (bullets-toggle-checkbox) :ToggleCheckbox<cr>
11051197
1198+ " Recompute checkbox list
1199+ nnoremap <silent> <Plug> (bullets-recompute-checkboxes) :RecomputeCheckboxes<cr>
1200+
11061201" Promote and Demote outline level
11071202inoremap <silent> <Plug> (bullets-demote) <C-o> :BulletDemote<cr>
11081203nnoremap <silent> <Plug> (bullets-demote) :BulletDemote<cr>
0 commit comments