How can I call an editor command from a vimscript? - vim

I want to remove an unwanted scrollbar from taglist. I created a function and a command like this:
function s:TlistWaToggle()
normal :TlistToggle<cr> " <- this does not work
set guioptions-=r
endfunction
command! -nargs=0 -bar TlistWaToggle call s:TlistWaToggle()
I want to wrap the call to :TlistToggle together with the command to remove the right scrollbar (I have that setting of course, but it always reappears, so this is a workaround). Currently my :TlistWaToggle doesn't do anything. How can I make it work?

Vim script uses ex commands, and apparently :TlistToggle is an ex command…
function! s:TlistWaToggle()
TlistToggle
set guioptions-=r
endfunction

In addition to #sidyll's answer: :normal is not a :*map, it accepts only raw character strings. Correct command will be execute "normal! :TlistToggle\<CR>" (or execute "normal! :TlistToggle\n"). Note that you should not use non-banged version in your scripts.
I don't think you will ever use :normal! to execute an ex command, but my answer would be useful when you want to pass any other special character. It also applies to feedkeys() call.
By the way, comments and other commands will be considered part of string passed to :normal command.

Related

Vim Surround command

I have trouble remembering all the various keywords in vim. However, things like 'surround' from Tim Pope's surround.vim plugin are very useful.
Is it possible to set up a generic text command such that when I execute something like
:surround (.)
it will replace the current selection with
(<current_selection>)
or if I execute
:surround preamble.somethingelse
It will replace the current selection with:
preamble<current_selection>somethingelse
Alternatively you can use a custom "surrounding" which will prompt you for the text:
let g:surround_{char2nr('-')} = "\1start: \1\r\2end: \2"
Now you can visually select what you want to wrap and then press S- to tigger the - surrounding. It will then prompt you for a "start" and "ending" text.
For more help see:
:h surround-customizing
:h char2nr()
:h curly-brace-names
Almost, yes.
:command! -range -nargs=1 Surround normal gv"zc<args><Esc>F.v"zp
With this, you can create a visual selection, then use
:Surround (.)
:Surround preamble.somethingelse
Note that user-defined commands can never start with a lowercase letter, so :surround that you ask for is not possible. Also, this is a quick hack, so it's rather fragile.
However, as per comments, I would urge you to use more standard Vim methods (and well-vetted plugins like surround.vim) before cooking up custom ways to use it.
Here's another way, using surround plugin: define a custom replacement and perform it (then fix the plugin config to what it was before):
command! -range -nargs=1 Surround call ArbitrarySurround(<q-args>)
function! ArbitrarySurround(repl)
let backup = b:surround_45
let b:surround_45 = substitute(a:repl, "\\.", "\r", "")
norm gvS-
let b:surround_45 = backup
endfunction
This is more robust than the previous one, allowing ad-hoc custom replacement pairs, but does require the surround plugin. It can be used with the same syntax as the above one.
In order to leverage the surround.vim plugin, you have to base your customization on what the plugin offers. For visual mode, it's basically this mapping (that by default is mapped to S):
vnoremap <silent> <Plug>VSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0)<CR>
The plugin does not expose any functions (the <SID>opfunc() it invokes is script-local), so you have to invoke the <Plug>-mapping; that can easily be done with :normal (without a [!]).
Also, the plugin only takes a single character (e.g. ( to surround with parens and whitespace, ) to surround with just parens); you can reference that in your custom command via <args>.
You create a custom command with :help :command; note that the command has to start with an uppercase letter:
command! -nargs=+ Surround execute "normal gv\<Plug>VSurround" . <q-args>
This re-enters visual mode (gv), then invokes the plugin, and finally supplies the passed argument. In order to directly invoke it from visual mode, just add a -range to the definition, so that the command takes (and ignores) the '<,'> range that Vim automatically supplies then.
You can then invoke the new custom command as :Surround ), for example, and it will surround the previous visual selection with (...).
It's not exactly what you asked, but since the problem is memorizing, how about putting this in your .vimrc:
vmap ( o<ESC>i(<ESC>gvo<ESC>a)<ESC>
vmap " o<ESC>i"<ESC>gvo<ESC>a"<ESC>
Make your selection, hit ( and it gets surrounded with ( ).
Make your selection, hit " and it gets surrounded with " ".
You could do the same with {, [ or '. For such a simple use-case there's no real need for a plugin.

.vimrc function - How to call "normal" mode keypress of equals (=) key

I am trying to write a function that I can call from a command, that will allow me to re-set all open panes to equal size. I started with this:
command Equal execute "normal! <C-w>="
Which allows me to call
:Equal
Which works the way I would expect it to. However, I need to add a little more functionality to this (an if statement, etc), so I need to declare it as a function. However, I cannot figure out how to call the "equals sign" key from within my function. Here is what I have:
command Equal call EqualizePanes()
function! EqualizePanes()
execute "normal! <C-w>="
endfunction
I know that my function declaration and method of calling it is correct, because I have replaced the contents of my function with something simple, like "echo foobar", which works as expected. However, as it stands, when I call :Equal, nothing happens. I have tried other things like:
command Equal call EqualizePanes()
function! EqualizePanes()
normal <C-w>=
endfunction
as well, without any luck. Any thoughts on what I am doing wrong here? Thanks in advance for your help.
Note that there's a special :wincmd Ex command that you can use instead of :normal; this avoids the :execute that is normally necessary to process the \<C-w> notation:
command Equal wincmd =
The problem is that normal doesn't parse special character sequences like <C-w>. So escape
command Equal call EqualizePanes()
function! EqualizePanes()
execute "normal! \<C-w>="
endfunction
:help expr-quote
:h execute
:h normal

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.

vim function only works properly the first time

I wrote a function in vim to insert text in "paste"-mode. If I leave the insert mode, the script also leaves the paste mode (set nopaste). Therefore I used the autocommand InsertLeave.
The Problem is, that the "set paste" command works only the first time I call the function. If I want to call it once more I have to restart vim.
This is the vim function:
function Paste_from_clipboard()
execute "normal! :set paste\<CR>\<Esc>o"
execute "startinsert"
autocmd InsertLeave * execute "normal! :set nopaste\<CR>"
endfunction
map <Leader>p :call Paste_from_clipboard()<CR>
What did I do wrong?
I think you are misunderstanding how VimScript works. Every line (be it
on .vimrc, a plugin, a syntax file) is just an ex command, where the
starting : is not needed. So when you write this:
execute "normal! :set paste\<CR>\<Esc>o"
You're basically calling a ex command (:exec) which calls another ex
command (:normal) which then simulates normal mode to what? To call
yet another ex command (:set) and using key codes to execute it. Why?
You can just use the final ex command directly:
set paste
This is also happening in your auto command. Also, it is important to
notice that you're recreating an auto command every time you call your
function. A simple fix is then to remove your extra commands and move
the auto command outside the function, so it is created only once. The
execution will then happen every time the event is triggered (without
another event listener being created over and over.
function Paste_from_clipboard()
set paste
startinsert
endfunction
autocmd InsertLeave * set nopaste
map <Leader>p :call Paste_from_clipboard()<CR>
Check the :h pt for the pastetoggle option. It might be an
alternative to what you are doing.

How to create an alias for ctags in vimscript

I have a function that is invoked based on a simple key mapping
function! JumpToDefinition()
let filetype=&ft
if filetype == 'coffee'
exe '<C-]>'
endif
endfunction
This works when I manually do <C-]> but when I try to "exe" it above I get a "trailing whitespace" error.
How can I invoke this in a standalone function like I have above?
Note that :execute runs the resulting expression as an Ex command,
which isn't probably what you want since there is no <C-]> Ex command. You
should be using :normal.
However, to be able to use these "special keys", instead of the characters
they represent, you have to pay attention to three things:
The correct way to represent them is with a backslash \<xxx>. Check the
help for expr-string.
Use double quotes, not single quotes
:normal accepts commands, not an expression like :execute
So, from items 1 and 2 above we know that "\<C-]>" should be used, but you
can't put this in front of :normal. Well, you can, but then it will be
executed as "quote, backslash, C, ...". The solution is to go back to using
:execute, this time to build the string with the visible "\<xxx>" in front
of :normal, which will be expanded to the actual character and executed.
:exe "norm \<C-]>"

Resources