From 9f14ae09744f7e7a38a86a9e4652638cca14fb8c Mon Sep 17 00:00:00 2001 From: adolenc Date: Sat, 22 Feb 2020 02:55:15 +0100 Subject: [PATCH 1/3] First support for n/l motions with targets.vim --- Makefile | 3 +- autoload/textobj/toplevel.vim | 54 +++++++++++++++++++++++++++++++++++ plugin/textobj/toplevel.vim | 4 +++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 58ac68d..c5236a2 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ .PHONY: test-dependencies tests tests: - dependencies/vim-vspec/bin/prove-vspec -d . -d dependencies/vim-textobj-user + dependencies/vim-vspec/bin/prove-vspec -d . -d dependencies/vim-textobj-user -d dependencies/targets.vim test-dependencies: -mkdir dependencies -git clone https://github.com/kana/vim-vspec dependencies/vim-vspec -git clone https://github.com/kana/vim-textobj-user dependencies/vim-textobj-user + -git clone https://github.com/wellle/targets.vim dependencies/targets.vim diff --git a/autoload/textobj/toplevel.vim b/autoload/textobj/toplevel.vim index ec8cdf7..19c7601 100644 --- a/autoload/textobj/toplevel.vim +++ b/autoload/textobj/toplevel.vim @@ -10,6 +10,60 @@ function! textobj#toplevel#select_a() endfunction +function! textobj#toplevel#target_new(args) + return { + \ 'args': { + \ 'count': get(a:args, 'c', 1), + \ }, + \ 'genFuncs': { + \ 'c': function('textobj#toplevel#target_current'), + \ 'n': function('textobj#toplevel#target_next'), + \ 'l': function('textobj#toplevel#target_last'), + \ }, + \ 'modFuncs': { + \ 'i': function('textobj#toplevel#modify_to_linewise'), + \ 'a': function('textobj#toplevel#select_trailing_whitespace'), + \ }} +endfunction + +function! textobj#toplevel#target_current(args, opts, state) + if !a:opts.first | return | endif + + return s:encode_targets(s:select_in_toplevel(line('.'))) +endfunction + +function! textobj#toplevel#target_next(args, opts, state) + let start = s:find_toplevel_end(line('.')) + let start = nextnonblank(s:below(start)) + + return s:encode_targets(s:select_in_toplevel(start)) +endfunction + +function! textobj#toplevel#target_last(args, opts, state) + let cursor_pos = line('.') + let [start, end] = s:select_in_toplevel(cursor_pos) + if (start <= cursor_pos && cursor_pos <= end) + return s:encode_targets(s:select_in_toplevel(s:above(start))) + else + return s:encode_targets([start, end]) + endif +endfunction + +function! s:encode_targets(range) + return [a:range[0], 1, + \a:range[1], len(getline(a:range[1])] +endfunction + +function! textobj#toplevel#modify_to_linewise(target, args) + let a:target.linewise = 1 +endfunction + +function! textobj#toplevel#select_trailing_whitespace(target, args) + let a:target.linewise = 1 + let [a:target.sl, a:target.el] = s:select_surrounding_blank_lines([a:target.sl, a:target.el]) +endfunction + + function! s:encode_linerange(range) let [start_linenr, end_linenr] = a:range return ['V', diff --git a/plugin/textobj/toplevel.vim b/plugin/textobj/toplevel.vim index 12d0577..21cb736 100644 --- a/plugin/textobj/toplevel.vim +++ b/plugin/textobj/toplevel.vim @@ -9,4 +9,8 @@ call textobj#user#plugin('toplevel', { \ } \ }) + +autocmd User targets#sources call targets#sources#register('toplevel', function('textobj#toplevel#target_new')) +autocmd User targets#mappings#plugin call targets#mappings#extend({'T': {'toplevel': [{'c': 1}]}}) + let g:loaded_textobj_toplevel = 1 From 94df0e950849dcc084641205cf71af25698930b6 Mon Sep 17 00:00:00 2001 From: adolenc Date: Sun, 23 Feb 2020 22:53:45 +0100 Subject: [PATCH 2/3] Implement basic tests for target motions --- autoload/textobj/toplevel.vim | 2 +- t/basics.vim | 1 - t/common.vim | 10 ++--- t/target-toplevel.vim | 72 +++++++++++++++++++++++++++++++++++ t/textobj-toplevel.vim | 51 ++++++++++++------------- 5 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 t/target-toplevel.vim diff --git a/autoload/textobj/toplevel.vim b/autoload/textobj/toplevel.vim index 19c7601..eaed010 100644 --- a/autoload/textobj/toplevel.vim +++ b/autoload/textobj/toplevel.vim @@ -51,7 +51,7 @@ endfunction function! s:encode_targets(range) return [a:range[0], 1, - \a:range[1], len(getline(a:range[1])] + \ a:range[1], len(getline(a:range[1])) + 1] endfunction function! textobj#toplevel#modify_to_linewise(target, args) diff --git a/t/basics.vim b/t/basics.vim index 95d09f4..a3f5ee7 100644 --- a/t/basics.vim +++ b/t/basics.vim @@ -1,5 +1,4 @@ runtime! plugin/textobj/*.vim -version describe 'The plugin' diff --git a/t/common.vim b/t/common.vim index da5e94b..7d167b6 100644 --- a/t/common.vim +++ b/t/common.vim @@ -1,12 +1,8 @@ -function! ExecuteViTFromLine(linenr) - execute "normal! " . a:linenr . "G" - execute "normal V\(textobj-toplevel-i)\" - return [line("'<"), line("'>")] -endfunction +runtime! plugin/textobj/*.vim -function! ExecuteVaTFromLine(linenr) +function! ExecuteTextObjFromLine(linenr, modifier) execute "normal! " . a:linenr . "G" - execute "normal V\(textobj-toplevel-a)\" + execute "normal V" . a:modifier . "T\" return [line("'<"), line("'>")] endfunction diff --git a/t/target-toplevel.vim b/t/target-toplevel.vim new file mode 100644 index 0000000..ac61088 --- /dev/null +++ b/t/target-toplevel.vim @@ -0,0 +1,72 @@ +source t/common.vim +runtime! plugin/targets.vim + + +describe 'Target n modifiers' + before + call SetBufferContents([ + \ 'import lib1', + \ 'import lib2', + \ '', + \ '', + \ '@cache', + \ 'def fn(i):', + \ '', + \ ' \"ok\"', + \ '', + \ ' return fn(i-1)', + \ '', + \ '@cache', + \ 'def test2(i):', + \ ' \"ok\"', + \ ' return test2(i-1)', + \ ]) + end + + it 'in selects first function from first imports' + Expect ExecuteTextObjFromLine(1, 'in') to_select_lines [5, 10] + Expect ExecuteTextObjFromLine(2, 'in') to_select_lines [5, 10] + end + + it 'in selects second function from first function' + for linenr in range(5, 10) + Expect ExecuteTextObjFromLine(linenr, 'in') to_select_lines [12, 15] + endfor + end +end + +describe 'Target l modifiers' + before + call SetBufferContents([ + \ 'import lib1', + \ 'import lib2', + \ '', + \ '', + \ '@cache', + \ 'def fn(i):', + \ '', + \ ' \"ok\"', + \ '', + \ ' return fn(i-1)', + \ '', + \ '@cache', + \ 'def test2(i):', + \ ' \"ok\"', + \ ' return test2(i-1)', + \ '', + \ 'fn(2)', + \ ]) + end + + it 'il selects imports from first function' + for linenr in range(5, 10) + Expect ExecuteTextObjFromLine(linenr, 'il') to_select_lines [1, 2] + endfor + end + + it 'il selects first function from second function' + for linenr in range(12, 15) + Expect ExecuteTextObjFromLine(linenr, 'il') to_select_lines [5, 10] + endfor + end +end diff --git a/t/textobj-toplevel.vim b/t/textobj-toplevel.vim index 6019304..12fe216 100644 --- a/t/textobj-toplevel.vim +++ b/t/textobj-toplevel.vim @@ -1,5 +1,4 @@ source t/common.vim -source plugin/textobj/*.vim describe 'Two groups of imports' @@ -17,12 +16,12 @@ describe 'Two groups of imports' end it 'selects the first group of imports' - Expect ExecuteViTFromLine(3) to_select_lines [3, 4] - Expect ExecuteViTFromLine(4) to_select_lines [3, 4] + Expect ExecuteTextObjFromLine(3, 'i') to_select_lines [3, 4] + Expect ExecuteTextObjFromLine(4, 'i') to_select_lines [3, 4] end it 'selects the third import' - Expect ExecuteViTFromLine(7) to_select_lines [7, 7] + Expect ExecuteTextObjFromLine(7, 'i') to_select_lines [7, 7] end end @@ -50,24 +49,24 @@ describe 'Python example' end it 'selects the correct individual import' - Expect ExecuteViTFromLine(1) to_select_lines [1, 2] - Expect ExecuteViTFromLine(2) to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(1, 'i') to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(2, 'i') to_select_lines [1, 2] end it 'selects the entire first function' for linenr in range(5, 10) - Expect ExecuteViTFromLine(linenr) to_select_lines [5, 10] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [5, 10] endfor end it 'selects the entire second function' for linenr in range(12, 15) - Expect ExecuteViTFromLine(linenr) to_select_lines [12, 15] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [12, 15] endfor end it 'selects the invocation of a function' - Expect ExecuteViTFromLine(17) to_select_lines [17, 17] + Expect ExecuteTextObjFromLine(17, 'i') to_select_lines [17, 17] end end @@ -98,19 +97,19 @@ describe 'C++ example' end it 'selects the two includes' - Expect ExecuteViTFromLine(1) to_select_lines [1, 2] - Expect ExecuteViTFromLine(2) to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(1, 'i') to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(2, 'i') to_select_lines [1, 2] end it 'selects the entire first function' for linenr in range(5, 11) - Expect ExecuteViTFromLine(linenr) to_select_lines [5, 11] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [5, 11] endfor end it 'selects the entire second function' for linenr in range(13, 20) - Expect ExecuteViTFromLine(linenr) to_select_lines [13, 20] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [13, 20] endfor end end @@ -143,36 +142,36 @@ describe 'Readme example' it 'selects the entire first function' for linenr in range(1, 7) - Expect ExecuteViTFromLine(linenr) to_select_lines [1, 7] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [1, 7] endfor end it 'selects the entire second function' for linenr in range(9, 15) - Expect ExecuteViTFromLine(linenr) to_select_lines [9, 15] + Expect ExecuteTextObjFromLine(linenr, 'i') to_select_lines [9, 15] endfor end it 'selects the invocation of a function' - Expect ExecuteViTFromLine(18) to_select_lines [18, 19] - Expect ExecuteViTFromLine(19) to_select_lines [18, 19] + Expect ExecuteTextObjFromLine(18, 'i') to_select_lines [18, 19] + Expect ExecuteTextObjFromLine(19, 'i') to_select_lines [18, 19] end it 'selects the trailing whitespace after first function' for linenr in range(1, 7) - Expect ExecuteVaTFromLine(linenr) to_select_lines [1, 8] + Expect ExecuteTextObjFromLine(linenr, 'a') to_select_lines [1, 8] endfor end it 'selects the trailing whitespace after second function' for linenr in range(9, 17) - Expect ExecuteVaTFromLine(linenr) to_select_lines [9, 17] + Expect ExecuteTextObjFromLine(linenr, 'a') to_select_lines [9, 17] endfor end it 'selects the preceding whitespace before the two function calls' - Expect ExecuteVaTFromLine(18) to_select_lines [16, 19] - Expect ExecuteVaTFromLine(19) to_select_lines [16, 19] + Expect ExecuteTextObjFromLine(18, 'a') to_select_lines [16, 19] + Expect ExecuteTextObjFromLine(19, 'a') to_select_lines [16, 19] end end @@ -189,8 +188,8 @@ describe 'Edgecase with only indented lines' end it 'selects all the lines until the top of the file' - Expect ExecuteViTFromLine(3) to_select_lines [1, 4] - Expect ExecuteViTFromLine(4) to_select_lines [1, 4] + Expect ExecuteTextObjFromLine(3, 'i') to_select_lines [1, 4] + Expect ExecuteTextObjFromLine(4, 'i') to_select_lines [1, 4] end end @@ -202,7 +201,7 @@ describe 'Edgecase with blank file' end it 'selects the blank line' - Expect ExecuteViTFromLine(1) to_select_lines [1, 1] + Expect ExecuteTextObjFromLine(1, 'i') to_select_lines [1, 1] end end @@ -215,7 +214,7 @@ describe 'Edgecase with only unindented lines' end it 'selects all unindented lines' - Expect ExecuteViTFromLine(1) to_select_lines [1, 2] - Expect ExecuteViTFromLine(2) to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(1, 'i') to_select_lines [1, 2] + Expect ExecuteTextObjFromLine(2, 'i') to_select_lines [1, 2] end end From a557c5bd4373b672d8e5fb4a6e7a76aaf9f751c1 Mon Sep 17 00:00:00 2001 From: adolenc Date: Sun, 23 Feb 2020 23:03:23 +0100 Subject: [PATCH 3/3] Drop the dummy "count" argument from targets mapping --- autoload/textobj/toplevel.vim | 3 --- plugin/textobj/toplevel.vim | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/autoload/textobj/toplevel.vim b/autoload/textobj/toplevel.vim index eaed010..eb7eedc 100644 --- a/autoload/textobj/toplevel.vim +++ b/autoload/textobj/toplevel.vim @@ -12,9 +12,6 @@ endfunction function! textobj#toplevel#target_new(args) return { - \ 'args': { - \ 'count': get(a:args, 'c', 1), - \ }, \ 'genFuncs': { \ 'c': function('textobj#toplevel#target_current'), \ 'n': function('textobj#toplevel#target_next'), diff --git a/plugin/textobj/toplevel.vim b/plugin/textobj/toplevel.vim index 21cb736..ca4ebab 100644 --- a/plugin/textobj/toplevel.vim +++ b/plugin/textobj/toplevel.vim @@ -11,6 +11,6 @@ call textobj#user#plugin('toplevel', { autocmd User targets#sources call targets#sources#register('toplevel', function('textobj#toplevel#target_new')) -autocmd User targets#mappings#plugin call targets#mappings#extend({'T': {'toplevel': [{'c': 1}]}}) +autocmd User targets#mappings#plugin call targets#mappings#extend({'T': {'toplevel': [{}]}}) let g:loaded_textobj_toplevel = 1