Vim: open a temporary buffer displaying executable's output - vim

I have found the :cwindow command to be very useful and I was wondering if I could get similar functionality using the output of my compiled code. I'd the output of :!./a.out to appear in a "quickfix" style buffer.
I've also noticed that even after taking the standard steps to prevent the "Press Enter to continue" message, it still happens at least once on :make and :!./a.out - using :silent to suppress this causes my tmux to go completely blank. My current workaround involves a mapping with a lot of carriage returns, is there another way?

Sure, you can use vim's preview window with a short function to execute the command, try this in your .vimrc:
fun! Runcmd(cmd)
silent! exe "noautocmd botright pedit ".a:cmd
noautocmd wincmd P
set buftype=nofile
exe "noautocmd r! ".a:cmd
noautocmd wincmd p
endfun
com! -nargs=1 Runcmd :call Runcmd("<args>")
Then you can:
:Runcmd ls
And see the results of ls in your preview window

I found this:
" Shell ------------------------------------------------------------------- {{{
function! s:ExecuteInShell(command) " {{{
let command = join(map(split(a:command), 'expand(v:val)'))
let winnr = bufwinnr('^' . command . '$')
silent! execute winnr < 0 ? 'botright vnew ' . fnameescape(command) : winnr . 'wincmd w'
setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap nonumber
echo 'Execute ' . command . '...'
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>:AnsiEsc<CR>'
silent! execute 'nnoremap <silent> <buffer> q :q<CR>'
silent! execute 'AnsiEsc'
echo 'Shell command ' . command . ' executed.'
endfunction " }}}
command! -complete=shellcmd -nargs=+ Shell call s:ExecuteInShell(<q-args>)
nnoremap <leader>! :Shell
" }}}
in steve losh's .vimrc - see which I shamelessly copied.

I have just discovered the :read command, which puts the output of a shell command into a window.
I went looking for this because I often want to grep for a file in the current directory which contains a certain string (and then open it into my current VIM).
Here is a shortcut I have in my $MYVIMRC:
noremap <leader>g :new<CR>:read ! grep -rn "
With this, when I press \g I get a new buffer created in a split window and find
:read ! grep -rn "
sitting waiting for me in the command area. Now I just type my search string, close the double quotes and hit <Enter> and the buffer fills with the command output.
Once done, a simple
:bw!
in that new buffer will kill it.

First open a preview window, and set it to autoread a file:
:botr pedit +:setl\ autoread /tmp/out.log
Now just run your command, and send the output to the file.
:!date > /tmp/out.log 2>&1
The result of your command should appear in the preview window.
However, we are still getting the "Press ENTER" prompt. A simple way to avoid that is to make a mapping that presses Enter for us:
:nmap <Leader>r :exec '!date > /tmp/out.log 2>&1'<CR><CR><CR>
I thought only two <CR>s would be needed but then found myself needing three.

Related

Prevent vim from jumping through source code

folks!
I'm using vim as an IDE for writing code in bash, Python and C. Besides, I have a key map to execute my current buffer depending on the file type. Here is a responsible parts of my .vimrc:
...
autocmd FileType python call Python_source()
autocmd FileType sh call Bash_source()
...
" Read lw (lispwords) modelise from current buffer and pass it as command arguments
func! LWargs()
set lw=''
doautocmd BufRead
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunc
func! Python_source()
setlocal number cursorline
setlocal shiftwidth=2
setlocal foldmethod=indent
map <F9> :w \| :exe '!python' '%:p' . LWargs()<CR>
imap <F9> <Esc> :w \| :exe '!python' '%:p' . LWargs()<CR>
" Comments on Ctrl-C
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
func! Bash_source()
setlocal number cursorline
setlocal shiftwidth=4
map <F9> :w \| :!./%<CR>
imap <F9> <Esc> :w \| :!./%<CR>
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
...
So, when I press F9, the magic happen and my code is executed with arguments passed by the LWargs. The only problem is after the program exits, vim will jump to the beginning of the file forcing me to position cursor back the line I was working and making my life harder. Is there is any way to prevent vim from jumping around?
The issue is with the doautocmd BufRead command in your LWargs(), which resets the cursor position to the top of the file.
(At least in my case, the command used to restore cursor position upon opening Vim is the one causing the cursor to move. You can inspect the list from :autocmd BufEnter * to see if you can spot a similar command or another one that might be causing the cursor to move. Looking again, it turns out I was also getting that same rule from my Linux distribution...)
A good way to prevent this is to use winsaveview() to save the cursor location and the window view in general (which line is at the top, whether your window is scrolled right to a column when word wrap is off) and winrestview() to restore it.
Here's an updated LWargs() that won't move your cursor:
function! LWargs()
set lw=
let saved_view = winsaveview()
doautocmd BufRead
call winrestview(saved_view)
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunction
Note also that the correct syntax is set lw=, if you use set lw='' you'll be setting a two-character string with two single quotes.

How to map VIM to run current RSpec file in console?

I'm trying this:
inoremap <F2> :!rspec %
But it doesn't work. Can anyone help?
The "i" in inoremap means "insert mode": your mapping is an insert mode mapping so it obviously won't work in normal mode.
Try nnoremap ("n" for "normal mode") instead and add <CR> at the end to actually execute the command:
inoremap <F2> :!rspec %<CR>
Since we are at it, you could also modify it to "write and run":
nnoremap <F2> :update<bar>!rspec %<CR>
Maybe this is a better solution to check the file with rspec (put these lines in to your .vimrc, it needs nocompatible to be set, works in insert mode):
" Open quickfix window after :make if there was errors.
autocmd QuickFixCmdPost * botright cwindow
inoremap <F2> :call Rspec()<CR>
" Check the file with rspec, don't forget to save it before calling.
function Rspec()
let save_makeprg = &makeprg
compiler rspec
let &makeprg = 'rspec "' . expand( '%' ) . '"'
echo expand( &makeprg )
silent make
let &makeprg = save_makeprg
redraw!
endfunction
It will list the errors in quickfix window.

Appending a line at the end of a file

Within VIM, I can execute Octave scripts via this mapping:
map <F6> :w<CR>:!run-octave -q % <CR>
However, as I recently discovered, Octave doesn't show any plots unless there is a "pause" command at the end of a script.
How can I map F6 so this pause command is added automatically each time I invoke the key?
Thanks.
This may not be the best way to handle running Octave scripts from Vim, see below for an alternative.
The requested solution
To accomplish what you want, define the following function somewhere:
function! RunOctave()
let save_cursor = getpos('.')
call append(line('$'), "pause")
write
execute "!run-octave -q " . expand('%')
$delete _
write
call setpos('.', save_cursor)
endfunction
And define a mapping to call it:
map <F6> :w<CR>:call RunOctave<CR>
A better approach
I started out by running Octave similar to the above, but as I tend to use slow computers this is not a good solution for me. I have instead configured Vim to remote-control an instance of Octave.
This can be done through a terminal multiplexer, similar to what I suggested for Matlab here, e.g.:
tmux new-session -s octave "octave -q"
For optimal flexibility, I want to be able to evaluate single lines in octave from Vim:
imap <buffer> <F1> <Esc>:!tmux send-keys -t octave "<C-r>=getline('.')<CR>"<C-r>=nr2char(13)<CR><CR>a
nmap <buffer> <F1> :!tmux send-keys -t octave "<C-r>=getline('.')<CR>"<C-r>=nr2char(13)<CR><CR>
Evaluate visual selections:
vmap <buffer> <F2> call ExecSelection()<CR><CR>
Which depends on the following function:
let s:octave_selection = '/var/tmp/vim_octave_selection.m'
function! ExecSelection()
if line(".") == line("'<'")
exec ".write! " . s:octave_selection
else
exec ".write! >> " . s:octave_selection
endif
if line(".") == line("'>")
exec "!tmux send-keys -t octave 'run " . s:octave_selection . "'" . nr2char(13)
endif
endfunction
And last, but not least, evaluate the whole file:
imap <buffer> <F3> <Esc>:!tmux send-keys -t octave "<C-r>=expand('%:t:r')<CR>"<C-r>=nr2char(13)<CR><CR>a
nmap <buffer> <F3> :!tmux send-keys -t octave "<C-r>=expand('%:t:r')<CR>"<C-r>=nr2char(13)<CR><CR>
This last mapping requires that the current working directory for the buffer is the same as that of the Octave script.

Allowing custom motion in a vim map?

I have the following mapping that allows to paste over a word from the yank buffer. (cpw = change paste word): nmap <silent> cpw "_cw<C-R>"<Esc>
What I would like to do is allow commands such as the following:
cpiw (change paste in word -> like the 'iw' motion)
cpaw (change paste a word -> like the 'aw' motion)
for any motion {m} cp{m}
Is this possible to allow in a mapping, so I don't have to write the nmap for each motion that I want to work with it?
Thanks in advance.
EDIT: typo fixes. My solution below
After diligently looking into the map-operator I was successful in making a function that did exactly as I wanted. For any who are interested it is as follows:
"This allows for change paste motion cp{motion}
nmap <silent> cp :set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
silent exe "normal! `[v`]\"_c"
silent exe "normal! p"
endfunction
EDIT - new version that might be better.
"This allows for change paste motion cp{motion}
nmap <silent> cp :set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent exe "normal! `<" . a:type . "`>\"_c" . #"
elseif a:type == 'line'
silent exe "normal! '[V']\"_c" . #"
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]\"_c" . #"
else
silent exe "normal! `[v`]\"_c" . #"
endif
endfunction
There is a way to define a custom operator, see :help :map-operator for details.

How to execute file I'm editing in Vi(m)

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:

Resources