How to put a normal command and a search command together in a vimscript? - search

I'm trying without success to put a normal command together with a search command in a function using vimscript:
This is my command:
d/\S
(delete from current cursor position to next "not space" character)
I don't know how to put this in vimscript.
I tried this but it doesn't work:
normal d
let #/ = \\S

Try this:
call search("\\S", "sW")
:normal d`'
The first line sets the ' previous context mark to the current cursor position, and then moves the cursor to the first non-whitespace char.
The second line then deletes backwards to the previous cursor position.

Related

VIM: Why is the dot operator working differently?

I want to understand what gets stored in Vim's dot(.) register.
Consider the following text:
This is Line one
This is Line two
This is Line three
With the cursor on the first line, if I do A;<esc> I can repeat the same action for the next line by j.
However, if I do the action like removing the last character on the first line by $x and then try to repeat it for the next line by j., it is not removing the last character of the line, instead it just deletes the character under the cursor.
So why is dot command able to remember the position in the first example A;<esc> whereas not able to do the same for $x ?
From :help .:
. Repeat last change, with count replaced with [count].
Also repeat a yank command, when the 'y' flag is
included in 'cpoptions'. Does not repeat a
command-line command.
With A;, the change is to insert a ; at the end of the current line. A moves the cursor and switches to insert mode.
With $x, the $ first moves the cursor, then the x command deletes a character. They are not linked together, so the change is only the deletion of the character at the current cursor position.
(Put another way, the motion is only part of the change if the command takes a motion operator after the command, like d, or if the motion is implicit in the command, like with A.)

How to run a search and replace command without cursor moving in Vim?

In Vim, when I run a substitution command like
:%s/foo/bar/g
it replaces all occurrences of foo with bar in the entire buffer. When it completes, the cursor moves to the last location where foo was replaced with bar.
How can I run :%s/foo/bar/g without having the cursor leave its original location where it was before the substitution command was issued?
Is there some option I can set in the .vimrc file?
When the :substitute command is run, prior to any replacements being
carried out, the current position of the cursor is stored in the jump
list (see :help jumplist).
In order to return to the position before the latest jump, one can use
the `` or '' Normal-mode commands. The former
jumps exactly to the stored position; the latter jumps to the first
non-whitespace character on the line the stored position belongs to.
It is possible to both invoke a substitution command and move
the cursor back afterwards, at once, by issuing the command
:%s/pat/str/g|norm!``
or, if jumping to the containing line is sufficient, by using
the command
:%s/pat/str/g|''
It is not necessary to preface '' with norm! in the latter
command, because the '' address is allowed by the range syntax
of Ex commands and refers to the same line the Normal-mode
command '' jumps to (see :help :range); both just look into
the contents of the ' psudo-mark.
I just type Ctrl+O after the replace to get back to the previous location.
It's old, but for anyone coming across this question, I wanted to share my solution since it will work correctly even if nothing is substituted:
:exe 'norm m`' | %s/pattern/substitution/eg | norm g``
exe is needed since norm treats the bar as an argument.

What is the best way to refactor a Ruby ‘if’ statement into one-line shorthand form in Vim?

I have the following Ruby code:
if some_cond && another
foo_bar
end
and I want to change it to:
foo_bar if some_cond && another
What are the most idiomatic ways to do that in Vim?
Assuming that the cursor is located at the if-line prior to
refactoring (not necessarily at the beginning of that line),
I would use the following sequence of Normal-mode commands:
ddjVpkJ<<
or this chain of Ex commands:
:m+|-j|<|+d
Here the if-line is first moved down one line by the :move + command.
The :move command cuts a given range of lines (the current line, if
not specified) and pastes it below the line addressed by the argument.
The + address is a shorthand for .+1 referring to the next line
(see :help {address}).
Second, the line containing the body of the conditional statement is
joined with the just moved if-line. The :join command concatenates
a given range of lines into a single line. The - range is a shortened
form of the .-1 address referring to the line just above the cursor
(see :help {address}).
Third, the newly joined line is unindented by one shiftwidth using
the :< command.
Finally, the remaining end-line, which can now be addressed as +,
is removed by the :delete command.
I see few (probably non-optimal) solutions:
cursor in first character in first line:
D - remove if condition but leave cursor in same position (don't delete line)
J - join next line to current
A <Space> <ESC> - append space and exit to Normal mode
p - paste if condition
and then remove remaining end with jdd
cursor in first character in first line, as previously:
j - move to next line
dd - remove this line
k - move back to if condition
P - paste removed line before actual line, cursor should be placed to pasted line
J - join next line to current
== or << - unindent current line
and then remove remaining end with jdd
another solution:
j - move to second line
JD - join line with next, remove what was joined
dd - remove current line
k - step to previous line
PJ<< - paste, join and unshift
It's probably not optimal, but I do it without thinking, because most of this commands are in my muscle memory (you don't think how to move around you, how to yank/delete and paste most of the time, and joining line is also helpful to remember).
If you have virtualedit enabled in config, instead of A <Space> <Esc> you can $ <Space>, but I find $ harder to use than A followed by Ctrl-[ (it's simmilar to ESC).
As an advice: if you use some upper letter commands, try to chain them if it's possible, so you only need to keep Shift pressed and then execute some commands, instead of mixing upper and lower letter commands and pressing two keys at a time (upper letter is 2 key press, one is Shift). Once I found combo helpful for restarting server in console Ctrl+cpj, which sends Ctrl+c, Ctrl+p (previous command) and Ctrl+j (Enter key) with single Ctrl press. Since then I try to find simmilar time-saving combination in Vim too mostly with Shift, as Ctrl is not much used in Vim.
Yet another way:
ddpkJjdd
ddp swap the two lines
kJ move up and join the lines
== re-indent the line
jdd move down and delete the last line
There are probably 30 ways to do this.
Here is one, assuming you are starting from the end of the word end in normal mode:
dd (delete last line)
"aD (delete and copy foo_bar to buffer a)
dd (delete now-empty line 2)
"aP (paste from buffer a before caret)
aSpaceEsc (insert space and return to normal mode)
Again, "properly" rarely applies in Vim because there are so many ways to accomplish something. This is so small a change that even re-typing foo_bar could be justifiable.

move cursor to next line after input from filter command in vim

In vim I filter, say the current single line, using !! through a Unix command. To achieve this I defined the following shortcut in .vimrc
:map <Enter> !!mycommand<CR>:,+1<CR>
Pressing <Enter> this takes me to the line below the current if mycommand replaces my single input line be exactly one output line. If the output has more lines (number of lines unknown before command execution) it will still take me to the line below the current.
Now, I would like to know how I can always get to the first line below the inserted output of mycommand.
The modified shortcut would then allow me to 'execute' the text file line by line using just <Enter> displaying the output each time.
If there is no way to do this without any previous knowledge of the output of mycommand, maybe there is one knowing say the first character of each output line.
Thanks a lot!
You can use the special marks '[ and '], which mark the start and end of the last changed (or yanked) text. Change your map to:
:map <Enter> !!mycommand<CR>']+
Note that I'm using + in place of your ex command. This will jump to the first non-blank character in next line. If that's not what you want, you may try simply j or, use a shorter version of your original map:
:map <Enter> !!mycommand<CR>']:+1<CR>
You don't really need the comma, to make this a range. This command is just a simplified :#, where # is a line number to jump. Here you can use . meaning "current line", and then :.+1 moves to the next line. But you can omit the dot, and that's why :+1 does the same.

How to delete line(s) below current line in vim?

Is there a command to delete a line (or several lines) that is immediately below current line?
Currently I'm doing it as:
jdd and then . to repeat as needed.
Is there a command that would combine all these?
UPDATE: The reason I would like to have such command is that I don't like to move away from current position, yet be able to delete lines below.
The delete ex command will work nicely.
:+,$d
This will delete all the lines from current +1 till the end ($)
To delete the next 2 lines the follow range would work, +1,+2 or shorthand +,+2
:+,+2d
As #ib mentioned the :delete or :d command will move the cursor to the start of the line next to the deleted text. (Even with nostartofline set). To overcome this we can issue the `` normal mode command. `` will jump back to the exact position before the last jump, in this case the :d command. Our command is now
:+,+2denter``
Or as one ex command
:+,+2d|norm! ``
To make this easier we wrap this all up in a command:
command! -count=1 -register D :+,+<count>d <reg><bar>norm! ``
Now to delete the next following 3 lines:
:3D
This command can also take a {reg} like :delete and :yank do. So deleting the next 4 lines into register a would be:
:4D a
For more information
:h :d
:h :command
:h :command-register
:h :command-count
:h ``
dG should work.
This means delete all rows until end of file from current cursor.
This will delete ALL lines below the current one:
jdG
Unfortunately that will move the cursor to the beginning of current line after the deletion is made.
well, to do it simply you could use the xxdd command. Most of the time I know (at least have an idea) the size of the script I am editing. So, the command as below is usually more than enough :
99dd
999dd to remove 999lines starting at the cursor position.
9999dd
99999dd for very long script ;)
The other solutions are informative, but I feel it'd be simpler to use a macro for this:
qq (begins recording)
jddk (go down, delete the line, and go back up - i.e. the thing you want to do)
q (end recording)
Now you can do #q to perform this action, maintaining the cursor at the current position. You could also do something like 5#q to delete 5 lines below the cursor.
And finally, if you're repeating the action more than once, you could just type ## after the first time you run #q (this repeats the last used macro - in this case q)
This is a job for marks!
Try maj20dd`a
ma sets the file-specific mark 'a', j20dd does the deletion you want (20 lines in this case), and `a restores you to the mark's position (line and column).
Obviously this pattern can be extended to do anything you want before returning to the mark. If you use mA (or any other capital letter) the mark will actually be unique across files, so you can even edit elsewhere before returning. If you have a very frequent usage you could make it a macro as suggested above.
You could enter the number of lines to delete: j 20 dd k.
Just for the fun of it, you can define a little function that does
exactly what you described: deletes the next n lines below the
current line and restores the initial cursor position.
function! DeleteNextLines(n, reg)
let l = line('.')
let m = min([a:n, line('$')-l])
if m > 0
let c = col('.')
exe '+,+'.m 'd' a:reg
call cursor(l, c)
endif
endfunction
Also, you can define a command that accepts the number of lines
to delete (one, if omitted) and the register name to use as an
optional argument (just like the :delete command).
:command! -range=1 -register -bar D call DeleteNextLines(<count>, <q-reg>)
Additionally, you can define a mapping for triggering the above
:D command, if it is necessary.

Resources