Vim delete parent parenthesis and reindent child - vim

I'm trying to go from here:
const f = function() {
if (exists) { // delete this
const a = 'apple'
}
}
to:
const f = function() {
const a = 'apple'
}
What's the fastest way to delete and reindent everything in between?

Assuming that cursor is inside the braces; any number of lines and nested operators; "else"-branch is not supported:
[{"_dd<]}']"_dd
Explanation:
[{ go to previous unmatched brace
"_dd delete the "{"-line (now the cursor is in the first line of the block)
<]} decrease identation until the next unmatched "}"
'] go to the last changed line (i.e. "}"-line)
"_dd and delete it
If the cursor is initially set on the "{"-line and you don't care for 1-9 registers, the command can be simplified to dd<]}']dd

Assuming your cursor is somewhere on the line containing const a
?{^M%dd^Odd== (where ^M is you hitting the Enter key and ^O is you hitting Ctrl+O).
Broken down this is:
?{^M - search backwards/upwards for the opening brace
% - jump to the corresponding brace (closing brace)
dd - delete the current line
^O - jump to previous location (the opening brace)
dd - delete the line
== - indent current line
You don't need a special macro or function or anything to do this since vim gives you all the powerful text manipulation tools to do the task. If you find yourself doing this an awful lot then you could always map it to a key combination if you want.
The above only works for single lines inside curly braces, but the one below will work for multiple lines (again assuming you are on some line inside the curly braces)
<i{0f{%dd^Odd I'll leave you to figure out how this one works. Type the command slowly and see what happens.

Great answers all around, and, as pointed out, you can always map these keys to a shortcut. If you'd like to try a slightly more generic solution, you could check my "deleft" plugin: https://github.com/AndrewRadev/deleft.vim

Related

Selecting a Javascript arrow function in Vim

It's been a challenge for me to select a whole arrow function via Vim without using relative line numbers. Any thoughts on how I can accomplish that?
Here is an example:
const myFunction = () => {
console.log('hello')
}
As always in Vim, there are lots of ways to do that but the right way is very context-dependent. For example, this problem:
const myFunction = () => {
console.log('hello')
}
is different from this problem:
console.log('---')
const myFunction = () => {
console.log('hello')
}
console.log('---')
or this one:
console.log('---')
const myFunction = () => {
console.log('hello')
}
console.log('---')
and the location of the cursor might be important as well.
Assuming your cursor is somewhere on the first or last line of the snippet, the following command should do what you want:
$V% " move cursor to end of line,
" enter visual-line mode,
" extend the selection to the matching pair
If the cursor is somewhere in the body, you could simply do Vip to visually select the current paragraph but that won't work if there are empty lines in the body or if you have other lines above and below. This is why context matters.
With the cursor somewhere in the body, you could do:
[mV% " jump to previous start of method,
" enter visual-line mode,
" extend the selection to the matching pair
But there are various ways in which it might not work, like when you are in a conditional or a switch.
FWIW, I have used (and updated) the following snippet for many years because the built-in ways have always felt limited to me:
" in after/ftplugin/javascript.vim
function! SelectFunction() abort
call search('\(function\|=>\)', "bWc")
call search("{", "Wc")
normal v%V
endfunction
xnoremap <buffer> af :<C-u>call SelectFunction()<CR>
onoremap <buffer> af :normal vaf<CR>
SelectFunction() searches for a typical JS function declaration at or before the cursor, then it searches for the opening brace at or after the cursor, then it visually selects until the matched pair.
The two mappings essentially define a custom pseudo-text object af that uses SelectFunction() under the hood. It is not a real text object in the sense that it doesn't support count or nesting, but it is nevertheless pretty damn useful:
vaf " visually select current function
daf " cut current function
yaf " yank current function
" etc.
vaf with the cursor somewhere in the body, then daf with the cursor on the closing brace:
Note that the function above probably has shortcomings. From previous interactions with the community, it seems that "select the current function" is not exactly a solved problem.

Move Cursor Immediately Before a Character on a Line in Vim

Say I have the following:
text function(contents) text
and I wanted it to be
text function() text
Placing the cursor right after the opening parenthesis, I thought the following command would work df); however, what I ended up with was the following
text function( text
So I would need someway to specify that I want the character just before the closing parenthesis, but I'm not sure how to do that. There may also be a better way to do this.
What would be the best way to go about this?
You were close! You need dt) as in delete till )
The f motion places the cursor on the ) (remember it like find)
As for the 'best' way to do it, there is at least a more general way: if the
cursor were somewhere in the middle of the ( and ) (or on one of them), you
can do di) (or equivalently di() to delete inside )
If you do da) (or equivalently da() to delete around ), you would
delete the stuff in between and including the brackets.
The same goes for di[, di{, di<, di', di" etc. Using these so-called
text objects, as opposed to the d{motion} way, has the advantage that you can
repeat the edit on other pairs of brackets/quotes without the cursor needing to
be in precisely the same place - it just needs to be on or in between them.
In the following you could position the cursor on e.g. the 'i' of 'initial' in
the first line, do di) to delete the words 'some initial text', then move the
cursor to the 'e' in 'more' in the second line and just do . to also delete
the words 'some more text'):
(some initial text)
(some more text)
This way also works when the brackets (or quotes) are on different lines. For
example, with the cursor somewhere between the {}, doing di} will change
this:
function( args ) {
body of function
}
to this:
function( args ) {
}

Substituting everything from = to end of the line in VIM

Let's say I have several lines like:
$repeat_on = $_REQUEST['repeat_on'];
$opt_days = $_REQUEST['opt_day'];
$opt_days = explode(",", $opt_days);
... and so on.
Let's say I use visual mode to select all the lines: how can I replace everything from = to the end of the line so it looks like:
$repeat_on = NULL;
$opt_days = NULL;
$opt_days = NULL;
With the block selected, use this substitute:
s/=.*$/= NULL;
The substitution regex changes each line by replacing anything between = and the end of the line, including the =, with = NULL;.
The first part of the command is the regex matching what is to be replaced: =.*$.
The = is taken literally.
The dot . means any character.
So .* means: 0 or more of any character.
This is terminated by $ for end of line, but this actually isn't necessary here: try it also without the $.
So the regex will match the region after the first = in each line, and replace that region with the replacement, which is = NULL;. We need to include the = in the replacement to add it back, since it's part of the match to be replaced.
When you have a block selected, and you hit : to enter a command, the command line will be automatically prefixed with a range for the visual selection that looks like this:
:'<,'>
Continue typing the command above, and your command-line will be:
:'<,'>s/=.*$/= NULL;
Which will apply the replacement to the selected visual block.
If you'll need to have multiple replacements on a single line, you'll need to add the g flag:
:'<,'>s/=.*$/= NULL;/g
Some alternatives:
Visual Block (fast)
On the first line/character do... Wl<C-v>jjCNULL;<Esc>bi<Space><Esc>
Macro (faster)
On the first line/character do... qqWllCNULL;<esc>+q2#q
:norm (fastest)
On the first line do... 3:no<S-tab> WllCNULL;<Enter>
Or if you've visually selected the lines leave the 3 off the beginning.

Linewise inclusive delete up to a pattern

Suppose you want to delete a C function from its name up to and including the line with the closing curly:
int main (void) /* Cursor on this line. */
{
while (...) {
...
}
}
I have tried d/^}<CR> but this does not delete the line with the closing curly. How can I have an inclusive find pattern delete? I must be missing something simple.
Edit You can assume the function's closing curly is at the start of a line and other curlies are never.
I just did a quick search and found the offset syntax of the / operator here.
d/^}/0
did the trick for me. It means "find the matching pattern, then select to the end of the 0th line after it" (i.e. the end of the line it is found on)
Use V][d.
It means:
V: Enter in Visual Mode.
][: Move until next }
d: Delete all visual selection.
Your command won't work on functions that have nested braces. I would delete to the first '{' with 0d]], followed by daB to delete the block.
Details for new vimmers: The '0' in the first command makes sure you're at the start of the line before editing the d command. ']]' is a motion that gets you to the next block, and 'aB' is a selecting motion that selects the whole block, including nested blocks. So 0d]]daB means delete from the start of the line to the next block, then delete the block.
You could do jVaBokd
j - move down a line
VaB - visual line select on outer block
o - move to the opposite end of the visual selection
k - move up a line
d - delete selection

Add a number of '=' in a rest (reStructuredText) document that equals to characters from last line?

I want to use a shortcut to add needed = (from Section/Title reStructuredText syntax) according to the last line.
So, suppose (being | the cursor position)
Title
|
and pressing an specific mapping mapped to a function, add a number of = that equals to the last line (where Title is), becoming:
Title
=====|
This sequence will get you close:
kyyp:.s/./=/g
Duplicate the previous line, then in that line, change every character to an equals sign. Map that to a key sequence you like, and try it out.
Another way:
:execute "normal " . strlen(getline(line(".") - 1)) . "i="
strlen(getline(line(".") - 1)) returns the lenght of the line above the current position. The result is that the command Ni= is executed, inserting = N times.
For a mapping I would have used:
put=repeat('=', col('$')-1)
For something more interactive, I would have use the same solution as Ned's.
(I don't like my mappings to change the various registers like #" or #/)
My vim-rst-sections vim plugin will convert lines to section headings:
http://www.vim.org/scripts/script.php?script_id=4486
In your case, you'd put the cursor on the line, and type <leader><leader>d to get a top-level heading like this:
#####
Title
#####
A few repeats of <leader><leader>d will take you down to the standard hierarchy of Python ReST sections to the =.

Resources