How do you select the entire PHP function definition? - vim

In PHP, if I have a function such as:
function test($b) {
var $a = 0;
while ($a < b) {
$a += 3;
}
return $a;
}
and the cursor is on the $a += 3 line, is it possible to quickly select the entire function?
"v2aB" would select everything including the function braces but not the declaration function test($b)

Press V after the selection command you post, to convert the selection to line selection, and it will select the function declaration:
v2aBV

It's been a long time since this question was asked and answered, but I will add my own answer because it's the one I was looking for and none of the others work exactly like this one:
nnoremap vaf ?func.*\n*\s*{<cr>ma/{<cr>%mb`av`b
vmap af o<esc>kvaf
The first mapping, "Visual around function" or vaf, will jump back to the start of the function definition, regardless that the { is in the same line or the next one, and even if it's a lambda function, and visually select it characterwise to it's ending bracket. This works in PHP, Javascript and Go.
The user can then press V to turn to linewise select mode if she wants to.
The only problem that I found is that when I am in the body of a big function, but below a line that uses a lambda (let's say "small") function, this will stop searching at the beginning of the small function and select it's body instead of reaching the start of the big function and select all of its body.
function show_video_server(v_server) {
// this whole function should get selected
var something = function(){ /* this function gets selected */ };
// | the cursor is here when I type "vaf"
}
As a workaround I use the second mapping: vmap af o<esc>kvaf. It feels like a repetition or expansion of the selection. What it really does is abandon the selection and go to the line before it, and then try it agan. If the "big" function uses several lambda functions the user has to repeat the af several times to reach the big one.
Usually, vaf es enough. Sometimes vaf af or vaf af af is needed. Anyway, it's the closest I could get to what I wanted, so this is the version I'm using.

Here's a mapping that seems to work very well, no matter the nesting level.
:map t ? function <CR>f{vaBV

Here's another method that will work if you have function-level folding turned on: z c v
That closes the current fold and selects it, but it leaves it closed. If you want it to remain open: z c v $
If you have block-level folding turned on, you would have to close twice, since you're inside the while loop, so: 2 z c v
To enable PHP class/function folding: let php_folding = 1

simple way
nmap vaf va}V
I like this
nmap vaf [{?function<CR>:nohl<CR>vf{]}
if ‘{’ is in new line
nmap vaF [{?function<CR>:nohl<CR>v/{<CR>]}

Yet another way. This should select the entire function definition regardless of your cursor position within the definition, not just when you're at the $a += 3 line.
Use this in normal mode (<CR> means press enter)
?func<CR>V/{%
Explanation of each part:
?func search backward for the word "func" (the idea is to get to the first line of the function definition)
V go to visual line mode
/{ search forward for the opening brace (I didn't use f{ because the opening brace might be on a separate line)
% go to the matching brace

If you are using OOP programming this works (it looks for extra words before function[public, private, protected])
nmap vaf [{?\S* function<CR>:nohl<CR>v/{<CR>]}
As a bonus here is a wrapper around if
nmap vai [{?if<CR>:nohl<CR>v/{<CR>]}

Related

Vim Multi-Line If-Statements into Single-Line If-Statements

Is it possible to have a vim binding that turns multi-line if-statements into single-line if-statements and vice-versa?
turn this
if () {
statement_1;
statement_2;
} else if () {
statement_3; statement_4;
} else if () {
statement_5;
} else {
}
into this
if () { statement_1; statement_2 }
else if () { statement_3; statement_4; }
else if () { statement_5; }
else { }
or anything close to the above behavior? I was thinking of having the command execute upon visually selecting the block to convert and then use searches for else if and entering new lines, etc. But my problem was determining how many else if were in the code
Join all the lines to a single line by selecting them in visual mode V and pressing J; then add a newline before every else with :s/else/\relse/. You'll end up with:
if () { statement_1; statement_2; }
else if () { statement_3; statement_4; }
else if () { statement_5; }
else { }
The \r in the replacement pattern is a newline (you need to use \n and search and \r in replace; don't ask me why).
Next step is to put all the start braces in the same column. I'd use the tabular plugin for this, which makes that very easy:
:%Tabularize /{/
With % we operate on the entire buffer, in a "real" file you'll probably want to use a more restrictive range or visual mode. There are some other plugins which do something similar as well.
You should now have the output you want.
If you don't want to use a plugin, you can use the column command:
:%!column -ts{ -o{
If you want a "Vim-only" solution, then it's a bit more complex:
:let b:column = 10
:%s/^\(.\{-}\) {/\=submatch(1) . repeat(' ', b:column - len(submatch(1))) . ' {'/
Breaking that down:
I used the b:column variable to specify the column to align to. You don't need this but it make it a bit easier to edit this number later on.
^\(.\{-}\) { puts everything before { in a subgroup.
In the replace we used an expression (as indicated with \=). See :help sub-replace-\=.
First we put the if ... back with submatch(1)
Then we insert as many spaces as we need with repeat(' ', b:column - len(submatch(1)))
Finally we insert the literal { back.
I told you it was a bit more complex ;-) If you don't want tabular. Personally, I'd just start insert mode to insert spaces, which will be a lot faster than writing & debugging this (relevant xkcd).
Note that I didn't make some "magic" command which re-arranges all the text with just a stroke of a key. I don't think such a command would be a good idea. In practice there will be a lot of edge cases that such a command won't handle. Fully "parsing" a programming language with ad-hoc editing commands and/or regexps doesn't really work all that great.
Where Vim really shines is giving the user powerful text editing commands, which can be applied and combined with minimal effort, which is exactly what I did above. There are several other ways one can use to get the same effect.
But if you really want to, you can of course combine all of the above in a command:
fun! s:reformat(line1, line2)
" Remember number of lines for later
let l:before = line('$')
" Join the lines
execute 'normal! ' . (a:line2 - a:line1 + 1) . 'J'
" Put newline before else
:s/else/\relse/
" Run tabular; since the number of lines change we need to calculate the range.
" You could also use one of the other methods here, if you want.
let l:line2 = a:line2 - (l:before - line('$'))
execute a:line1 . ',' . l:line2 . 'Tabularize /{/'
endfun
command! -range Reformat :call s:reformat(<line1>, <line2>)

Quickest way to switch order of comma-sparated list in Vim

Supose I have a function such as
myfunc(arg1 = whatever, arg2 = different)
I would like to transform it to
myfunc(arg2 = different, arg1 = whatever)
What is the quickest command sequence to achieve this? suppose the cursor is on the first "m". My best attempt is fadt,lpldt)%p.
There is a vim plugin: vim-exchange
visual select arg1 = whatever
press Shiftx
visual select arg2 = different
press Shiftx
I would recommend you change it a bit so it will work from wherever the cursor is and so that it will work on any arguments:
0f(ldt,lpldt)%p
All I changed from your method was I added 0 to move the cursor to the beginning and I changed fa to f(l so that it will work regardless of argument name.
Now you can either put this into a macro, or, if you use it a lot, you can make it a mapping:
nnoremap <C-k> 0f(ldt,lpldt)%p
I arbitrarily chose Ctrl-k here put you can use whatever you like.
I wrote a plugin for manipulating function arguments called Argumentative. With it you just execute >, and the argument your cursor is on will shift to the right. It also provides argument text object in the form of i, and a,.
With pure vim, with your cursor at the start of the line:
%3dB%pldt,lp
This is the quickest I could think of on the spot (12 strokes).
This should work for all names as long as there is always a space around the equal signs.
% " Jump to closing brace
3dB " Delete to the beginning of 3 WORDS, backwards
% " Jump to the beginning brace
p " Paste the deleted text from the default register
l " Move right one character
dt, " Delete until the next comma
l " Move right one character
p " paste the deleted text from the default register
You could also turn this into a Macro to use at any time.

Vim keyboard shortcut: Deleting code without deleting the if statement

This is probably a basic vim questions to all those vim gurus out there.
If I want to delete a particular if statement and its closing bracket, but without deleting the code inside the expression, how can I achieve this through some keyboard shortcuts?
eg:
if (a == 2) {
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
}
The result will be:
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
Thanks for any help.
Alternatively to Zachs solution, you could use this:
di{Vk]p
Explanation:
You start inside the { } block, and with di{ you delete inside the { } block.
Then, you select the the if statement, which you want to remove, which now is two lines. Vk selects both, and you paste the block you just deleted over it with p. The ] makes the pasted block be correctly indented (thanks, Kache)
There could be a more elegant solution to the pasting part, buth this was the first one that came to mind.
The anvantage over Zachs answer is that you don't have to move the cursor to the line with the if, you just need to be inside it (although you may run into trouble with nested {} blocks, which there usually are). In addition, you don't need to fix indentation.
Move your cursor anywhere on the line with the if statement. Here is the sequence of commands (will be explained after)
$%dd''.
Explanation:
$ goes to the end of the line (cursor is now on {).
% goes to the matching curly brace (or parentheses etc...). Cursor is now on } of the if statement
dd deletes the line the cursor is on (} is now deleted)
'' goes to the line of the last jump. The last jump was when we used %. (cursor is back on if statement line)
. Repeats the last command (dd). This deletes the line (if statement line is now deleted)
Result:
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
The indentation will be off so you can use gg=G (which correctly indents the file) to fix it.
If you do this alot you can make map it to a key in your .vimrc or you can make it into a macro
Making it into a mapping:
Example of a mapping in your .vimrc:
nnoremap <C-d> $%dd''.gg=G
Whenever you press control-d (you can choose any key, just replace <C-d>) Vim will run those commands, doing what I explained before followed by an entire file indentation this time. Make sure your cursor is on the if statement line when using this command otherwise there could be unintended results.

VIM: Replace [aeiou]' with the respective accented letter

I know that VIM support digraph, and it would be perfect if it's possible to use with :s command, but I can't find a way to use it!
I think something like:
:%s/\([aeiouAEIOU]\)'/\=digraph(submatch(1)."!")/g
Would be perfect, but I didn't find a digraph function.
Thanks in advance.
EDIT
Ok, after a bit of diggin in the built-in VIM's functions, I've found tr and a first solution to the problem:
:%s/\([aeiouAEIOU]\)'/\=tr(submatch(1), 'aeiouAEIOU', 'àèìòùÀÈÌÒÙ')/g
However, I still want to know if there's a way to use digraph in expressions :)
function! Digraph(letter, type)
silent exec "normal! :let l:s = '\<c-k>".a:letter.a:type."'\<cr>"
return l:s
endfunction
This function will allow you to generate any digraph you want.
It simulates typing <c-k><char><char> by running it with the normal command and assigning it to the local variable s. And then it returns s.
After this function is defined and you can use it like this.
:%s/\([aeiouAEIOU]\)'/\=Digraph(submatch(1), "!")/g
Note: This was based off of the source code for EasyDigraph
Here's another approach using a hand-coded vim function (add to your vimrc):
" get a matching digraph for a given ASCII character
function! GetDigraph(var1)
"incomplete dictionary of digraphs, add your own....
let DigDict = {'a': 'à', 'e': 'è', 'i': 'ì'}
"get the matching digraph. If no match, just return the given character
let DictEntry = get(DigDict, a:var1, a:var1)
return DictEntry
endfunction
Call it like this :%s/\([aeiouAEIOU]\)'/\=GetDigraph(submatch(1))/g

Reordering methods using vim

I have 2 methods in a source file:
def Foo
puts "hello"
puts "bar"
end
def Bar
puts "hello"
puts "bar"
end
I would like to swap the order of the methods, so Bar is first.
Assuming the cursor is on the d in def Foo, the simple way is to:
shift v -> jjjj -> d -> jjj -> p -> O -> esc
But that feels a little long winded and does not account well for arbitrarily long methods:
What is the most efficient way to do this in Vim, keystroke wise?
EDIT
Keep in mind, I would like the solution to account for a situation where the methods are in a context of a big class, so G is probably best avoided
Assuming the cursor is somewhere in the first method, press dap}p and they should be swapped.
What dap does is simply "delete a paragraph". Try :help object-select to learn other way of deleting/selecting text objects in VIM.
EDIT: Replaced G with } in the command.
Similar to Spatz's
d}}p
delete to the next blank line (below Foo), skip to the next blank line (below Bar), paste.
Found another method ( from godlygeek on #vim ):
with:
def function():
first
first
first
def lol():
second
second
second
From line 1, count up until the 'def lol', which is 5. Then:
:1,5m$
A couple of ways off the top of my head. You could say
5dd/end[enter key]pO
Deletes five lines, searches for end, places the lines underneath, adds a space.
If you have VimRuby installed, I believe you can use % to jump between def and end. In that case, you could say
v%x5jpO
Edit: I defer to spatz on this :P
From line 1, 5ddGp , or 5dd:5p is the most concise/shortest I can think of.
personally, I would go '4dd' then down under bar and press 'p', but I'm not a vim guru

Resources