TextMate has a nice feature that allows you to execute a script from within the current context and shows you the output in a separate window. This lets you write and test code on the go. I'm almost certain there is a similar feature with MacVim/gVIM, but I'm not sure what it is. Currently I save my buffers to disk, then go to the command line and execute the script in that respect. How do I improve that workflow with vim?
You can do this in vim using the ! command. For instance to count the number of words in the current file you can do:
:! wc %
The % is replaced by the current filename. To run a script you could call the interpreter on the file - for instance if you are writing a perl script:
:! perl %
vim tutorial: Mapping keys in Vim
You can map keys so perl executes current script as suggested by jts above.
map <C-p> :w<CR>:!perl %<CR>
will map Ctrl+P to write file and run it by perl
imap <C-p> <Esc>:w<CR>:!perl %<CR>
lets you call the same in insert mode.
You should have .vimrc (_vimrc for Windows) file in your vim/home folder. It has instructions on how vim should behave.
map <C-p> :w<CR>:!perl %<CR> is just instruction to map Ctrl+p to:
a) write current the file :w
b) run command (perl) using % (currently open file) as parameter :!perl %
<CR> after each command stands for "carriage return": an instruction to execute specific command. imap does the same as map but listens Ctrl+p while in insert mode.
You could run it right from vim:
:!./script.sh
All suggestions here merely showcased :!{cmd} %, which passes current buffer to the shell cmd.
But there is another option :write !{cmd}
For example, the effect of the :write !sh command is that each line of the current buffer is executed in the shell.It is often useful, when for instance you've added a couple of lines to you buffer, and want to see execution result immediately without saving the buffer first.Also it is possible to execute some range, rather than whole content of the buffer::[range]write !{cmd}
save the file and call the script using an interpreter
eg.:
:!python %
It sounds like you're looking for !:
:!{cmd} Execute {cmd} with the shell.
You can use % to denote the current filename, if you need to pass it to the script:
!proofread-script %
You can also use ! with a range, to use the command as a filter:
!{motion}{filter} " from normal mode
:{range}!{filter} " from command mode
(In the first case, as with many other commands, when you type the motion, it'll pass you into command mode, converting the motion into a range, e.g. :.,.+2!)
And finally, if you don't actually need to pass input from your file, but want the output in your file, that's essentially a trivial filter, and the fastest way to do it is !!{cmd}. This will replace the current line with the output of the command.
To execute the current executable script, use
:!./%
! executes a shell command, % is the current filename and ./ adds the current dir in front.
Put this small snippet in your .vimrc to execute the current file with one keystroke (like F5) and display the result in a new split-pane buffer.
:! is okay but you need to switch to your terminal to see the result.
While you can do that with ctrl-z and bring vim back with fg it still means you need to switch context a lot.
The way this snippet works is by first guessing the executable based on the filetype and then running it with the current file as its argument.
Next a handy utility method takes the output and dumps it into a new buffer.
It's not perfect, but really fast for common workflows.
Here's the snippet copied below:
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"""""""""""""""""""""""""" RUN CURRENT FILE """""""""""""""""""""""""""""
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Execute current file
nnoremap <F5> :call ExecuteFile()<CR>
" Will attempt to execute the current file based on the `&filetype`
" You need to manually map the filetypes you use most commonly to the
" correct shell command.
function! ExecuteFile()
let filetype_to_command = {
\ 'javascript': 'node',
\ 'coffee': 'coffee',
\ 'python': 'python',
\ 'html': 'open',
\ 'sh': 'sh'
\ }
let cmd = get(filetype_to_command, &filetype, &filetype)
call RunShellCommand(cmd." %s")
endfunction
" Enter any shell command and have the output appear in a new buffer
" For example, to word count the current file:
"
" :Shell wc %s
"
" Thanks to: http://vim.wikia.com/wiki/Display_output_of_shell_commands_in_new_window
command! -complete=shellcmd -nargs=+ Shell call RunShellCommand(<q-args>)
function! RunShellCommand(cmdline)
echo a:cmdline
let expanded_cmdline = a:cmdline
for part in split(a:cmdline, ' ')
if part[0] =~ '\v[%#<]'
let expanded_part = fnameescape(expand(part))
let expanded_cmdline = substitute(expanded_cmdline, part, expanded_part, '')
endif
endfor
botright new
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap
call setline(1, 'You entered: ' . a:cmdline)
call setline(2, 'Expanded Form: ' .expanded_cmdline)
call setline(3,substitute(getline(2),'.','=','g'))
execute '$read !'. expanded_cmdline
setlocal nomodifiable
1
endfunction
Well it depends on your OS - actually I did not test it on M$ Window$ - but Conque is one of the best plugins around there: http://code.google.com/p/conque/
Actually, it can be better, but works. You can embed a shell window in a vim "window".
Related
I have set up a line of vimscript in my .vimrc file to pretty print JavaScript files:
nnoremap <leader>p :!js-beautify -r -j %<cr>
I want to just automatically reload the file instead of being prompted when vim comes back from the shell, is that possible? Thanks.
:! can be used a) to execute arbitrary shell commands or b) as a filter.
You are using it to run js-beautify against the file associated with the current buffer with the following consequences:
You are forced to exit Vim.
The file is modified outside of Vim so you get a prompt asking you if you want to reload or not.
Hence the many seemingly pointless <CR>.
What you actually want is to run js-beautify as a filter against the current buffer, which doesn't require you to exit Vim or press <CR>:
nnoremap <leader>p :%!js-beautify -f - -j<cr>
the special range % represents the whole buffer, it's the range on which we want to apply our filter
-f - passes the content of the buffer via stdin
That's it: no <CR>, no prompt, no mess.
As a bonus, here is a custom command from my config (I didn't want a mapping for that):
command! -buffer -range=% Format execute <line1> . "," . <line2> . "!js-beautify -f - -j -B -s " . &shiftwidth
edit
You can use context marks to return the cursor to its initial position:
nnoremap <leader>p m`:%!js-beautify -f - -j<CR>``
As the help for :! notes:
Vim redraws the screen after the command is finished,
because it may have printed any text. This requires a
hit-enter prompt, so that you can read any messages.
To avoid this use:
:silent !{cmd}
The screen is not redrawn then, thus you have to use
CTRL-L or ":redraw!" if the command did display
something.
So if you wanted to use the :redraw! approach, for example, you could do this using something like
nnoremap <leader>p :silent !js-beautify -r -j %<cr>:e!<cr>:redraw!<cr>
As everyone knows, we can :source current buffer by :so % .
But sometimes I want to :source just a part of the buffer, not the whole buffer. Say, I just added something to my .vimrc, and want to source that part, but I don't want to re-source all the rest stuff.
I tried select text and :so (actually :'<,'>so ) , but it reported that range is not allowed. So, how could this be done?
Of course I can save needed part to the temp file and source it, but it is clearly annoying.
Why not have the following mappings for sourcing a file (or a range):
" source the current file
nmap <leader>vs :source %<CR>
" source a visual range
vmap <leader>vs y:#"<CR>
Now, you can press ,vs (if your mapleader is ,), and the selected range will be sourced or otherwise, in normal mode, current file will be sourced.
You can define the following command, which operates on the current line or passed range:
":[range]Execute Execute text lines as ex commands.
" Handles |line-continuation|.
command! -bar -range Execute silent <line1>,<line2>yank z | let #z = substitute(#z, '\n\s*\\', '', 'g') | #z
Thanks to Ingo Karkat, I have taken his main idea and improved it. What I wanted to improve:
We have to use additional user-specified command :Execute instead of standard :so (ok, we can name user-specified command :So, anyway it's annoying to use new capitalized version of the command)
There is little side effect: register #z is corrupted after executing the command.
With my script below, we can use :so {file} command as before, and we are also able to use it with range: :'<,'>so (which actually expands to :'<,'>Source)
Here:
" This script provides :Source command, a drop-in replacement for
" built-in :source command, but this one also can take range and execute just
" a part of the buffer.
"
" Sources given range of the buffer
function! <SID>SourcePart(line1, line2)
let tmp = #z
silent exec a:line1.",".a:line2."yank z"
let #z = substitute(#z, '\n\s*\\', '', 'g')
#z
let #z = tmp
endfunction
" if some argument is given, this command calls built-in command :source with
" given arguments; otherwise calls function <SID>SourcePart() which sources
" visually selected lines of the buffer.
command! -nargs=? -bar -range Source if empty("<args>") | call <SID>SourcePart(<line1>, <line2>) | else | exec "so <args>" | endif
" in order to achieve _real_ drop-in replacement, I like to abbreviate
" existing :so[urce] command to the new one.
"
" So, we can call :so % just as before, and we are also call '<,'>so
cnoreabbr so Source
cnoreabbr sou Source
cnoreabbr sour Source
cnoreabbr sourc Source
cnoreabbr source Source
The following works if you only selected one line:
yq:p<enter>
This will also work:
y:<control-r>"<enter>
I want to pipe the selected text to a shell command and receive the one-line output from this shell command on the vim info/command line?
What I'm really trying to do: Pipe the selected text to a pastebin-type shell command and I want to receive the output of the shell cmd (which is the http link to the pastebin). Is this possible?
For multi line version you can do this after selecting the text:
:'<,'>:w !command<CR>
See the official Vim docs at :help :w_c.
You can map it to simple Visual mode shortcut like this:
xnoremap <leader>c <esc>:'<,'>:w !command<CR>
Hit <leader key>+c in visual mode to send the selected text to a stdin of the command. stdout of the command will be printed below vim's statusbar.
Real world example with CoffeeScript:
https://github.com/epeli/vimconfig/commit/4047839c4e1c294ec7e15682f68563a0dbf0ee6d
Simply highlight the lines using visual line select shift-v, the hit :! and type the command you wish to send the commands to. The resulting output will then replace your selected text.
When you type your command it will appear at the bottom as:
:'<,'>!somecmd
the '<,'> is indicating that the range you have visually selected will be passed to the command specified after the !
I would do it like this:
Place this function in your vimrc:
function Test() range
echo system('echo '.shellescape(join(getline(a:firstline, a:lastline), "\n")).'| pbcopy')
endfunction
This will allow you to call this function by doing:
:'<,'>call Test()
Then you can also map that like this (just under the function declaration in your vimrc):
com -range=% -nargs=0 Test :<line1>,<line2>call Test()
So you can call the function doing this:
:'<,'>Test
Note: :<','> are range selectors, in order to produce them just select the pertinent lines in visual mode and then go to command mode (pressing the colon key)
Maybe you should use something like
:echo system('echo '.shellescape(#").' | YourCommand')
Starting from some vim-7.4 version it is better to use
:echo system('YourCommand', getreg('"', 1, 1))
. This is basically the only way to keep NUL bytes untouched should they be present in the file. Passing #" in one or the other way will transform NUL bytes into NL (newline).
#matias 's solution is not work well for me, because it seems shellescape will append \ to each line.
So I use sed to accomplish this, and it works just fine!
"dump selected lines
function! DumpLines() range
echo system('sed -n '.a:firstline.','.a:lastline.'p '.expand('%'))
endfunction
com! -range=% -nargs=0 Dump :<line1>,<line2>call DumpLines()
An imperative way to do it is to:
yank your selection
drop into command mode with :
! + paste the register in the command-line like <Ctrl> r "
So: y : ! <Ctrl> r "
Another answer:
function Pastebin() range
let savedreg=#"
silent execute a:firstline.",".a:lastline."yank"
python import vim, subprocess
python p=subprocess.Popen(["pastebin"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
python p.stdin.write(vim.eval('#"'))
let #"=savedreg
python p.stdin.close()
python retstatus=p.poll()
python print p.stdout.read()
endfunction
Requires python support. Use it just like matias' function.
When I use :%! to run the contents of a file through a filter and the filter fails (it returns another code than 0) and prints an error message to stderr I get my file replaced with this error message. Is there a way to tell vim to skip the filtering if the filter returns an status code that indicates an error and/or ignore output the filter program writes to stderr?
There are cases where you want your file to replaced with the output of the filter but most often this behavior is wrong. Of course I can just undo the filtering with one keypress but it isn't optimal.
Also I have a similar problem when writing a custom vim script to do the filtering. I have a script that calls a filter program with system() and replaces the file in the buffer with its output but there doesn't seem to be a way to detect if the lines returned by system() where written to stdout or to stderr. Is there a way to tell them apart in vim script?
:!{cmd} Executes {cmd} with the shell and sets v:shell_error.
If you happen to set up mappings to call your filters, you could do something like the following:
function! UndoIfShellError()
if v:shell_error
undo
endif
endfuntion
nmap <leader>filter :%!/path/to/filter<CR>:call UndoIfShellError()<CR>
You can use Python to distinguish between stdout and stderr:
python import vim, subprocess
python b=vim.current.buffer
python line=vim.current.range.start
python p=subprocess.Popen(["command", "argument", ...], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
python returncode=p.poll()
python if not returncode: b.append(("STDOUT:\n"+p.stdout.read()+"\nSTDERR:\n"+p.stderr.read()).split("\n"), line)
To Vim 7 were added new autocommand events: ShellCmdPost and ShellFilterPost
augroup FILTER_ERROR
au!
autocmd ShellFilterPost * if v:shell_error | undo | endif
augroup END
An alternative would be to run the filter command such as it modifies the file on disk.
For example, for gofmt (www.golang.org) I have these mappings in place:
map <f9> :w<CR>:silent !gofmt -w=true %<CR>:e<CR>
imap <f9> <ESC>:w<CR>:silent !gofmt -w=true %<CR>:e<CR>
Explanation:
:w - save file
:silent - avoid pressing enter at the end
% - passes the file to gofmt
-w=true - tells gofmt to write back to the file
:e - tells vim to reload modified file
This is what I ended up doing:
function MakeItAFunction(line1, line2, args)
let l:results=system() " call filter via system or systemlist
if v:shell_error
"no changes were ever actually made!
echom "Error with etc etc"
echom results
endif
"process results if anything needed?
" delete lines but don't put in register:
execute a:line1.",".a:line2." normal \"_dd"
call append(a:line1-1, l:result) " add lines
call cursor(a:line1, 1) " back to starting place
" echom any messages
endfunction
command -range <command keys> MakeItAFunction(<line1>,<line2>,<q-args>)
" or <f-args>, etc.
You can see my full code at http://vim.wikia.com/wiki/Perl_compatible_regular_expressions
It's complicated, but it works and when it's used, it's fairly transparent and graceful. Hope that helps in any way!
How to execute file that I'm editing in Vi(m) and get output in split window (like in SciTE)?
Of course I could execute it like that:
:!scriptname
But is it posible to avoid writing script name and how to get output in split window instead just bottom of the screen?
There is the make command. It runs the command set in the makeprg option. Use % as a placeholder for the current file name. For example, if you were editing a python script:
:set makeprg=python\ %
Yes, you need to escape the space. After this you can simply run:
:make
If you wish, you can set the autowrite option and it will save automatically before running the makeprg:
:set autowrite
This solves the execute part. Don't know any way of getting that output into a split window that doesn't involve redirection to file.
To access the current buffer's filename, use %. To get it into a variable you can use the expand() function. To open a new window with a new buffer, use :new or :vnew. To pipe the output from a command into the current buffer, use :.! . Putting it all together:
:let f=expand("%")|vnew|execute '.!ruby "' . f . '"'
obviously replacing ruby with whatever command you want. I used execute so I could surround the filename with quotation marks, so it'll work if the filename has spaces in it.
Vim has ! ("bang") command which executes shell command directly from VIM window. Moreover it allows launching sequence of commands that are connected with pipe and read stdout.
For example:
! node %
is equivalent to opening command prompt window and launching commands:
cd my_current_directory
node my_current_file
See "Vim tips: Working with external commands" for details.
I have a shortcut for that in my vimrc:
nmap <F6> :w<CR>:silent !chmod 755 %<CR>:silent !./% > .tmp.xyz<CR>
\ :tabnew<CR>:r .tmp.xyz<CR>:silent !rm .tmp.xyz<CR>:redraw!<CR>
This writes the current buffer, makes the current file executable (unix only), executes it (unix only) and redirects the output to .tmp.xyz, then creates a new tab, reads the file and then deletes it.
Breaking it down:
:w<CR> write current buffer
:silent !chmod 755 %<CR> make file executable
:silent !./% > .tmp.xyz<CR> execute file, redirect output
:tabnew<CR> new tab
:r .tmp.xyz<CR> read file in new tab
:silent !rm .tmp.xyz<CR> remove file
:redraw!<CR> in terminal mode, vim get scrambled
this fixes it
For Shell script I've used
:set makeprg=%
:make
Vim 8 has an interactive terminal built in. To run the current bash script in a split pane:
:terminal bash %
or for short
:ter bash %
% expands to the current file name.
From :help terminal:
The terminal feature is optional, use this to check if your Vim has it:
echo has('terminal')
If the result is "1" you have it.
I use a slightly more intrusive mechanism through maps:
map ;e :w<CR>:exe ":!python " . getreg("%") . "" <CR>
Just makes it so I don't have to save, then go. Just go.
You can use vim's plugin bexec. To my knowledge the latest version is 0.5.
Then:
$ mkdir -p ~/.vim/plugin
$ mv bexec-0.5.vba ~/.vim/plugin
$ vim ~/.vim/plugin/bexec-0.5.vba
Inside vim itself while editing the .vba file do:
:so %
Some output will show up letting you know that bexec.vim has been written as well as documentation, etc..
Now, you can test it by opening your (whatever language script that has an #! interpreter working properly) in vim and run
:Bexec
Note: I wanted the split to be vertical rather than horizontal, so I did:
$ grep -i -n split ~/.vim/plugin/bexec.vim | grep -i hor
102: let bexec_splitdir = "hor" " hor|ver
261: exec {"ver":"vsp", "hor":"sp"}[g:bexec_splitdir]
and changed the value of from "hor" to "ver"..
I know it's an old question, but I hope this can help someone out there. I have been running in the same issue while taking Coursera's Startup Engineering course where professor Palaji uses Emacs and I don't like Emacs..
Just use colon and exclamatory mark as shown below
:!< script_name>
Based on #SethKriticos and #Cyril answers I now use the following:
function! Setup_ExecNDisplay()
execute "w"
execute "silent !chmod +x %:p"
let n=expand('%:t')
execute "silent !%:p 2>&1 | tee ~/.vim/output_".n
" I prefer vsplit
"execute "split ~/.vim/output_".n
execute "vsplit ~/.vim/output_".n
execute "redraw!"
set autoread
endfunction
function! ExecNDisplay()
execute "w"
let n=expand('%:t')
execute "silent !%:p 2>&1 | tee ~/.vim/output_".n
" I use set autoread
"execute "1 . 'wincmd e'"
endfunction
:nmap <F9> :call Setup_ExecNDisplay()<CR>
:nmap <F2> :call ExecNDisplay()<CR>
Use F9 to setup the new window and F2 to execute your script and tee to your output file.
I also added the script name to the output file name, so that you can use this for multiple scripts at the same time.
In your .vimrc you can paste this function
function! s:ExecuteInShell(command)
let command = join(map(split(a:command), 'expand(v:val)'))
let winnr = bufwinnr('^' . command . '$')
silent! execute ':w'
silent! execute winnr < 0 ? 'vnew ' . fnameescape(command) : winnr . 'wincmd w'
setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap number
silent! execute 'silent %!'. command
silent! redraw
silent! execute 'au BufUnload <buffer> execute bufwinnr(' . bufnr('#') . ') . ''wincmd w'''
silent! execute 'nnoremap <silent> <buffer> <LocalLeader>r :call <SID>ExecuteInShell(''' . command . ''')<CR>'
silent! execute 'wincmd w'
" echo 'Shell command ' . command . ' executed.'
endfunction
command! -complete=shellcmd -nargs=+ Shell call s:ExecuteInShell(<q-args>)
cabbrev shell Shell
After that, in vim run command :shell python ~/p.py as example. And you will get the output in splitted window.
+ After changes in p.py as example you will run the same command again, this function will not create new window again, it will display the result in the previous(same) splitted window.
#xorpaul
I was looking for this script (python/Windows) for quite some time. As there is no "tee" in Windows I changed it to:
function! Setup_ExecNDisplay()
execute "w"
let n=expand('%:t')
execute "silent ! python % > d:\\temp\\output_".n ." 2>&1"
execute "vsplit d:\\temp\\output_".n
execute "redraw!"
set autoread
endfunction
function! ExecNDisplay()
execute "w"
let n=expand('%:t')
execute "silent ! python % > d:\\temp\\output_".n . " 2>&1"
endfunction
:nmap <F9> :call Setup_ExecNDisplay()<CR>
:nmap <F2> :call ExecNDisplay()<CR>
I'd recommend the plugin quickrun. It's fast and simple to configure. Here's a little demonstration: