@@ -161,7 +161,7 @@ fun! s:parse_bullet_text(line_text)
161161 if s: bullet_cache isnot v: null
162162 let l: cached = get (s: bullet_cache , a: line_text , v: null )
163163 if l: cached isnot v: null
164- " Return a copy so as not to break the referene
164+ " Return a copy so as not to break the reference
165165 return copy (l: cached )
166166 endif
167167 endif
@@ -177,11 +177,11 @@ fun! s:parse_bullet_text(line_text)
177177 let l: roman = empty (l: bullet ) && empty (l: num ) ? s: match_roman_list_item (a: line_text ) : {}
178178
179179 let l: kinds = s: filter ([l: bullet , l: check , l: num , l: alpha , l: roman ], ' !empty(v:val)' )
180-
180+
181181 if s: bullet_cache isnot v: null
182182 let s: bullet_cache [a: line_text ] = l: kinds
183183 endif
184-
184+
185185 return l: kinds
186186endfun
187187
@@ -574,7 +574,11 @@ fun! s:insert_new_bullet()
574574 " We don't want to create a new bullet if the previous one was not used,
575575 " instead we want to delete the empty bullet - like word processors do
576576 if g: bullets_delete_last_bullet_if_empty
577- call setline (l: curr_line_num , ' ' )
577+ if g: bullets_delete_last_bullet_if_empty == 1
578+ call setline (l: curr_line_num , ' ' )
579+ elseif g: bullets_delete_last_bullet_if_empty == 2
580+ call <SID> change_bullet_level (1 , 0 )
581+ endif
578582 let l: send_return = 0
579583 endif
580584 elseif ! (l: bullet .bullet_type == # ' abc' && s: abc2dec (l: bullet .bullet) + 1 > s: abc_max )
@@ -775,6 +779,85 @@ fun! s:set_child_checkboxes(lnum, checked)
775779 endif
776780endfun
777781
782+ " Recompute partial checkboxes of a full checkbox tree given the root lnum
783+ fun ! s: recompute_checkbox_tree (lnum)
784+ if ! g: bullets_nested_checkboxes
785+ return
786+ endif
787+
788+ let l: indent = indent (a: lnum )
789+ let l: bullet = s: closest_bullet_types (a: lnum , l: indent )
790+ let l: bullet = s: resolve_bullet_type (l: bullet )
791+
792+ if l: bullet .bullet_type !=# ' chk'
793+ return
794+ endif
795+
796+ " recursively recompute checkbox tree for all children, then finally self
797+
798+ let l: children = s: get_children_line_numbers (a: lnum )
799+ for l: child_nr in l: children
800+ " nb: this skips 'grandchildren' checkboxes (i.e., children who aren't
801+ " checkboxes but have checkbox children themselves), but those grandkids
802+ " will be targeted by s:recompute_checkboxes_in_range anyway
803+ call s: recompute_checkbox_tree (l: child_nr )
804+ endfor
805+
806+
807+ if empty (l: children )
808+ " if no children, preserve previous checked state
809+ " partially completed checkboxes become unchecked
810+ if empty (l: bullet ) || ! has_key (l: bullet , ' checkbox_marker' )
811+ return
812+ endif
813+
814+ let l: checkbox_markers = split (g: bullets_checkbox_markers , ' \zs' )
815+ let l: partial_markers = join (l: checkbox_markers [1 :-2 ], ' ' )
816+
817+ if l: bullet .checkbox_marker = ~# ' \v[' . l: partial_markers . ' ]'
818+ call s: set_checkbox (a: lnum , l: checkbox_markers [0 ])
819+ endif
820+ else
821+ " if children exist, recompute this checkbox status
822+ let l: first_child = l: children [0 ]
823+ let l: completion_marker = s: sibling_checkbox_status (l: first_child )
824+ call s: set_checkbox (a: lnum , l: completion_marker )
825+ endif
826+ endfun
827+
828+ fun ! s: recompute_checkboxes_in_range (start , end )
829+ if ! g: bullets_nested_checkboxes
830+ return
831+ endif
832+
833+ call s: enable_bullet_cache ()
834+ for l: nr in range (a: start , a: end )
835+ " find all bullets who do not have a checkbox parent
836+ let l: parent = s: get_parent (l: nr )
837+ if ! empty (l: parent ) && l: parent .bullet_type == # ' chk'
838+ continue
839+ end
840+
841+ call s: recompute_checkbox_tree (l: nr )
842+ endfor
843+ call s: disable_bullet_cache ()
844+ endfun
845+
846+ " Recomputes checkboxes for the whole list containing the cursor.
847+ fun ! s: recompute_checkboxes ()
848+ if ! g: bullets_nested_checkboxes
849+ return
850+ endif
851+
852+ call s: enable_bullet_cache ()
853+ let l: first_line = s: first_bullet_line (line (' .' ))
854+ let l: last_line = s: last_bullet_line (line (' .' ))
855+ if l: first_line > 0 && l: last_line > 0
856+ call s: recompute_checkboxes_in_range (l: first_line , l: last_line )
857+ endif
858+ call s: disable_bullet_cache ()
859+ endfun
860+
778861command ! SelectCheckboxInside call <SID> select_checkbox (1 )
779862command ! SelectCheckbox call <SID> select_checkbox (0 )
780863command ! ToggleCheckbox call <SID> toggle_checkboxes_nested ()
@@ -957,6 +1040,7 @@ endfun
9571040
9581041command ! -range =% RenumberSelection call <SID> renumber_selection ()
9591042command ! RenumberList call <SID> renumber_whole_list ()
1043+ command ! RecomputeCheckboxes call <SID> recompute_checkboxes ()
9601044
9611045" --------------------------------------------------------- }}}
9621046
@@ -967,7 +1051,7 @@ fun! s:change_line_bullet_level(direction, lnum)
9671051 if a: direction == 1
9681052 if l: curr_line != [] && indent (a: lnum ) == 0
9691053 " Promoting a bullet at the highest level will delete the bullet
970- call setline (a: lnum , l: curr_line [0 ].text_after_bullet)
1054+ call setline (a: lnum , l: curr_line [-1 ].text_after_bullet)
9711055 return
9721056 else
9731057 execute a: lnum . ' normal! <<'
@@ -1103,6 +1187,9 @@ nnoremap <silent> <Plug>(bullets-renumber) :RenumberList<cr>
11031187" Toggle checkbox
11041188nnoremap <silent> <Plug> (bullets-toggle-checkbox) :ToggleCheckbox<cr>
11051189
1190+ " Recompute checkbox list
1191+ nnoremap <silent> <Plug> (bullets-recompute-checkboxes) :RecomputeCheckboxes<cr>
1192+
11061193" Promote and Demote outline level
11071194inoremap <silent> <Plug> (bullets-demote) <C-o> :BulletDemote<cr>
11081195nnoremap <silent> <Plug> (bullets-demote) :BulletDemote<cr>
0 commit comments