In Vim, how can I save a file to a path stored in a variable? - vim

This is inside a script:
:let s:submission_path = expand("%:p:h") . '\submissions\test_submission.csv'
:echo s:submission_path
:write s:submission_path
The script raises an error "can't open file for writing", because I'm on windows and ':' is not a valid filename character. If I name the variable 'submission_path' a file with that name is written (edit: and the contents of the file are as I expect, the contents of the buffer the script is editing).
I don't understand this. The echo command before the write does what I expect it to, output the full path to the file I want to write. But write seems to ignore the variable value and use its name instead? Do I need to dereference it somehow?

Try this:
:let s:submission_path = expand("%:p:h") . '\submissions\test_submission.csv'
:echo s:submission_path
:execute "write " . s:submission_path
Execute will allow you to compose an ex command with string operations and then execute it.

vim has very screwy evaluation rules (and you just need to learn them). write does not take an expression it takes a string so s:submission_path is treated literally for write. However echo does evaluate its arguments so s:submission_path is evaluated.
To do what you want you need to use the execute command.
exec 'write' s:submission_path

Environment variables don't have this problem, but you have to dereference them like in unix with a dollar sign:
:let $submission_path = expand("%:p:h") . '\submissions\test_submission.csv'
:echo $submission_path
:write $submission_path
AND it modifies your environment within the vim process, so just be aware of that.

Related

function failed when call it from a command in vim

When I find the word in the current file, I need to first type "/keyword", but I can't see all the matched rows, So I tried to use the following command to do a shortcut, but it doesn't work, could you please help check why it failed?
function! FindCurrentFile(pattern)
echo a:pattern
execute ":vimgrep" . a:pattern . " %"
execute ":cw"
endfunction
command! -nargs=1 Fi call FindCurrentFile(<args>)
By the way, if you just need a quick overview over the matches you can simply use
:g//print
or
:g//p
(You may even leave out the p completely, since :print is the default operation for the :global command.)
When the current buffer has line numbers turned off, the results produced by :g//p can be difficult to take in fast. In that case use :g//# to show the matches with the line numbers.
Another trick that works for keywords is the normal mode command [I. It shows a quick overview of all the instances of the keyword under the cursor in the current buffer. See :h [I.
try to change the line in your function into this:
execute ':vimgrep "' . a:pattern . '" ' . expand("%")
<args> is replace with the command argument as is - that means that if you write:
Fi keyword
the command will run:
call FindCurrentFile(keyword)
which is wrong - because you want to pass the string "keyword", not a variable named keyword.
What you need is <q-args>, which quotes the argument.
BTW, if you wanted more than one argument, you had to use <f-args>, which quotes multiple arguments and separates them with ,.

Vim: How do I chdir to path in a variable

I have a path stored in a variable (say l:s) and want to execute lcd l:s in a vim script, but it tells me the path "l:s" doesn't exist. What is the problem here, because vim resolves variable names in other ex commands just fine (echo, etc.). I don't understand the difference.
You can use exe and construct the command:
let s:some_dir_name = "foo"
exe "lcd " . s:some_dir_name
That'll evaluate the variable s:some_dir_name and execute the command lcd foo.
(I didn't use l:s from your question because that's not a legal variable name, but I think you get the idea.)
Vim lets you set environment variables within a script, and these work with :cd and :lcd. For example:
function foo()
let $SOME_PATH = '/some/path'
lcd $SOME_PATH
endfunction

Can i use something like tunnel in vim?

For example:
If my current directory is /temp/src/com. And the file edited in vim is from /java/test.And now i want to add the path of the file to path environment. So if there is a cmd like set path+=$(filepath) in vim?
case 2:
Run make in terminal will start to compile a project, and it will out put logs about this compile. And now i want to read the outputed logs into vim using some command like r !make.
1) Pull the path into the current Vim buffer:
:r !echo \%PATH\%
Append to the path:
:let $PATH="C:\Test" . $PATH
2) This question is ambiguous, because it depends on your makefile behavior.
If your Makefile simply print to the console, then, :r make should do the trick.
If your make file actually writes to files explicitly, then there is no automatic way.
You'll have to write a custom vimscript function to pull in the logs.
1) Part 2
I do not know of what a way to do it in one line, but here's one way to achieve the functionality you want.
:redir #a "redirect output to register a
:pwd
:redir END "stop redirecting
:let #a = substitute(#a, '\n', '', 'g') "remove the newlines
:let $PATH=#a .":". $PATH
You should be able to wrap this in a function if you need to use it often.
You may reference environment variables using $MYVAR syntax. To set system environment variables use
let $MYVAR=foo
e.g.
let $PATH = "/foo" . $PATH
See http://vim.wikia.com/wiki/Environment_variables or :help :let-environment
Then you may use filename-modifiers to get directory name of a file in a current buffer:
let $PATH = expand("%:p:h") . $PATH
To read and parse compilation output in vim you might be interested to check quickfix mode
Use :make instead of :!make

Calling substitute() and expand() inside new command definition in Vim

I'm trying to define a new command in Vim that calls an external script with the name of the current file, but slightly modified. Here's how I defined the command:
:command MyNewCommand !/tmp/myscript.sh substitute(expand("%:p"), "-debug", "", 'g')
In other words, myscript.sh takes one parameter, which is the full pathname of the file being edited, with the string -debug in the pathname removed. My command definition doesn't work because rather than passing the pathname, Vim seems to pass to myscript.sh the entire string itself, beginning with the word substitute. How do I define the command to do what I want? Thanks :).
You can use the system() function to execute the script.
Change the command definition as follows:
:command! MyNewCommand call system('/tmp/myscript.sh ' .
\ shellescape(substitute(expand('%:p'), '-debug', '', 'g')))
To see the output of the command, replace call with echo.
You can use solution similar to #ib's one, but with ! which will show you the output of the shell command (and will also handle case when expand('%:p') contains newlines (it can if FS is fully POSIX-compliant)):
command MyNewCommand execute '!/tmp/myscript.sh' shellescape(substitute(expand('%:p'), '-debug', '', 'g'))
The generic approach is to build a string and then execute it. The problem in your command is that substitute(expand(... is not being evaluated and it's passed as is.
So in a generic example
command MyNewCommand OldCommand expand("%:p")
should be converted to
command MyNewCommand execute 'OldCommand '.expand("%:p")
That way MyNewCommand will just invoke execute with the expression 'OldCommand '.expand("%:p"). execute will evaluate the expression and therefore expand() will get evaluated to the filename and concatenated to 'OldCommand ' resulting in a string of the form 'OldCommand myfilename'. That string then gets executed as an Ex command by the same execute.

How can I save each match in a different file with Vim

I would like to save the output of g/pattern1/,/pattern2/ to a file (for each match, a different file).
e.g.
def
.......
end
def
.......
end
you would end up with a file for each "def...end".
Tried using tempname() like so:
g/pattern1/,/pattern2/exe 'w ' . tempname() but this fails with no range allowed for exe
also tried
g/pattern1/,/pattern2/w `tempname()`
to get tempname() evaluated but this failed with a "too many filenames" error.
What am I missing? Can this be done by using global and other commands, or would you need vimscript to do it?
g/pattern1/,/pattern2/execute "w ".fnameescape(tempname())<CR>
Use execute whenever you want to insert variable into command-line if it is a mapping. If it is not, try using
g/pattern1/,/pattern2/w <C-r>=fn<Tab>e<Tab>te<Tab>)<CR><CR>
Here fn<Tab> with wildmode=longest,list:full will expand to fname, fnamee<Tab> will expand to fnameescape(, te<Tab> will expand to tempname(), so this is a short way to input <C-r>=fnameescape(tempname())<CR>. You can omit fnameescape if you are sure that tempname will not return filename with special characters.
And note that backticks will not execute vimscript function, they execute shell command, so `tempname()` tries to call tempname() in a shell and substitute filename with the result of this call. According to the help, you should have written `=tempname()`.
Try :g/pattern1/normal! :.,/pattern2/w `tempname()`^M with ^M entered as CTRL-V then ENTER

Resources