How to quickly navigate function arg lists in vim - vim

I have the following function definition:
def test(these, are=0, args=1):
pass
I want to find a quick and intuitive way to hop between each argument.
For example, pressing w/b is too slow because it will hit the commas, but even worse it is it will hit the =. W/B works great except with that first argument because test(these is a "WORD".
Is there an existing way to navigate this list that works the same for all arguments, or is there a common modification to do this? For example, can I redefine what a WORD is and make the (/) break up a WORD? I can't think of a good example of when I would have a legitimate WORD with a paren right in the middle.

The big complication with argument lists is complex expressions and nested function calls (e.g. foo(1, bar(2, 3), 4)). To handle those, at least some basic parsing is necessary; simple pattern matching (as can be done with built-in commands) won't do.
I personally use a combination of basic Vim commands, tailored to the current situation (i.e. w / W / f{char}), and the following plugins:
sideways has mappings to jump to next / previous arguments, to move arguments around, and corresponding text objects
fieldtrip builds on top of sideways and offers a submode, where individual keypresses can then be used to jump / move

Just try using ft f0 or f1 to see if these jumps can help you.

Related

Is there a better method for find and replace in Vim?

Edit: I moved this over to the Vi and Vim site: https://vi.stackexchange.com/questions/13689/how-to-find-and-replace-in-vim-without-having-to-type-the-original-word
I'd like to optimize my "find and replace" workflow in Vim. It's something I do often, as I'm sure most of you do too. Usually something along the lines of -- copy a block and change the name of a variable in a few places. I know, I know, that probably triggers your "why are you copying and pasting code" reflex, but let's not go down that road... There are plenty of valid use cases :)
I'm well aware of the search and replace commands: :s or :%s but I don't like them. It forces me to type out both the full variable name I'm searching for and what I'm changing it to. Maybe there is a better way fix the the amount of typing with :%s? I often use long descriptive variable names, so that is really a deal breaker for me. I also don't like how typing out a variable name from scratch is typo prone and can consume time and brainpower hunting down typos. I much prefer typing it once, and then copying and pasting to just avoid this entirely if possible.
My current workflow uses some combination of movement/yank/select/search/put to move around the file and replace one by one. It is not great but has the benefit of avoiding typing out full variable names. I might just need to type the first few letters with / or use another movement command (i.e. fx) depending on what's around and then hit ve to select the whole word. I also don't mind that I have to repeat for every instance. I never do a full find replace without confirming each change. But it would be much preferable if I could repeat the replacement action with a single keystroke (which I can't do with this method). each replacement is usually something like n then ve then p (or even worse "0p)
Is there a faster way?
My own workflow is similar to yours:
To start, get the cursor on one instance, possibly with / or by navigation.
Hit * to find the next instance of that word.
Change one instance with cw and then the new variable name.
Then it's fast: n/N to get to the next/previous instance, and . to repeat the last edit.
This workflow gives me the same advantage as yours, in that I can review each case before applying the change, but it's just two keystrokes for each additional change.
Hope this helps.
I like the "visual highlight then edit" approach.
shift + v to highlight the region that you want to modify.
then :s/old/new/r where old is what word you want to replace with new.
r changes the first instance of that word old.
Note* There are options other than r which modify its behavior how you want to replace the word.

Emacs / vim quick copy paste

I'm trying to make a transition to emacs (using evil mode/vim keybindings) and I'm having a hard time feeling more efficient/productive than if I just used the mouse. Here is an example of a situation where I find myself really slow:
for i in range(self.allData.shape[0]):
self.correctSequence = self.displayNumbers(i, self.allData)
self.userSequence = self.numberEntry()
self.allData.set_value(i, 'userSequence', ''.join(self.userSequence))
if len(self.correctSequence) != len(self.userSequence):
self.allData.set_value(i, 'correct', 0)
else:
if list(reversed(self.correctSequence)) == self.userSequence:
self.allData.set_value(i, 'correct', 1)
else:
self.allData.set_value(i, 'correct', 0)
It would be very common for me to have to change the first 4 instances of self.allData to something else (self.testData, for example), leaving the last 2 untouched.
Normally this wouldnt be too bad with a mouse. For example, I could replace the first allData with testData, copy it, use the mouse to the next 3 occurences and just hit CTRL-V for each one. Or better yet, just use multiple cursors in sublime/atom and replace all 4 in one go
I use spacesmacs in emacs with vim keybindings. So, in emacs I find myself having to do something like the following:
SPC-SPC a (avy jump to words beginning with a)
cw testData
Repeat those 2 steps once for each word I want to replace
This seems really inefficient and I'm wondering: am I just using an inefficient method? Is there a faster way to do what I want?
It seems that even if I managed to complete those steps really fast (4 times), theres still A LOT more typing one would have to do, and I fail to see how this would be faster than just reaching for the mouse. Yes, one could make the argument that I'm losing time by constantly reaching for the mouse, but in my mind I'm saving typing time by reaching for the mouse because I can just hit CTRL-V a few times to achieve what I want. Where exactly are the vim speed gains in a situation like this?
If you just want to replace, you can use query-replace, and replace the word one by one.
You can use replace-string too, but remember to limit replacement to part of the buffer, activate the region around that part.
Anyway, these commands could prevent you from finding the word by your eyes, moving cursor by mouse and moving your hand back to keybaord. And they could avoid probable overlook too. At least I don't want to leave my hands from the keyboard when typing. :)
I'm not sure how "vim-like" Spacemacs is, but you could do it like this in Vim:
/all<CR>
cgntest<Esc>
.
.
.
or:
/all<CR>
cetestData<Esc>
n.
n.
n.
or:
:,$s/allD/testD/gc<CR>
Maybe one of these methods works in Spacemacs too?
In addition to the usual (and generally the best) answer, query-replace (see #songyuanyao's answer), you can use the secondary selection to advantage to selectively paste the same thing at various places. Unlike the region, the secondary selection is unrelated to the cursor position (aka point), so it is always available regardless of where the cursor is.
And unlike query-replacing, you can paste it on demand, instead of having to answer for each matching occurrence. If you use delete-selection mode then just select some text to replace and paste the secondary selection to replace it.
You can set and paste the secondary selection using the mouse - see Secondary Selection on the Emacs Wiki, and see the Emacs manual, node Secondary Selection.
If you use library second-sel.el then you can use the secondary selection from the keyboard as well, and get a lot more use out of it.

Move relative to the end of line in Vim

Imagine I have a sentence like this:
Block chain, the decentralised public ledger that records transactions on the bitcoin network.
And if my cursor is at the end of the first word, is there a way to move relative to the end of the sentence rather than from the cursor position? Think of something like, the first c from right hand side is where I want to go, is there a way to reach rather than going to the end first and using F to reach the c ($Fc).
Yes, Vim has (an abundance of) motions that move relative to the current (cursor) position: l, w, f among them. And you can re-position the cursor easily with many motions: ^, 0, $, gm. When combined, that means you can reach almost any place with just a few keystrokes, and it's possible to remember each of those quite easily.
Given that there's a limit to available keys (and that Vim out of the box already uses most of them!), and a limit to what you can memorize, I think that's a perfect balance. In that light, I think $Fc is nothing to worry about (just compare with other editors!)
If that particular motion's inefficiency bothers you, you can always write a custom mapping (and assign one of the few available keys), but that doesn't scale well.
If you think $Fc 3 keystrokes is too many......
operator + target char have already 2 strokes.
We can dynamic capture the target char. But to make it 2 strokes, we have to scarify a normal mode key, I don't know which one you don't use, I just cannot find one on my keyboard, so as example I use the <F6> you can change it as you like.
This mapping allows you press <F6>c to that place, of course, c could be any character.
nnoremap <expr> <space> '$F'. nr2char(getchar())
And this won't work if the target char, i.e (c) is at the EOL. Well you can do further checking, e.g. write your own function to do it, if you think it is really necessary.
Personally I don't think it is worthwhile. Just get used to the $Fx.

goto definition (gd) while inside function call

If I am editing a function call like:
many_params(param1, "a long...string", p|aram3); // Cursor located at |
Then typing gd will jump to the definition of param3, not many_params. What is the most efficient idiom for moving the cursor to many_params, without using ^.
To clarify, please do not answer with ^, because I want an idiom that is also compatible with:
// Lets jump to func3's definition
func1(func2(123, "aaaaa"), func3("bbbbb", 3|, 4, 5));
The only idea I have is to:
go to the left opening parenthesis - F(
move one word backwards - b
so the command is F(b. However it won't work with text like func1("abc(d", 222|2)
It feels a bit dirty to me, but you can handle nested parentheses as well with ya(h. Note that this yanks the parenthesized bits to your yank buffer, which may not be desirable, but you would work around that by mapping to "_ya(h. This will go to the open paren of the enclosing parens, and subsequent executions of it will go to subsequent levels of parenthesization. I don't know of another way to get to the enclosing parentheses without being interrupted by intervening parentheticals.
If you're using this, you need to use the h instead of b in case you have multiple parentheses in a row. ...but then again, maybe the behavior with b would be desirable.

Is there a good Vi(m) command for transposing arguments in a function call? Bonus points for Emacs

For example if I have some code like:
foo = bar("abc", "def", true, callback);
Is there a nice command to move true to the 1st or 2nd position leaving the commas intact?
P.S as a bonus my friend want to know if this works in Emacs too.
In Vim if you place the cursor at the start of the first word and do dWWP then it will have the desired effect. Here is a breakdown:
dW delete the current word, including the comma and the following whitespace
W move to the start of the next word
P insert the deleted text before the cursor
This will work if there are further parameters after the pair to be swapped - it will need to be modified if there are only two parameters or you want to swap the last two parameters, since it will paste the text after the closing bracket.
Alternatively you could use a regex substitution:
:%s/(\([^,]\+\),\s*\([^,)]\+\)/(\2, \1/
This will find the first two arguments after the open bracket and swap them.
update:
A search of vim.org found the swap parameters plugin, which should do exactly what you want and can handle situations that either of the above methods cannot.
I don't know the answer for vi, but in Emacs, transpose-sexps (C-M-t) will swap two arguments either side of the cursor. Actually transpose-words (M-t) was my first guess, but that leaves the quotes behind.
You need a transpose emacs command. But its limited to not guessing that its transposing in lists, it only considers text (it can't guess the 1st, 2nd word of list). Try this.
Keep your cursor at after comma of true. Use M-x transpose-words. By default it will transpose with next word from the point. Shortcut is M-t.
You can use C-u 2 M-t for transpose with next second word.
Now coming to your question. If you want to move true, to backward 1 word, use C-u -1 M-t, and for backward 2 words C-u -2 M-t.
Am not a VIM guy. So sorry bout that.
If you want to do this as a refactoring, not just as text manipulation, I'd suggest looking into Xrefactory, a refactoring tool for Emacsen (free for C/Java, commercial for C++).
Transposing previous (Ctrl-t p) and next (Ctrl-t n) argument ... add the
following into your .vimrc file:
map <C-t>p ?,\\|(<CR>wd/,\\|)<CR>?,\\|(<CR>"_dw?,\\|(<CR>a, <C-c>?,<CR>P/,<CR>w
map <C-t>n ?,\\|(<CR>wv/,<CR>d"_dw/\\,\\|)<CR>i, <C-r>"<C-c>?,<CR>?,\\|(<CR>w

Resources