Use a user defined vim command inside another user defined vim command - vim

I am trying to have to custom commands in vim as follows:
The first command just runs a bash script. this works fine on its own.
:command Build :!./build-linux.sh
Now I want to use this command in another such as this:
:command BuildAndDebug :Build | :call vimspector#Launch()
This command is supposed to run the bash script and then launch a debugger.
When I try to do this, I get this error back:
E488: Trailing characters: :Build | :call vimspector#Launch()
I can't seem to find the trailing characters here. I suspect the :Build is the cause of this because it does not know where the command ends. However I can't seem to get it to work at all.

You need to add the -bar attribute to your command definition if you want to be able to "chain" other commands after it with |:
:command -bar Build :!./build-linux.sh
This is described in chapter 40 of the user manual: :help usr_40, and documented under :help :command-bar.

Related

Vim Running Terminal Command Automatically

I am trying to write a command (in vim rc) that automatically creates a new split and starts a terminal (i.e by executing :Term) and then automatically executing some bash commands on that terminal (specifically 'conda activate')
This is how far I got:
command CustomTerminal execute "vsplit ." <bar> execute "Term"
How to I extended this command such that, it pipes and executes some bash commands on this terminal?
I see the following issues with your command definition:
you don't need :execute here; also <Bar> is only needed in mappings
the vsplit . creates a directory listing; is that intended?
have you defined a custom :Term command? The build-in command to open a terminal is :terminal
:term performs a (horizontal) split on its own; its :help :terminal mentions
If you want to split the window vertically, use:
:vertical terminal
The :terminal command accepts optional shell commands already. If that's what you need, you can easily extend your custom command to take and pass this argument:
:command -nargs=? CustomTerminal vertical terminal <args>
See :help :command-nargs and :help <args>.
Additionally, you can add :help :command-completion via -complete=shellcmd.
Keeping the terminal and feeding commands to it
If you want to run more than one command and then close the terminal, so to reuse a single terminal session, you have to follow :help terminal-to-job to send commands from Vim to the terminal (received by the interactive shell or whatever application currently is running). It looks like this:
call term_sendkeys(buf, "ls *.java\<CR>")

Vi multiple command in one line

I want to achieve the following things in vi :
Remove first few columns
Remove lines starting with specific words
Remove everything after first word.
I have the following command with respect to above requirements
:%s/new page //g to remove first two columns.
:g/abc/d , :g/xyz/d , :g/ddd/d to remove lines starting with specific words.
:%s/ .*//g to remove everything after first word.
Overall I want to run the following commands :
:%s/new page //g
:g/abc/d
:g/xyz/d
:g/ddd/d
:%s/ .*//g
How can I execute all the above commands in one single command.
I have tried | but it did not worked.
:g/abc/d|:g/xyz/d|:g/ddd/d
I am getting the following error :
E147: Cannot do :global recursive
How can I achieve this. I want to execute all commands in one single command.
Thanks
You can put all those commands in a function:
function! AllMyCommands()
%s/new page //g
g/abc/d
g/xyz/d
g/ddd/d
%s/ .*//g
endfunction
and call it either directly:
:call AllMyCommands()
or via a custom command:
command! Foo call AllMyCommands()
:Foo
or via a custom mapping:
nnoremap <key> :<C-u>call AllMyCommands()<CR>
<key>
I have tried | but it did not worked.
:g/abc/d|:g/xyz/d|:g/ddd/d
In general, commands can be executed sequentially, separated by |, but there are exceptions, as :help :bar tells:
These commands see the '|' as their argument, and can therefore not be
followed by another Vim command:
[...]
:global
[...]
As a workaround, you can wrap them in :execute:
:exe 'g/abc/d'|exe 'g/xyz/d'|g/ddd/d
But putting them into a :function, as per #romainl's answer, is probably better.

How to execute vim internal global commands with this setup?

I have something like the below in my .vimrc on MacOSx El Capitan.
let g:plantuml_executable_script='java -jar /Users/megan/Downloads/plantuml.jar'
To be sure Java is available I typed which java on the command line and I got /usr/bin/java, so I have that added to $PATH.
Now, when I do the following in Vim by opening up a plantuml file:
:! java -jar /Users/megan/Downloads/plantuml.jar
I get a nice class diagram image generated.
What could I do in Vim so that I can type plantuml_executable_script instead and get the nice diagram?
I think you're referring to PlantUML (please let me know if I'm wrong). If it's so this webpage can get you started.
Let's say you're editing a file called sequenceDiagram.txt which has the following lines:
#startuml
Alice -> Bob: test
#enduml
and then you save it with :w command. After that you can execute:
:!java -jar /Users/megan/Documents/plantuml.jar %
to get a file called sequenceDiagram.png. Note that Vim will replace % at the end of the command line with the file name you're editing.
Now, if you want a command called plantuml_executable_script to execute :!java -jar /Users/megan/Documents/plantuml.jar %, you can create it by executing:
:command! PlantUMLExecutableScript !java -jar /Users/megan/Documents/plantuml.jar %
Please note:
An uppercaseP was used because user-defined commands must start with a capital letter.
Underscores cannot be used.
See :help 40.2 and :help user-commands for more information.
In that way you can execute :PlantUMLExecutableScript to run PlantUML. But you can go one better by creating a mapping:
:noremap <F2> :PlantUMLExecutableScript<CR>
So you can hit F2 (or whatever key you choose) to call :PlantUMLExecutableScript. See :help 05.3, :help 40.1 and :help key-mapping for more information.

How to escape a shell command in the rhs of a mapping?

I am trying to create a simple mapping in vim to execute a shell command. The command I want to execute is this:
ruby -e "Dir.glob('./spec/*_spec.rb').each {|f| require f}"
which works fine when I run it at the command line.
However, if I run the following in vim:
nmap ,t :!ruby -e "Dir.glob('./spec/*_spec.rb').each {|f| require f}"<cr>
I get the error:
E492: Not an editor command: require f}"<cr>
Note: I'm on windows, if that's relevant.
What am I doing wrong?
Bonus: How can alter the above command so that it does not depend on the current file being in the directory containing "spec"? Ideally, if the current file's directory did not contain "spec", it would check the parent directory, and so on, recursively, until it found a directory containing "spec". At that point it would run the command with "." replaced by the directory it found in my code above.
Final Solution
Based on Ingo's answer, my final solution was this:
nnoremap ,tt :call RunAllMinitestSpecs()<cr>
function! RunAllMinitestSpecs()
let l:dir = finddir('spec', '.;')
let l:dir = substitute(l:dir, '\', '/', 'g') " so it works on windows
let l:ruby_cmd = "\"Dir.glob('" . l:dir . "/*_spec.rb').each {|f| require f}\""
exe('!ruby -e ' . l:ruby_cmd)
endfunction
The | separates Vim commands; for Vim, the mapping ends at the first |, and Vim tries to interpret the remainder as a command (which obviously fails). You need to either escape via \ or (better) use the special <Bar> notation in mappings:
:nnoremap ,t :!ruby -e "Dir.glob('./spec/*_spec.rb').each {<Bar>f<Bar> require f}"<cr>
Tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Bonus answer
You can get an upward directory search (:help file-searching) via finddir(), then pass the result to glob(). See
:echo finddir('spec', '.;')
(I would then move the implementation into a :function, and invoke that from the mapping. This would have also avoided the | escaping problem.)

vim: map command with confirmation to key

I've written a few macros in my .vimrc for the version control system I'm using (Perforce) (please don't suggest the perforce plugin for vim, I tried it and I don't like it). They all work fine except the revert macro, which breaks due to a confirmation prompt (which I need so I don't accidentally fat-finger my changes away). It currently looks like this:
map <F8> :if confirm('Revert to original?', "&Yes\n&No", 1)==1 | !p4 revert <C-R>=expand("%:p")<CR><CR><CR>:edit<CR> | endif
This causes bash to complain when vim tries to load the file:
bin/bash: -c: line 0: syntax error near unexpected token `('
Looking at the buffer bash sees, it looks like the error is that vim sends it everything after the first pipe, not just the part meant for bash. I tried a few alternatives but I can't seem to make it work. I've got it to show confirm dialog correctly when I removed the pipes and endif (using shorthand if), but then vim complains after the user gives a response.
I think you want something along these lines:
:map <F8> :if confirm('Revert to original?', "&Yes\n&No", 1)==1 <Bar> exe "!p4 revert" . expand("%:p") <Bar> edit <Bar> endif<CR><CR>
Remember that :map is a dumb sequence of keystrokes: what you're mapping F8 to has to be a sequence of keystrokes that would work if typed. A <CR> in the middle of the :if statement doesn't mean ‘and press Enter when executing the command at this point if the condition is true’; it means ‘press Enter here when in the middle of typing in the :if command’, which obviously isn't what you want.
Building it up a piece at time, from the inside out:
There's a shell command you sometimes want to run.
That shell command needs to be inside an :if to do the ‘sometimes’ bit, and so have an :endif following it.
After a literal ! everything following is passed to the shell, including | characters which normally signify the start of another Vim command. That's reasonable, because | is a perfectly good character to use in shell commands. So we need some way of containing the shell command. :exe can do this; it executes the supplied string as a command — and its argument, being a string, has a defined end. So the general form is :if condition | exe "!shell command" | endif.
Your shell command has an expression in it. Using :exe makes this easy, since you can simply concatenate the string constant parts of the command with the result of the expression. So the command becomes :exe "!p4 revert" . expand("%:p") — try that out on its own on a file, and check it does what you want before going any further.
Putting that inside the condition gives you :if confirm('Revert to original?', "&Yes\n&No", 1)==1 | exe "!p4 revert" . expand("%:p") | edit | endif — again try that out before defining the mapping.
Once you have that working, define the mapping. A literal | does end a mapping and signify the start of the next Vim command. In your original the mapping definition only went to the end of the condition (check it with :map <F8> after loading a file) and the !p4 part was being run immediately, on the Vim file that defines the mapping! You need to change each | in your command into <Bar>, similarly to how each press of Enter in your command needs writing as <CR>. That gives you the mapping above. Try it by typing it at the command line first, then do :map <F8> again to check it's what you think it is. And only then try pressing F8.
If that works, put the mapping in your .vimrc.
Use of the pipe to string multiple vim commands together is not particularly well-defined, and there are numerous eccentricities. Critically, (see :help :bar) it can't be used after a command like the shell command :! which sees a | character as its argument.
You might find it easier to use the system() function.
E.G.
:echo system("p4 revert " . shellescape(expand("%:p")))
The shellescape() wrapper is useful in case you have characters like spaces or quotes in the filename (or have cleverly named it ; rm -rf ~ (Don't try this at home!)).
In the interest of creating more readable/maintainable code, you may want to move your code into a function:
function Revert()
if confirm('Revert to original?', "&Yes\n&No", 1)==1
return system("p4 revert " . shellescape(expand("%:p")))
endif
endfunction
which you would access by using the :call or :echo command in your macro.

Resources