How to let and execute in the same line? - vim

I was trying to make a simple setup for Perl in Vim and used this:
function! PerlEnvSetup()
nnoremap <F10> :let f=expand("%")|vnew|execute '.!perl "' . f . '"'<CR>
endf
autocmd FileType perl :call PerlEnvSetup()<CR>
But when I do vim XX.pl, the error in the bottom saying:
Error detected while processing function PerlEnvSetup:
line 1:
E121: Undefined variable: f
E15: Invalid expression: '.!perl "' . f . '"'<CR>
Error detected while processing FileType Auto commands for "perl":
E488: Trailing characters
And the window splits into two (which should not happen unless I press F10). What am I doing wrong here?

What you are seeing, is that the | is used to separate commands (and therefore explicitly ends your :nnoremap command.
You therefore want to use the special idiom <Bar> as described by the help below :h map_bar

Here's another option:
function! PerlExecuteCurrentFile()
let f=expand("%")
vnew
execute '.!perl "' . f . '"'
endf
function! PerlEnvSetup()
nnoremap <F10> :call PerlExecuteCurrentFile()<CR>
endf

Related

vglobal with parameter in function, to filter textfile into temporary file

I've got a textfile which I want to filter for a pattern, to get a quick overview. My normal approach in vim is to filter via :v/pattern/d. This works fine, but If I save the file accidentally, after I did the filtering, I loose the not filtered information.
To avoid this, I search for a solution where the orginal textfile cannot be destroy by accident.
My current solution is a function where I read the textile into a temporary file and run the filter on this file. But the function does not work if I try to search something like ^linestart. Furthermore I want to highlight the search pattern, which as well does not work as expected.
Here is my function in vimscript:
function! FilterJournal(pattern)
:exe 'e ~/tempfile'
" delete all existing lines
:1,$d
:exe 'r ~/journal.txt'
:exe 'silent v/ ' . a:pattern . '/d'
" to highlight the search pattern
:exe 'silent / ' . a:pattern
endfunction
:command! -nargs=1 Fijo :call FilterJournal("<args>")
When I run the command: Fijo foo I get the result, but the highlightning does not work.
When I run the command: Fijo ^foo I get some error messages and the tempfile is empty:
Error during execution of "function FilterJournal":
Line 6:
E486: Pattern not found: ^foo
How can I filter my textfile without destroy it by accident or get my function to work?
You can try this based on your function. Please note that
The space in v/ causes E486: Pattern not found
I tried set hlsearch but it did not work in this case. However, :match works well for highlighting in the current buffer
Leading : is not necessary
function! FilterJournal(pattern)
exe 'e ~/tempfile'
" delete all existing lines
1,$d
exe 'r ~/journal.txt'
exe 'v/' . a:pattern . '/d'
" to highlight the search pattern
exe 'match Search /' . a:pattern . '/'
endfunction
:command! -nargs=1 Fijo :call FilterJournal("<args>")
If you really want "to get a quick overview", you could simply do:
:g/pattern
which prints the matching lines without affecting the buffer.
Give this a try, it yanks the matched lines and show it in a vsplit buffer:
function! FilterIt(pat)
call setreg('x', '')
call setreg('/', a:pat)
exec 'g/'.a:pat.'/y X'
exec 'vsp /tmp/t'
exec 'norm! ggVG"xp'
endfunction
:command! -nargs=1 Fijo :call FilterIt("<args>")

How to avoid remap in vim

I have the following on my .vimrc
au FileType ruby nnoremap <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
This executes rspec on the line specified, and gives me option to edit the line before pressing enter. But I have to be on the test file so it get the file name and line number correctly.
While developing I run nnoremap <leader>t :!rspec some/spec/file.rb:123 manually to run the test I want from anywhere in the code. So I can code and fire the test without need to visit the test file.
The problem is that if I visit another ruby file the mapping in .vimrc runs again and I loose the nnoremap command I used before. Is there a command to only map (in normal mode) if there isn't already a map for that sequence?
Regards,
This should be a buffer-local mapping. Use <buffer>:
au FileType ruby nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
We can do better!
Use an augroup and make is self clearing to make it safe to re-:source.
augroup ruby_testing
autocmd!
autocmd FileType ruby nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
augroup END
Even better forgo the autocmd and put this mapping in the after directory. Add the following line to ~/.vim/after/ftplugin/ruby.vim:
nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
For more help see:
:h :map-local
:h :augroup
:h after-directory
Vim has the :help :map-<unique> modifier which makes the mapping fail if such mapping already exists.
au FileType ruby nnoremap <unique> <expr> <leader>t ...
You can suppress the error message with :silent!:
au FileType ruby silent! nnoremap <unique> <expr> <leader>t ...
Alternatively (and this is slightly better because it doesn't suppress any other errors in the mapping definition, like syntax errors), you could explicitly check for the mapping's existence via maparg():
au FileType ruby if empty(maparg('<leader>t', 'n')) | nnoremap <expr> <leader>t ... | endif
Note that this literally implements that (I understand) you're asking for; i.e. the first Ruby file will define the mapping, and any subsequent Ruby files are ignored; the mapping will always use the first file's name and line number. If you instead want to have different right-hand sides for the same mapping, depending on the currently edited file, the solution is a buffer-local mapping as per #PeterRincker's answer. But with that, you have to be inside the original Ruby buffer to trigger the correct mapping.
A remedy for that might be to recall the executed command-line from the command-line history (should happen automatically for your incomplete mapping, else via histadd()), so that you can easily recall the command from another file.

Vim - Pydoc plugin not working with operator mapping

I have a small operator mapping for use with the Pydoc plugin. The code for it is below:
nnoremap <buffer> <localleader>d :set operatorfunc=<SID>PydocOperator<cr>g#
vnoremap <buffer> <localleader>d :<c-u>call <SID>PydocOperator(visualmode())<cr>
function! s:PydocOperator(type)
let l:orig_register = ##
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
execute 'Pydoc ' . shellescape(##)
let ## = l:orig_register
endfunction
However, vim throws an error:
E116: Invalid arguments for function <SNR>117_ShowPyDoc
The same error happens if I copy some text manually and run this command:
execute 'Pydoc ' . shellescape(##)
This is very odd, considering that the :Pydoc should work as a normal command, taking one argument as its input. I looked at the code where the :Pydoc command is defined, (that line of code is here) and discovered that passing an argument to the :Pydoc command that is in quotes might be causing an issue. So I ran :Pydoc 'sys' to see if it would throw the same error as the operator mapping, which it did. So if it is having an issue with the quotes around the argument, how do I format the execute command so that it doesn't give an invalid argument?
The shellescape() function is not necessary for the :Pydoc command. shellescape includes quotes in the returned string, which causes :Pydoc to self destruct. However, if the command were :grep, for instance, shellescape would need to be used.
Relevant help topics:
:help shellescape()
:help 'operatorfunc'
:help :map-operator

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.

Vim: open a temporary buffer displaying executable's output

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.

Resources