Vim split function arguments - vim

I found that I often do refactor:
def function(param1, param2, (...), paramK):
to
def function(
param1,
param2,
(...),
paramK,
):
And tried to write mapping for it.
I started with
command! Split normal! qqqf(a<cr><esc>qqf s<cr><esc>#qq#qf)i<cr><esc>
qqq - reset content of macro q.
f(a<cr><esc> - find bracket, make newline and return to normal mode.
qq - start to record macro q.
f s<cr><esc> - change space to newline.
#q - run macro recursively.
q#q - end recording and run.
f)i<cr><esc> - add last newline before closing bracket.
My idea was, that macro will fail when it won't find space, but something is wrong with that. It raised some questions.
1) How can I check if some motion succeed? e.g. How to check if there is a space in current line?
2) Is there better idea to achieve what I want? Maybe some plugin or clear function?
3) What is wrong with my idea? When I run this combination from hand it works, but while calling :Split it doesn't.

Regarding why it doesn't work quite like it does when you type it manually:
When you type normal! <esc>, Vim parses this as "type the letters <, e, s, c, >". You might be able to insert a literal escape key there by typing <c-v><esc>, but that can look a bit weird in the configuration. Instead, a better way is to use the :exe command (:help :exe):
exe "normal! \<esc>"
The \<esc> gets interpolated by the string to be a literal escape key. So, the exe "normal! ..." gets translated to an invocation to normal! with the keys you're looking for. You also need to escape <cr> the same way. I'd also use \<space> instead of , I'm not entirely sure if a normal space is going to work here. After that, hopefully, you should get the same results as when you type it manually.
As for the actual problem you're trying to solve, I do have a plugin for that: splitjoin. By default, it splits your example like this:
def function(param1,
param2,
(...),
paramK):
pass
But there's a setting you can change to adjust it to your liking. Alternatively, from Jordan Running's link, it seems you could also use the argwrap plugin, which might be more reliable for argument-splitting in particular (splitjoin handles a wider variety of cases, but maybe doesn't do as good with arguments? Not sure.)

Answers:
I don't understand the question. You can search for spaces with f or with the / syntax. Why do you want to do this?
Yes. See below.
The vimrc syntax is super different from normal vim syntax. I don't know why and I don't fully understand it.
Code:
nnoremap <C-r> :s/, */,\r /g<cr>:s/)/\r)<cr>:nohl<cr>
remaps ctrl+r to search for a comma followed by 0 or more spaces and replace that with a comma, newline, and tab. then searches for ) and replaces that with newline and ). then undoes the highlighting it just did.
To enter a literal tab instead of 4 spaces, you'll have to type CtrlVtab in place of the 4 spaces you see in the command

Related

Using "register recall" to search and replace doesn't work when register contains newline character

I've been using the answer to Using visual selection or register for search and replace as follows:
v visually select
y yank
:%s/
Ctrl+r
"
This works fine in most cases. However, if newline characters are part of the visual selection I have to manually replace ^M with \n, first. What am I doing wrong?
What am I doing wrong?
Nothing. It's just Vim being well optimised for some workflows and not for others.
The linked thread actually contains some of the ingredients of the solution to the problem, namely that, after yanking, multiline text needs a bit of massaging if we want to use it for something else than p or P. The massaging is needed because newlines are stored as control characters in the buffer and that's what you get when you yank. But regular expressions don't really like control characters so literal ^#s must be changed into \ns and other characters must be escaped, too, like . or [, because they have a special meaning.
The solution to this problem is thus to write a function that:
escapes what needs to be escaped,
transforms newlines into \ns.
Here is one way to do it (with more intermediary steps than I would do in real life but that's better for demonstration):
function! EscapeRegister(reg)
let raw_text = getreg(a:reg)
let escaped_text = escape(raw_text, '\/.*$^~[]')
return substitute(escaped_text, "\n", '\\n', "g")
endfunction
which you would use like so:
v<motion>
y
:%s/
<C-r>=EscapeRegister(e)<CR>
/foo/g
<CR>
Feel free to call your function ER() or whatever for saving typing.
See :help getreg(), :help escape(), :help substiute(), :help #=.
The function above is fairly low-level and could be composed with other things to make higher-level tools that, for example, could handle everything in the macro above in a couple of keystrokes.

Vim Custom Replace Function

Note: I'm currently using Neovim v0.2.2 (But I believe this shouldn't change anything related this post)
I'm currently attempting to create a function within vim that allows for easily replacing text.
I understand I can create a shortcuts and macros and all that, but ideally I just want to give 2 args, and not think about what specifics go where as this can interupt my thought process.
So I decided to just have a simple wrapper disguised as a function (Which I will create a command wrapper for as well, once I figure out what I did wrong here)
function! VisualReplace(query, replacement)
" Example = '<,'>s/query\%V/replacement/g
'<,'>s/a:query\%V/a:replacement/g
endfunction
As you can see, it's a very simple function that just applies the args in it's respective position, Yet, this fails even when called as a function using : call VisualReplace('some_query', 'some_replacement'
Alternatively, if you simply use the Example I have commented out directly, there's no issue, So I was hoping someoen could enlighten me on a potential fix
If need be, I could possibly look into string building & build it incrementally
Error msg:
Pattern not found: a:query\%V
General theory
Vimscript is evaluated exactly like the Ex commands typed in the : command-line. There were no variables in ex, so there's no way to specify them. When typing a command interactively, you'd probably use <C-R>= to insert variable contents:
:sleep <C-R>=timetowait<CR>m<CR>
... but in a script, :execute must be used. All the literal parts of the Ex command must be quoted (single or double quotes), and then concatenated with the variables:
execute 'sleep' timetowait . 'm'
Your function
In order to get the a:query and a:replacement arguments into :substitute, use :execute and either string concatenation or printf():
function! VisualReplace(query, replacement)
execute "'<,'>s/" . a:query . '\%V/' . a:replacement . '/g'
endfunction
Additional critique
Passing a range to a function is so common, there's special syntactic sugar for it: The range attribute to :function, and a:firstline and a:lastline implicit arguments. Read more about it at :help function-range-example. While your use case here seems to be specifically for visual mode, in general it's useful to keep the scope of functions as broad as possible.
#Ingo Karkat answered perfectly. However, I feel like there might be some workflow alternatives which might help. (Assuming you aren't trying to script this behavior)
Visual Star
It looks like you are build a search based on a visual section. You may want to consider using a visual-star plugin to simplify the process. Here is a an example of a visual star mapping:
xnoremap * :<c-u>let #/=#"<cr>gvy:let [#/,#"]=[#",#/]<cr>/\V<c-r>=substitute(escape(#/,'/\'),'\n','\\n','g')<cr><cr>
This mapping will allow you to visually select text and then execute * to make it a search pattern. Similar to how * works in normal mode on the current word.
Search refining
I get the impression that you are trying to refine your search pattern. Vim has a nice way of doing this with q/ or pressing <c-f> while searching with /. See :h q/. This will bring up the command-line window which will allow you to edit the query/command-line with all your normal Vim keys.
Search and Replace with gn motion
Sometimes doing a substitution is just overkill or doesn't quite fit the situation right. You can mimic a search and replace by using the gn motion to operate on a search pattern. By using an operator and the gn motion together you can use the dot command, ., to repeat the action easily.
Example:
/foo
cgnbar<esc>
Now you can use . to repeat the foo -> bar replacement. Use n to skip. You can use other operators as well, e.g. gU to uppercase.
See :h gn and :h operator for more help.
Related Vimcasts episodes:
Refining search patterns with the command-line window
Operating on search matches using gn
Search for the selected text

Returning to Normal Mode from Insert Mode after "execute normal"

You can perform normal mode commands programmatically in Ex mode, via execute normal, e.g.
:execute "normal" "iNEWTEXT\<Esc>0"
This switches to insert mode (i), writes "NEWTEXT", escapes to normal mode (\< Esc>), then moves to the start of the line (0).
However, using a non-constant string, either a register or variable, the behavior is different. For example, suppose you have the same command above saved on a line in any file (not necessarily a vimscript file):
iNEWTEXT\<Esc>0
You can then copy the text into any register (here, z) via "zy$ and execute the register via #z. This time, though, the output is different:
NEWTEXT\<Esc>0
After entering insert mode, the Escape is no longer treated as a special character, and is instead taken literally. Alternative forms like \e don't work either. Is there a way around this?
EDIT: Using Ingo's answer, I created the the following function. Basically, the use is for having a set of normal/insert commands embedded within the text of the file, and being able to execute them. More commonly, something similar is used for running Ex commands from a line of text, but I couldn't find anything that did this exact thing for normal and insert mode.
So, you'd have text like the following in your file:
jy10j10jpO\<Esc>jEll
When on that line, you could call the function or a remap, and the commands would execute (in this example, copying and pasting 10 lines, and moving 2 columns past the first word). Ingo's alternatives are better for serious usage, namely sourcing commands from another file, having the command in the .vimrc, or a file-type specific option. Macros saved by a session would work just as well, and are more practical than having commands scattered throughout a file. In my case, I was syncing across multiple devices, and didn't want to have another file or clutter my vimrc with this very specific command, but didn't mind cluttering this specific file itself. Think of this like a portable macro.
" Execute current line as Vim normal mode commands.
nnoremap <A-y> :call EvaluateLineAsNormalModeCmd()<CR>
function! EvaluateLineAsNormalModeCmd()
let g:getCurrentLine = getline(".")
"have to :execute twice: once to get the contents of the
"register inserted into a double-quoted string, and then once for
"the :normal to evaluate the string.
execute 'execute "normal" "' . g:getCurrentLine . '"'
endfunction
EDIT2/3: Here are two functions using Christian Brabandt's answer. They work about the same but can put the user in insert mode at the end (whereas, based on my minimal information, 'i' in the other context is considered an incomplete command and not executed, and :startinsert can't be used in that situation). PS: Please don't ask me what all those single and double quotes are doing, as I can't wrap my head around it O_o
function! EvaluateLineAsNormalModeCmd()
normal! 0y$
execute ':call feedkeys("'.#".'", "t")'
endfunction
function! EvaluateLineAsNormalModeCmd()
let g:getCurrentLine = getline(".")
execute ':call feedkeys("'.g:getCurrentLine.'", "t")'
endfunction
If you really need this (the use case is dubious), you have to :execute twice: once to get the contents of the register inserted into a double-quoted string, and then once for the :normal to evaluate the string.
:execute 'execute "normal" "' . #z . '"'
PS: Please give more background; what is your final goal? When a question is only about a small technical step, it's difficult to provide a good answer. If you don't tell us why you want this, it's easy to succumb to the XY problem.
I would rather use the feedkeys() function. E.g. for your sample, this should work:
exe ':call feedkeys("'.#".'", "t")'
(If you yanked your line into the unnamed register, else adjust the register name accordingly). Note, quoting could get ugly.
To understand what is going on, this is what is done:
exe ':call feedkeys(' - First part of the feedkeys() function call
" - Start of Quote for the first argument
. - String concatenation
#" - content of the unnamed register
. - String concatenation
' - Start of second part of the feedkeys function call
" - End of Quote for the first argument
, "t")' - Second argument of feedkeys() function call
You could also do it in 2 steps like this:
exe ':let a="'. #". '"' - Also needs to quote #" correctly.
call feedkeys(a, 't')
which should be easier to understand. The exe call is only to translate the normalized key notation into literal keys.

vim script exec pastes unformated text

In all honesty the title is bad. Consider the following 5 lines:
function Example()
let ## = "-_-"
execute "normal! ]P"
call cursor(line('.'), col('.')-1)
endfunction
When this function is called, I expect to get -_- as output and the cursor should be moved to the left, meaning that it is at the third character, so if I press a key, like I for example I will get -_i-
What happens in reality is quite different (and to some degree interesting)
The output the first time this is called is - _- and after that it's _--
I assume that "cursor" shifts the position of the word under the cursor.
Basically: Why is it happening? How can I get the desired effect ?
Very important edit:
Apperantly the problem isn't in the plugins. When I go for:
call Example()
It works flawlessly. Thing is it is supposed to be triggered by a key. I have currently bound it like so:
inoremap ' <C-O>: call Example()<CR>
So now I am thinking that something in the mapping is broken...
I cannot reproduce your strange behavior. I get ----_-_-_-_- on repeated invocations, as expected. I again suspect there are plugins at work. Try with vim -N -u NONE. As this is a paste, there's little that could influence this function, though. You could try to workaround via :noautocmd call Example(), but I'd rather try to find the root cause of this disconcerting strangeness.
The "-_-" is not a full line, so the ]P (paste with adapted indent) has no effect here. You could just as well have used P.
To move the cursor one left, rather use :normal! h. The subtraction from col('.') again only works for single-byte ASCII characters.

preventing trailing whitespace when using vim abbreviations

I am a new user of vim (gvim in windows), and have found abbreviations a nice time saver - however they would be even better if i could stop the trailing whitespace at times.
I have some directories that i use a lot, and so i added some abbreviation/path pairs to my _vimrc:
:ab diR1 C:/dirA/dira/dir1/
:ab diR2 C:/dirA/dirb/dir2/
etc ...
Now when i type diR1 <space> i get C:/dirA/dira/dir1/[]| where the whitespace is represented by [] and the cursor is the | character. I would like to get rid of the [] == whitespace.
This is a minor complaint: however you seem to be able to customise everthing else in Vim so i figured i'd ask -- is it possible to avoid the trailing whitespace when one uses abbreviations in vim?
An alternate tool used within Vim is a good answer - my objective is to save re-typing frequently used directory structures, but to have the cursor handy as i would almost always add something to the end, such as myFile.txt.
The trailing white space (doubtless due to the fact that the space triggered the abbreviation) which i backspace over before adding myFile.txt to the end is less annoying than typing the whole thing over and over, but it would be ideal if i could avoid doing so ...
pb2q answer is exactly what you want in your current scenario, but does not fully answer the question presented in the title. This exact problem is addressed in the vim help file. See :helpgrep Eatchar. The example it gives is this:
You can even do more complicated things. For example, to consume the space
typed after an abbreviation: >
func Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunc
iabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>
You would put the Eatchar function in your ~/.vimrc file and then use like so in your abbreviations:
iabbr <silent> diR1 C:/dirA/dira/dir1/<c-r>=Eatchar('\m\s\<bar>/')<cr>
This would "eat" any trailing white space character or a slash. Note that I used iabbr instead of just abbr, because it is rare to actually want abbreviations to expand in command line mode. You must be careful with abbreviations in command line mode as they will expand in unexpected places such as searches and input() commands.
For more information see:
:h abbreviations
:helpgrep Eatchar
:h :helpgrep
This is possible, without more customization than just abbrev.
The abbreviation is being triggered by the space character, as you know. The space is a non-keyword character, and remains after the abbreviation is expanded.
But there are other ways to trigger the expansion, such as other non-keyword characters, including /. So if you instead define your abbreviations like this:
:ab diR1 C:/dirA/dira/dir1
That is, without the trailing path separator, then you can type diR1/, have the abbreviation expand for you because of the slash /, and continue typing, appending to your path with a file name.
Alternately, you can force abbreviation expansion using Ctrl-]. That is, type the abbreviation: diR1, with no following space or other non-keyword character, and then type Ctrl-]. The abbreviation will be expanded and you'll remain in insert mode, and can append your file name to the expanded path.
Check out :help abbreviations, there may be something else useful for you there, including more complicated constructions for always consuming e.g. the space character that triggered the abbreviation.
Instead of abbreviations, you could use mappings. They're expanded as soon as you have typed the last character of the mapping, so there won't be a trailing space:
:inoremap diR1 c:/dirA/dira/dir1
The downside for this approach is that the letters you type while a mapping could be expanded are not displayed until the mapping is finished. This takes some using used to.

Resources