How to properly expand <cword> inside an execute command in vim - vim

I am trying to use vim's :exe command to vimgrep the word under cursor, but I am having problems with the command construction:
If my command is
:execute "vimgrep " shellescape(expand("<cword>")) " somedir"
it expands to
execute "vimgrep 'foo' somedir"
But if my command is
execute "vimgrep " .. shellescape(expand("<cword>")) .. " somedir"
it expands to
execute "vimgrep '' somedir"
How do I correctly expand <cword> raw, without single quotes?

Wrapping something in single quotes is the main purpose of shellescape(). If you don't want those quotes, don't use shellescape().
:help shellescape() is used for preparing commands before they are shelled out. Since :help vimgrep is an internal tool and nothing is sent to a shell, there is no need for quoting.
This means that the following command should cover most of your needs:
:execute "vimgrep " .. expand("<cword>") .. " somedir"
Also, :vimgrep searches in files, not directories. Your command should then look more like:
:execute "vimgrep " .. expand("<cword>") .. " somedir/*"

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>")

Escape `#` in a command when opening a url

I am trying to open the following url:
:execute 'silent !open https://github.com/junegunn/fzf#environment-variables'
However, it seems like when I run this command, the # is replaced (again) with the filepath instead of appearing as # literally. How do I prevent this?
Escape it with a backslash:
:execute 'silent !open https://github.com/junegunn/fzf\#environment-variables'
Call escape():
:execute 'silent !open' escape('https://github.com/junegunn/fzf#environment-variables', '#')

Make vimscript bang command interpret vim variables

I'm writing a vimscript tool to allow me to look at git diffs from within my current vim session by calling git difftool . This works fine when I hard-code the two sha values, but fails when I try to specify them as variables. I wrote a 2 line shell script do_git_difftool to explore the problem, and it shows that instead of passing the values, the literal variable names "l:left_sha" and "l:right_sha" are passed, even though they are correctly interpreted in the echom on the previous line. What gives?
function! s:GitDiff (...)
let l:the_count = 1
:let l:syscommand = "get_sha back " . l:the_count
:let l:left_sha = system(l:syscommand)
:let l:syscommand = "get_sha top"
:let l:right_sha = system(l:syscommand)
echom "sha values" l:left_sha l:right_sha
:silent !git difftool l:left_sha l:right_sha
:silent !do_git_difftool l:left_sha l:right_sha
endfunction
# in a bash script named do_git_difftool
echo args are $1 and $2 > thelog
git difftool $1 $2
in the log file I see:
args are l:left_sha and l:right_sha
TL;DR: yes. You should probably use
exec "!git_difftool " . l:left_sha . " " . l:right_sha
Bang commands will pass everything after them to the terminal without vim touching it (since shell commands don't use vim syntax). To get variable interpretation, make it an exec command to create the bang command.
Edit:
If you need it silent, just prepend the line with silent

Export variable or register to file

I'm trying to export a variable to a file:
To simplify what I'm doing right now, I use the variable #" which contains the content of the current register :
function! CopyVar()
call system("printf '%s' '".#"."' > /tmp/varfile")
endfunction
nnoremap <Leader>y :call CopyVar()<cr>
This works fine, but doesn't work if the variable contains single quotes for example.
If the input is for example " hi ' , the command fails with E484: Can't open file /tmp/v7IzDCI/74
I think I could escape the #" (because if ' single quotes are present, the command will fail), however, I'm not sure it is the best method to export a variable to a file.
How can I do it to guarantee that it will work with any input (with quotes, multilines and other special characters) ?
I know that you can use the system clipboard, it's not what I'm trying to achieve here.
That is what shellescape() is for. BTW for better quoting, I would write your function like this:
function! CopyVar()
let cmd="printf '%s'". shellescape(#") . " > /tmp/varfile"
call system(cmd)
endfunction
nnoremap <Leader>y :call CopyVar()<cr>
However, your solution looks overly complex, by adding some shell quotation to the complex VimL quotation. That is not needed. I would rather do it differently:
:redir! > /tmp/varfile | sil exe 'echo #"' |redir end
This adds an extra leading blank line at the beginning, but you could remove this afterwards using sed or something.
Or even better, use the writefile() function:
nnoremap <Leader>y :call writefile(split(#", '\n'), '/tmp/varfile')
#" is a register, not a variable.
You can use a substitution to clean up your text:
let foo = substitute(getreg('"'), "'", "\"", "g")
before the "write" step.
I'll let you figure out what patterns you need to clean up.
Using redir is probably the most robust solution
function! CopyVar()
redir! > /tmp/varfile | sil exe 'echo #"' |redir end
call system ("sed '1d' /tmp/varfile > /tmp/tmpvarfile; mv /tmp/tmpvarfile /tmp/varfile")
endfunction
nnoremap <Leader>y :call CopyVar()<cr>
It works with any characters. The second part is needed because the redirection will add an additional newline which is not wanted, hence the sed command.

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