How to restrict operations to certain lines? - vim

I have to work on some relatively huge code files in vim.
How do I restrict some operations like find-next normal-n and others to a certain function / block?
How would I visually know if I'm within that block or outside it?
Looking and line numbers seems awkward, specially that the line numbers I need to work with are generally 5 digits long!

You can set marks at the beginning and end of your block using m (e.g. ma, mb) and then refer to them in the range of a ex command as :'a,'b.
Like bignose said you can use a visual block to create an implicit region for a command, which can be passed to an ex command using :'<,'>
You can also use regexes to delimit a block (e.g. for all the lines between start and end use :/start/,/end/
For example, to make a substitution in a range of lines:
:'<,'>s/foo/bar/g
:'a,'bs/baz/quux/g
:/harpo/,/chico/s/zeppo/groucho/g
The last visually selected range is remembered so you can reuse it without reselecting it.
For more on ranges, see :help range
You can further restrict yourself within a range by using g//. For example, if you wanted to replace foo with bar only on lines containing baz in the selected range:
:'<,'>g/baz/s/foo/bar/g
When you define a new ex command, you can pass the range given to the ex-command using as <line1>,<line2>. See :help user-commands for more on defining ex-commands.
Within a vimscript function, you can access an implicitly passed range using a:firstline and a:lastline. You can detect your current linenumber using line('.'), and detect whether you're inside the block using normal boolean logic (a:firstline <= line('.') && line('.') <= a:lastline). See :help functions for more on vimscript functions.
Another approach, is to use vim's inner i and single a selectors. For example, to delete the entirety of a double quoted string, use da" in normal mode. To leave the quotes, use di". See :help object-select for more.

Vimtips has exactly what you were looking for:
Search in current function
See also :help pattern-atoms

Vim uses Shift-v to select by lines. Having selected a series of lines, many commands will then be restricted to the selection.
I don't think the search commands (/, n, etc.) are restricted that way though.

For commands like search and replace, you can easily limit yourself to a couple of lines:
:.,+3s/foo/bar/cg
replaces every occurrence of "foo" in the current line and the following 3 lines with "bar". I don't think you can do that for search, though.

Related

Use line range within a fold

Is it possible to restrict line range to within current fold? Say I want to do a substitution from second line through end of fold i.e.
2,$s/str/replace/
but I want it to apply to current fold only.
There are motions for jumping to the beginning of the current fold (:help [z) or to its end (:help ]z) but they don't have equivalent marks. If that were the case, one could do this with the imaginary '* mark:
:2,'*s/str/replace/
but that mark doesn't exist so it won't work.
One way to achieve what you want would be to visually select the area:
]zv[zj
and then do your substitution on the visual selection:
:'<,'>s/str/replace/
with the range '<,'> automatically inserted for you.
Or you could go the "grok vi" way and place manual marks:
]zme[zmb
before using them in your substitution:
:'b+,'es/str/replace/
but it seems less optimal than the "visual way".
Another solution would be to close the fold:
zc
and do the substitution on the closed fold, with confirmation so that you can skip the substitutions that occur on the first line:
:s/str/replace/c
So there are two approaches, here: one where care is put into defining the exact range and one where care is put into not substituting the wrong thing.

How to repeat the same search and replace command over disjunct line ranges in Vim?

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:
:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g
I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).
Short of writing a function, is there any neater way of doing this?
On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running
:'a,'b s/\=#s/\=#r/ | 'c,'d s/\=#s/\=#r/ | 'e,'f s/\=#s/\=#r/
This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=#r was fine, but the \=#s bit in the search pattern gave me an error.
Any tips would be appreciated.
If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).
The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.
Then use :g in conjunction with :s. eg:
:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g
Finally, remove the temporary prefixes.
In order to get rid of the necessity to retype the same search
pattern, substitution string and flags, one can simply use the
:& command with the & flag:
:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&
(See :help :& for details.)
Instead of using marker use this one :
:68,104s/FOO/BAR/g << substitue from line 68 to 104
This should make your job a little bit easier and clearer.
inspired by #Vdt's answer:
I am not sure but you could write all the substitutions down in a file and source that file i think.
substitutions.vim:
68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g
and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.
With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.
The s/\=#s/\=#r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=#s. Preferably use the default register, then it's a simple s//, but you probably know that already.
When performed over a closed fold, substitutions are limited to that fold.
fold each region
put the cursor on one closed fold
perform the substitution: :s/foo/bar<CR>
move to the next closed fold with zj or zk
use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
repeat…
You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.
Here's the simplest way to do it
:5,10s/old/new/g
5,10 : startlinenum,endlinenum

Setting editing range in Vim

Is there a way to set the editing range for a file in Vim so that it will apply to any subsequent command? I'm about to do some major refactoring of a function and I would like to limit all of my changes (substitutions in this case) to just that function. In the past I've just copied the function to a new buffer, made the changes and copied it back to the main file, but I'm hoping there is a more clever way.
I know I can set a mark at the start and end of the function and use the marks for the range of the substitute command and I know I can use visual mode to select the range of lines, do something, use gv to reselect and then do the next command, but both of these methods rely on me remembering to specify the range before each command.
Any other suggestions?
Here is a cool trick with folds:
Fold your whole function.
Perform a substitution, :s/foo/bar/c<Enter>, note the c flag for "confirmation", it's vital when doing refactoring in Vim. The substitution will be applied to every line in the fold. AFAIK, you can run any Ex command that way but I've never used this trick with anything else than :s and :g/:v.
Hit y/n to accept/reject each substitution.
After you accepted/rejected the last substitution the fold goes back instantly to its "closed" state: you don't have to type a range for the next substitution.
GOTO 2
Assuming vim was compiled with +textobjects, you can select the function as a block. So perhaps you are looking for:
va{:s/foo/bar
To replace foo with bar only in the current {} bracketed function. If your functions are not delimted by braces, you could write a function to select the range according to some syntax rules, but perhaps this is good enough for your needs.
Use Narow Region Plugin of vim http://www.vim.org/scripts/script.php?script_id=3075 put them on your vim folder and use like this:
:[range]NR
See this page: https://github.com/chrisbra/NrrwRgn

Using Vim, how do you use a variable to store count of patterns found?

This question was helpful for getting a count of a certain pattern in Vim, but it would be useful to me to store the count and sum the results so I can echo a concise summary.
I'm teaching a class on basic HTML to some high schoolers, and I'm using this script to be quickly check numbers of required elements throughout all their pages without leaving Vim. It works fine, but when students have more than 10 .html files it gets cumbersome to add up the various sections by hand.
Something like:
img_sum = :bufdo %s/<img>//gen
would be nice. I think I'll write a ruby script to check the pages more thoroughly and check for structure, but for now I'm curious about how to do this in Vim.
The problem can be solved by a counter separate from the one built-in into the
:substitute command: Use Vim-script variable to hold the number of pattern
matches. A convenient way to register every match and modify a particular
variable accordingly, is to take advantage of the substitute with an
expression feature of the :substitute command (see :help sub-replace-\=).
The idea is to use a substitution that evaluates an expression increasing
a counter on every occurrence, and does not change the text it is operating
on.
The first part of the technique cannot be implemented straightforwardly
because it is forbidden to use Ex commands in expressions (including \=
substitute expressions), and therefore it is not possible to use the :let
command to modify a variable. Answering the question "gVim find/replace
with counter", I have proposed a simple trick to overcome that limitation,
which is based on using a single-item list (or dictionary containing a single
key-value pair). Since the map() function transforms a list or a dictionary
in place, that only item could be changed in a constrained expression context.
To do that, one should call the map() function passing an expression
evaluating to the new value along with the list containing the current value.
The second half of the technique is how to avoid changing text when using
a substitution command. In order to achieve that, one can make the pattern
have zero-width by prepending \ze or by appending \zs atoms to it (see
:help /\zs, :help /\ze). In such a way, the modified pattern captures
a string of zero width just before or after the occurrence of the initial
pattern. So, if the replacement text is also empty, substitution does not
cause any change in the contents of a buffer. To make the substitute
expression evaluate to an empty string, one can just extract an empty
substring or sublist from the resulting value of that expression.
The two ideas are put into action in the following command.
:let n=[0] | bufdo %s/pattern\zs/\=map(n,'v:val+1')[1:]/ge
I think that answer above is hard to understand and more pretty way to use external command grep like this:
:let found=0
:bufdo let found=found+(system('grep "<p>" '.expand('%:p') . '| wc -l'))
:echo found

Vim - Search and replace the results

I'm getting more and more comfortable with Vim after a few months.
BUT, there is only one simple feature I can't get any answer from the web. That is "Search and replace the results". The problem is that I know:
:/keyword to search, and hit enter "keyword" will be highlighted (of course with set hlsearch)
n, or N to navigate
:% s/keyword/new_keyword/g to replace all occurences of keyword with new_keyword.
BUT, I would think that there must be a way to search, and replace the matched keyword (highlighted) with any new_keyword WITHOUT doing ":% s/keyword/new_keyword/g", which is a lot of typing considering search & replace is such a day-to-day feature.
Any answers/comments will be greatly appreciated!
If you've already done a search you can do a substitution for the same pattern by simply leaving out the pattern in the substitute command. eg:
/keyword
searchs for "keyword", and then:
:%s//new_keyword/g
will replace all occurrences of "keyword" with "new_keyword".
Searching and using the dot command (you didn't meantion you are using the dot command, that's why I highlight it) to repeat the last input action is my best bet here.
I use s///g for search and replace.
Well, since #keyword# and #new_keyword# account for most of the characters, and you need some way to differentiate between them (i.e., a character in vim, or tab between entry fields in dialog in a different editor), you're left with maybe four or five keystrokes beyond that.
So I think you're probably overestimating number of keystrokes and also forgetting that (1) it becomes very natural, and (2) working this way allows you also to naturally modify the action performed by specifying a different range or option flag.
But you can cut down on keystrokes. If you want you can map a key to automatically bring up the command line with '%s/' already in place. e.g.:
nmap s :%s/
The command above would remap 's' (I'm not recommending remapping to that key, but it gives the idea) and set you up to insert the keyword.
Also, you can set the 'gdefault' option to default to substituting multiple times per line. This lets you skip the ending '/g' in your keystrokes:
set gdefault
See ':h gdefault' for help section on that option.
In the end I would say just get used to the default way it works, because using it that way allows you to keep same basic operation when you want to specify different ranges or option flags, and creating a new special map is just another thing to remember. gdefault may be worth setting if you think you're going to want it majority of time, adding /g flag at end when gdefault is set has effect of turning /g off. . .
Move to the first highlighted word then record a macro for replacing the word and moving to the next one, e.g:
gg
n
qq
caw new_word^[
n
q
#q
##
##
...

Resources