From a386018e18b2c515b4a0939692ac5c5ff98d9989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Sun, 15 Aug 2021 00:10:06 +0200 Subject: [PATCH 01/21] test: toggle checklist in first line of file --- test/examples/lists.txt | 2 ++ test/test-numbered.vim | 6 +++--- test/test-toggle.vim | 18 +++++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/test/examples/lists.txt b/test/examples/lists.txt index 04751b9..54cd58a 100644 --- a/test/examples/lists.txt +++ b/test/examples/lists.txt @@ -1,3 +1,5 @@ +- [ ] Top of file list entry + # List examples 1 - [ ] First entry diff --git a/test/test-numbered.vim b/test/test-numbered.vim index 270a368..b00faf9 100644 --- a/test/test-numbered.vim +++ b/test/test-numbered.vim @@ -3,14 +3,14 @@ source init.vim silent edit examples/lists.txt " Numbered lists general -let [s:root, s:current] = lists#parser#get_at(22) +let [s:root, s:current] = lists#parser#get_at(24) call assert_equal( \ '1. Ordered lists are also cool', \ s:current.text[0]) " Numbered todo lists -call lists#toggle(23) -let [s:root, s:current] = lists#parser#get_at(23) +call lists#toggle(25) +let [s:root, s:current] = lists#parser#get_at(25) call assert_equal('DONE', s:current.states[s:current.state]) call lists#test#finished() diff --git a/test/test-toggle.vim b/test/test-toggle.vim index b24a6c1..72d78db 100644 --- a/test/test-toggle.vim +++ b/test/test-toggle.vim @@ -5,16 +5,20 @@ let g:lists_todos = ['TODO', 'INPROGRESS', 'DONE'] silent edit examples/lists.txt " Checkbox lists -call lists#toggle(7) -call lists#toggle(10) -let [s:root, s:current] = lists#parser#get_at(3) -call assert_equal(len(s:current.children), 3) +call lists#toggle(1) +let [s:root, s:current] = lists#parser#get_at(1) +call assert_true(s:current.checked) + +call lists#toggle(9) +call lists#toggle(12) +let [s:root, s:current] = lists#parser#get_at(5) +call assert_equal(3, len(s:current.children)) call assert_true(s:current.checked) call assert_true(s:root.children[1].children[0].checked) " Todo lists -call lists#toggle(17) -let [s:root, s:current] = lists#parser#get_at(17) -call assert_equal(s:current.states[s:current.state], 'DONE') +call lists#toggle(19) +let [s:root, s:current] = lists#parser#get_at(19) +call assert_equal('DONE', s:current.states[s:current.state]) call lists#test#finished() From 9a1489efda16dc41328e52f3b3dbd7e76389b24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Sun, 15 Aug 2021 00:11:06 +0200 Subject: [PATCH 02/21] fix: recognize lists in first line of file refer: #1 --- autoload/lists/parser.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/lists/parser.vim b/autoload/lists/parser.vim index 698542c..7f53f00 100644 --- a/autoload/lists/parser.vim +++ b/autoload/lists/parser.vim @@ -51,7 +51,7 @@ function! s:get_list_items(opts) abort " {{{1 let l:save_pos = getcurpos() call setpos('.', [0, l:lnum, l:cnum, 0]) - let l:list_start = search(s:items_re, 'Wn') + let l:list_start = search(s:items_re, 'Wcn') if l:list_start == 0 \ || l:list_start > l:save_pos[1] call setpos('.', l:save_pos) From d7cb61bcf054515372f0a9dd1707b2b49c9d45fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 12 Nov 2021 19:50:47 +0100 Subject: [PATCH 03/21] test: updated example --- test/examples/lists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/examples/lists.txt b/test/examples/lists.txt index 54cd58a..335d013 100644 --- a/test/examples/lists.txt +++ b/test/examples/lists.txt @@ -15,9 +15,9 @@ # List examples 2 -- TODO: Task 1 -- INPROGRESS: Task 2 -- DONE: Task 3 +* TODO: Task 1 +* INPROGRESS: Task 2 +* DONE: Task 3 # List examples 3 From 3e225e6e1b59a8e5952e5b30b9b5f4cd6b56ebdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 12 Nov 2021 19:51:22 +0100 Subject: [PATCH 04/21] chore: minor adjustment --- autoload/lists/item/general.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autoload/lists/item/general.vim b/autoload/lists/item/general.vim index 9782140..1445044 100644 --- a/autoload/lists/item/general.vim +++ b/autoload/lists/item/general.vim @@ -24,6 +24,12 @@ let s:item = { \} function! s:item.new(start, end) abort dict " {{{1 + " This is a template and must be combined with a real item type! + if !has_key(self, 're_item') + echoerr 'THIS IS A GENERIC FUNCTION!' + return {} + endif + let l:new = deepcopy(self) unlet l:new.new @@ -33,12 +39,6 @@ function! s:item.new(start, end) abort dict " {{{1 let l:new.text = getline(l:new.lnum_start, l:new.lnum_end) let l:new.indent = indent(a:start) - " This is a template and must be combined with a real item type! - if !has_key(self, 're_item') - echoerr 'THIS IS A GENERIC FUNCTION!' - return {} - endif - let l:new.header = matchstr(l:new.text[0], self.re_item) let l:new.state = index(self.states, matchstr(l:new.text[0], \ self.re_item . '\zs' . join(self.states, '\|') . '\ze:')) From c6cb119c3290db7ec054ac8d970ed4451f01e70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 12 Nov 2021 19:51:50 +0100 Subject: [PATCH 05/21] feat: add lists#bullet#... functions --- autoload/lists/bullet.vim | 70 +++++++++++++++++++++++++++++++ autoload/lists/item/checkbox.vim | 1 + autoload/lists/item/unordered.vim | 15 +++++++ test/test-change-bullet.vim | 40 ++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 autoload/lists/bullet.vim create mode 100644 test/test-change-bullet.vim diff --git a/autoload/lists/bullet.vim b/autoload/lists/bullet.vim new file mode 100644 index 0000000..8972d00 --- /dev/null +++ b/autoload/lists/bullet.vim @@ -0,0 +1,70 @@ +" A Vim plugin to handle lists +" +" Maintainer: Karl Yngve LervÄg +" Email: karl.yngve@gmail.com +" + +function! lists#bullet#get(...) abort " {{{1 + let [l:root, l:current] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + + return get(l:current, 'bullet', '') +endfunction + +" }}}1 + +function! lists#bullet#toggle_all(...) abort " {{{1 + let [l:_, l:current] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + + let l:bullet = get(l:current, 'bullet', '') + if empty(l:bullet) | return | endif + + let l:new = l:bullet ==# '*' ? '-' : '*' + call lists#bullet#change_all(l:new, a:0 > 0 ? a:1 : line('.')) +endfunction + +" }}}1 +function! lists#bullet#change_all(new, ...) abort " {{{1 + let [l:cur, l:_] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + + while !empty(l:cur) + let l:cur = l:cur.next + if has_key(l:cur, 'set_bullet') + call l:cur.set_bullet(a:new) + endif + endwhile +endfunction + +" }}}1 + +function! lists#bullet#toggle_local(...) abort " {{{1 + let [l:_, l:current] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + + let l:bullet = get(l:current, 'bullet', '') + if empty(l:bullet) | return | endif + + let l:new = l:bullet ==# '*' ? '-' : '*' + call lists#bullet#change_local(l:new, a:0 > 0 ? a:1 : line('.')) +endfunction + +" }}}1 +function! lists#bullet#change_local(new, ...) abort " {{{1 + let [l:_, l:start] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + + for l:item in l:start.parent.children + if has_key(l:item, 'set_bullet') + call l:item.set_bullet(a:new) + endif + endfor +endfunction + +" }}}1 diff --git a/autoload/lists/item/checkbox.vim b/autoload/lists/item/checkbox.vim index ca68d63..69d365e 100644 --- a/autoload/lists/item/checkbox.vim +++ b/autoload/lists/item/checkbox.vim @@ -22,6 +22,7 @@ let s:item = extend(lists#item#unordered#new(), { function! s:item.init() abort dict " {{{1 let self.checked = match(self.text[0], self.re_item_checked) >= 0 + let self.bullet = matchstr(self.header, self.re_bullet) endfunction " }}}1 diff --git a/autoload/lists/item/unordered.vim b/autoload/lists/item/unordered.vim index e826ec7..ebb9a90 100644 --- a/autoload/lists/item/unordered.vim +++ b/autoload/lists/item/unordered.vim @@ -15,11 +15,26 @@ endfunction let s:item = extend(lists#item#general#new(), { \ 'type' : 'unordered', \ 're_item': '^\s*[*-]\(\s\|$\)', + \ 're_bullet': '^\s*\zs[*-]', \}) +function! s:item.init() abort dict "{{{1 + let self.bullet = matchstr(self.header, self.re_bullet) +endfunction + +" }}}1 function! s:item.next_header() abort dict "{{{1 return substitute(copy(self.header), '^\s*\zs.*', '', '') \ . matchstr(self.header, '[*-]') . ' ' endfunction " }}}1 +function! s:item.set_bullet(new) abort dict "{{{1 + if index(['*', '-'], a:new) < 0 | return | endif + if a:new ==# self.bullet | return | endif + + call setline(self.lnum_start, + \ substitute(self.text[0], self.re_bullet, a:new, '')) +endfunction + +" }}}1 diff --git a/test/test-change-bullet.vim b/test/test-change-bullet.vim new file mode 100644 index 0000000..5af0f27 --- /dev/null +++ b/test/test-change-bullet.vim @@ -0,0 +1,40 @@ +source init.vim + +silent edit examples/lists.txt + +call assert_equal('-', lists#bullet#get(5)) +call assert_equal('*', lists#bullet#get(19)) + +call lists#bullet#change_all('*', 7) +call assert_equal('*', lists#bullet#get(5)) +call assert_equal('*', lists#bullet#get(12)) +silent undo +call assert_equal('-', lists#bullet#get(5)) +call assert_equal('-', lists#bullet#get(12)) + +call lists#bullet#toggle_all(7) +call assert_equal('*', lists#bullet#get(5)) +call assert_equal('*', lists#bullet#get(12)) +silent undo +call assert_equal('-', lists#bullet#get(5)) +call assert_equal('-', lists#bullet#get(12)) + +call lists#bullet#change_local('*', 7) +call assert_equal('-', lists#bullet#get(5)) +call assert_equal('*', lists#bullet#get(6)) +call assert_equal('*', lists#bullet#get(7)) +call assert_equal('-', lists#bullet#get(8)) +call assert_equal('*', lists#bullet#get(11)) +call assert_equal('-', lists#bullet#get(12)) +silent undo + +call lists#bullet#toggle_local(7) +call assert_equal('-', lists#bullet#get(5)) +call assert_equal('*', lists#bullet#get(6)) +call assert_equal('*', lists#bullet#get(7)) +call assert_equal('-', lists#bullet#get(8)) +call assert_equal('*', lists#bullet#get(11)) +call assert_equal('-', lists#bullet#get(12)) +silent undo + +call lists#test#finished() From 3ca7bf3a5703d691bd542a48234bf5b2373d1b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 12 Nov 2021 20:22:28 +0100 Subject: [PATCH 06/21] feat: add bullet toggle map and cmd --- autoload/lists.vim | 33 ++++++++++++++++++++++----------- doc/lists.txt | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 7ceae9b..738c2e7 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -16,17 +16,26 @@ function! lists#init() abort " {{{1 command! -buffer ListsUniqLocal call lists#uniq(1) command! -buffer ListsShowItem call lists#show_item() - nnoremap (lists-moveup) :ListsMoveUp - nnoremap (lists-movedown) :ListsMoveDown - nnoremap (lists-toggle) :ListsToggle - nnoremap (lists-uniq) :ListsUniq - nnoremap (lists-uniq-local) :ListsUniqLocal - nnoremap (lists-show-item) :ListsShowItem - inoremap (lists-toggle) :call lists#new_item() - onoremap (lists-al) :call lists#text_obj#list_element(0, 0) - xnoremap (lists-al) :call lists#text_obj#list_element(0, 1) - onoremap (lists-il) :call lists#text_obj#list_element(1, 0) - xnoremap (lists-il) :call lists#text_obj#list_element(1, 1) + command! -buffer -bang ListsToggleBullet + \ if empty() | + \ call lists#bullet#toggle_all() | + \ else | + \ call lists#bullet#toggle_local() | + \ endif + + nnoremap (lists-moveup) :ListsMoveUp + nnoremap (lists-movedown) :ListsMoveDown + nnoremap (lists-toggle) :ListsToggle + nnoremap (lists-uniq) :ListsUniq + nnoremap (lists-uniq-local) :ListsUniqLocal + nnoremap (lists-show-item) :ListsShowItem + inoremap (lists-toggle) :call lists#new_item() + onoremap (lists-al) :call lists#text_obj#list_element(0, 0) + xnoremap (lists-al) :call lists#text_obj#list_element(0, 1) + onoremap (lists-il) :call lists#text_obj#list_element(1, 0) + xnoremap (lists-il) :call lists#text_obj#list_element(1, 1) + nnoremap (lists-bullet-toggle-all) :call lists#bullet#toggle_all() + nnoremap (lists-bullet-toggle-local) :call lists#bullet#toggle_local() for [l:rhs, l:lhs] in items({ \ '(lists-toggle)': '', @@ -35,6 +44,8 @@ function! lists#init() abort " {{{1 \ '(lists-uniq)': 'wlu', \ '(lists-uniq-local)': 'wlU', \ '(lists-show-item)': 'wls', + \ '(lists-bullet-toggle-all)': 'wlt', + \ '(lists-bullet-toggle-local)': 'wlT', \ 'i_(lists-toggle)': '', \ 'o_(lists-al)': 'al', \ 'x_(lists-al)': 'al', diff --git a/doc/lists.txt b/doc/lists.txt index 0b02cb5..6d0899c 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -16,13 +16,13 @@ License: MIT license {{{ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - IN THE SOFTWARE. + The software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall the + authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising + from, out of or in connection with the software or the use or other dealings + in the software. }}} @@ -57,6 +57,15 @@ The following is a list of the commands and mappings provided by |lists.vim|. *(lists-toggle)* Toggle a list item. The mapping works in both normal and insert mode. +*:ListsToggleBullet* +*(lists-bullet-toggle-all)* + Toggle the list bullet type between `-` and `*`. for the entire list. + +*:ListsToggleBullet!* +*(lists-bullet-toggle-local)* + Toggle the list bullet type between `-` and `*` for the sibling list + entries. + *:ListsMoveUp* *:ListsMoveDown* *(lists-moveup)* @@ -84,15 +93,17 @@ indicated by the first letters of the various map commands (|nmap|, |imap|, |omap|, and |xmap|). -------------------------------------------------------------------~ - PLUG DEFAULT MODE~ + PLUG DEFAULT MODE~ -------------------------------------------------------------------~ - |(lists-toggle)| `in` - |(lists-moveup)| wlk `n` - |(lists-movedown)| wlj `n` - |(lists-uniq)| wlu `n` - |(lists-uniq-local)| wlU `n` - |(lists-al)| al `ox` - |(lists-il)| il `ox` + |(lists-toggle)| `in` + |(lists-moveup)| wlk `n` + |(lists-movedown)| wlj `n` + |(lists-uniq)| wlu `n` + |(lists-uniq-local)| wlU `n` + |(lists-bullet-toggle-local)| wlT `n` + |(lists-bullet-toggle-all)| wlt `n` + |(lists-al)| al `ox` + |(lists-il)| il `ox` -------------------------------------------------------------------~ ============================================================================== From cbe4ee9cde29f9ecb0cb5ad74a424b52cd4c8df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Tue, 26 Apr 2022 21:33:51 +0200 Subject: [PATCH 07/21] feat: add option to disable default mappings refer: #2 --- autoload/lists.vim | 3 +++ doc/lists.txt | 17 +++++++++++------ plugin/lists.vim | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 738c2e7..f675e80 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -37,6 +37,9 @@ function! lists#init() abort " {{{1 nnoremap (lists-bullet-toggle-all) :call lists#bullet#toggle_all() nnoremap (lists-bullet-toggle-local) :call lists#bullet#toggle_local() + " Only apply default maps if desired + if !g:lists_enable_default_maps | return | endif + for [l:rhs, l:lhs] in items({ \ '(lists-toggle)': '', \ '(lists-moveup)': 'wlk', diff --git a/doc/lists.txt b/doc/lists.txt index 6d0899c..9b16f78 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -88,13 +88,13 @@ The following is a list of the commands and mappings provided by |lists.vim|. The following mappings are available as visual mode and operator mode mappings, i.e. |xmap| and |omap|. -The default mappings are listed in the following table. The map mode is -indicated by the first letters of the various map commands (|nmap|, |imap|, -|omap|, and |xmap|). +The default mappings are listed in the following table. To disable the default +mappings, see |g:lists_enable_default_maps|. The map mode is indicated by the +first letters of the various map commands (|nmap|, |imap|, |omap|, and +|xmap|). - -------------------------------------------------------------------~ PLUG DEFAULT MODE~ - -------------------------------------------------------------------~ + ---- ------- ----~ |(lists-toggle)| `in` |(lists-moveup)| wlk `n` |(lists-movedown)| wlj `n` @@ -104,7 +104,6 @@ indicated by the first letters of the various map commands (|nmap|, |imap|, |(lists-bullet-toggle-all)| wlt `n` |(lists-al)| al `ox` |(lists-il)| il `ox` - -------------------------------------------------------------------~ ============================================================================== CONFIGURATION *lists-config* @@ -114,6 +113,12 @@ CONFIGURATION *lists-config* Default: [] +*g:lists_enable_default_maps* + Set to |v:false| or 0 to prevent |lists.vim| from applying the default maps. + Useful if you want to create your own mappings. + + Default: |v:true| + *g:lists_todos* A list of TODO toggles that may be toggled with |(lists-toggle)|, which is by default mapped to ``. diff --git a/plugin/lists.vim b/plugin/lists.vim index acab063..041540d 100644 --- a/plugin/lists.vim +++ b/plugin/lists.vim @@ -9,6 +9,7 @@ let g:loaded_lists = 1 call lists#u#init_option('lists_filetypes', []) +call lists#u#init_option('lists_enable_default_maps', v:true) call lists#u#init_option('lists_todos', ['TODO', 'DONE']) command! ListsEnable call lists#init() From 92553812f0b326a7d10e2d998a28a5f7744ad836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Wed, 27 Apr 2022 22:26:12 +0200 Subject: [PATCH 08/21] fix: bug in lists#move refer: #3 --- autoload/lists.vim | 14 +++++++------- autoload/lists/parser.vim | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index f675e80..87c17fd 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -115,15 +115,15 @@ function! lists#move(direction, ...) abort "{{{1 let l:target = -1 let l:next = l:current.next while !empty(l:next) - if l:next.indent > l:current.indent - let l:next = l:next.next - continue + if l:next.indent <= l:current.indent + \ && l:next.indent >= l:current.parent.indent + let l:target = l:next.indent < l:current.indent + \ ? l:next.lnum_end + \ : l:next.lnum_last + break endif - let l:target = l:next.indent < l:current.indent - \ ? l:next.lnum_end - \ : l:next.lnum_last - break + let l:next = l:next.next endwhile let l:target_pos[1] += l:target - l:current.lnum_last diff --git a/autoload/lists/parser.vim b/autoload/lists/parser.vim index 7f53f00..27f22ae 100644 --- a/autoload/lists/parser.vim +++ b/autoload/lists/parser.vim @@ -145,7 +145,7 @@ function! s:get_tree_from_items(items) abort " {{{1 let l:current.counter = l:counter " Update nested counter - if l:prev == l:parent + if l:prev == l:parent || empty(l:current.prev_sibling) let l:counter_nested = 1 else if l:prev.indent > l:current.indent From fe1fcb045d317987aa6a3224a185e0ae66ac7ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Wed, 27 Apr 2022 22:30:44 +0200 Subject: [PATCH 09/21] test: more tests for lists#move refer: #3 --- test/test-move.vim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test-move.vim b/test/test-move.vim index 72bd7af..52a6c48 100644 --- a/test/test-move.vim +++ b/test/test-move.vim @@ -2,6 +2,12 @@ source init.vim silent edit examples/move_in.txt +normal! 12G +call lists#move(1) +call lists#move(1) +call lists#move(0) +call lists#move(0) + call lists#move(0, 5) call lists#move(0, 7) call lists#move(0, 7) From bf7c1465763ccad9adc61198c2b74a3579f38d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Wed, 27 Apr 2022 22:45:11 +0200 Subject: [PATCH 10/21] doc: clarify type of lists supported --- README.md | 13 +++++++++++++ doc/lists.txt | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/README.md b/README.md index e0c3d3f..f3ebc22 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,19 @@ following features are provided. The default mappings are indicated. See the * `wlk` and `wlj`: Move list items up or down the list. * `wlu`: Remove repeated entries in a list (recursively) +`lists.vim` is quite minimal and does not have a lot of available +configuration choices. The implementation is based on the idea that list items +are indented consistently. The indentation makes it easy to parse a list to +determine its structure. + +These types of lists are currently supported: +* Bullet lists. Bullets are either "*" or "-". +* Numbered lists formated similar to "1.". +* List items can be decorated with check boxes. Examples: + * [x] checked + * [ ] unchecked +* List items can be decorated with labels like "TODO:" and "DONE:". + # Installation If you use [vim-plug](https://github.com/junegunn/vim-plug), then add the diff --git a/doc/lists.txt b/doc/lists.txt index 9b16f78..44340c9 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -42,6 +42,22 @@ is now therefore a separate plugin. [0]: https://github.com/lervag/wiki.vim [1]: https://github.com/lervag/wiki.vim/issues/131 +============================================================================== +LIST TYPES *lists-types* + +|lists.vim| is quite minimal and does not have a lot of available +configuration choices. The implementation is based on the idea that list items +are indented consistently. The indentation makes it easy to parse a list to +determine its structure. + +These types of lists are currently supported: +* Bullet lists. Bullets are either "*" or "-". +* Numbered lists formated similar to "1.". +* List items can be decorated with check boxes. Examples: + * [x] checked + * [ ] unchecked +* List items can be decorated with labels like "TODO:" and "DONE:". + ============================================================================== FEATURES *lists-features* From 3a258ecc91d31ce4281fd1082d1b04b8fed96e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Thu, 28 Jul 2022 14:13:02 +0200 Subject: [PATCH 11/21] feat: change formatoptions to better support lists --- autoload/lists.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 87c17fd..a70d86e 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -5,9 +5,10 @@ " function! lists#init() abort " {{{1 - " Set 'comments' option for lists + " Set 'comments' and 'formatoptions' to work well with lists setlocal comments+=fb:*,f:*\ TODO:,b:*\ [\ ],b:*\ [x] setlocal comments+=fb:-,f:-\ TODO:,b:-\ [\ ],b:-\ [x] + setlocal formatoptions+=ron command! -buffer ListsMoveUp call lists#move(0) command! -buffer ListsMoveDown call lists#move(1) From 089a6141d85fdd74a2cfb8626dd8da87237a8f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Thu, 4 Aug 2022 13:43:51 +0200 Subject: [PATCH 12/21] feat: add g:lists_maps_default_override Also change the name of existing option from g:lists_enable_default_maps to g:lists_maps_default_enable refer: #5 --- autoload/lists.vim | 6 +++--- doc/lists.txt | 25 ++++++++++++++++++++++--- plugin/lists.vim | 3 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index a70d86e..1328862 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -39,9 +39,9 @@ function! lists#init() abort " {{{1 nnoremap (lists-bullet-toggle-local) :call lists#bullet#toggle_local() " Only apply default maps if desired - if !g:lists_enable_default_maps | return | endif + if !g:lists_maps_default_enable | return | endif - for [l:rhs, l:lhs] in items({ + for [l:rhs, l:lhs] in items(extend({ \ '(lists-toggle)': '', \ '(lists-moveup)': 'wlk', \ '(lists-movedown)': 'wlj', @@ -55,7 +55,7 @@ function! lists#init() abort " {{{1 \ 'x_(lists-al)': 'al', \ 'o_(lists-il)': 'il', \ 'x_(lists-il)': 'il', - \}) + \}, g:lists_maps_default_override)) if l:rhs[0] !=# '<' let l:mode = l:rhs[0] let l:rhs = l:rhs[2:] diff --git a/doc/lists.txt b/doc/lists.txt index 44340c9..015459c 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -104,9 +104,11 @@ The following is a list of the commands and mappings provided by |lists.vim|. The following mappings are available as visual mode and operator mode mappings, i.e. |xmap| and |omap|. + *lists-maps-defaults* The default mappings are listed in the following table. To disable the default -mappings, see |g:lists_enable_default_maps|. The map mode is indicated by the -first letters of the various map commands (|nmap|, |imap|, |omap|, and +mappings, see |g:lists_maps_default_enable|. They can also be overrided +individually with |g:lists_maps_default_override|. The map mode is indicated +by the first letters of the various map commands (|nmap|, |imap|, |omap|, and |xmap|). PLUG DEFAULT MODE~ @@ -129,12 +131,29 @@ CONFIGURATION *lists-config* Default: [] -*g:lists_enable_default_maps* +*g:lists_maps_default_enable* Set to |v:false| or 0 to prevent |lists.vim| from applying the default maps. Useful if you want to create your own mappings. Default: |v:true| +*g:lists_maps_default_override* + A dictionary that allows to override one or more of the default mappings. + The dictionary keys should be one of the available mappings, possibly with + mode indicator. The value should be the desired map target. The available + mappings with default values are listed in |lists-maps-defaults|. + + For example, to remap the insert-mode variant of |(lists-toggle)| to + `` and the normal mode variant of |(lists-toggle)| to ``, + one could do this: > + + let g:lists_maps_default_override = { + \ 'i_(lists-toggle)': '', + \ '(lists-toggle)': '', + \} +< + Default: `{}` + *g:lists_todos* A list of TODO toggles that may be toggled with |(lists-toggle)|, which is by default mapped to ``. diff --git a/plugin/lists.vim b/plugin/lists.vim index 041540d..957e3a7 100644 --- a/plugin/lists.vim +++ b/plugin/lists.vim @@ -9,7 +9,8 @@ let g:loaded_lists = 1 call lists#u#init_option('lists_filetypes', []) -call lists#u#init_option('lists_enable_default_maps', v:true) +call lists#u#init_option('lists_maps_default_enable', v:true) +call lists#u#init_option('lists_maps_default_override', {}) call lists#u#init_option('lists_todos', ['TODO', 'DONE']) command! ListsEnable call lists#init() From 78e8b2c7302f27b4f13956b0d1a54ce777cddd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Wed, 17 Aug 2022 19:31:36 +0200 Subject: [PATCH 13/21] feat: add mapping to insert new bullet This alsos improves the current insert mode toggler. refer: #5 --- autoload/lists.vim | 37 +++++++++++++++++++------------- autoload/lists/item/checkbox.vim | 7 ++++++ autoload/lists/item/general.vim | 5 +++++ doc/lists.txt | 21 +++++++++++++----- test/init.vim | 1 + test/test-newlines.vim | 9 ++++++++ 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 test/test-newlines.vim diff --git a/autoload/lists.vim b/autoload/lists.vim index 1328862..05f7c4b 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -30,7 +30,8 @@ function! lists#init() abort " {{{1 nnoremap (lists-uniq) :ListsUniq nnoremap (lists-uniq-local) :ListsUniqLocal nnoremap (lists-show-item) :ListsShowItem - inoremap (lists-toggle) :call lists#new_item() + inoremap (lists-toggle) :call lists#toggle_item_insertmode() + inoremap (lists-new-element) :call lists#new_item() onoremap (lists-al) :call lists#text_obj#list_element(0, 0) xnoremap (lists-al) :call lists#text_obj#list_element(0, 1) onoremap (lists-il) :call lists#text_obj#list_element(1, 0) @@ -51,11 +52,14 @@ function! lists#init() abort " {{{1 \ '(lists-bullet-toggle-all)': 'wlt', \ '(lists-bullet-toggle-local)': 'wlT', \ 'i_(lists-toggle)': '', + \ 'i_(lists-new-element)': '', \ 'o_(lists-al)': 'al', \ 'x_(lists-al)': 'al', \ 'o_(lists-il)': 'il', \ 'x_(lists-il)': 'il', \}, g:lists_maps_default_override)) + if empty(l:lhs) | continue | endif + if l:rhs[0] !=# '<' let l:mode = l:rhs[0] let l:rhs = l:rhs[2:] @@ -227,31 +231,34 @@ function! lists#show_item(...) abort "{{{1 endfunction " }}}1 -function! lists#new_item() abort "{{{1 +function! lists#toggle_item_insertmode() abort "{{{1 " Go back properly to insert mode - let l:col_last = col('$') - 1 - let l:col_cur = col('.') + let l:col_lineend = col('$') - 1 + let l:col_cursor = col('.') normal! l - " Toggle TODOstate if cursor inside valid todo list item - let l:line = getline('.') - if l:line !~# '^\s*$' + " Toggle TODO-state only when cursor inside valid todo list item + if getline('.') !~# '^\s*$' let [l:root, l:current] = lists#parser#get_current() if !empty(l:current) call l:current.toggle() - let l:col_new = col('$') - 1 endif - " Go back properly to insert mode - if l:col_cur == l:col_last - startinsert! - else - startinsert - endif + endif - return + " Go back properly to insert mode + if l:col_cursor == l:col_lineend + startinsert! + else + startinsert endif +endfunction + +" }}}1 +function! lists#new_item() abort "{{{1 + " Go back properly to insert mode + normal! l " Find last used list item type let [l:root, l:current] = lists#parser#get_previous() diff --git a/autoload/lists/item/checkbox.vim b/autoload/lists/item/checkbox.vim index 69d365e..43e1754 100644 --- a/autoload/lists/item/checkbox.vim +++ b/autoload/lists/item/checkbox.vim @@ -78,3 +78,10 @@ function! s:item.toggle_parents(status) abort dict "{{{1 endfunction " }}}1 + +function! s:item.next_header() abort dict "{{{1 + return substitute(copy(self.header), '^\s*\zs.*', '', '') + \ . matchstr(self.header, '[*-]') . ' [ ] ' +endfunction + +" }}}1 diff --git a/autoload/lists/item/general.vim b/autoload/lists/item/general.vim index 1445044..beed9f9 100644 --- a/autoload/lists/item/general.vim +++ b/autoload/lists/item/general.vim @@ -93,6 +93,11 @@ function! s:item.toggle() abort dict "{{{1 \ . (self.state < 0 ? '' : self.states[self.state] . ':') \ . '\s*\ze' + " Edge case: missing space after bullet + if self.text[0] =~# '\S$' + let self.text[0] .= ' ' + endif + let self.state = ((self.state + 2) % (len(self.states) + 1)) - 1 let l:line = substitute(self.text[0], l:re_old, diff --git a/doc/lists.txt b/doc/lists.txt index 015459c..a13d312 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -71,7 +71,14 @@ The following is a list of the commands and mappings provided by |lists.vim|. *:ListsToggle* *(lists-toggle)* - Toggle a list item. The mapping works in both normal and insert mode. + Toggle a list items "status". For checkboxes, this means to toggle the + checkmark on/off. Elsewhere, we toggle between statuses defined in + |g:lists_todos|. This mapping exists in both normal and insert mode. + +*i_(lists-new-element)* + An insert mode mapping to add a new element in the current list. This is + useful after creating a new line below a list element to add the + bullet/dash/number in front, possibly with a checkbox if relevant. *:ListsToggleBullet* *(lists-bullet-toggle-all)* @@ -114,6 +121,7 @@ by the first letters of the various map commands (|nmap|, |imap|, |omap|, and PLUG DEFAULT MODE~ ---- ------- ----~ |(lists-toggle)| `in` + |i_(lists-new-element)| `i` |(lists-moveup)| wlk `n` |(lists-movedown)| wlj `n` |(lists-uniq)| wlu `n` @@ -143,12 +151,15 @@ CONFIGURATION *lists-config* mode indicator. The value should be the desired map target. The available mappings with default values are listed in |lists-maps-defaults|. - For example, to remap the insert-mode variant of |(lists-toggle)| to - `` and the normal mode variant of |(lists-toggle)| to ``, - one could do this: > + To disable a map, one can link it to an empty string. + + For example, to remap the insert-mode mapping |(lists-new-element)| to + `` and the normal mode variant of |(lists-toggle)| to ``, as + well as disabling the insert mode variant of the latter, one could do this: > let g:lists_maps_default_override = { - \ 'i_(lists-toggle)': '', + \ 'i_(lists-new-element)': '', + \ 'i_(lists-toggle)': '', \ '(lists-toggle)': '', \} < diff --git a/test/init.vim b/test/init.vim index 0d90723..077793f 100644 --- a/test/init.vim +++ b/test/init.vim @@ -3,6 +3,7 @@ let &runtimepath = \ simplify(fnamemodify(expand(''), ':h') . '/..') \ . ',' . &runtimepath set noswapfile +set noshowmode set nomore nnoremap q :qall! diff --git a/test/test-newlines.vim b/test/test-newlines.vim new file mode 100644 index 0000000..696ec26 --- /dev/null +++ b/test/test-newlines.vim @@ -0,0 +1,9 @@ +source init.vim + +silent read examples/lists.txt +ListsEnable + +normal 15GoNew +call assert_equal(' - [x] New', getline(16)) + +call lists#test#finished() From d5b92bdfc181e25e2c036ac1972a2e6dffd42916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 19 Aug 2022 08:57:57 +0200 Subject: [PATCH 14/21] fix: use as default imap for new element The default CTRL-d for de-indent is very useful and we should not overwrite it. --- autoload/lists.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 05f7c4b..c28cf3d 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -52,7 +52,7 @@ function! lists#init() abort " {{{1 \ '(lists-bullet-toggle-all)': 'wlt', \ '(lists-bullet-toggle-local)': 'wlT', \ 'i_(lists-toggle)': '', - \ 'i_(lists-new-element)': '', + \ 'i_(lists-new-element)': '', \ 'o_(lists-al)': 'al', \ 'x_(lists-al)': 'al', \ 'o_(lists-il)': 'il', From a896ae2b3014e3b436fdb5639576fceaac058ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Fri, 19 Aug 2022 08:59:18 +0200 Subject: [PATCH 15/21] doc: use as default imap for new element The default CTRL-d for de-indent is very useful and we should not overwrite it. --- doc/lists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lists.txt b/doc/lists.txt index a13d312..9238e30 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -121,7 +121,7 @@ by the first letters of the various map commands (|nmap|, |imap|, |omap|, and PLUG DEFAULT MODE~ ---- ------- ----~ |(lists-toggle)| `in` - |i_(lists-new-element)| `i` + |i_(lists-new-element)| `i` |(lists-moveup)| wlk `n` |(lists-movedown)| wlj `n` |(lists-uniq)| wlu `n` From 1b03b71d77b67ac507cc87c005322870ffaee8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Thu, 25 Aug 2022 13:58:51 +0200 Subject: [PATCH 16/21] fix: minor regression in todo toggle --- autoload/lists/item/general.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/lists/item/general.vim b/autoload/lists/item/general.vim index beed9f9..a486d0c 100644 --- a/autoload/lists/item/general.vim +++ b/autoload/lists/item/general.vim @@ -94,7 +94,7 @@ function! s:item.toggle() abort dict "{{{1 \ . '\s*\ze' " Edge case: missing space after bullet - if self.text[0] =~# '\S$' + if self.text[0] =~# self.re_item . '$' && self.text[0] =~# '\S$' let self.text[0] .= ' ' endif From 822e623212f48e6afaf068a1adf2dcac1cdaaf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Wed, 15 Feb 2023 23:06:18 +0100 Subject: [PATCH 17/21] fix: lists#new_item should not remove text --- autoload/lists.vim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index c28cf3d..e58f11c 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -272,8 +272,14 @@ function! lists#new_item() abort "{{{1 let l:cur = l:cur.next endwhile - call setline(line('.'), l:cur.next_header()) - startinsert! + let l:line = substitute(getline('.'), '^\s*', '', '') + call setline(line('.'), l:cur.next_header() . l:line) + + if virtcol('.') == virtcol('$') - 1 + startinsert! + else + startinsert + endif endfunction " }}}1 From df32a23a8362642b7effb772a31a904e89e2c790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Tue, 21 Feb 2023 11:14:46 +0100 Subject: [PATCH 18/21] fix: still problems with new_item --- autoload/lists.vim | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index e58f11c..669d093 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -257,14 +257,10 @@ endfunction " }}}1 function! lists#new_item() abort "{{{1 - " Go back properly to insert mode - normal! l - " Find last used list item type let [l:root, l:current] = lists#parser#get_previous() - if empty(l:root) - startinsert - return + if empty(l:root) || l:current.lnum_start == line('.') + return s:resume(1) endif let l:cur = l:root @@ -275,7 +271,15 @@ function! lists#new_item() abort "{{{1 let l:line = substitute(getline('.'), '^\s*', '', '') call setline(line('.'), l:cur.next_header() . l:line) - if virtcol('.') == virtcol('$') - 1 + return s:resume(1 + strchars(l:cur.next_header()) - indent('.')) +endfunction + +function! s:resume(count) abort + for l:_ in range(a:count) + normal! l + endfor + + if virtcol('.') >= virtcol('$') - 1 startinsert! else startinsert From aaafe911bfec1342ee2c5f47695308e3ad413936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Tue, 28 Feb 2023 21:50:50 +0100 Subject: [PATCH 19/21] fix: another attempt at fixing --- .gitignore | 1 + autoload/lists.vim | 24 ++++++++++-------------- test/test-newlines.vim | 11 ++++++++++- 3 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b20c769 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test/issues diff --git a/autoload/lists.vim b/autoload/lists.vim index 669d093..5b97be9 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -258,26 +258,22 @@ endfunction " }}}1 function! lists#new_item() abort "{{{1 " Find last used list item type - let [l:root, l:current] = lists#parser#get_previous() - if empty(l:root) || l:current.lnum_start == line('.') - return s:resume(1) + let [l:root, l:cur] = lists#parser#get_previous() + if empty(l:root) || l:cur.lnum_start == line('.') + return s:resume() endif - let l:cur = l:root - while !empty(l:cur.next) - let l:cur = l:cur.next - endwhile - + let l:pos = virtcol('$') - virtcol('.') let l:line = substitute(getline('.'), '^\s*', '', '') - call setline(line('.'), l:cur.next_header() . l:line) + let l:header = l:cur.next_header() + call setline(line('.'), l:header . l:line) + call cursor(0, max([strchars(l:header), virtcol('$') - l:pos])) - return s:resume(1 + strchars(l:cur.next_header()) - indent('.')) + return s:resume() endfunction -function! s:resume(count) abort - for l:_ in range(a:count) - normal! l - endfor +function! s:resume() abort + normal! l if virtcol('.') >= virtcol('$') - 1 startinsert! diff --git a/test/test-newlines.vim b/test/test-newlines.vim index 696ec26..835990f 100644 --- a/test/test-newlines.vim +++ b/test/test-newlines.vim @@ -3,7 +3,16 @@ source init.vim silent read examples/lists.txt ListsEnable -normal 15GoNew +normal 15GoNew call assert_equal(' - [x] New', getline(16)) +normal 10GoNew +call assert_equal(' - [x] New', getline(11)) + +normal 9GoNew +call assert_equal(' - New', getline(10)) + +normal 9GoNew +call assert_equal(' - New', getline(10)) + call lists#test#finished() From 646abff243c07d09e9423ae618958f026d3a0c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Tue, 28 Mar 2023 09:04:51 +0200 Subject: [PATCH 20/21] chore: minor adjustment --- autoload/lists.vim | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 5b97be9..86f4a7d 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -30,7 +30,7 @@ function! lists#init() abort " {{{1 nnoremap (lists-uniq) :ListsUniq nnoremap (lists-uniq-local) :ListsUniqLocal nnoremap (lists-show-item) :ListsShowItem - inoremap (lists-toggle) :call lists#toggle_item_insertmode() + inoremap (lists-toggle) :call lists#toggle_insertmode() inoremap (lists-new-element) :call lists#new_item() onoremap (lists-al) :call lists#text_obj#list_element(0, 0) xnoremap (lists-al) :call lists#text_obj#list_element(0, 1) @@ -86,6 +86,31 @@ function! lists#toggle(...) abort "{{{1 call l:current.toggle() endfunction +" }}}1 +function! lists#toggle_insertmode() abort "{{{1 + " Go back properly to insert mode + let l:col_lineend = col('$') - 1 + let l:col_cursor = col('.') + normal! l + + " Toggle TODO-state only when cursor inside valid todo list item + if getline('.') !~# '^\s*$' + let [l:root, l:current] = lists#parser#get_current() + + if !empty(l:current) + call l:current.toggle() + endif + + endif + + " Go back properly to insert mode + if l:col_cursor == l:col_lineend + startinsert! + else + startinsert + endif +endfunction + " }}}1 function! lists#move(direction, ...) abort "{{{1 let [l:root, l:current] = a:0 > 0 @@ -230,31 +255,6 @@ function! lists#show_item(...) abort "{{{1 call lists#log#echo(join(l:current.to_string(), "\n")) endfunction -" }}}1 -function! lists#toggle_item_insertmode() abort "{{{1 - " Go back properly to insert mode - let l:col_lineend = col('$') - 1 - let l:col_cursor = col('.') - normal! l - - " Toggle TODO-state only when cursor inside valid todo list item - if getline('.') !~# '^\s*$' - let [l:root, l:current] = lists#parser#get_current() - - if !empty(l:current) - call l:current.toggle() - endif - - endif - - " Go back properly to insert mode - if l:col_cursor == l:col_lineend - startinsert! - else - startinsert - endif -endfunction - " }}}1 function! lists#new_item() abort "{{{1 " Find last used list item type From 33ced550dc7cc9b9025f2b8b5428bee1d32f355c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Tue, 28 Mar 2023 11:16:35 +0200 Subject: [PATCH 21/21] feat: add lists-toggle-checkbox refer: #7 --- autoload/lists.vim | 59 +++++++++++++++++++++++++++---- autoload/lists/item/unordered.vim | 12 +++++++ doc/lists.txt | 6 ++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/autoload/lists.vim b/autoload/lists.vim index 86f4a7d..53806e6 100644 --- a/autoload/lists.vim +++ b/autoload/lists.vim @@ -10,12 +10,13 @@ function! lists#init() abort " {{{1 setlocal comments+=fb:-,f:-\ TODO:,b:-\ [\ ],b:-\ [x] setlocal formatoptions+=ron - command! -buffer ListsMoveUp call lists#move(0) - command! -buffer ListsMoveDown call lists#move(1) - command! -buffer ListsToggle call lists#toggle() - command! -buffer ListsUniq call lists#uniq(0) - command! -buffer ListsUniqLocal call lists#uniq(1) - command! -buffer ListsShowItem call lists#show_item() + command! -buffer ListsMoveUp call lists#move(0) + command! -buffer ListsMoveDown call lists#move(1) + command! -buffer ListsToggle call lists#toggle() + command! -buffer ListsToggleCheckbox call lists#toggle_checkbox() + command! -buffer ListsUniq call lists#uniq(0) + command! -buffer ListsUniqLocal call lists#uniq(1) + command! -buffer ListsShowItem call lists#show_item() command! -buffer -bang ListsToggleBullet \ if empty() | @@ -27,10 +28,12 @@ function! lists#init() abort " {{{1 nnoremap (lists-moveup) :ListsMoveUp nnoremap (lists-movedown) :ListsMoveDown nnoremap (lists-toggle) :ListsToggle + nnoremap (lists-toggle-checkbox) :ListsToggleCheckbox nnoremap (lists-uniq) :ListsUniq nnoremap (lists-uniq-local) :ListsUniqLocal nnoremap (lists-show-item) :ListsShowItem inoremap (lists-toggle) :call lists#toggle_insertmode() + inoremap (lists-toggle-checkbox) :call lists#toggle_checkbox_insertmode() inoremap (lists-new-element) :call lists#new_item() onoremap (lists-al) :call lists#text_obj#list_element(0, 0) xnoremap (lists-al) :call lists#text_obj#list_element(0, 1) @@ -44,6 +47,7 @@ function! lists#init() abort " {{{1 for [l:rhs, l:lhs] in items(extend({ \ '(lists-toggle)': '', + \ '(lists-toggle-checkbox)': '', \ '(lists-moveup)': 'wlk', \ '(lists-movedown)': 'wlj', \ '(lists-uniq)': 'wlu', @@ -52,6 +56,7 @@ function! lists#init() abort " {{{1 \ '(lists-bullet-toggle-all)': 'wlt', \ '(lists-bullet-toggle-local)': 'wlT', \ 'i_(lists-toggle)': '', + \ 'i_(lists-toggle-checkbox)': '', \ 'i_(lists-new-element)': '', \ 'o_(lists-al)': 'al', \ 'x_(lists-al)': 'al', @@ -100,7 +105,48 @@ function! lists#toggle_insertmode() abort "{{{1 if !empty(l:current) call l:current.toggle() endif + endif + + " Go back properly to insert mode + if l:col_cursor == l:col_lineend + startinsert! + else + startinsert + endif +endfunction + +" }}}1 +function! lists#toggle_checkbox(...) abort "{{{1 + let [l:root, l:current] = a:0 > 0 + \ ? lists#parser#get_at(a:1) + \ : lists#parser#get_current() + if empty(l:current) | return | endif + if l:current.type ==# 'checkbox' + call l:current.toggle() + elseif has_key(l:current, 'to_checkbox') + call l:current.to_checkbox() + endif +endfunction + +" }}}1 +function! lists#toggle_checkbox_insertmode() abort "{{{1 + " Go back properly to insert mode + let l:col_lineend = col('$') - 1 + let l:col_cursor = col('.') + normal! l + + " Toggle checkbox-state only when cursor inside valid list item + if getline('.') !~# '^\s*$' + let [l:root, l:current] = lists#parser#get_current() + + if !empty(l:current) + if l:current.type ==# 'checkbox' + call l:current.toggle() + elseif has_key(l:current, 'to_checkbox') + call l:current.to_checkbox() + endif + endif endif " Go back properly to insert mode @@ -112,6 +158,7 @@ function! lists#toggle_insertmode() abort "{{{1 endfunction " }}}1 + function! lists#move(direction, ...) abort "{{{1 let [l:root, l:current] = a:0 > 0 \ ? lists#parser#get_at(a:1) diff --git a/autoload/lists/item/unordered.vim b/autoload/lists/item/unordered.vim index ebb9a90..c2a03ce 100644 --- a/autoload/lists/item/unordered.vim +++ b/autoload/lists/item/unordered.vim @@ -38,3 +38,15 @@ function! s:item.set_bullet(new) abort dict "{{{1 endfunction " }}}1 +function! s:item.to_checkbox() abort dict "{{{1 + " Edge case: missing space after bullet + if self.text[0] =~# self.re_item . '$' && self.text[0] =~# '\S$' + let self.text[0] .= ' ' + endif + + let l:line = substitute(self.text[0], '^\s*[*-]\s\zs', '[ ] ', '') + + call setline(self.lnum_start, l:line) +endfunction + +" }}}1 diff --git a/doc/lists.txt b/doc/lists.txt index 9238e30..a5a7910 100644 --- a/doc/lists.txt +++ b/doc/lists.txt @@ -75,6 +75,11 @@ The following is a list of the commands and mappings provided by |lists.vim|. checkmark on/off. Elsewhere, we toggle between statuses defined in |g:lists_todos|. This mapping exists in both normal and insert mode. +*:ListsToggleCheckbox* +*(lists-toggle-checkbox)* + Toggle a checklist item. If the current item does not have a checkbox, it + will be added. This mapping exists in both normal and insert mode. + *i_(lists-new-element)* An insert mode mapping to add a new element in the current list. This is useful after creating a new line below a list element to add the @@ -121,6 +126,7 @@ by the first letters of the various map commands (|nmap|, |imap|, |omap|, and PLUG DEFAULT MODE~ ---- ------- ----~ |(lists-toggle)| `in` + |(lists-toggle-checkbox)| NONE `in` |i_(lists-new-element)| `i` |(lists-moveup)| wlk `n` |(lists-movedown)| wlj `n`