commit a9f21c6d4a8cedc58971d57df0c2bd2dea8c4d4c
parent 61093f4f2aa2748a7d7fcd8ea7a4542f8723cdfb
Author: Michael F. Schönitzer <michael@schoenitzer.de>
Date: Mon, 8 Jun 2020 14:31:35 +0200
Functions to remove all todo items that are done (#906)
Add function VimwikiRemoveDone that will delete lines with todo list items marked as done. Works on ranges as well as without range on the current list.
Co-authored-by: Tinmarino <tinmarino@gmail.com>
Diffstat:
4 files changed, 329 insertions(+), 0 deletions(-)
diff --git a/autoload/vimwiki/lst.vim b/autoload/vimwiki/lst.vim
@@ -155,6 +155,24 @@ function! s:line_has_marker(lnum) abort
endfunction
+" Remove a list item and it's children (recursive)
+function! s:remove_including_children(item)
+ let num_removed_lines = 1
+ let child = s:get_first_child(a:item)
+ while child.type != 0
+ let num_removed_lines += s:remove_including_children(child)
+ let child = s:get_first_child(a:item)
+ endwhile
+ exec a:item.lnum.'delete _'
+ return num_removed_lines
+endfunction
+
+
+function! s:is_done(item)
+ return a:item.type != 0 && a:item.cb !=# '' && s:get_rate(a:item) == 100
+endfunction
+
+
" ---------------------------------------------------------
" get properties of a list item
" ---------------------------------------------------------
@@ -1085,6 +1103,105 @@ function! vimwiki#lst#remove_cb_in_list() abort
endfunction
+" Iterate over given todo list and remove all task that are done
+" If recursive is true, child items will be checked too
+function! s:remove_done_in_list(item, recursive)
+ " Clause non-null item type
+ if a:item.type == 0
+ return
+ endif
+
+ " Recurse self on list item
+ let first_item = s:get_first_item_in_list(a:item, 0)
+ let total_lines_removed = 0
+ let cur_item = first_item
+ while 1
+ let next_item = s:get_next_list_item(cur_item, 0)
+ if s:is_done(cur_item)
+ let lines_removed = s:remove_including_children(cur_item)
+ elseif a:recursive
+ let lines_removed = s:remove_done_in_list(s:get_first_child(cur_item), a:recursive)
+ else
+ let lines_removed = 0
+ endif
+ let total_lines_removed += lines_removed
+
+ if next_item.type == 0
+ break
+ else
+ let next_item.lnum -= lines_removed
+ let cur_item = next_item
+ endif
+ endwhile
+
+ " Update state of parent item (percentage of done)
+ call s:update_state(s:get_parent(first_item))
+ return total_lines_removed
+endfunction
+
+
+" Iterate over the list that the cursor is positioned in
+" and remove all lines of task that are done.
+" If recursive is true, child items will be checked too
+function! vimwiki#lst#remove_done_in_current_list(recursive)
+ let item = s:get_corresponding_item(line('.'))
+ call s:remove_done_in_list(item, a:recursive)
+endfunction
+
+
+" Remove selected lines if they contain a task that is done
+function! vimwiki#lst#remove_done_in_range(first_line, last_line)
+ let first_item = s:get_corresponding_item(a:first_line)
+ let last_item = s:get_corresponding_item(a:last_line)
+
+ " Clause non-null first and last type item
+ if first_item.type == 0 || last_item.type == 0
+ return
+ endif
+
+ " For each line, delete done tasks
+ let parent_items_of_lines = []
+ let cur_ln = first_item.lnum
+ let end_ln = last_item.lnum
+ while cur_ln > 0 && cur_ln <= end_ln
+ let cur_item = s:get_item(cur_ln)
+ if s:is_done(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
+ exe cur_ln.'delete _'
+ let cur_ln -= 1
+ let end_ln -= 1
+ endif
+ let cur_ln = s:get_next_line(cur_ln)
+ endwhile
+
+ " Update all parent state (percentage of done)
+ for parent_item in parent_items_of_lines
+ call s:update_state(parent_item)
+ endfor
+endfunction
+
+
+" wrapper function to distinguish between function used with a range or not
+" vim 8.0.1089 and newer and corresponding neovim versions allow to use <range> to distinguish if
+" the function has been called with a range. For older versions we use remove_done_in_range if
+" first and last line are identical, which means there was either no range or the range was within
+" one line.
+function! vimwiki#lst#remove_done(recursive, range, first_line, last_line)
+ if a:range ==# '<range>'
+ let range = a:first_line != a:last_line
+ else
+ let range = a:range > 0
+ endif
+ if range
+ call vimwiki#lst#remove_done_in_range(a:first_line, a:last_line)
+ else
+ call vimwiki#lst#remove_done_in_current_list(a:recursive)
+ endif
+endfunction
+
" ---------------------------------------------------------
" change the level of list items
diff --git a/doc/vimwiki.txt b/doc/vimwiki.txt
@@ -799,6 +799,18 @@ Vimwiki file.
*:VimwikiRenameFile*
Rename the wiki page you are in.
+*:VimwikiRemoveDone*
+ With range: Remove all lines that have a checked |vimwiki-todo-lists| checkbox
+
+ Otherwise:
+ Remove all lines in the current todo-list that have a checked box. This is
+ applied to the list in which the cursor is in, as well as all its sublists.
+ The status of parents is updated accordingly.
+ If you want to remove only items of the current nesting level, (re)define
+ a command that calls the same function with `0` as first argument: >
+ :command! -buffer -range VimwikiRemoveDone call
+ \ vimwiki#lst#remove_done(0, "<range>", <line1>, <line2>)
+<
*:VimwikiNextTask*
Jump to the next unfinished task in the current wiki page.
diff --git a/ftplugin/vimwiki.vim b/ftplugin/vimwiki.vim
@@ -297,6 +297,7 @@ command! -buffer VimwikiRemoveCBInList call vimwiki#lst#remove_cb_in_list()
command! -buffer VimwikiRenumberList call vimwiki#lst#adjust_numbered_list()
command! -buffer VimwikiRenumberAllLists call vimwiki#lst#adjust_whole_buffer()
command! -buffer VimwikiListToggle call vimwiki#lst#toggle_list_item()
+command! -buffer -range VimwikiRemoveDone call vimwiki#lst#remove_done(1, "<range>", <line1>, <line2>)
" table commands
command! -buffer -nargs=* VimwikiTable call vimwiki#tbl#create(<f-args>)
diff --git a/test/list_clean.vader b/test/list_clean.vader
@@ -0,0 +1,199 @@
+Include: vader_includes/vader_setup.vader
+
+Given vimwiki (simple list):
+ * [X] Done 1
+ * [ ] Todo 1
+ * [X] Done 2
+ * [ ] Todo 2
+
+Execute (Set syntax to default):
+ call SetSyntax('default')
+
+Do (clean done, without recursion):
+ :call vimwiki#lst#remove_done_in_current_list(0)\<Enter>
+
+Expect (two removed):
+ * [ ] Todo 1
+ * [ ] Todo 2
+
+Given vimwiki (simple list):
+ * [X] Done 1
+ * [ ] Todo 1
+ * [X] Done 2
+ * [ ] Todo 2
+
+Do (clean done with recursion, function):
+ :call vimwiki#lst#remove_done_in_current_list(1)\<Enter>
+
+Expect (two removed):
+ * [ ] Todo 1
+ * [ ] Todo 2
+
+Given vimwiki (simple list):
+ * [X] Done 1
+ * [ ] Todo 1
+ * [X] Done 2
+ * [ ] Todo 2
+
+Do (clean done with recursion, command):
+ :VimwikiRemoveDone\<Enter>
+
+Expect (two removed):
+ * [ ] Todo 1
+ * [ ] Todo 2
+
+Given vimwiki (with sub items):
+ * [X] Done 1
+ * [X] Subdone 1
+ * [ ] Todo 1
+ * [o] Done 2
+ * [X] Subdone1
+ * [ ] Subtodo
+ * [ ] Todo 2
+
+Do (clean done, without recursion):
+ :call vimwiki#lst#remove_done_in_current_list(0)\<Enter>
+
+Expect (first removed):
+ * [ ] Todo 1
+ * [o] Done 2
+ * [X] Subdone1
+ * [ ] Subtodo
+ * [ ] Todo 2
+
+Given vimwiki (with sub items):
+ * [ ] Todo 1
+ * [o] Done 2
+ * [X] Subdone1
+ * [ ] Subtodo
+ * [ ] Todo 2
+
+Do (clean done, with recursion):
+ :call vimwiki#lst#remove_done_in_current_list(1)\<Enter>
+
+Expect (all removed):
+ * [ ] Todo 1
+ * [ ] Done 2
+ * [ ] Subtodo
+ * [ ] Todo 2
+
+Given vimwiki (nested list with space and code):
+ * [X] Done 1
+ * [ ] Todo 1
+
+ * [ ] Todo Post space
+ * [X] Done Post space
+
+ * [ ] Todo code
+ {{{code
+ * [X] print "hello, world"
+ }}}
+
+ * [ ] Post code Todo
+ * [X] Done Sub-child
+ * [X] Sub-sub-child
+ * Without cb
+ * [X] Post code Done
+ * [X] Done Sub-child
+ * [X] Sub-sub-child
+ * Without cb
+
+Do (clean done, without recursion):
+ :call vimwiki#lst#remove_done_in_current_list(0)\<Enter>
+
+Expect (removed):
+ * [ ] Todo 1
+
+ * [ ] Todo Post space
+
+ * [ ] Todo code
+ {{{code
+ * [X] print "hello, world"
+ }}}
+
+ * [ ] Post code Todo
+ * [X] Done Sub-child
+ * [X] Sub-sub-child
+ * Without cb
+
+Given vimwiki (nested list with space and code):
+ * [X] Done 1
+ * [ ] Todo 1
+
+ * [ ] Todo Post space
+ * [X] Done Post space
+
+ * [ ] Todo code
+ {{{code
+ * [X] print "hello, world"
+ }}}
+
+ * [ ] Post code Todo
+ * [X] Done Sub-child
+ * [X] Sub-sub-child
+ * Without cb
+ * [X] Post code Done
+ * [X] Done Sub-child
+ * [X] Sub-sub-child
+ * Without cb
+
+Do (clean done, with recursion):
+ :call vimwiki#lst#remove_done_in_current_list(1)\<Enter>
+
+Expect (removed):
+ * [ ] Todo 1
+
+ * [ ] Todo Post space
+
+ * [ ] Todo code
+ {{{code
+ * [X] print "hello, world"
+ }}}
+
+ * [ ] Post code Todo
+
+Given vimwiki (two lists):
+ * [X] Done 1
+ * [ ] Todo 1
+
+ Line in between.
+
+ * [ ] Todo Post space
+ * [X] Done Post space
+
+Do (clean done, with recursion):
+ :call vimwiki#lst#remove_done_in_current_list(1)\<Enter>
+
+Expect (only first is removed):
+ * [ ] Todo 1
+
+ Line in between.
+
+ * [ ] Todo Post space
+ * [X] Done Post space
+
+Given vimwiki (list):
+ * [X] Done 1
+ * [ ] Todo 1
+
+ Line in between.
+
+ * [X] Done 2
+ * [X] Done 3
+ * [ ] Todo 2
+ * [X] Done 4
+
+Do (clean done, with range):
+ :1,6VimwikiRemoveDone\<Enter>
+
+Expect (only first is removed):
+ * [ ] Todo 1
+
+ Line in between.
+
+ * [X] Done 3
+ * [ ] Todo 2
+ * [X] Done 4
+
+
+Include: vader_includes/vader_teardown.vader