(Neo)vim substitute over block/regular visual range, and not line visual range - vim

If I highlight a block of text in visual mode in Vim and then press :, I can run a command on it. For example
:'<,'>s/foo/bar/g to substitute,
or :'<,'> !bash to send the lines to the bash shell and get the output,
or :'<,'>sort to sort those lines,
or :'<,'> !some_cli_util to send those lines to some_cli_util
This is fine if the text I want to execute is conveniently on it's own line, but often that isn't the case. For example, if I'm writing docs and want to give and example of a cli command and it's output, then it would be nice to just write:
$ head requirements.txt
and then to select the text head requirements.txt, type : !sh<CR>, and be given the actual output of the command. But in reality, when I select head requirements.txt and type :, then my selection gets expanded to a full-line selection, which causes the leading $ to be clobbered in with the actual command, and so because of the leading $, the command fails.
A less trivial example of where this would be useful, is if I had a LaTeX table like:
\begin{table}
\begin{tabular}{lllll}
col1 & col2 & col3 & col4 & col5 \\
foo19s & fook2e & foojd9 & alfooc & 9dfooj \\
9foo1s & ekfoo2 & lqfoo & alfoo & 9efoo9s \\
1sfoo9 & d9fooe & fooj9d & alsdfo & 9hfooje \\
19sfoo & 9dfooe & lqfoo & dfoo9e & ajfoo
\end{tabular}
\end{table}
and I wanted to replace all the instances of foo with bar, but only in the columns titled col2. If I could block-select only col2, and then execute a substitute command like :`<,`>s/foo/bar/g then this would work. But currently, if I block select only col2 and then press :, Vim will auto-select the entire range of lines that were in my block selection.
Ideally, I'd like vim to be smart enough such that if am in block-visual mode and press :, then it'll use the :`<,`> range selector, but if I'm in line-visual mode and press : then it'll use the :'<,'> range selector.
:h `< tells me that the `< and `> marks do exist, but I can't see how to apply them to commands.
What can I do to enable vim (or neovim) to execute commands on block-selections? or is there an issue open for this problem?

I can't just use the regular visual-line selector :'<,'>s/ba/__/g because that will incorrectly replace all the bazs with __zs.
Yes, you definitely can, but with a more refined search pattern:
:[range]s/ba\zer/__/g
See :help \ze.
--- EDIT ---
As explained in the other answer, Ex commands only work on lines and ranges are ranges of lines. You wont be able to pass something else than one or several lines to :help :s, :help :range! or :help :sort.
There is no way around that.
While the range of Ex commands is always linewise, some of them may honor the visual selection in some way.
In the case of :help :s, what you can do is, again, use a more refined search pattern:
<C-v><motions>
:[range]s/\%V.*\zsfoo\ze.*%V/bar
See :help \%V.

How to get it done is described perfectly in romainl's answer. I'd like to answer the "why?", though.
You wrote:
Ideally, I'd like vim to be smart enough such that if am in block-visual mode and press :, then it'll use the :`<,`> range selector, but if I'm in line-visual mode and press : then it'll use the :'<,'> range selector.
This behavior is explicitly impossible, at least for now (Vim 9.0). Quoting from :h :visual_example:
Currently the ":" command works on whole lines only. When you select part of
a line, doing something like ":!date" will replace the whole line. If you
want only part of the line to be replaced you will have to make a mapping for
it. In a future release ":" may work on partial lines.
Likewise, :h :s says (emphasis mine):
For each line in [range] replace a match of {pattern}
It is a common misconception that Vim would operate on the (block) visual selection only. Not all commands do. Instead, they operate on lines that are selected.
So you'll have to use a line-wise substitution like (as pointed out by romainl and included for the sake of completeness) :s/ba\zer/__/g.

It's a reasonable tool to look for, but as others have pointed out, ranges are only line-wise. For the cases where you'd like to use a selection as an argument to an external program, I'd recommend yanking and pasting in the command-line.
A plugin that might give you a general-purpose solution is vis: https://github.com/vim-scripts/vis. Instead of :'<,'>s/foo/bar/g, you'd execute :'<,'>B s/foo/bar/g, which is less convenient, but will do the trick.

Related

Is there any way to enclose a variable with print() statement or any function in vim especially using vim vs code extension

suppose I have a variable named
a_variable
is there a surround or some combination of keystrokes which will do
print(a_variable)
or
print("a_variable: ", a_variable)
when my cursor in on the line where a_variable is defined ?
I could not find any relevant material on the web .
Any help is appreciated.
This answers is written with Vim in mind. Some, all, or none of it may apply to your specific Vim emulator so YMMV.
With the surround plugin
First case
You can position your cursor on a_variable and do:
ysiwfprint(<CR>
to obtain:
print(a_variable)
Second case
It is currently impossible to achieve with surround only.
Without surround
First case
You can position your cursor on a_variable and do:
ciwprint(<C-r>")<Esc>
Second case
The scond case is a variant of the first case where you insert the variable name two times instead of one:
ciwprint("<C-r>": ", <C-r>")<Esc>
Turn it into a mapping if you need to do it often.
See :help c, :help iw, :help i_ctrl-r, :help "".

c, i and s commands combined in VIM

I've been using VIM for a while and it surprises me each time. Under "Building Sentences" section in this tutorial, I saw the combination of commands cis and yip. I have used Vim quite a while and I am familiar with most commands in Normal Mode. I also know combining the commands in a meaningful way to produce combined actions.
However, the examples I showed above (cis and yip) totally broke my understanding of VIM command system in normal mode. "c" stands for change, "i" stands for insert and "s" stands for substitute but combined action is different than I would expect. I also went through VIM help files but never saw an example illustrating given usage.
Could someone clarify what's going on ?
cis
In vim help it is described as follows
:help c
"Delete {motion} text [into register x] and start insert …"
The next part of the command cis refer to the "motion" part. These commands are for text object selection. An explanation on the different types of text object selections can you get here:
:help text-objects
e.g. for
is – "inner sentence", select [count] sentences …
Analog to the explanation above its the same with yip
:help y
"Yank {motion} text [into register x] … "
And the text selection part yip
ip – "inner paragraph", select [count] paragraphs (see paragraph) …
In this case i does not stand for Insert and s not for substitute.
cis = change inner sentence.
This is completly logic once you understand the basic principle. Each command is like a sentence, it needs an Verb(Action) and an Noun(Object) and there are modifiers.
So the first button is your action C (Change). Now the following keystrokes will not be actions, until the c action ended (Until an Object is provided, or an invalid sequence is inserted). I (inner) is a modifier here and S the Object (Sentence).
I find this especially usefull for Changing words. if you only press cw on a word, you have to have the cursor on the beginning of the word.
With ciw you can change the whole word regardless of the cursor position (Note if you have / or some other seperators in the word, you maybe need ciW)
same letter can have different meanings. E.g. (/{ move to sentences/paragraph back, but ci( or ci{ means change in (...)/{...}.
Same as your s case, s in normal mode alone, does delete & start insert, however in cis, das means sentence.
p case: in normal mode alone, means paste, however in cip, yap ... means paragraph.
:h text-objects
will show you the concept of text-objects. It is a must skill for vim user. ;-)

Pasting inside delimiters without using visual selection

In Vim, let's say I want to replace the contents of one String with the content of another.
Original input
var1 = "January"
var2 = "February"
Desired output
var1 = "January"
var2 = "January"
What I would usually do is:
Move cursor to line 1
y i " (yank inner quotes)
Move cursor to the destination quote in line 2
v i " p (visual select inner quotes, paste)
While this works well, I generally try to avoid visual mode when possible, so I am not completely satisfied with my step 4 (v i " p).
Is there any way to specify a "destination paste area" without using Visual mode? I suspect it might be something chained to g, but I can't think of anything.
There are many ways of doing this however using visual mode is the easiest.
Use the black hole register to delete the content then paste. e.g. "_di"P
Do ci"<c-r>0. <c-r> inserts the contents of a register
Simply paste and then move over a character and delete the old text. e.g pldt"
However visual mode still has my vote. I find that the concerns most people have is that using visual mode + paste is that the default register is swap with the selected text and it doesn't repeat well. Good news everybody! The 0 register always stores the last yank. The bad news is visual mode still doesn't repeat well. Take a look at this vimcast episode, Pasting from Visual mode, for more information. It mentions a few plugin that help with this.
I need this so often, I wrote a plugin to simplify and allow maximum speed: ReplaceWithRegister.
This plugin offers a two-in-one gr command that replaces text covered by a {motion} / text object, entire line(s) or the current selection with the contents of a register; the old text is deleted into the black-hole register, i.e. it's gone. It transparently handles many corner cases and allows for a quick repeat via the standard . command. Should you not like it, its page has links to alternatives.
It's not particularly pretty, but here goes:
Go to line one and yi"
Move to line two
Type "_di"hp
That deletes what's in the quotes, but sends the deleted text to a black hole register. Then it moves the cursor back one, and pastes what you yanked from line one.
All in all, you can start on the first line and type yi"j"_di"hp. Why is it that people find vim intimidating? ;)
Alternatively, yank the first line as normal, then drop to line two and type ci"<Ctrl+p> and select the previously yanked text from the menu.
Excerpt from my .vimrc:
" Delete to 'black hole' register
nnoremap <leader>d "_d
vnoremap <leader>d "_d
So, you just need to \di"P as your last step (assuming you use \ as a <leader>).
There is a plugin that addresses this problem precisely. It was presumably born out of the same feeling you're feeling, namely that Visual mode often feels less than ideal to advanced Vim users.
operator-replace – Operator to replace text with register content
With operator-replace installed the replacing goes like this.
Yank the word inside the quotes.
yi"
Move to the target line
j
Replace inside the quotes with the yanked text. (I've set up gr as the replace operator: :map gr <Plug>(operator-replace). Pick your own mapping.)
gri"
Check it out! This is part of the truly excellent textobj/operator frameworks. I couldn't work without them.

How to remove quotes surrounding the first two columns in Vim?

Say I have the following style of lines in a text file:
"12" "34" "some text "
"56" "78" "some more text"
.
.
.
etc.
I want to be able to remove the quotes surrounding the first two columns. What is the best way to do this with Vim (I'm currently using gVim)?
I figured out how to at least delete the beginning quote of each line by using visual mode and then enter the command '<,'>s!^"!!
I'm wondering if there is a way to select an entire column of text (one character going straight down the file... or more than 1, but in this case I would only want one). If it is possible, then would you be able to apply the x command (delete the character) to the entire column.
There could be better ways to do it. I'm looking for any suggestions.
Update
Just and FYI, I combined a couple of the suggestions. My _vimrc file now has the following line in it:
let #q=':%s/"\([0-9]*\)"/\1/g^M'
(Note: THE ^M is CTRLQ + Enter to emulate pressing the Enter key after running the command)
Now I can use a macro via #q to remove all of the quotes from both number columns in the file.
use visual block commands:
start mode with Ctrl-v
specify a motion, e.g. G (to the end of the file),
or use up / down keys
for the selected block specify an action, e.g. 'd' for delete
For more see
:h visual-mode
Control-V is used for block select. That would let you select things in the same character column.
It seems like you want to remove the quotes around the numbers. For that use,
:%s/"\([0-9]*\)"/\1/g
Here is a list of what patterns you can do with vim.
There is one more (sort of ugly) form that will restrict to 4 replacements per line.
:%s/^\( *\)"\([ 0-9]*\)"\([ 0-9]*\)"\([ 0-9]*\)"/\1\2\3\4/g
And, if you have sed handy, you can try these from the shell too.
head -4 filename.txt | sed 's/pattern/replacement/g'
that will try your command on the first 4 lines of the file.
Say if you want to delete all columns but the first one, the simple and easy way is to input this in Vim:
:%!awk '{print $1}'
Or you want all columns but the first one, you can also do this:
:%!awk '{$1="";$0=$0;$1=$1;print}'
Indeed it requires external tool to accomplish the quest, but awk is installed in Linux and Mac by default, and I think folks with no UNIX-like system experience rarely use Vim in Windows, otherwise you probably known how to get a Windows version of awk.
Although this case was pretty simple to fix with a regex, if you want to do something even a bit more advanced I also recommend recording a macro like Bryan Ward. Also macros come easier to me than remembering which characters need to be escaped in vim's regexes. And macros are nice because you can see your changes take place immediately and work on your line transformation in smaller bits at a time.
So in your case you would have pressed qw to start recording a macro in register w (you can of course use any letter you want). I usually start my macros with a ^ to move to the start of the line so the macro doesn't rely on the location of the cursor. Then you could do a f" to jump to the first ", x to delete it, f" to jump to the next " and x to delete that too. Then q to finish recording.
Instead of making your macro end on the next line I actually as late as today figured out you can just V (visually line select) all lines you want to apply your macro to and execute :normal #w which applies your macro in register w to each visually selected line.
See column editing in vim. It describes column insert, but basically it should work in the same way for removing.
You could also create a macro (q) that deletes the quotes and then drops down to the next line. Then you can run it a bunch of times by telling vi how many times to execute it. So if you store the macro to say the letter m, then you can run 100#m and it will delete the quotes for 100 lines. For some more information on macros:
http://vim.wikia.com/wiki/Macros
The other solutions are good. You can also try...
:1,$s/^"\(\w\+\)"/\1/gc
For more Vim regex help also see http://vim.wikia.com/wiki/Search_patterns.
Start visual-block by Ctrl+v.
Jump at the end and select first two columns by pressing: G, EE.
Type: :s/\%V"//g which would result in the following command:
:'<,'>s/\%V"//g
Press Enter and this will remove all " occurrences in the selected block.
See: Applying substitutes to a visual block at Vim Wikia

How to repeat a command with substitution in Vim?

In Unix the ^ allows you to repeat a command with some text substituted for new text. For example:
csh% grep "stuff" file1 >> Results
grep "stuff" file1
csh% ^file1^file2^
grep "stuff" file2
csh%
Is there a Vim equivalent? There are a lot of times I find myself editing minor things on the command line over and over again.
Specifically for subsitutions: use & to repeat your last substitution on the current line from normal mode.
To repeat for all lines, type :%&
q: to enter the command-line window (:help cmdwin).
You can edit and reuse previously entered ex-style commands in this window.
Once you hit :, you can type a couple characters and up-arrow, and it will character-match what you typed. e.g. type :set and it will climb back through your "sets". This also works for search - just type / and up-arrow. And /abc up-arrow will feed you matching search strings counterchronologically.
There are 2 ways.
You simply hit the . key to perform an exact replay of the very last command (other than movement). For example, I type cw then hello to change a word to "hello". After moving my cursor to a different word, I hit . to do it again.
For more advanced commands like a replace, after you have performed the substition, simply hit the : key then the ↑ up arrow key, and it fills your command line with the same command.
To repeat the previous substition on all lines with all of the same flags you can use the mapping g&.
If you have made a substitution in either normal mode :s/A/B/g (the current line) or visual mode :'<,>'s/A/B/g (lines included in the current selection) and you want to repeat that last substitution, you can:
Move to another line (normal mode) and simply press &, or if you like, :-&-<CR> (looks like :&), to affect the current line without highlighting, or
Highlight a range (visual mode) and press :-&-<CR> (looks like :'<,'>&) to affect the range of lines in the selection.
With my limited knowledge of Vim, this solves several problems. For one, the last visual substitution :'<,'>s/A/B/g is available as the last command (:-<UP>) from both normal and visual mode, but always produces an error from normal mode. (It still refers to the last selection from visual mode - not to the empty selection at the cursor like I assumed - and my example substitution exhausts every match in one pass.) Meanwhile, the last normal mode substitution starts with :s, not :'<,'>s, so you would need to modify it to use in visual mode. Finally, & is available directly from normal mode and so it accepts repetitions and other alternatives to selections, like 2& for the next two lines, and as user ruohola said, g& for the entire file.
In both versions, pressing : then & works as if you had pressed : and then retyped s/A/B/, so the mode you were in last time is irrelevant and only the current cursor line or selection determines the line(s) to be affected. (Note that the trailing flags like g are cleared too, but come next in this syntax too, as in :&g/: '<,'>&g. This is a mixed blessing in my opinion, as you can/must re-specify flags here, and standalone & doesn't seem to take flags at all. I must be missing something.)
I welcome suggestions and corrections. Most of this comes from experimentation just now so I'm sure there's a lot more to it, but hopefully it helps anyway.
Take a look at this: http://vim.wikia.com/wiki/Using_command-line_history for explanation.

Resources