├── tests ├── t5020-PrintMatches-range.msgok ├── t5070-PrintMatches-closed-fold.msgok ├── t5040-PrintMatches-replacement.msgok ├── _setup.vim ├── t5060-PrintMatches-replace-expression.msgok ├── t5210-PrintUniqueMatches-predicate.msgok ├── t5100-PrintUniqueMatches.msgok ├── t3000-PutMatches.vim ├── t3100-PutUniqueMatches.vim ├── t5000-PrintMatches.msgok ├── t3020-PutMatches-first-match.vim ├── t3030-PutMatches-last-search.vim ├── t5040-PrintMatches-replacement.vim ├── t3060-PutMatches-replace-expressions.vim ├── t5070-PrintMatches-closed-fold.vim ├── t3200-PutMatches-predicate.vim ├── t3040-PutMatches-replacement.vim ├── helpers │ └── predicates.vim ├── t5020-PrintMatches-range.vim ├── text.txt ├── t3210-PutUniqueMatches-predicate.vim ├── t4060-SubstituteAndYank-DeleteMatches.ok ├── t5010-PrintMatches-no-matches.vim ├── t5050-PrintMatches-replacement-special.msgok ├── t2020-YankMatches-range.vim ├── t4040-SubstituteAndYank-no-matches.ok ├── t4000-SubstituteAndYank.ok ├── t4050-SubstituteAndYank-range.ok ├── t3030-PutMatches-last-search.ok ├── t3100-PutUniqueMatches.ok ├── t4080-SubstituteAndYank-closed-fold.ok ├── t1020-GrepToReg-range.vim ├── t4100-SubstituteAndYankUnique.ok ├── t3020-PutMatches-first-match.ok ├── t3060-PutMatches-replace-expressions.ok ├── t3210-PutUniqueMatches-predicate.ok ├── t4020-SubstituteAndYank-replace-expression.ok ├── t4030-SubstituteAndYank-special-numbering.ok ├── t3000-PutMatches.ok ├── t3040-PutMatches-replacement.ok ├── t3050-PutMatches-replacement-specials.ok ├── t3050-PutMatches-replacement-specials.vim ├── t4010-SubstituteAndYank-specials.ok ├── t2040-YankMatches-replacement.vim ├── t5060-PrintMatches-replace-expression.vim ├── t1010-GrepToReg-no-matches.vim ├── t2010-YankMatches-no-matches.vim ├── t5100-PrintUniqueMatches.vim ├── t1110-GrepRangeToReg-no-matches.vim ├── t1050-GrepToReg-closed-fold.vim ├── t2070-YankMatches-replacement-verymagic-zs.vim ├── t2080-YankMatches-closed-fold.vim ├── t5210-PrintUniqueMatches-predicate.vim ├── t3200-PutMatches-predicate.ok ├── t4010-SubstituteAndYank-specials.vim ├── t4080-SubstituteAndYank-closed-fold.vim ├── t3010-PutMatches-no-matches.vim ├── t5000-PrintMatches.vim ├── t1140-GrepRangeToReg-multiline-match.vim ├── t4040-SubstituteAndYank-no-matches.vim ├── t1030-GrepToReg-last.vim ├── t4060-SubstituteAndYank-DeleteMatches.vim ├── longer.txt ├── t2071-YankMatches-replacement-ic-zs.vim ├── t4050-SubstituteAndYank-range.vim ├── t5200-PrintMatches-predicate.msgok ├── t5050-PrintMatches-replacement-special.vim ├── t2060-YankMatches-replace-expression.vim ├── t2210-YankUniqueMatches-predicate.vim ├── t4030-SubstituteAndYank-special-numbering.vim ├── t2100-YankUniqueMatches.vim ├── t1115-GrepRangeToReg-error.vim ├── t2000-YankMatches.vim ├── t4000-SubstituteAndYank.vim ├── t1130-GrepRangeToReg-range.vim ├── t4020-SubstituteAndYank-replace-expression.vim ├── t2050-YankMatches-replacement-special.vim ├── t1120-GrepRangeToReg-overlapping.vim ├── t4100-SubstituteAndYankUnique.vim ├── t1000-GrepToReg.vim ├── t5200-PrintMatches-predicate.vim ├── t1100-GrepRangeToReg.vim └── t2200-YankMatches-predicate.vim ├── .gitignore ├── ExtractMatches.manifest ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── plugin └── ExtractMatches.vim ├── doc └── ExtractMatches.txt ├── README.md └── autoload └── ExtractMatches.vim /tests/t5020-PrintMatches-range.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo 3 | end 4 | -------------------------------------------------------------------------------- /tests/t5070-PrintMatches-closed-fold.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo 3 | foo 4 | foo 5 | fox 6 | for 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.msgout 2 | *.msgresult 3 | *.out 4 | *.tap 5 | doc/*.description 6 | doc/*.install 7 | tags 8 | -------------------------------------------------------------------------------- /tests/t5040-PrintMatches-replacement.msgok: -------------------------------------------------------------------------------- 1 | start 2 | "fooo", "fooo", "fooo", "foxx", "forr", 3 | end 4 | -------------------------------------------------------------------------------- /tests/_setup.vim: -------------------------------------------------------------------------------- 1 | call vimtest#AddDependency('vim-ingo-library') 2 | 3 | runtime plugin/ExtractMatches.vim 4 | -------------------------------------------------------------------------------- /ExtractMatches.manifest: -------------------------------------------------------------------------------- 1 | autoload/ExtractMatches.vim 2 | plugin/ExtractMatches.vim 3 | doc/ExtractMatches.txt 4 | -------------------------------------------------------------------------------- /tests/t5060-PrintMatches-replace-expression.msgok: -------------------------------------------------------------------------------- 1 | start 2 | 3 | end 4 | 5 | start 6 | fooo, fooo, fooo, foxx, forr, 7 | end 8 | -------------------------------------------------------------------------------- /tests/t5210-PrintUniqueMatches-predicate.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo 3 | bar 4 | cum 5 | quo 6 | end 7 | 8 | start 9 | ffoo-lloquor-ffolio- 10 | end 11 | -------------------------------------------------------------------------------- /tests/t5100-PrintUniqueMatches.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo 3 | fox 4 | for 5 | end 6 | 7 | start 8 | foo 9 | fox 10 | end 11 | 12 | start 13 | in 14 | end 15 | -------------------------------------------------------------------------------- /tests/t3000-PutMatches.vim: -------------------------------------------------------------------------------- 1 | " Test putting all matches. 2 | 3 | edit text.txt 4 | 1 5 | $PutMatches/\/ 6 | 7 | call vimtest#SaveOut() 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t3100-PutUniqueMatches.vim: -------------------------------------------------------------------------------- 1 | " Test putting all unique matches. 2 | 3 | edit text.txt 4 | 1 5 | $PutUniqueMatches/\/ 6 | 7 | call vimtest#SaveOut() 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t5000-PrintMatches.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo 3 | foo 4 | foo 5 | fox 6 | for 7 | end 8 | 9 | start 10 | foo 11 | foo 12 | fox 13 | end 14 | 15 | start 16 | vehementi 17 | end 18 | -------------------------------------------------------------------------------- /tests/t3020-PutMatches-first-match.vim: -------------------------------------------------------------------------------- 1 | " Test putting the first match in each line. 2 | 3 | edit text.txt 4 | 1 5 | $PutMatches!/\/ 6 | 7 | call vimtest#SaveOut() 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t3030-PutMatches-last-search.vim: -------------------------------------------------------------------------------- 1 | " Test putting all matches of the last search pattern. 2 | 3 | edit text.txt 4 | 1 5 | let @/ = '\/"&\1", / 7 | echomsg 'end' 8 | 9 | call vimtest#Quit() 10 | -------------------------------------------------------------------------------- /tests/t3060-PutMatches-replace-expressions.vim: -------------------------------------------------------------------------------- 1 | " Test putting first matches with replace expressions. 2 | 3 | edit text.txt 4 | 1 5 | $PutMatches!/\/\='<' . submatch(0) . '>'/ 6 | 7 | call vimtest#SaveOut() 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t5070-PrintMatches-closed-fold.vim: -------------------------------------------------------------------------------- 1 | " Test printing all matches within a single closed fold. 2 | 3 | edit text.txt 4 | %fold 5 | 6 | echomsg 'start' 7 | 3PrintMatches/\/ 8 | echomsg 'end' 9 | 10 | call vimtest#Quit() 11 | -------------------------------------------------------------------------------- /tests/t3200-PutMatches-predicate.vim: -------------------------------------------------------------------------------- 1 | " Test putting matches with predicate. 2 | 3 | source helpers/predicates.vim 4 | edit text.txt 5 | 1 6 | $PutMatches/\<\w\{3}\>/MyContextPredicate(v:val) 7 | 8 | call vimtest#SaveOut() 9 | call vimtest#Quit() 10 | -------------------------------------------------------------------------------- /tests/t3040-PutMatches-replacement.vim: -------------------------------------------------------------------------------- 1 | " Test putting all matches with replacement. 2 | " Tests that matches are concatenated in one line. 3 | 4 | edit text.txt 5 | 1 6 | $PutMatches/\/"&\1", / 7 | 8 | call vimtest#SaveOut() 9 | call vimtest#Quit() 10 | -------------------------------------------------------------------------------- /tests/helpers/predicates.vim: -------------------------------------------------------------------------------- 1 | let g:toggle = 0 2 | function! MyPredicate() abort 3 | let g:toggle = ! g:toggle 4 | return g:toggle 5 | endfunction 6 | 7 | function! MyContextPredicate( context ) abort 8 | return (a:context.matchStart[1] > 9) 9 | endfunction 10 | -------------------------------------------------------------------------------- /tests/t5020-PrintMatches-range.vim: -------------------------------------------------------------------------------- 1 | " Test printing all matches within a range. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | 2,4PrintMatches/\c\/ 7 | echomsg 'end' 8 | call vimtap#Is(@@, "foo\n", 'Yank matches to default register') 9 | 10 | call vimtest#Quit() 11 | -------------------------------------------------------------------------------- /tests/text.txt: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t3210-PutUniqueMatches-predicate.vim: -------------------------------------------------------------------------------- 1 | " Test putting unique matches with predicate. 2 | 3 | edit text.txt 4 | 1 5 | $PutUniqueMatches/\<\([fl]\)\w\+\>/\1&-/ len(ingo#str#trcd(v:val.replacement, 'o')) > 1 && v:val.acceptedCount < 3 6 | 7 | call vimtest#SaveOut() 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t4060-SubstituteAndYank-DeleteMatches.ok: -------------------------------------------------------------------------------- 1 | Estuans interius vehementi in bar loquor menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t5010-PrintMatches-no-matches.vim: -------------------------------------------------------------------------------- 1 | " Test error when the pattern doesn't match. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | edit text.txt 6 | call vimtap#err#ErrorsLike('^E486: .*: doesNotExist', 'PrintMatches/doesNotExist/', 'Pattern not found error shown') 7 | 8 | call vimtest#Quit() 9 | -------------------------------------------------------------------------------- /tests/t5050-PrintMatches-replacement-special.msgok: -------------------------------------------------------------------------------- 1 | start 2 | foo;;foo;;foo;;fox;;for 3 | end 4 | 5 | start 6 | foo;;foo;;foo;;fox;;for;; 7 | end 8 | 9 | start 10 | foo 11 | foo 12 | foo 13 | fox 14 | for 15 | end 16 | 17 | start 18 | 19 | end 20 | 21 | start 22 | 23 | end 24 | -------------------------------------------------------------------------------- /tests/t2020-YankMatches-range.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matches within a range. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | 2,4YankMatches/\c\/ 9 | call vimtap#Is(@@, "foo\n", 'Yank matches to default register') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t4040-SubstituteAndYank-no-matches.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t4000-SubstituteAndYank.ok: -------------------------------------------------------------------------------- 1 | Estuans interius [vehementi] in bar loquor mentiti: factus de 2 | materia, cinisis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit . 4 | Stultus , bar and baz and quux. 5 | comparor Foo flu labenti, sub eodemem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t4050-SubstituteAndYank-range.ok: -------------------------------------------------------------------------------- 1 | Estuans interius vehementi in bar loquor menti: factus de 2 | materia, cinis elementi similis [cum] folio, de quo ludunt venti. 3 | [Cum] sit foo. 4 | Stultus [fox], for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t3030-PutMatches-last-search.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | vehementi 7 | -------------------------------------------------------------------------------- /tests/t3100-PutUniqueMatches.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | foo 7 | fox 8 | for 9 | -------------------------------------------------------------------------------- /tests/t4080-SubstituteAndYank-closed-fold.ok: -------------------------------------------------------------------------------- 1 | Estuans interius vehementi in bar loquor menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit . 4 | Stultus , bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t1020-GrepToReg-range.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matching lines within a range. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | 2,4GrepToReg/\/ 9 | call vimtap#Is(@@, "Cum sit foo.\n", 'Yank matching line to default register') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t4100-SubstituteAndYankUnique.ok: -------------------------------------------------------------------------------- 1 | Estuans interius [vehementi] in bar loquor mentiti: factus de 2 | materia, cinisis elementi similis cum folioio, de quo ludunt ventiti. 3 | Cum sit . 4 | Stultus , bar and baz and quux. 5 | comparor Foo flu labenti, sub eodemem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t3020-PutMatches-first-match.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | foo 7 | foo 8 | fox 9 | -------------------------------------------------------------------------------- /tests/t3060-PutMatches-replace-expressions.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | 7 | -------------------------------------------------------------------------------- /tests/t3210-PutUniqueMatches-predicate.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | ffoo-lloquor-ffolio- 7 | -------------------------------------------------------------------------------- /tests/t4020-SubstituteAndYank-replace-expression.ok: -------------------------------------------------------------------------------- 1 | Estuans interius [vehementi] in bar loquor mentiti: factus de 2 | materia, cinisis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit . 4 | Stultus , bar and baz and quux. 5 | comparor Foo flu labenti, sub eodemem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t4030-SubstituteAndYank-special-numbering.ok: -------------------------------------------------------------------------------- 1 | Estuans interius 1:foo vehementi in bar loquor 2:foo 0>menti: factus de 2 | materia, 1>cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit 3:foo. 4 | Stultus 4:fox, 5:for bar and baz and quux. 5 | comparor Foo flu labenti, sub 2>eodem foobar tramite nunquam permanenti. 6 | -------------------------------------------------------------------------------- /tests/t3000-PutMatches.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | foo 7 | foo 8 | foo 9 | fox 10 | for 11 | -------------------------------------------------------------------------------- /tests/t3040-PutMatches-replacement.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | "fooo", "fooo", "fooo", "foxx", "forr", 7 | -------------------------------------------------------------------------------- /tests/t3050-PutMatches-replacement-specials.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | foo;;foo;;fox 7 | menti;;cinis;;eodem;; 8 | -------------------------------------------------------------------------------- /tests/t3050-PutMatches-replacement-specials.vim: -------------------------------------------------------------------------------- 1 | " Test putting first matches with replacement specials. 2 | " Tests that a literal &;; separator is removed from the last element, but \0 is 3 | " not. 4 | 5 | edit text.txt 6 | 1 7 | $PutMatches!/\/&;;/ 8 | $PutMatches!/\<.....\>/\0;;/ 9 | 10 | call vimtest#SaveOut() 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t4010-SubstituteAndYank-specials.ok: -------------------------------------------------------------------------------- 1 | Estuans interius 2 | vehementi in bar loquor 3 | menti: factus de 4 | materia, cinis elementi similis cum folio, de quo ludunt venti. 5 | Cum sit 6 | . 7 | Stultus 8 | , 9 | bar and baz and quux. 10 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 11 | -------------------------------------------------------------------------------- /tests/t2040-YankMatches-replacement.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matches with replacement. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | YankMatches/\/"&\1", / 9 | call vimtap#Is(@@, '"fooo", "fooo", "fooo", "foxx", "forr", ', 'Yank replaced matches to default register') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t5060-PrintMatches-replace-expression.vim: -------------------------------------------------------------------------------- 1 | " Test printing all matches with replace expression. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | PrintMatches/\/\='<' . submatch(0) . '>'/ 7 | echomsg 'end' 8 | 9 | echomsg 'start' 10 | PrintMatches/\/\=submatch(0) . submatch(1) . ', '/ 11 | echomsg 'end' 12 | 13 | call vimtest#Quit() 14 | -------------------------------------------------------------------------------- /tests/t1010-GrepToReg-no-matches.vim: -------------------------------------------------------------------------------- 1 | " Test error when the pattern doesn't match. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @a = '' 6 | edit text.txt 7 | call vimtap#err#ErrorsLike('^E486: .*: doesNotExist', 'GrepToReg/doesNotExist/a', 'Pattern not found error shown') 8 | call vimtap#Is(@a, '', 'Register unchanged') 9 | 10 | call vimtest#Quit() 11 | -------------------------------------------------------------------------------- /tests/t2010-YankMatches-no-matches.vim: -------------------------------------------------------------------------------- 1 | " Test error when the pattern doesn't match. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @a = '' 6 | edit text.txt 7 | call vimtap#err#ErrorsLike('^E486: .*: doesNotExist', 'YankMatches/doesNotExist/a', 'Pattern not found error shown') 8 | call vimtap#Is(@a, '', 'Register unchanged') 9 | 10 | call vimtest#Quit() 11 | -------------------------------------------------------------------------------- /tests/t5100-PrintUniqueMatches.vim: -------------------------------------------------------------------------------- 1 | " Test printing all unique matches. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | PrintUniqueMatches/\/ 7 | echomsg 'end' 8 | 9 | echomsg 'start' 10 | PrintUniqueMatches!/\/ 11 | echomsg 'end' 12 | 13 | let @/ = '\/ 10 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nCum sit foo.\n", 'Yank matching lines to default register') 11 | 12 | call vimtest#Quit() 13 | -------------------------------------------------------------------------------- /tests/t2070-YankMatches-replacement-verymagic-zs.vim: -------------------------------------------------------------------------------- 1 | " Test yanking with a replace expression that changes to very magic and uses match limiting. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | YankMatches/\v \zsqu+.>/<&>/ 9 | call vimtap#Is(@@, "", 'Yank \v..\zs replaced matches to default register') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t2080-YankMatches-closed-fold.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matches within a single closed fold. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | edit text.txt 7 | %fold 8 | 9 | 3YankMatches/\/ 10 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor\n", 'Yank matches to default register') 11 | call vimtap#Ok(! &modified, 'Buffer not modified') 12 | 13 | call vimtest#Quit() 14 | -------------------------------------------------------------------------------- /tests/t5210-PrintUniqueMatches-predicate.vim: -------------------------------------------------------------------------------- 1 | " Test printing unique matches with predicate. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | PrintUniqueMatches/\<\w\{3}\>/v:val.matchCount < 5 7 | echomsg 'end' 8 | 9 | echomsg 'start' 10 | PrintUniqueMatches/\<\([fl]\)\w\+\>/\1&-/len(ingo#str#trcd(v:val.replacement, 'o')) > 1 && v:val.acceptedCount < 3 11 | echomsg 'end' 12 | 13 | call vimtest#Quit() 14 | -------------------------------------------------------------------------------- /tests/t3200-PutMatches-predicate.ok: -------------------------------------------------------------------------------- 1 | Estuans interius foo vehementi in bar loquor foo menti: factus de 2 | materia, cinis elementi similis cum folio, de quo ludunt venti. 3 | Cum sit foo. 4 | Stultus fox, for bar and baz and quux. 5 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 6 | foo 7 | bar 8 | foo 9 | cum 10 | quo 11 | for 12 | bar 13 | and 14 | baz 15 | and 16 | Foo 17 | flu 18 | sub 19 | -------------------------------------------------------------------------------- /tests/t4010-SubstituteAndYank-specials.vim: -------------------------------------------------------------------------------- 1 | " Test substituting and yanking with specials. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | %SubstituteAndYank/\/\t<&>\n/g/\t(&)\n/ 9 | call vimtap#Is(@@, "\t(foo)\n\t(foo)\n\t(foo)\n\t(fox)\n\t(for)\n", 'Yank replacement with \t\n matches to default register') 10 | 11 | call vimtest#SaveOut() 12 | call vimtest#Quit() 13 | -------------------------------------------------------------------------------- /tests/t4080-SubstituteAndYank-closed-fold.vim: -------------------------------------------------------------------------------- 1 | " Test substituting and yanking all and first matches within a single closed fold. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | %fold 8 | 9 | 3SubstituteAndYank/\/<&>/g/(&)/ 10 | call vimtap#Is(@@, "(foo)(foo)(foo)(fox)(for)", 'Yank replacement matches to default register') 11 | 12 | call vimtest#SaveOut() 13 | call vimtest#Quit() 14 | -------------------------------------------------------------------------------- /tests/t3010-PutMatches-no-matches.vim: -------------------------------------------------------------------------------- 1 | " Test error when the pattern doesn't match. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(3) 5 | edit text.txt 6 | let s:lineNum = line('$') 7 | call vimtap#err#ErrorsLike('E486: .*: doesNotExist', '$PutMatches/doesNotExist/', 'Pattern not found error shown') 8 | call vimtap#Ok(&modified, 'Buffer is modified') 9 | call vimtap#Is(line('$'), s:lineNum, 'Number of lines unchanged') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t5000-PrintMatches.vim: -------------------------------------------------------------------------------- 1 | " Test printing all matches. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | PrintMatches/\/ 7 | echomsg 'end' 8 | 9 | call vimtest#StartTap() 10 | call vimtap#Plan(1) 11 | call vimtap#Ok(! &modified, 'Buffer not modified') 12 | 13 | 14 | echomsg 'start' 15 | PrintMatches!/\/ 16 | echomsg 'end' 17 | 18 | let @/ = '\/g/(&)/', 'Pattern not found error shown') 8 | call vimtap#Ok(empty(@@), 'Default register not modified') 9 | call vimtap#Ok(&modified, 'Buffer is modified') 10 | 11 | call vimtest#SaveOut() 12 | call vimtest#Quit() 13 | -------------------------------------------------------------------------------- /tests/t1030-GrepToReg-last.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matching lines, including a match in the last. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(1) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | GrepToReg/er/ 9 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nmateria, cinis elementi similis cum folio, de quo ludunt venti.\ncomparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti.\n", 'Yank matching lines to default register') 10 | 11 | call vimtest#Quit() 12 | -------------------------------------------------------------------------------- /tests/t4060-SubstituteAndYank-DeleteMatches.vim: -------------------------------------------------------------------------------- 1 | " Test deleting and yanking like with a :DeleteMatches command. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | let @a = '' 7 | edit text.txt 8 | 9 | 1SubstituteAndYank/\\s//g// 10 | call vimtap#Is(@@, "foo \nfoo \n", 'Yank original matches to default register') 11 | 12 | 2,4SubstituteAndYank/\<...\>/&///a 13 | call vimtap#Is(@a, "cum\nCum\nfox\n", 'Yank original matches to register a') 14 | 15 | call vimtest#SaveOut() 16 | call vimtest#Quit() 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an enhancement for the plugin 4 | title: '' 5 | labels: enhancement 6 | 7 | --- 8 | 9 | **Motivation** 10 | 11 | _A clear and concise description of what currently is hard to do._ 12 | 13 | **Request and Purpose** 14 | 15 | _A clear and concise description of what you want to happen. Possible fit criteria._ 16 | 17 | **Alternatives** 18 | 19 | _A clear and concise description of any alternative solutions or features you've considered._ 20 | -------------------------------------------------------------------------------- /tests/longer.txt: -------------------------------------------------------------------------------- 1 | beginning 2 | some bar first 3 | { 4 | #START 5 | hi 6 | bar 7 | } 8 | { 9 | spacer one 10 | Estuans interius foo vehementi in bar loquor foo menti: factus de 11 | materia, cinis elementi similis cum folio, de quo ludunt venti. 12 | Cum sit foo. 13 | spacer two 14 | Stultus fox, for bar and baz and quux. 15 | spacer three 16 | } 17 | { 18 | gaga nolos 19 | comparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti. 20 | } 21 | { 22 | bar 23 | #END 24 | } 25 | more bar 26 | end 27 | -------------------------------------------------------------------------------- /tests/t2071-YankMatches-replacement-ic-zs.vim: -------------------------------------------------------------------------------- 1 | " Test yanking with a replace expression that changes to ignorecase and uses match limiting. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | YankMatches/\ce\w\w\zs[UANSNTI]\{3,4}\>/<&>/ 9 | call vimtap#Is(@@, "", 'Yank \c..\zs replaced matches to default register') 10 | 11 | YankMatches/e\ze[MEST]\{2}\c/<&>/ 12 | call vimtap#Is(@@, "", 'Yank \ze..\c replaced matches to default register') 13 | 14 | call vimtest#Quit() 15 | -------------------------------------------------------------------------------- /tests/t4050-SubstituteAndYank-range.vim: -------------------------------------------------------------------------------- 1 | " Test substituting and yanking within a line and passed range. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | let @a = '' 7 | edit text.txt 8 | 1 9 | 10 | SubstituteAndYank/\/<&>/g/(&)/ 11 | call vimtap#Is(@@, "(foo)(foo)", 'Yank replacement matches to default register') 12 | 13 | 2,4SubstituteAndYank/\<...\>/[\0]//(\0)/a 14 | call vimtap#Is(@a, "(cum)(Cum)(fox)", 'Yank replacement matches to register a') 15 | 16 | call vimtest#SaveOut() 17 | call vimtest#Quit() 18 | -------------------------------------------------------------------------------- /tests/t5200-PrintMatches-predicate.msgok: -------------------------------------------------------------------------------- 1 | start 2 | 42==42 42==42 42==42 42==42 42==42 3 | end 4 | 5 | start 6 | foo 7 | foo 8 | foo 9 | fox 10 | for 11 | end 12 | 13 | start 14 | foo 15 | foo 16 | fox 17 | end 18 | 19 | start 20 | foo 21 | foo 22 | for 23 | end 24 | 25 | start 26 | foo 27 | bar 28 | foo 29 | cum 30 | quo 31 | for 32 | bar 33 | and 34 | baz 35 | and 36 | Foo 37 | flu 38 | sub 39 | end 40 | 41 | start 42 | foo 43 | bar 44 | foo 45 | cum 46 | end 47 | 48 | start 49 | foo 50 | foo 51 | folio 52 | foo 53 | foobar 54 | end 55 | -------------------------------------------------------------------------------- /tests/t5050-PrintMatches-replacement-special.vim: -------------------------------------------------------------------------------- 1 | " Test printing all matches with replacement specials. 2 | 3 | edit text.txt 4 | 5 | echomsg 'start' 6 | PrintMatches/\/&;;/ 7 | echomsg 'end' 8 | 9 | echomsg 'start' 10 | PrintMatches/\/\0;;/ 11 | echomsg 'end' 12 | 13 | echomsg 'start' 14 | PrintMatches/\/&\n/ 15 | echomsg 'end' 16 | 17 | echomsg 'start' 18 | PrintMatches/vehe\zsm.n\zeti/<&>/ 19 | echomsg 'end' 20 | 21 | echomsg 'start' 22 | PrintMatches/\%>3l\%>2v\/ 23 | echomsg 'end' 24 | 25 | call vimtest#Quit() 26 | -------------------------------------------------------------------------------- /tests/t2060-YankMatches-replace-expression.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matches with replace expression. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | YankMatches/\/\='<' . submatch(0) . '>'/ 9 | call vimtap#Is(@@, '', 'Yank replace-expression matches to default register') 10 | 11 | YankMatches/\/\=submatch(0) . submatch(1) . ', '/ 12 | call vimtap#Is(@@, 'fooo, fooo, fooo, foxx, forr, ', 'Yank submatch(1) replace-expression matches to default register') 13 | 14 | call vimtest#Quit() 15 | -------------------------------------------------------------------------------- /tests/t2210-YankUniqueMatches-predicate.vim: -------------------------------------------------------------------------------- 1 | " Test yanking unique matches with predicate. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | edit text.txt 6 | 7 | YankUniqueMatches/\<\w\{3}\>/c v:val.matchCount < 5 8 | call vimtap#Is(@c, "foo\nbar\ncum\nquo\n", 'First four unique matches extracted via direct context expression') 9 | 10 | YankUniqueMatches/\<\([fl]\)\w\+\>/\1&-/d len(ingo#str#trcd(v:val.replacement, 'o')) > 1 && v:val.acceptedCount < 3 11 | call vimtap#Is(@d, 'ffoo-lloquor-ffolio-', 'First three accepted unique replacements extracted via direct context expression') 12 | 13 | call vimtest#Quit() 14 | -------------------------------------------------------------------------------- /tests/t4030-SubstituteAndYank-special-numbering.vim: -------------------------------------------------------------------------------- 1 | " Test substituting and yanking with special numbering. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @@ = '' 6 | edit text.txt 7 | 8 | %SubstituteAndYank/\/\#:&/g/\#=&\n/ 9 | call vimtap#Is(@@, "1=foo\n2=foo\n3=foo\n4=fox\n5=for\n", 'Yank replacement with \# matches to default register') 10 | 11 | %SubstituteAndYank/\<\w\w\w\(\w\w\)\>/\=v:key . '>' . submatch(0)//\=v:key . '=' . submatch(1) . "\n"/ 12 | call vimtap#Is(@@, "0=ti\n1=is\n2=em\n", 'Yank replacement matches to default register') 13 | 14 | call vimtest#SaveOut() 15 | call vimtest#Quit() 16 | -------------------------------------------------------------------------------- /tests/t2100-YankUniqueMatches.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all unique matches. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(4) 5 | let @@ = '' 6 | let @a = '' 7 | edit text.txt 8 | 9 | YankUniqueMatches/\/ 10 | call vimtap#Is(@@, "foo\nfox\nfor\n", 'Yank matches to default register') 11 | 12 | YankUniqueMatches!/\/a 13 | call vimtap#Is(@@, "foo\nfox\nfor\n", 'Default register unchanged when register is specified') 14 | call vimtap#Is(@a, "foo\nfox\n", 'Yank first matches to register a') 15 | 16 | let @/ = '\/ 10 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor\n", 'Yank matches to default register') 11 | call vimtap#Ok(! &modified, 'Buffer not modified') 12 | 13 | YankMatches!/\/a 14 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor\n", 'Default register unchanged when register is specified') 15 | call vimtap#Is(@a, "foo\nfoo\nfox\n", 'Yank first matches to register a') 16 | 17 | let @/ = '\/<&>/g/(&)/ 10 | call vimtap#Is(@@, "(foo)(foo)(foo)(fox)(for)", 'Yank replacement matches to default register') 11 | 12 | %SubstituteAndYank/\<...\(..\)\>/\0\1//(\1)/a 13 | call vimtap#Is(@@, "(foo)(foo)(foo)(fox)(for)", 'Default register unchanged when register is specified') 14 | call vimtap#Is(@a, "(ti)(is)(em)", 'Yank replacement matches to register a') 15 | 16 | let @/ = '\/\='<' . submatch(0) . '>'/g/\='(' . submatch(0) . ')'/ 10 | call vimtap#Is(@@, "(foo)(foo)(foo)(fox)(for)", 'Yank replacement matches to default register') 11 | 12 | %SubstituteAndYank/\<...\(..\)\>/\0\1//\='(' . submatch(1) . ')'/a 13 | call vimtap#Is(@@, "(foo)(foo)(foo)(fox)(for)", 'Default register unchanged when register is specified') 14 | call vimtap#Is(@a, "(ti)(is)(em)", 'Yank replacement matches to register a') 15 | 16 | let @/ = '\/&;;/ 9 | call vimtap#Is(@@, 'foo;;foo;;foo;;fox;;for', 'Yank &;; replaced matches to default register') 10 | YankMatches/\/\0;;/ 11 | call vimtap#Is(@@, 'foo;;foo;;foo;;fox;;for;;', 'Yank \0;; replaced matches to default register') 12 | 13 | YankMatches/\/&\n/ 14 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor", 'Yank &\n replaced matches to default register') 15 | 16 | YankMatches/vehe\zsm.n\zeti/<&>/ 17 | call vimtap#Is(@@, "", 'Yank \zs..\ze replaced matches to default register') 18 | 19 | YankMatches/\%>3l\%>2v\/ 20 | call vimtap#Is(@@, "", 'Yank \%>3l\%>2v replaced matches to default register') 21 | 22 | call vimtest#Quit() 23 | -------------------------------------------------------------------------------- /tests/t1120-GrepRangeToReg-overlapping.vim: -------------------------------------------------------------------------------- 1 | " Test yanking overlapping ranges. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(2) 5 | let @a = '' 6 | edit longer.txt 7 | 8 | let @/ = 'foo' 9 | GrepRangeToReg .-1,.+1 a 10 | call vimtap#Is(@a, "spacer one\nEstuans interius foo vehementi in bar loquor foo menti: factus de\nmateria, cinis elementi similis cum folio, de quo ludunt venti.\nCum sit foo.\nspacer two\ngaga nolos\ncomparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti.\n}\n", 'yank foo plus one around') 11 | 12 | GrepRangeToReg /\/ ?{?+1,/}/-1 b 13 | call vimtap#Is(@b, "spacer one\nEstuans interius foo vehementi in bar loquor foo menti: factus de\nmateria, cinis elementi similis cum folio, de quo ludunt venti.\nCum sit foo.\nspacer two\nStultus fox, for bar and baz and quux.\nspacer three\n", 'yank \ surrounded by {...}') 14 | 15 | call vimtest#Quit() 16 | -------------------------------------------------------------------------------- /tests/t4100-SubstituteAndYankUnique.vim: -------------------------------------------------------------------------------- 1 | " Test substituting and yanking all unique matches. 2 | 3 | call vimtest#SkipAndQuitIf(v:version < 704 || v:version == 704 && ! has('patch2119'), 'Need support for recursive sub-replace-expression') 4 | call vimtest#StartTap() 5 | call vimtap#Plan(4) 6 | let @@ = '' 7 | let @a = '' 8 | edit text.txt 9 | 10 | %SubstituteAndYankUnique/\/<&>/g/(&)/ 11 | call vimtap#Is(@@, "(foo)(fox)(for)", 'Yank replacement matches to default register') 12 | 13 | %SubstituteAndYankUnique/\<.\zs..\(..\)\>/\0\1/g/(\1)/a 14 | call vimtap#Is(@@, "(foo)(fox)(for)", 'Default register unchanged when register is specified') 15 | call vimtap#Is(@a, "(ti)(is)(io)(em)", 'Yank replacement matches to register a') 16 | 17 | let @/ = '\/ 10 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nCum sit foo.\n", 'Yank matching lines to default register') 11 | 12 | GrepToReg!/^\u/a 13 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nCum sit foo.\n", 'Default register unchanged when register is specified') 14 | call vimtap#Is(@a, "materia, cinis elementi similis cum folio, de quo ludunt venti.\ncomparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti.\n", 'Yank non-matching lines to register a') 15 | 16 | let @/ = '\', 'PrintMatches/\/ 42==11', 'Pattern not found error shown') 9 | 10 | echomsg 'start' 11 | PrintMatches/\/ 42==42/ 12 | echomsg 'end' 13 | 14 | echomsg 'start' 15 | PrintMatches/\/ 42==42 16 | echomsg 'end' 17 | 18 | echomsg 'start' 19 | PrintMatches!/\/11!=0 20 | echomsg 'end' 21 | 22 | echomsg 'start' 23 | PrintMatches/\/MyPredicate() 24 | echomsg 'end' 25 | 26 | echomsg 'start' 27 | PrintMatches/\<\w\{3}\>/MyContextPredicate(v:val) 28 | echomsg 'end' 29 | 30 | echomsg 'start' 31 | PrintMatches/\<\w\{3}\>/v:val.matchCount < 5 32 | echomsg 'end' 33 | 34 | echomsg 'start' 35 | PrintMatches/\/len(ingo#str#trcd(v:val.match, 'o')) > 1 36 | echomsg 'end' 37 | 38 | call vimtest#Quit() 39 | -------------------------------------------------------------------------------- /tests/t1100-GrepRangeToReg.vim: -------------------------------------------------------------------------------- 1 | " Test yanking all matching lines. 2 | 3 | call vimtest#StartTap() 4 | call vimtap#Plan(4) 5 | let @@ = '' 6 | let @a = '' 7 | edit longer.txt 8 | 9 | let @/ = '\' 10 | GrepRangeToReg . 11 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nCum sit foo.\n", 'Yank current line range to default register') 12 | 13 | GrepRangeToReg! /^\u/ . a 14 | call vimtap#Is(@@, "Estuans interius foo vehementi in bar loquor foo menti: factus de\nCum sit foo.\n", 'Default register unchanged when register is specified') 15 | call vimtap#Is(@a, "beginning\nsome bar first\n{\n#START\nhi\nbar\n}\n{\nspacer one\nmateria, cinis elementi similis cum folio, de quo ludunt venti.\nspacer two\nspacer three\n}\n{\ngaga nolos\ncomparor Foo flu labenti, sub eodem foobar tramite nunquam permanenti.\n}\n{\nbar\n#END\n}\nmore bar\nend\n", 'Yank non-matching lines to register a') 16 | 17 | let @/ = '\/ 42==11 12 | call vimtap#Is(@@, '', 'Nothing extracted with always-false predicate') 13 | 14 | YankMatches/\/ 42==42/ 15 | call vimtap#Is(@@, ' 42==42 42==42 42==42 42==42 42==42', 'With trailing separator, what looks like predicate is taken as replacement') 16 | 17 | YankMatches/\/ 42==42 18 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor\n", 'Everything extracted with always-true predicate') 19 | 20 | YankMatches!/\/a 11!=0 21 | call vimtap#Is(@@, "foo\nfoo\nfoo\nfox\nfor\n", 'Default register unchanged when register is specified') 22 | call vimtap#Is(@a, "foo\nfoo\nfox\n", 'Yank first matches to register a with always-true predicate') 23 | 24 | YankMatches/\/b MyPredicate() 25 | call vimtap#Is(@b, "foo\nfoo\nfor\n", 'Every second match extracted with toggle predicate without context') 26 | 27 | YankMatches/\<\w\{3}\>/b MyContextPredicate(v:val) 28 | call vimtap#Is(@b, "foo\nbar\nfoo\ncum\nquo\nfor\nbar\nand\nbaz\nand\nFoo\nflu\nsub\n", 'Every match after column 9 extracted with context predicate') 29 | 30 | YankMatches/\<\w\{3}\>/c v:val.matchCount < 5 31 | call vimtap#Is(@c, "foo\nbar\nfoo\ncum\n", 'First four matches extracted via direct context expression') 32 | 33 | YankMatches/\/d len(ingo#str#trcd(v:val.match, 'o')) > 1 34 | call vimtap#Is(@d, "foo\nfoo\nfolio\nfoo\nfoobar\n", 'Matches with more than one o extracted via direct context expression') 35 | 36 | call vimtest#Quit() 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: The plugin isn't working at all or shows wrong or unexpected behavior 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | **Frequent Issues** 9 | 10 | * **E117: Unknown function: ingo#...**: Have you installed the [ingo-library plugin](http://www.vim.org/scripts/script.php?script_id=4433) (or via [GitHub](https://github.com/inkarkat/vim-ingo-library)) as well, as documented in the _dependencies_ section of the readme and plugin help? 11 | * **Sudden problems after updating**: Did you check out the default _master_ branch? Unless you want to participate in feature development and alpha testing, I would recommend that you switch from _master_ to the _stable_ branch; this way, you'll only update to released versions that are (hopefully) better tested and documented. 12 | * **Neovim**: I don't explicitly consider nor test Neovim compatibility; my plugins are written for Vim. If a plugin can be made to work on Neovim with trivial changes, I wouldn't mind including them, but anything more involved should in my opinion be filed as a compatibility bug against Neovim (as its homepage proclaims: _Fully compatible with Vim's editing model and the Vimscript language._) 13 | 14 | **Describe the bug** 15 | 16 | _A clear and concise description of what the bug is._ 17 | 18 | **How to Reproduce** 19 | 20 | _Detailed steps to reproduce the behavior._ 21 | 22 | **Expected Behavior** 23 | 24 | _A clear and concise description of what you expected to happen._ 25 | 26 | **Environment** 27 | - Plugin version (e.g. stable version _1.10_) / revision _a1b2c3d4_ from the master branch 28 | - Dependency versions (e.g. [ingo-library plugin](https://github.com/inkarkat/vim-ingo-library), or external tool versions) 29 | - Vim version (e.g. _8.1.1234_) Or paste the result of `vim --version`. 30 | - OS: (e.g. _Ubuntu 18.04_, _Windows 10 1809_, _macOS 10.14_) 31 | - Install method: (e.g. manually via Vimball or ZIP file, GitHub clone as pack plugin, Plugin manager _NAME_) 32 | - Other plugins / additional context if you think this could be important 33 | -------------------------------------------------------------------------------- /plugin/ExtractMatches.vim: -------------------------------------------------------------------------------- 1 | " ExtractMatches.vim: Yank matches from range into a register. 2 | " 3 | " DEPENDENCIES: 4 | " - ingo-library.vim plugin 5 | " 6 | " Source: 7 | " Implementation inspired by 8 | " http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard 9 | " Use case inspired from a post by Luc Hermitte at 10 | " http://www.reddit.com/r/vim/comments/ef9zh/any_better_way_to_yank_all_lines_matching_pattern/ 11 | 12 | " Copyright: (C) 2010-2020 Ingo Karkat 13 | " The VIM LICENSE applies to this script; see ':help copyright'. 14 | " 15 | " Maintainer: Ingo Karkat 16 | 17 | " Avoid installing twice or when in unsupported Vim version. 18 | if exists('g:loaded_ExtractMatches') || (v:version < 700) 19 | finish 20 | endif 21 | let g:loaded_ExtractMatches = 1 22 | 23 | command! -bang -nargs=? -range=% GrepToReg if ! ExtractMatches#GrepToReg(, , , 0) | echoerr ingo#err#Get() | endif 24 | command! -bang -nargs=? -range=% GrepRangeToReg if ! ExtractMatches#GrepRangeToReg(, , , 0) | echoerr ingo#err#Get() | endif 25 | 26 | command! -bang -nargs=? -range=% YankMatches if ! ExtractMatches#YankMatches(, , , 0, 0) | echoerr ingo#err#Get() | endif 27 | command! -bang -nargs=? -range=% YankUniqueMatches if ! ExtractMatches#YankMatches(, , , 0, 1) | echoerr ingo#err#Get() | endif 28 | 29 | command! -bang -nargs=? -range=% PrintMatches if ! ExtractMatches#PrintMatches(, , , 0, 0) | echoerr ingo#err#Get() | endif 30 | command! -bang -nargs=? -range=% PrintUniqueMatches if ! ExtractMatches#PrintMatches(, , , 0, 1) | echoerr ingo#err#Get() | endif 31 | 32 | command! -nargs=+ -range SubstituteAndYank call setline(, getline()) | if ! ExtractMatches#SubstituteAndYank(, , , 0) | echoerr ingo#err#Get() | endif 33 | command! -nargs=+ -range SubstituteAndYankUnique call setline(, getline()) | if ! ExtractMatches#SubstituteAndYank(, , , 1) | echoerr ingo#err#Get() | endif 34 | 35 | command! -bang -nargs=? -range=-1 PutMatches call setline(, getline()) | call ExtractMatches#PutMatches( == -1 ? : , , 0, 0) 36 | command! -bang -nargs=? -range=-1 PutUniqueMatches call setline(, getline()) | call ExtractMatches#PutMatches( == -1 ? : , , 0, 1) 37 | 38 | " vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax : 39 | -------------------------------------------------------------------------------- /doc/ExtractMatches.txt: -------------------------------------------------------------------------------- 1 | *ExtractMatches.txt* Yank matches from range into a register. 2 | 3 | EXTRACT MATCHES by Ingo Karkat 4 | *ExtractMatches.vim* 5 | description |ExtractMatches-description| 6 | usage |ExtractMatches-usage| 7 | installation |ExtractMatches-installation| 8 | limitations |ExtractMatches-limitations| 9 | known problems |ExtractMatches-known-problems| 10 | todo |ExtractMatches-todo| 11 | history |ExtractMatches-history| 12 | 13 | ============================================================================== 14 | DESCRIPTION *ExtractMatches-description* 15 | 16 | This plugin provides a toolbox of commands to copy all (or only unique first) 17 | search matches / matches of a passed pattern / entire lines matching, to a 18 | register, or directly |:put| them into the buffer. The commands are easier to 19 | remember and quicker to type than the various idioms for that, and they are 20 | robust, i.e. also support patterns spanning multiples lines. 21 | 22 | SOURCE * 23 | 24 | Implementation inspired by 25 | http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard 26 | Use case inspired from a post by Luc Hermitte at 27 | http://www.reddit.com/r/vim/comments/ef9zh/any_better_way_to_yank_all_lines_matching_pattern/ 28 | 29 | ALTERNATIVES * 30 | 31 | One can employ a |sub-replace-expression| to capture the matches, as described 32 | in 33 | http://stackoverflow.com/questions/9079561/how-to-extract-regex-matches-using-vim 34 | The idea is to use the side effect of |add()| in the expression, and force an 35 | empty return value from it through the inverse range of [1:0]. To avoid text 36 | modification, we make the pattern match nothing by appending |/\zs|; with 37 | this, \0 will be empty, so we have to capture the match as \1: > 38 | let t=[] | %s/\(fo*)\zs/\=add(t, submatch(1))[1:0]/g 39 | Since this has the side effect of setting 'modified', anyway, we can 40 | alternatively have add() return the last added element [-1]; this saves us 41 | from the zero-width match and capture: > 42 | let t=[] | %s/fo*/\=add(t, submatch(0))[-1]/g 43 | 44 | SEE ALSO * 45 | 46 | - The |PatternsOnText.vim| plugin (vimscript #4602) provides commands that 47 | print, substitute, or delete certain duplicates or matches directly in the 48 | buffer. 49 | 50 | RELATED WORKS * 51 | 52 | - The yankitute plugin (vimscript #4719) provides a similar 53 | :[range]Yankitute[register]/{pattern}/[string]/[flags]/[join] command. 54 | - yankmatches 55 | (https://github.com/thoughtstream/Damian-Conway-s-Vim-Setup/blob/master/plugin/yankmatches.vim) 56 | can yank / delete entire lines with / without matches, similar to 57 | :GrepToReg. 58 | 59 | ============================================================================== 60 | USAGE *ExtractMatches-usage* 61 | 62 | All commands default to the entire buffer if the [range] is omitted. 63 | *:GrepToReg* 64 | :[range]GrepToReg[!] /{pattern}/[x] 65 | :[range]GrepToReg[!] [{pattern}] 66 | Yank all lines in [range] that match {pattern} (or the 67 | last search pattern if omitted), with !: do not match, 68 | into register [x] (or the unnamed register). 69 | *:GrepRangeToReg* 70 | :[range]GrepRangeToReg[!] /{pattern}/ {range} [x] 71 | :[range]GrepRangeToReg[!] {range} [x] 72 | Yank all lines in [range] that match {pattern} (or the 73 | last search pattern if omitted), with !: do not match, 74 | and all lines in {range} around it, into register [x] 75 | (or the unnamed register). 76 | With this, you can emulate grep's context line control 77 | -A -B -C / --after-context --before-context --context 78 | (but without the "--" group separator). 79 | *:YankMatches* 80 | :[range]YankMatches[!] /{pattern}/[x] [{predicate}] 81 | :[range]YankMatches[!] [{pattern}] 82 | Yank text matching {pattern} (or the last search 83 | pattern if omitted) in [range] into register [x] (or 84 | the unnamed register). Each match is put on a new 85 | line. This works like "grep -o". With [!]: Yank only 86 | the first match in each line. If {predicate} is given, 87 | it is evaluated at each match and only those matches 88 | where a true value is returned are taken. 89 | *:YankMatches-v:val* 90 | Inside {predicate}, you can reference a context object 91 | via |v:val|. It provides the following information: 92 | match: current matched text 93 | matchStart: [lnum, col] of the match start 94 | matchEnd: [lnum, col] of the match end (this is 95 | also the cursor position) 96 | replacement:current replacement text (if passed, 97 | else equal to match) 98 | matchCount: number of current (unique for 99 | |:YankUniqueMatches|) match of 100 | {pattern} 101 | acceptedCount: 102 | number of matches already accepted by 103 | the predicate 104 | It also contains pre-initialized variables for use by 105 | {predicate}. These get cleared by each |:YankMatches| 106 | invocation: 107 | n: number / flag (0 / false) 108 | m: number / flag (1 / true) 109 | l: empty List [] 110 | d: empty Dictionary {} 111 | s: empty String "" 112 | :[range]YankMatches[!] /{pattern}/{replacement}/[x] [{predicate}] 113 | Grab text matching {pattern} (or the last search 114 | pattern if omitted) in [range], and put {replacement} 115 | into register [x] (or the unnamed register). You can 116 | refer to the match via |s/\&| and submatches (|s/\1|). 117 | The matches are simply concatenated without a newline 118 | character here. Append \n at {replacement} to have 119 | one. When {replacement} is "&...", ... is assumed to 120 | be a (literal) separator and is removed from the last 121 | element; if you don't want that, use \0 instead of &. 122 | With [!]: Yank only the first match in each line. 123 | *:YankUniqueMatches* 124 | :[range]YankUniqueMatches[!] /{pattern}/[x] [{predicate}] 125 | :[range]YankUniqueMatches[!] [{pattern}] 126 | Yank text matching {pattern} (or the last search 127 | pattern if omitted) in [range] into register [x] (or 128 | the unnamed register), but only once. Each match is 129 | put on a new line. With [!]: Yank only the first match 130 | in each line. If {predicate} is given, it is evaluated 131 | at each match and only those matches where a true 132 | value is returned are taken. 133 | :[range]YankUniqueMatches[!] /{pattern}/{replacement}/[x] [{predicate}] 134 | *:PrintMatches* 135 | :[range]PrintMatches[!] /{pattern}/ [{predicate}] 136 | :[range]PrintMatches[!] [{pattern}] 137 | Print text matching {pattern} (or the last search 138 | pattern if omitted) in [range]. Each match is printed 139 | on a new line. This works like "grep -o". With [!]: 140 | Print only the first match in each line. If 141 | {predicate} is given, it is evaluated at each match 142 | and only those matches where a true value is returned 143 | are taken. Cp. |:YankMatches-v:val|. 144 | :[range]PrintMatches[!] /{pattern}/{replacement}/ [{predicate}] 145 | Like |:YankMatches|, but print the replacement instead 146 | of yanking. *:PrintUniqueMatches* 147 | :[range]PrintUniqueMatches[!] /{pattern}/ [{predicate}] 148 | :[range]PrintUniqueMatches[!] [{pattern}] 149 | :[range]PrintUniqueMatches[!] /{pattern}/{replacement}/ [{predicate}] 150 | Like |:YankUniqueMatches|, but print instead of 151 | yanking. 152 | 153 | *:SubstituteAndYank* 154 | :[range]SubstituteAndYank /{pattern}/{replacement}/[flags]/{yank-replacement}/[x] 155 | Replace all matches of {pattern} in the current line / 156 | [range] with {replacement}, like with |:substitute| 157 | (using [flags] as |:s_flags|), and put the 158 | {yank-replacement} (simply concatenated without a 159 | newline) into register [x] (or the unnamed register). 160 | Supports the same replacements as |:YankMatches|; 161 | additionally, \# is replaced with a (1-based) count 162 | of the current yank and in a |sub-replace-expression|, 163 | v:key stands for the 0-based index. 164 | *:SubstituteAndYankUnique* 165 | :[range]SubstituteAndYankUnique /{pattern}/{replacement}/[flags]/{yank-replacement}/[x] 166 | Like |:SubstituteAndYank|, but only add unique matches 167 | to the register. For non-unique matches, \# and v:key 168 | refer to the corresponding existing match in the 169 | register. 170 | 171 | *:PutMatches* 172 | :[line]PutMatches[!] /{pattern}/ [{predicate}] 173 | :[line]PutMatches[!] [{pattern}] 174 | :[line]PutMatches[!] /{pattern}/{replacement}/ [{predicate}] 175 | Put text matching {pattern} (or the last search 176 | pattern if omitted) after [line] (default current 177 | line). Each match is put on a new line (except when 178 | {replacement} is specified; see |:YankMatches|). This 179 | works like "grep -o". With [!]: Put only the first 180 | match in each line. If {predicate} is given, it is 181 | evaluated at each match and only those matches where a 182 | true value is returned are taken. Cp. 183 | |:YankMatches-v:val|. 184 | *:PutUniqueMatches* 185 | :[line]PutUniqueMatches[!] /{pattern}/ [{predicate}] 186 | :[line]PutUniqueMatches[!] [{pattern}] 187 | :[line]PutUniqueMatches[!] /{pattern}/{replacement}/ [{predicate}] 188 | Put text matching {pattern} (or the last search 189 | pattern if omitted) after [line] (default current 190 | line). Each match is once put on a new line. With [!]: 191 | Put only the first match in each line. If {predicate} 192 | is given, it is evaluated at each match and only those 193 | matches where a true value is returned are taken. 194 | 195 | ============================================================================== 196 | INSTALLATION *ExtractMatches-installation* 197 | 198 | The code is hosted in a Git repo at 199 | https://github.com/inkarkat/vim-ExtractMatches 200 | You can use your favorite plugin manager, or "git clone" into a directory used 201 | for Vim |packages|. Releases are on the "stable" branch, the latest unstable 202 | development snapshot on "master". 203 | 204 | This script is also packaged as a |vimball|. If you have the "gunzip" 205 | decompressor in your PATH, simply edit the *.vmb.gz package in Vim; otherwise, 206 | decompress the archive first, e.g. using WinZip. Inside Vim, install by 207 | sourcing the vimball or via the |:UseVimball| command. > 208 | vim ExtractMatches*.vmb.gz 209 | :so % 210 | To uninstall, use the |:RmVimball| command. 211 | 212 | DEPENDENCIES *ExtractMatches-dependencies* 213 | 214 | - Requires Vim 7.0 or higher. 215 | - Requires the |ingo-library.vim| plugin (vimscript #4433), version 1.041 or 216 | higher. 217 | 218 | ============================================================================== 219 | LIMITATIONS *ExtractMatches-limitations* 220 | 221 | KNOWN PROBLEMS *ExtractMatches-known-problems* 222 | 223 | TODO *ExtractMatches-todo* 224 | 225 | IDEAS *ExtractMatches-ideas* 226 | 227 | CONTRIBUTING *ExtractMatches-contribute* 228 | 229 | Report any bugs, send patches, or suggest features via the issue tracker at 230 | https://github.com/inkarkat/vim-ExtractMatches/issues or email (address 231 | below). 232 | 233 | ============================================================================== 234 | HISTORY *ExtractMatches-history* 235 | 236 | 1.50 10-Nov-2024 237 | - ENH: :{Extract,Print,Put}[Unique]Matches now take an optional [{predicate}] 238 | argument at the end with which matches can be restricted with very flexible 239 | rules (e.g. by checking the syntax group). 240 | *** You need to update to ingo-library (vimscript #4433) version 1.041! *** 241 | 242 | 1.42 20-Feb-2020 243 | - BUG: :Grep[Range]ToReg and :{Print,Yank}[Unique]Matches do not consider all 244 | lines when executed on a closed fold. 245 | - Adapt: :Put[Unique]Matches need to check == -1 instead of to 246 | support current line as well as a lnum of 0 (since Vim 8.1.1241). 247 | 248 | 1.41 04-Nov-2018 249 | - Move PatternsOnText#ReplaceSpecial(), and PatternsOnText#DefaultReplacer() 250 | to ingo-library. 251 | - Does not require the |PatternsOnText.vim| plugin (vimscript #4602), version 252 | 2.00 or higher for the :SubstituteAndYank[Unique] commands any longer. 253 | *** You need to update to ingo-library (vimscript #4433) version 1.035! *** 254 | 255 | 1.40 24-Jan-2017 256 | - ENH: Add :GrepRangeToReg command. 257 | - :YankMatches does not handle magicness modifier atoms (\v, \M, etc.) before 258 | / after \zs / \ze. They get cut away, and then the remaining pattern does 259 | not match any longer, and a custom {replacement} is not applied. Normalize 260 | the magicness in the pattern. Additionally, also keep a case-sensitivity 261 | atom (\c, \C). Reported by bjornmelgaard on Stack Overflow. 262 | *** You need to update to ingo-library (vimscript #4433) version 1.029! *** 263 | 264 | 1.32 07-Dec-2016 265 | - In PatternsOnText.vim version 2.0, PatternsOnText#Selected#ReplaceSpecial() 266 | has been moved to PatternsOnText#DefaultReplacer(). 267 | 268 | 1.31 06-Dec-2014 269 | - BUG: :GrepToReg runs into endless loop when the last line of the buffer 270 | belongs to the range and is matching. 271 | - Refactoring: Use ingo#cmdargs#pattern#ParseUnescaped(). 272 | *** You need to update to ingo-library (vimscript #4433) version 1.020! *** 273 | 274 | 1.30 13-Mar-2014 275 | - CHG: Rename :Yank[Unique]MatchesToReg to :Yank[Unique]Matches; the 276 | "register" part is implied by the yank. 277 | - CHG: Change default range of :SubstituteAndYank[Unique] to current line 278 | instead of buffer, to be consistent with :substitute and the :Substitute... 279 | commands defined by PatternsOnText.vim. 280 | - Add :Print[Unique]Matches variant of :Yank[Unique]Matches. 281 | - FIX: Inline pasting (with replacements) doesn't use the specified line and 282 | doesn't create a new empty line. 283 | - FIX: Typo in variable name prevented elimination of \ze. 284 | - FIX: Remove escaping of a:replacement to apply the DWIM trailing separator 285 | removal also to \\, \n, \t etc. 286 | - Handle \r, \n, \t, \b in replacement, too. 287 | 288 | 1.20 20-Feb-2014 289 | - Add :SubstituteAndYank and :SubstituteAndYankUnique commands. 290 | - All commands now properly abort on errors. 291 | 292 | 1.10 18-Feb-2014 293 | - DWIM: When {replacement} is "&...", assume ... is a (literal) separator and 294 | remove it from the last element. 295 | - Add heuristic that drops \zs, \ze, and all location-aware atoms (like \%v) 296 | for the separate substitution for {replacement}, to allow it to match. 297 | Beforehand, either nothing or the entire match have been wrongly returned as 298 | the result. 299 | 300 | 1.00 11-Dec-2013 301 | First published version. 302 | 303 | 0.01 09-Dec-2010 304 | Started development. 305 | 306 | ============================================================================== 307 | Copyright: (C) 2010-2024 Ingo Karkat 308 | The VIM LICENSE applies to this plugin; see |copyright|. 309 | 310 | Maintainer: Ingo Karkat 311 | ============================================================================== 312 | vim:tw=78:ts=8:ft=help:norl: 313 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EXTRACT MATCHES 2 | =============================================================================== 3 | _by Ingo Karkat_ 4 | 5 | DESCRIPTION 6 | ------------------------------------------------------------------------------ 7 | 8 | This plugin provides a toolbox of commands to copy all (or only unique first) 9 | search matches / matches of a passed pattern / entire lines matching, to a 10 | register, or directly :put them into the buffer. The commands are easier to 11 | remember and quicker to type than the various idioms for that, and they are 12 | robust, i.e. also support patterns spanning multiples lines. 13 | 14 | ### SOURCE 15 | 16 | - [Implementation inspiration](http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard) 17 | - [Use case inspired from a post by Luc Hermitte at](http://www.reddit.com/r/vim/comments/ef9zh/any_better_way_to_yank_all_lines_matching_pattern/) 18 | 19 | ### ALTERNATIVES 20 | 21 | One can employ a sub-replace-expression to capture the matches, as described 22 | in 23 | http://stackoverflow.com/questions/9079561/how-to-extract-regex-matches-using-vim 24 | The idea is to use the side effect of add() in the expression, and force an 25 | empty return value from it through the inverse range of [1:0]. To avoid text 26 | modification, we make the pattern match nothing by appending /\\zs; with 27 | this, \\0 will be empty, so we have to capture the match as \\1: 28 | 29 | let t=[] | %s/\(fo*)\zs/\=add(t, submatch(1))[1:0]/g 30 | 31 | Since this has the side effect of setting 'modified', anyway, we can 32 | alternatively have add() return the last added element [-1]; this saves us 33 | from the zero-width match and capture: 34 | 35 | let t=[] | %s/fo*/\=add(t, submatch(0))[-1]/g 36 | 37 | ### SEE ALSO 38 | 39 | - The PatternsOnText.vim plugin ([vimscript #4602](http://www.vim.org/scripts/script.php?script_id=4602)) provides commands that 40 | print, substitute, or delete certain duplicates or matches directly in the 41 | buffer. 42 | 43 | ### RELATED WORKS 44 | 45 | - The yankitute plugin ([vimscript #4719](http://www.vim.org/scripts/script.php?script_id=4719)) provides a similar 46 | :[range]Yankitute[register]/{pattern}/[string]/[flags]/[join] command. 47 | - yankmatches 48 | (https://github.com/thoughtstream/Damian-Conway-s-Vim-Setup/blob/master/plugin/yankmatches.vim) 49 | can yank / delete entire lines with / without matches, similar to 50 | :GrepToReg. 51 | 52 | USAGE 53 | ------------------------------------------------------------------------------ 54 | 55 | All commands default to the entire buffer if the [range] is omitted. 56 | 57 | :[range]GrepToReg[!] /{pattern}/[x] 58 | :[range]GrepToReg[!] [{pattern}] 59 | Yank all lines in [range] that match {pattern} (or the 60 | last search pattern if omitted), with !: do not match, 61 | into register [x] (or the unnamed register). 62 | 63 | :[range]GrepRangeToReg[!] /{pattern}/ {range} [x] 64 | :[range]GrepRangeToReg[!] {range} [x] 65 | Yank all lines in [range] that match {pattern} (or the 66 | last search pattern if omitted), with !: do not match, 67 | and all lines in {range} around it, into register [x] 68 | (or the unnamed register). 69 | With this, you can emulate grep's context line control 70 | -A -B -C / --after-context --before-context --context 71 | (but without the "--" group separator). 72 | 73 | :[range]YankMatches[!] /{pattern}/[x] [{predicate}] 74 | :[range]YankMatches[!] [{pattern}] 75 | Yank text matching {pattern} (or the last search 76 | pattern if omitted) in [range] into register [x] (or 77 | the unnamed register). Each match is put on a new 78 | line. This works like "grep -o". With [!]: Yank only 79 | the first match in each line. If {predicate} is given, 80 | it is evaluated at each match and only those matches 81 | where a true value is returned are taken. 82 | 83 | Inside {predicate}, you can reference a context object 84 | via v:val. It provides the following information: 85 | match: current matched text 86 | matchStart: [lnum, col] of the match start 87 | matchEnd: [lnum, col] of the match end (this is 88 | also the cursor position) 89 | replacement:current replacement text (if passed, 90 | else equal to match) 91 | matchCount: number of current (unique for 92 | :YankUniqueMatches) match of 93 | {pattern} 94 | acceptedCount: 95 | number of matches already accepted by 96 | the predicate 97 | It also contains pre-initialized variables for use by 98 | {predicate}. These get cleared by each :YankMatches 99 | invocation: 100 | n: number / flag (0 / false) 101 | m: number / flag (1 / true) 102 | l: empty List [] 103 | d: empty Dictionary {} 104 | s: empty String "" 105 | :[range]YankMatches[!] /{pattern}/{replacement}/[x] [{predicate}] 106 | Grab text matching {pattern} (or the last search 107 | pattern if omitted) in [range], and put {replacement} 108 | into register [x] (or the unnamed register). You can 109 | refer to the match via s/\& and submatches (s/\1). 110 | The matches are simply concatenated without a newline 111 | character here. Append \n at {replacement} to have 112 | one. When {replacement} is "&...", ... is assumed to 113 | be a (literal) separator and is removed from the last 114 | element; if you don't want that, use \0 instead of &. 115 | With [!]: Yank only the first match in each line. 116 | 117 | :[range]YankUniqueMatches[!] /{pattern}/[x] [{predicate}] 118 | :[range]YankUniqueMatches[!] [{pattern}] 119 | Yank text matching {pattern} (or the last search 120 | pattern if omitted) in [range] into register [x] (or 121 | the unnamed register), but only once. Each match is 122 | put on a new line. With [!]: Yank only the first match 123 | in each line. If {predicate} is given, it is evaluated 124 | at each match and only those matches where a true 125 | value is returned are taken. 126 | :[range]YankUniqueMatches[!] /{pattern}/{replacement}/[x] [{predicate}] 127 | 128 | :[range]PrintMatches[!] /{pattern}/ [{predicate}] 129 | :[range]PrintMatches[!] [{pattern}] 130 | Print text matching {pattern} (or the last search 131 | pattern if omitted) in [range]. Each match is printed 132 | on a new line. This works like "grep -o". With [!]: 133 | Print only the first match in each line. If 134 | {predicate} is given, it is evaluated at each match 135 | and only those matches where a true value is returned 136 | are taken. Cp. :YankMatches-v:val. 137 | :[range]PrintMatches[!] /{pattern}/{replacement}/ [{predicate}] 138 | Like :YankMatches, but print the replacement instead 139 | of yanking. 140 | :[range]PrintUniqueMatches[!] /{pattern}/ [{predicate}] 141 | :[range]PrintUniqueMatches[!] [{pattern}] 142 | :[range]PrintUniqueMatches[!] /{pattern}/{replacement}/ [{predicate}] 143 | Like :YankUniqueMatches, but print instead of 144 | yanking. 145 | 146 | :[range]SubstituteAndYank /{pattern}/{replacement}/[flags]/{yank-replacement}/[x] 147 | Replace all matches of {pattern} in the current line / 148 | [range] with {replacement}, like with :substitute 149 | (using [flags] as :s_flags), and put the 150 | {yank-replacement} (simply concatenated without a 151 | newline) into register [x] (or the unnamed register). 152 | Supports the same replacements as :YankMatches; 153 | additionally, \# is replaced with a (1-based) count 154 | of the current yank and in a sub-replace-expression, 155 | v:key stands for the 0-based index. 156 | 157 | :[range]SubstituteAndYankUnique /{pattern}/{replacement}/[flags]/{yank-replacement}/[x] 158 | Like :SubstituteAndYank, but only add unique matches 159 | to the register. For non-unique matches, \# and v:key 160 | refer to the corresponding existing match in the 161 | register. 162 | 163 | :[line]PutMatches[!] /{pattern}/ [{predicate}] 164 | :[line]PutMatches[!] [{pattern}] 165 | :[line]PutMatches[!] /{pattern}/{replacement}/ [{predicate}] 166 | Put text matching {pattern} (or the last search 167 | pattern if omitted) after [line] (default current 168 | line). Each match is put on a new line (except when 169 | {replacement} is specified; see :YankMatches). This 170 | works like "grep -o". With [!]: Put only the first 171 | match in each line. If {predicate} is given, it is 172 | evaluated at each match and only those matches where a 173 | true value is returned are taken. Cp. 174 | :YankMatches-v:val. 175 | 176 | :[line]PutUniqueMatches[!] /{pattern}/ [{predicate}] 177 | :[line]PutUniqueMatches[!] [{pattern}] 178 | :[line]PutUniqueMatches[!] /{pattern}/{replacement}/ [{predicate}] 179 | Put text matching {pattern} (or the last search 180 | pattern if omitted) after [line] (default current 181 | line). Each match is once put on a new line. With [!]: 182 | Put only the first match in each line. If {predicate} 183 | is given, it is evaluated at each match and only those 184 | matches where a true value is returned are taken. 185 | 186 | INSTALLATION 187 | ------------------------------------------------------------------------------ 188 | 189 | The code is hosted in a Git repo at 190 | https://github.com/inkarkat/vim-ExtractMatches 191 | You can use your favorite plugin manager, or "git clone" into a directory used 192 | for Vim packages. Releases are on the "stable" branch, the latest unstable 193 | development snapshot on "master". 194 | 195 | This script is also packaged as a vimball. If you have the "gunzip" 196 | decompressor in your PATH, simply edit the \*.vmb.gz package in Vim; otherwise, 197 | decompress the archive first, e.g. using WinZip. Inside Vim, install by 198 | sourcing the vimball or via the :UseVimball command. 199 | 200 | vim ExtractMatches*.vmb.gz 201 | :so % 202 | 203 | To uninstall, use the :RmVimball command. 204 | 205 | ### DEPENDENCIES 206 | 207 | - Requires Vim 7.0 or higher. 208 | - Requires the ingo-library.vim plugin ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)), version 1.041 or 209 | higher. 210 | 211 | CONTRIBUTING 212 | ------------------------------------------------------------------------------ 213 | 214 | Report any bugs, send patches, or suggest features via the issue tracker at 215 | https://github.com/inkarkat/vim-ExtractMatches/issues or email (address 216 | below). 217 | 218 | HISTORY 219 | ------------------------------------------------------------------------------ 220 | 221 | ##### 1.50 10-Nov-2024 222 | - ENH: :{Extract,Print,Put}[Unique]Matches now take an optional [{predicate}] 223 | argument at the end with which matches can be restricted with very flexible 224 | rules (e.g. by checking the syntax group). 225 | 226 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.041!__ 227 | 228 | ##### 1.42 20-Feb-2020 229 | - BUG: :Grep[Range]ToReg and :{Print,Yank}[Unique]Matches do not consider all 230 | lines when executed on a closed fold. 231 | - Adapt: :Put[Unique]Matches need to check <count> == -1 instead of <line2> to 232 | support current line as well as a lnum of 0 (since Vim 8.1.1241). 233 | 234 | ##### 1.41 04-Nov-2018 235 | - Move PatternsOnText#ReplaceSpecial(), and PatternsOnText#DefaultReplacer() 236 | to ingo-library. 237 | - Does not require the PatternsOnText.vim plugin ([vimscript #4602](http://www.vim.org/scripts/script.php?script_id=4602)), version 238 | 2.00 or higher for the :SubstituteAndYank[Unique] commands any longer. 239 | 240 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.035!__ 241 | 242 | ##### 1.40 24-Jan-2017 243 | - ENH: Add :GrepRangeToReg command. 244 | - :YankMatches does not handle magicness modifier atoms (\\v, \\M, etc.) before 245 | / after \\zs / \\ze. They get cut away, and then the remaining pattern does 246 | not match any longer, and a custom {replacement} is not applied. Normalize 247 | the magicness in the pattern. Additionally, also keep a case-sensitivity 248 | atom (\\c, \\C). Reported by bjornmelgaard on Stack Overflow. 249 | 250 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.029!__ 251 | 252 | ##### 1.32 07-Dec-2016 253 | - In PatternsOnText.vim version 2.0, PatternsOnText#Selected#ReplaceSpecial() 254 | has been moved to PatternsOnText#DefaultReplacer(). 255 | 256 | ##### 1.31 06-Dec-2014 257 | - BUG: :GrepToReg runs into endless loop when the last line of the buffer 258 | belongs to the range and is matching. 259 | - Refactoring: Use ingo#cmdargs#pattern#ParseUnescaped(). 260 | 261 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.020!__ 262 | 263 | ##### 1.30 13-Mar-2014 264 | - CHG: Rename :Yank[Unique]MatchesToReg to :Yank[Unique]Matches; the 265 | "register" part is implied by the yank. 266 | - CHG: Change default range of :SubstituteAndYank[Unique] to current line 267 | instead of buffer, to be consistent with :substitute and the :Substitute... 268 | commands defined by PatternsOnText.vim. 269 | - Add :Print[Unique]Matches variant of :Yank[Unique]Matches. 270 | - FIX: Inline pasting (with replacements) doesn't use the specified line and 271 | doesn't create a new empty line. 272 | - FIX: Typo in variable name prevented elimination of \\ze. 273 | - FIX: Remove escaping of a:replacement to apply the DWIM trailing separator 274 | removal also to \\\\, \\n, \\t etc. 275 | - Handle \\r, \\n, \\t, \\b in replacement, too. 276 | 277 | ##### 1.20 20-Feb-2014 278 | - Add :SubstituteAndYank and :SubstituteAndYankUnique commands. 279 | - All commands now properly abort on errors. 280 | 281 | ##### 1.10 18-Feb-2014 282 | - DWIM: When {replacement} is "&...", assume ... is a (literal) separator and 283 | remove it from the last element. 284 | - Add heuristic that drops \\zs, \\ze, and all location-aware atoms (like \\%v) 285 | for the separate substitution for {replacement}, to allow it to match. 286 | Beforehand, either nothing or the entire match have been wrongly returned as 287 | the result. 288 | 289 | ##### 1.00 11-Dec-2013 290 | - First published version. 291 | 292 | ##### 0.01 09-Dec-2010 293 | - Started development. 294 | 295 | ------------------------------------------------------------------------------ 296 | Copyright: (C) 2010-2024 Ingo Karkat - 297 | The [VIM LICENSE](http://vimdoc.sourceforge.net/htmldoc/uganda.html#license) applies to this plugin. 298 | 299 | Maintainer: Ingo Karkat <ingo@karkat.de> 300 | -------------------------------------------------------------------------------- /autoload/ExtractMatches.vim: -------------------------------------------------------------------------------- 1 | " ExtractMatches.vim: Yank matches from range into a register. 2 | " 3 | " DEPENDENCIES: 4 | " - ingo-library.vim plugin 5 | " 6 | " Copyright: (C) 2010-2020 Ingo Karkat 7 | " The VIM LICENSE applies to this script; see ':help copyright'. 8 | " 9 | " Maintainer: Ingo Karkat 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | let s:writableRegisterPattern = '\s*\(' . ingo#register#Writable() . '\)\?' 14 | let s:predicatePattern = '\s*\(.*\)' 15 | let s:registerAndPredicatePattern = s:writableRegisterPattern . '\%(\s*$\|\%(^\|\s\+\)\(.*\)\)' 16 | 17 | function! s:GetPredicate() abort 18 | return (empty(s:predicateArg) ? 19 | \ '' : 20 | \ (s:predicateArg =~# ingo#actions#GetValExpr() ? 21 | \ function('ExtractMatches#PredicateWithContext') : 22 | \ function('ExtractMatches#PredicateWithoutContext') 23 | \ ) 24 | \) 25 | endfunction 26 | 27 | function! s:Grep( firstLnum, lastLnum, isNonMatchingLines, pattern, range, register ) 28 | let [l:firstLnum, l:lastLnum] = [ingo#range#NetStart(a:firstLnum), ingo#range#NetEnd(a:lastLnum)] 29 | 30 | let l:save_view = winsaveview() 31 | let l:matchingLines = {} 32 | let l:cnt = 0 33 | let l:isBlocks = 0 34 | let l:didClobberSearchHistory = 0 35 | let l:startLnum = l:firstLnum 36 | while l:startLnum <= line('$') 37 | call cursor(l:startLnum, 1) 38 | let l:startLnum = search(a:pattern, 'cnW', l:lastLnum) 39 | if l:startLnum == 0 | break | endif 40 | let l:endLnum = search(a:pattern, 'cenW', l:lastLnum) 41 | if l:endLnum == 0 | break | endif 42 | for l:line in range(l:startLnum, l:endLnum) 43 | if empty(a:range) 44 | let l:matchingLines[l:line] = 1 45 | else 46 | call cursor(l:line, 1) 47 | let [l:linesInRange, l:ignoreStartLnums, l:ignoreEndLnums, l:didClobberSearchHistory] = ingo#range#lines#Get(l:firstLnum, l:lastLnum, a:range, 0) 48 | call extend(l:matchingLines, l:linesInRange) 49 | endif 50 | endfor 51 | let l:cnt += 1 52 | let l:isBlocks = l:isBlocks || (l:startLnum != l:endLnum) 53 | let l:startLnum += 1 54 | endwhile 55 | call winrestview(l:save_view) 56 | if l:didClobberSearchHistory 57 | call histdel('search', -1) 58 | endif 59 | 60 | if l:cnt == 0 61 | call ingo#err#Set('E486: Pattern not found: ' . (empty(a:pattern) ? @/ : a:pattern)) 62 | return 0 63 | else 64 | "****D echomsg l:cnt string(sort(keys(l:matchingLines),'ingo#collections#numsort')) 65 | if a:isNonMatchingLines 66 | let l:lineNums = filter(range(l:firstLnum, l:lastLnum), '! has_key(l:matchingLines, v:val)') 67 | else 68 | let l:lineNums = sort(keys(l:matchingLines), 'ingo#collections#numsort') 69 | endif 70 | let l:lines = join(map(l:lineNums, 'getline(v:val)'), "\n") 71 | call setreg(a:register, l:lines, 'V') 72 | 73 | echo printf('%d %s%s yanked', l:cnt, (l:isBlocks ? 'block' : 'line'), (l:cnt == 1 ? '' : 's')) 74 | return 1 75 | endif 76 | endfunction 77 | function! ExtractMatches#GrepToReg( firstLnum, lastLnum, arguments, isNonMatchingLines ) 78 | let [l:pattern, l:register] = ingo#cmdargs#pattern#ParseUnescaped(a:arguments, s:writableRegisterPattern) 79 | let l:register = (empty(l:register) ? '"' : l:register) 80 | 81 | return s:Grep(a:firstLnum, a:lastLnum, a:isNonMatchingLines, l:pattern, '', l:register) 82 | endfunction 83 | function! ExtractMatches#GrepRangeToReg( firstLnum, lastLnum, arguments, isNonMatchingLines ) 84 | let [l:pattern, l:range, l:register] = ingo#cmdargs#pattern#ParseUnescaped(a:arguments, '\s\+\(' . ingo#cmdargs#range#RangeExpr(). '\)\%(\s\+\(' . s:writableRegisterPattern . '\)\)\?', 2) 85 | if empty(l:range) && ! empty(l:pattern) || l:pattern ==# a:arguments 86 | " Prefer (mandatory) range over (optional) pattern; it might get parsed incorrectly. 87 | let l:pattern = '' 88 | let [l:range, l:register] = ingo#cmdargs#register#ParseAppendedWritableRegister(a:arguments, '[-+,;''[:alnum:][:space:]\\"|]\@![\x00-\xFF]') 89 | endif 90 | let l:register = (empty(l:register) ? '"' : l:register) 91 | 92 | if empty(l:range) 93 | call ingo#err#Set('Wrong syntax: Need to pass {range}') 94 | return 0 95 | endif 96 | "****Dechomsg '****' string([l:pattern, l:range, l:register]) 97 | return s:Grep(a:firstLnum, a:lastLnum, a:isNonMatchingLines, l:pattern, l:range, l:register) 98 | endfunction 99 | 100 | function! s:SpecialReplacement( pattern, replacement ) 101 | let l:specialAtomExpr = '\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@]\?[''].\|%[<>]\?\d\+[lcv]\)' 102 | if ! empty(a:replacement) && a:pattern =~# l:specialAtomExpr 103 | " As there is no "match in buffer and return substitutions as string" 104 | " function, the substitution for {replacement} has to be applied 105 | " separately, on the extracted match. But then, context for {pattern} is 106 | " lost, and neither location-aware atoms (like /\%v) nor lookahead / 107 | " lookbehind can be used. 108 | 109 | " To alleviate that problem, we can at least add a heuristic to drop ...\zs 110 | " and \ze... from the pattern (having fulfilled its limiting condition) 111 | " for the replacement (which is now done on the sole match). 112 | " Note: This simplistic rule won't correctly handle the atoms inside 113 | " branches. 114 | " Note: Atoms that change the magicness or case sensitivity cannot 115 | " simply be dropped. We can solve the former by normalizing them out. As 116 | " the latter globally affect the entire pattern, we need to keep them. 117 | let l:replacePattern = ingo#regexp#magic#Normalize(a:pattern) 118 | let l:keepCaseSensitivityAtomPattern = '.*\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@ 0 275 | call s:PutMatchesToRegister(l:accumulatorReplacements, s:yankReplacement, l:register) 276 | echo printf('%d %smatch%s yanked', len(l:accumulatorReplacements), (a:isUnique ? 'unique ' : ''), (len(l:accumulatorReplacements) == 1 ? '' : 'es')) 277 | endif 278 | return 1 279 | catch /^Vim\%((\a\+)\)\=:/ 280 | call ingo#err#SetVimException() 281 | return 0 282 | endtry 283 | endfunction 284 | function! s:ExpandIndexInRepl( replacement, index ) 285 | return substitute(a:replacement, '\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@', a:index, 'g') 289 | endfunction 290 | function! s:Collect( accumulatorMatches, accumulatorReplacements, isUnique ) 291 | let l:match = submatch(0) 292 | if a:isUnique 293 | let l:idx = index(a:accumulatorMatches, l:match) 294 | if l:idx == -1 295 | call add(a:accumulatorMatches, l:match) 296 | let l:idx = len(a:accumulatorMatches) - 1 297 | endif 298 | else 299 | call add(a:accumulatorMatches, l:match) 300 | let l:idx = len(a:accumulatorMatches) - 1 301 | endif 302 | 303 | if len(a:accumulatorReplacements) < l:idx + 1 304 | " This is a newly added match; need to process the replacement here in 305 | " order to be able to let sub-replace-expressions have access to 306 | " context-dependent functions like submatch(), line(), etc. 307 | call add(a:accumulatorReplacements, s:ReplaceYank(l:match, l:idx)) 308 | endif 309 | 310 | if s:substReplacement =~# '^\\=' 311 | " Handle sub-replace-special. 312 | return eval(s:ExpandIndexInExpr(s:substReplacement[2:], l:idx)) 313 | else 314 | " Handle & and \0, \1 .. \9, and \r\n\t\b (but not \u, \U, etc.) 315 | return ingo#subst#replacement#ReplaceSpecial('', s:ExpandIndexInRepl(s:substReplacement, l:idx), '\%(&\|\\[0-9rnbt]\)', function('ingo#subst#replacement#DefaultReplacer')) 316 | endif 317 | endfunction 318 | function! s:ReplaceYank( match, idx ) 319 | if empty(s:yankReplacement) 320 | " When no replacement has been specified, yank the original matches with 321 | " trailing newlines. This is consistent with :YankMatches/{pat}//, and 322 | " better than simply returning nothing here, which would yank N-1 323 | " newlines. 324 | return a:match 325 | elseif s:yankReplacement =~# '^\\=' 326 | return eval(s:ExpandIndexInExpr(s:yankReplacement[2:], a:idx)) 327 | else 328 | let l:replacement = s:SpecialReplacement(s:pattern, s:ExpandIndexInRepl(s:yankReplacement, a:idx)) 329 | if type(l:replacement) == type([]) 330 | return substitute(a:match, l:replacement[0], l:replacement[1], 'g') 331 | else 332 | return substitute(a:match, (empty(s:pattern) ? @/ : s:pattern), l:replacement, '') 333 | endif 334 | endif 335 | endfunction 336 | 337 | function! ExtractMatches#PutMatches( lnum, arguments, isOnlyFirstMatch, isUnique ) 338 | call ingo#register#KeepRegisterExecuteOrFunc( 339 | \ function('ExtractMatches#YankAndPaste'), 340 | \ 'Yank' . (a:isUnique ? 'Unique' : '') . 'Matches' . (a:isOnlyFirstMatch ? '!' : '') . ' ' . a:arguments, 341 | \ a:lnum 342 | \) 343 | endfunction 344 | function! ExtractMatches#YankAndPaste( yankCommand, lnum ) 345 | execute a:yankCommand 346 | execute a:lnum . 'put' 347 | endfunction 348 | 349 | let &cpo = s:save_cpo 350 | unlet s:save_cpo 351 | " vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax : 352 | --------------------------------------------------------------------------------