What is the simplest way to run a vim function/script? For example, to run a python script I'll usually open two windows, the script on top and the shell on bottom:
# script.py
def hello():
print ('hello')
if __name__ == '__main__':
hello()
-----------------------
$ python script.py
I assume to run a vim script I would want to be 'inside' the vim process (so for example, I could highlight text or do whatever might be necessary for the input to the vim script). Currently I do this in a quite tedious manner: I put the function in vimrc and then whenever I want to run it I do something like:
Re-save file - :w
Re-source vimrc - :so $MYVIMRC
Re-run function - :call MyFunction()
I was hoping I could just create a file called something like script.vim (in any directory), open the file and then 'run it'. What would be the best way to do this? For reference, here is the function I want to run
function MyFunction()
echom "Hello"
echom getline('.')
endfunction
You can use :so % to source the file, which will run any commands in it. For example:
function MyFunction()
echom "Hello"
echom getline('.')
endfunction
call MyFunction()
Now running:
:w | so%
Will print at the bottom:
Hello
call MyFunction() <-- current line the current is on
Press ENTER or type command to continue
Related
I'm trying to incorporate vim into my main workflow. A major sticking point for me has been interactively editing and running programs/scripts.
For example given that I'm currently vimmed into test.py
print('hello')
x = 5
y = x+2
print(y)
Without leaving vim how would I:
a) run the whole script without leaving vim
b) run just "print('hello')"
Commenters and the other answer have pointed out how to run a file from vim. But they glossed over some really powerful possibilities. I'd like to explain how some of those work in more detail.
The simplest possible way of running a python script in vim, is to just call the python interpreter on the file, e.g.
:!python %
or, as I prefer to do to make sure there are no unsaved changes,
:w | !python %
But it is not even necessary to have a file to run a python script in vim. The reason why is because :w != save! :w means write, and if no argument is provided, it happens to write to the file you are editing. However, you can write to STDOUT, to another file, or even to another program. So if you'd like to run your buffer as python code without having a file to save and run, you may simply do:
:w !python
This meanse write the current buffer into the external program "python". This literally just sends the contents of your buffer directly to python.
Now here's where it gets really cool. In vim, :w is an "ex command", e.g. a command that you run from the vim command line that originally came from ex, a very old line based unix text editor. The awesome thing about ex commands is that since they are all line based, you can directly state which lines you would like the command to apply to. For example:
:2w myfile.txt
will write only line two to the file "myfile.txt". You can even supply a range, e.g.
:2,7w myfile.txt
will write lines 2-7 to "myfile.txt". This means that using your example, we can run
:1w !python
to run just
print('hello')
To make this more convenient, you can use visual mode to select every line you would like to run, which will automatically fill in the right range for you. This will look like
:'<,'>w !python
To make this more convenient, I would recommend adding something like
xnoremap <leader>p :w !python<cr>
to your .vimrc. Then you can visually select whatever you want and run it as python code by typing
\p
(replace \ with whatever you have set up as your leader). You could also do
nnoremap <leader>p :w !python<cr>
or
nnoremap <leader>p :w | !python %<cr>
depending on whether you want to save to a file or not.
Create a function for a range as discussed in this question:
fu PyRun() range
echo system('python -c ' . shellescape(join(getline(a:firstline, a:lastline), "\n")))
endf
Create a mapping for visual mode:
vmap <C-F6> :call PyRun()<CR>
Then you can select a range and press Control-F6. The range of lines will be executed by python. The result will be displayed in the command area.
You can run a program from vim using :!, i.e. :!python3 % to run your current script.
If you want to bind a key to it, another easy way would be to set makeprg to your python executable: :set makeprg=python3 and then bind a key to :make<cr>. In that case I would set up autocmds that switch the makeprg depending on the file type.
If you want to run a simple statement, you could either use Python's -c switch:
:!python3 -c 'print("Hello world")', or you could just run :!python3 without arguments to be dropped into a REPL without leaving Vim.
Let me jump right in.
What I'm trying to do is simply print out the file path of any open buffer when I exit vim. This is useful because I often open other files in vim buffers with a vim script I wrote that can search through my codebase for a specific function call.
I figure I can set up an autocommand, either for when I open a file or when I leave vim, and use the output from :ls to list all currently open buffers. The problem that I'm having is that I can't get any output to show up in terminal. I have tried various combinations of :!echo in my function, but to no avail. I have been trying something like the following in my .vimrc
function! PrintFileName()
:!echo "hello"
:exec "!echo world"
"... etc
endfunction
au BufRead * call PrintFileName()
Both :!echo foobar and :call PrintFileName() work for me if I do it from the command line. I also figure I might have to use some form of silent/redraw! so I don't have to hit enter to continue.
Really the main problem here is that I can't see a way to get output to stdout from inside my function and called by an autocommand.
Thanks for any help.
Okay, so I've found this solution, which works as long as I enter vim from the last line of my terminal. Otherwise this prints out a line below the current line and will get overwritten when you press enter. If anyone knows how to fix that let me know, otherwise I will use this.
function! PrintBuffers()
redir => files
:ls
redir END
" Regex to strip out everything from :ls but the buffer filenames
let files = substitute(files, '^[^"]*"', '', 'g')
let files = substitute(files, '"[^"]*\n[^"]*"', '\n', 'g')
let files = substitute(files, '"[^"]*$','','g')
" This is the magic line
exe '!echo; echo ' . shellescape(&t_te . files)
endfunction
au VimLeave * call PrintBuffers()
*Note - As I'm writing this, I realize that this won't display the right path if you did a :cd at some point. So I guess its pretty fragile, but it does the job.
Vim-go plugin has a :GoFile function to show source files that depends on the current package. The output looks like this:
['/home/tretkow/tut/main.go', '/home/tretkow/tut/test.go']
How to open files from the list?
:GoFiles only echoes the output of go#tool#Files().
From the look of your snippet, it should be possible to extract a filename with something like:
:e <C-r>=go#tool#Files()[0]<CR>
or put that list in a scratch buffer:
:vnew<CR>
:0put=join(go#tool#files(), '\r')<CR>
/home/tretkow/tut/main.go
/home/tretkow/tut/test.go
and use gf to jump to the file under the cursor.
Here is a more sophisticated solution: the :GoFile command lets you choose what file from the :GoFiles command to edit via custom tab-completion.
" the command
command! -nargs=1 -complete=customlist,GoFilesComplete GoFile call GoFile(<f-args>)
" the completion function
function! GoFilesComplete(ArgLead, CmdLine, CursorPos)
return filter(go#tool#Files(), 'v:val =~ a:ArgLead')
endfunction
" the :edit wrapper
function GoFile(file)
execute "edit " . a:file
endfunction
Usage:
:GoFile <Tab>
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".
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!