Run terminal command from VIM? - vim

I wish to write a function which I can add to my .vimrc file that will call a terminal command, and then bind it to <leader>u.
I cant seem to get it to work tho. I believe that I can use the system() function, but there is very little documentation available and I cant seem to get it to work.
The terminal command in question is 'git push origin master'.
I know that there are plugins available for git but I am not looking for any of these, just a simple function to bind a terminal command to a key combination.
function gitPush()
system("git push origin master")
endfunction
:nmap <leader>u :call gitPush()
I know this is waaay out, but vim doesnt seem to want to make documentation very available.
Ty

function GitPush()
!git push origin master
endfunction
Is the way to run a command in a subshell.
EDIT: User defined functions must begin with a capital letter too ;)

Why do you use call to call your own function and fail to use it for builtin? It is one of three errors, other was mentioned by #Richo: user-defined function must either start with a capital or with b:, w:, t: (note that neither of these are local functions), g:, s: (only inside scripts, and you will have to replace s: with <SID> in mappings), even \w: (for example, function _:foo() works) or with {filename_without_extension}# (if filename matches \w+.vim). If it is anonymous function:
let dict={}
function dict["foo"]()
endfunction
function dict.bar()
endfunction
it also does not require to start with a capital. So the correct solution is:
function g:gitPush()
call system("git push origin master")
endfunction
nnoremap <leader>u :call g:gitPush()<CR>
Third error is omitting <CR>. I changed nmap to nnoremap because it is good to use nore where possible. Having : at the start of the command does not hurt and is not an error, but I just do not write it: it is required in normal mode mappings to start command mode, but not inside scripts.

Related

How do you execute a global command in a vimscript function?

How do I write a function that executes the :NERDTree commands? Note: I am using neovim but i'm assuming the vimscript syntax is the same
here is my code
nmap <expr> <C-n> Toggle()
func Toggle()
if g:open == 0
let g:open += 1
execute g:NERDTreeCWD
else
let g:open -= 1
execute g:NERDTreeClose
endfunc
The NERDTree commands are custom Ex commands, so you invoke them (interactively) via :NERDTreeCWD followed by Enter. In a Vimscript, you can drop the : prefix.
Maybe part of the confusion (also seen in the comments) arises from the fact that the NERDTree commands are implemented by global functions with the same name:
:verbose command NERDTreeCWD
Name Args Address Complete Definition
| NERDTreeCWD 0 call NERDTreeCWD()
So you could also bypass the custom function and call NERDTreeCWD() directly, but this would make you depend on implementation details of the plugin, and is therefore discouraged.
Implementing NERDTree toggling
Are you aware that the plugin already has a :NERDTreeToggle command?
Also, you don't need to define your own flag variable (g:open) - just reuse the one from the plugin (exposed via the g:NERDTree.IsOpen() function). Yes, this makes you depend on plugin details (but this looks like a public API, not internal implementation, so it should be far more stable) - it's still better than trying to reinvent the wheel. (Your global flag would have problems with multiple tab pages - each could have a NERDTree active or not.)

VimScript execute search does not work anymore

I don't know since when, but my visual selection search function is not working anymore. I broke the problem down to this minimal example.
Assume the following buffer:
word
word
word
When I run /word, I find all results and can jump between them.
When I run :execute '/word' this works the same as before.
When I write a short autoload function just doing the same it does not work the same:
~/.config/nvim/autoload/utils/search.vim:
function! utils#search#visual_selection() abort
execute '/word'
endfunction
Executing :call utils#search#visual_selection() makes the cursor land on the first result, but no results are highlighted. Moreover it is using the old search pattern instead of the new one. So if I search first for something non existing like foo and then execute this function, pressing n give me the error message Pattern not found: foo.
What has changed. What is the difference here?
This is actually documented behavior of Vim and NeoVim. It's not really related to the use of :execute (you can reproduce it with a direct use of /word), but with how search (and redo) work in a function.
See :help function-search-undo, which states:
The last used search pattern and the redo command "." will not be changed by the function. This also implies that the effect of :nohlsearch is undone when the function returns.
You can work around that by explicitly setting the search pattern register, which you can do with a let command.
function! utils#search#visual_selection()
let #/ = 'word'
execute "normal /\<cr>"
endfunction
The second command executes a simple / from normal mode, that is enough to search for word, since it will look for the last search pattern which is now set to what you wanted.
After the function is finished, the search pattern will keep its value, which means highlighting through 'hlsearch' will work, and so will the n command to find the next match.
A restriction from the approach above is that you can't really set search direction for repeats with n. Even though there is v:searchforward, which can be set, that variable is also reset after a function as part of the :help function-search-undo effects. There doesn't seem to be anything you can do about that one...
If the purpose of this function is for use in a key mapping, you might consider a completely different approach, using nnoremap <expr> and having the function return the normal mode command for the search as a string, that way the actual search happens outside of the function and the restrictions from function-search-undo won't apply.
For example:
function! utils#search#visual_selection(searchforward)
let pattern = 'word'
if a:searchforward
let dir = '?'
else
let dir = '/'
endif
return dir.pattern."\<cr>"
endfunction
And then:
" Mappings for next-word and previous-word
nnoremap <expr> <leader>nw utils#search#visual_selection(1)
nnoremap <expr> <leader>pw utils#search#visual_selection(0)
This avoids the issue with :help function-search-undo altogether, so consider something like this approach, if possible in your case.

Vim: Issue normal-mode commands in user-defined command

I'm certain this will have been asked somewhere, but I can't for the life of me find it, and it's not in the Defining command-line commands section of the Vim documentation.
I want to create a user-defined command which will insert the word foo before the current word.
(Note: I want it to be a function because I don't trust myself to remember yet another shortcut key. I know how to do it with noremap...)
In my .vimrc I add:
command AddFoo bifoo<esc>w
But when I type :AddFoo I get Not an editor command: bifoow.
Is it possible to have a function which issues normal mode commands?
The :normal Ex command allows to issue arbitrary normal mode commands.
command AddFoo normal! bifoo<esc>w
If you want to interpolate expressions etc., you need to use :execute; I'll just show your example again with the use of :help key-notation:
command AddFoo execute "normal! bifoo\<esc>w"
The ! after :normal prevents the use of custom mappings, like :noremap (vs :map).
Please make sure you get the difference between a command and a function.
The right hand side of a command definition is supposed to be at least one Ex command like write or bnext:
command! Foo update | tabnext
You can call a function:
command! Bar call Bar()
or execute a normal mode macro:
command! Baz normal ciw"<C-r>""
See :help :normal.
Should be simple to get what you want, you just need to switch to normal mode to make your changes:
command AddFoo normal bifoo<esc>w

How to use Ctrl-R_= with :execute

I'm trying to get an expression on a variable expanded on a :execute command. I've guessed this could be achieved by using Ctrl-R_=, but it is not clear how the special characters should be inserted. None of the following worked:
exec 'echo ^R=1+1^M'
exec "echo <ctrl-r>=1+1<cr>"
The purpose is set a global variable used as an option in a plugin to select how to show the results. It is used on an :execute command, and works fine for 'vsplit' or 'split'. But the choice between vertical or horizontal split sometimes depends on the window layout. In order to do this without adding extra complexity to the plugin I've thought of something like the following:
let var = '<ctrl-r>=(winwidth(0) > 160 ? "vsplit" : "split")<cr>'
Edit
Currently the plugin has something like the following:
exec 'pluginCommands' . g:splitCmd . ' morePluginCommands'
The g:splitCmd is a plugin option, which works for when set with "split", "vsplit", "tabe", etc. My intent is to change this fixed behavior, setting g:splitCmd in such a way that it represents an expression on the execute above, instead of a fixed string.
Now that I'm understanding the issue better, I think a dynamic re-evaluation inside the config var is impossible if the variable's value is inserted in an :execute g:pluginconf . 'split' statement. To achieve that, you'd need another nested :execute, or switch to command-line mode via :normal! :...; both approaches will fail on the appended . 'split', because you can't add quoting around that.
The way I would solve this is by prepending a :help :map-expr to the plugin's mapping; change
:nmap <Leader>x <Plug>Plugin
to
:nnoremap <expr> <SID>(PluginInterceptor) PluginInterceptor()
:nmap <Leader>x <SID>(PluginInterceptor)<Plug>Plugin
Now, you're get called before the mapping is executed, and can influence the plugin config there:
fun! PluginInterceptor()
let g:plugconf = winwidth(0) > 160 ? "vsplit" : "split"
return ''
endfun
If modifying the plugin mapping is for some reason difficult, you could also trigger the PluginInterceptor() function via :autocmd; for this particular case e.g. on WinEnter events.
With :execute, you already have a way to evaluate expressions; just move them out of the static strings to get them evaluated:
exec 'echo ' . 1+1
The <C-R> only works in command-line mode (and insert mode), so only within a :cnoremap ... command (or with :normal). (And even there, you can use :map <expr>, which often gives you simpler code.)
I think that what you want is simply
:let var = (winwidth(0) > 160) ? "vsplit" : "split"
It seems to me like
exec 'pluginCommands' . eval(g:splitCmd) . ' morePluginCommands'
should work just fine, and is a simple solution to this problem.

Use last arguments from vim's :make command

I have a one key mapping to build my project.
noremap <F5> :make<CR>
This works great. However I sometimes am building a only a piece of the project. In this case I use the command :make smaller_part to build just that piece. I'd also like a one key mapping for this use case as well.
noremap <S-F5> :make last_arguments()<CR>
Is this possible? A last_arguments() function isn't required. It's just how I imagine the solution would look. The important part is I hit one key and it runs :make with the arguments that I gave it last time.
I use
map <f2> :wa<cr>:Make <Up>
to run make with the last arguments
by the way
command -nargs=* Make write | make <args> | cwindow 6
is the Make.
I don't know whether you can programmatically retrieve the last line from the command history, or specific arguments on it. I expect you can, but I don't know how to do it offhand.
But what are the constraints here? If you'll allow your initial invocation of make to call a function you've defined, say :MyMake(smaller_part), then that can save the smaller_part in a variable and you can define a Remake() function that will call make on the target saved in that variable.
Is that solution acceptable to you? Or must the original invocation be of the form :make smaller_part?
What if you wrap the :make command in a couple commands of your own? One command runs make with whatever argument you supplied (possibly none). It also stores the argument in a variable. Then you map this command to <F5>. The other command runs make with the argument stored in your variable (again, if any). You map this command to <S-F5>. I think the vim command for defining your own commands like this is Command.

Resources