A non-executing version of :execute - vim

Is there a command or function like :execute that allows you to send text to the Vim cmdline but that doesn't actually execute it? I want to have the cursor left at the end of the cmdline. I've been going through the help docs for half an hour, can't find anything.

It depends on how that gets triggered. If it's through a mapping, you can simply use :help :map-expr:
fun! MyText()
return (localtime() % 2 ? ':echo ' : ':echomsg ')
endfun
:nnoremap <expr> <Leader>e MyText()
Else (i.e. through a custom command or by :autocmd), you have to use the feedkeys() function to insert the typed characters into Vim's input event loop:
:command! MyText call feedkeys(MyText(), 'n')
You cannot just use :normal, because that assumes a full command, and will abort if it's incomplete.

Related

Is it possible create a shortcut on vim to replace multiple times on a file?

I would like create a shortcut on vim to replace multiple times on a file, this a tool that I use several time.
Example of tool:
:%s/this.props/otherProps/gc
This command will replace all this.props to otherProps and ask to confirm
But I'm trying to convert this command in a shortcut like the example below:
// on .vimrc
noremap <leader>F :%s/$1/$2/gc
So in this case, I would every time a press <leader> + F, I type the first word, and after I press tab and type second word and after press enter.
I've put in the $1 and $2 as an attempt, but it doesn't work.
I tried to search some document about this, but I didn't find anything, maybe have some word more specific about that I don't know yet.
You could use a custom function by adding something like the following to your .vimrc:
function! Replace()
call inputsave()
let pattern = input('Enter pattern:')
let replacement = input('Enter replacement:')
call inputrestore()
execute '%s/'.pattern.'/'.replacement.'/gc'
endfunction
nnoremap <leader>f :call Replace()<cr>
With this everytime you hit <leader>f the function is called. Which is then gonna ask you first for a pattern, then for a replacement and is then gonna execute the substitute command.
Whilst this doesn't do exactly what you asked for, it does get pretty close.
noremap <leader>F :%s//gc<LEFT><LEFT><LEFT>
This will type out the command noremap <leader>F :%s//gc but leave your cursor between the 2 slashes, ready to type in the stuff you want to find and replace.
So now you can just hit <leader>F and start typing this.props/otherprops and then hit enter. It does exactly what you want but instead of pressing tab you press /
cnoreabbrev <expr> esr getcmdtype() == ":" && getcmdline() == 'esr' ? '%s:\<end\>: & /*}*/:g' : 'esr'
This replaces end to end: /*}*/
You can add this to gvimrc; whenever :esr is typed, the above will be executed.

Enter insert mode between two tags after command in vim

I'm looking to change the following vim command so that after entering the :%s/temp123//g part it will put me into insert mode between the \begin and \end tags.
inoremap \\beg \begin{temp123}<enter><enter>\end{temp123}<enter><esc>:%s/temp123//g<left><left>
I have been able to use :startinsert to get into go into insert mode after entering the search/replace command but I am not able to place the cursor between the \begin and \end tags.
Any help/solutions/improvements would be appreciated.
TL;DR: can't.
:star[tinsert][!] Start Insert mode just after executing this command.
Works like typing "i" in Normal mode. When the ! is
included it works like "A", append to the line.
Otherwise insertion starts at the cursor position.
Note that when using this command in a function or
script, the insertion only starts after the function
or script is finished.
This command does not work from :normal.
I was trying to get the following line working:
nnoremap <MiddleMouse> :set paste<cr>:startinsert<MiddleMouse><esc>
What this means is that all of my commands that I put after :startinsert instead run immediately before :startinsert and then :startinsert runs and changes the mode to insert (Note: this appears to hold true for using i instead of :startinsert as well).
My next step was to try to make a nested function, where I had one function that calls another function, and the second function runs :startinsert and then returns to the first function, which then completes the paste:
function! Paste()
call InsertMode()<cr>
:set paste<cr>
<S-Insert>
:set nopaste<cr>
<esc>
endfunction
function! InsertMode()
:startinsert<cr>
endfunction
nnoremap <MiddleMouse> call Paste()<cr>
But this did not work either. I also tried using the "+p and "*p registers without :startinsert with nnoremap <MiddleMouse> :set paste<cr>"+p:set nopaste<cr>, but this again just pastes directly as if I were typing it in, it does not enter insert mode first. I am willing to believe this would work on a version of Vim compiled with +clipboard, but that is not the version I have. Link to my original question and answer
Yep, you're looking for an interactive substitution, but :s/new/old/gc doesn't let you edit each match. For this kind of work, switch to the gn command + . recipe:
First search for {temp123} (or whatever you wish to replace) with /
Then press cgn to change the visually selected next match
Once done, go back to normal mode to commit your edits.
If the next edit is the same as the last, press only . otherwise cgn
Rise and repeat.
For more ideas, see this vimcast: gn command
This is the solution I am now using.
function TexBegin()
let currline = line(".")
call inputsave()
let tagname = input("enter tag name: ")
call inputrestore()
call setline(currline, "\\begin{" . tagname . " }")
normal o
normal o
call setline(currline + 2, "\\end{" . tagname . "}")
normal l
startinsert
normal o<Esc>
endfunction
inoremap \\beg <Esc>:call TexBegin()<CR>

VIM command or expression to select text from a function

In my .vimrc file, I would like to configure a minimalist function to perform a smart selection.
For example try to select inner (), if selection is empty try inner [], if still empty try inner {}, etc.
But I am stuck at the very beginning: to call / execute a command / expression to select text from a function.
function! SelectInnerBrackets():
" failed attempts
call visualmode()
execute "vi("
execute "visual! i("
endfunction
Fyi: I actually use neovim, but it probably does not make difference to this issue.
EDIT: based on solution proposed by #Ingo Karkat, I share my final piece of code. Note that it does not work perfectly with cross nested delimiters.
function! SelectInner(delimiter)
" We have to switch to normal mode to compare positions
execute "normal! vi".a:delimiter."\<C-\>\<C-n>"
return getpos("'<") != getpos("'>")
endfunction
function! TrySelectInner(delimiters)
for delimiter in a:delimiters
if SelectInner(delimiter)
normal! gv
break
endif
endfor
endfunction
" quickly select a word, expression or brackets content
nnoremap W viw
nnoremap E :call TrySelectInner(["'", '"'])<CR>
nnoremap R :call TrySelectInner(['(', '[', '{'])<CR>
If you read :help visualmode(), you'll notice that the (non-argument version of the) function is a query that has no side effects. Just :calling doesn't do any good, and you don't need the current / previous visual mode, as you build the selection yourself.
Commands like vi( are normal mode commands. To invoke them from a Vimscript function, you need the :normal! command. :execute is used on Ex commands, in order to interpolate variable values (this is called eval() in many other languages), or use special :help key-notation (we'll use that later).
In order to test whether a selection was made, Vim conveniently has two special marks ('< and '>) that specify the boundaries of the selection. Unfortunately, they are only set after visual mode has been left (by operating on it, or via <Esc>). Within a plugin, it's better to use <C-\><C-n> keys instead of <Esc>; it will return to normal mode, too, but doesn't beep if we're already in normal mode. I use a separate :normal! command for that (with :execute to use the special key notation) to ensure that it will also execute when the previous command sequence aborts because no such selection can be made.
Taken together, here's the corrected version of your attempt:
function! SelectInnerBrackets()
echomsg "trying (...)"
normal! vi(
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "trying [...]"
normal! vi[
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "trying {...}"
normal! vi{
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "nothing found"
return 0
endfunction
In order to re-select, you can use gv afterwards, e.g. via this mapping:
nnoremap <Leader>V :if SelectInnerBrackets() <Bar> execute "normal! gv" <Bar> endif<CR>

Programmatically equalize windows inside a function

I've got the following vimscript function:
function! WindowCommand(cmd)
execute a:cmd
if !g:golden_ratio_enabled
normal <C-w>=
endif
endfunction
And I use it like so:
map <space>w/ :call WindowCommand(':vs')<cr>
It's supposed to equalize the windows, but only if g:golden_ratio_enabled is 0, otherwise it should do nothing.
It doesn't work, though, and I'm not sure why, because the following DOES work:
map <space>w/ :vs<cr><C-w>=
What am I doing wrong here?
There are a couple fixes. Thankfully, the fix is really simple
For whatever reason, normal <C-w>foo does not work; You must use wincmd instead. From :h wincmd
*:winc* *:wincmd*
These commands can also be executed with ":wincmd":
:[count]winc[md] {arg}
Like executing CTRL-W [count] {arg}. Example: >
:wincmd j
Moves to the window below the current one.
This command is useful when a Normal mode cannot be used (for
the |CursorHold| autocommand event). Or when a Normal mode
command is inconvenient.
The count can also be a window number. Example: >
:exe nr . "wincmd w"
This goes to window "nr".
So in this case, you should do
wincmd =
Alternatively, you could enter a literal <C-w> character, by typing <C-v><C-w>. In your vim session, this character will be displayed as ^W.
To execute actions with <notation>, use instead:
:exe "normal \<notation>"
I use it a lot to debug mappings.
But in this case, prefer indeed wincmd.

Passing register name in mapping to an ex command

Suppose I want to create a map in vim that will call an ex command as part of its work. And suppose that the ex command takes a register name as input.
Here is a toy example:
nmap <leader>p :put x
The problem there is that the 'x' register will always be used.
But when typing, I want to be able to write:
"a<leader>p
To use the 'a' register, or:
"b<leader>p
to use the 'b' register.
Is there a way that I can pass the 'current normal-mode register' along to the 'ex' command?
Ah, I figured it out.
The trick is to use <expr> in the mapping.
So, for my example, the answer is:
nnoremap <expr> <leader>p ':put ' . v:register . '<CR>'
Check :h map-expression and :h v:register for more info.

Resources