How can I expand the full path of the current file to pass to a command in Vim? - vim

When I go to command mode and type
:!mycommand %
I get my command executed on the current file (% is expanded to the current file name).
Is there a similar construct that expands the full file name (with the full path)?
I am using Windows.

:!mycommand %:p
Related:
:!cd %:p:h

The other two answers didn’t work for me (for some reason). However, I found that this combo displays the full path when typed in Normal mode:
Press 1 then CtrlG
Source: “Get the name of the current file” on the Vim Tips Wiki. See also the {count}CTRL-G section of :help CTRL-G.

Append :p, e.g.
:!mycommand %:p
And %:p:h will give you the path of the directory that the file resides in.

To print out the current vim filename:
:help expand
:echo expand("%:p") " absolute path
:echo expand("%:p:h") " absolute path dirname
:echo expand("%:p:h:h")" absolute path dirname dirname
:echo expand("%:.") " relative path
:echo expand("%:.:h") " relative path dirname
:echo expand("%:.:h:h")" relative path dirname dirname
:echo expand("<sfile>:p") " absolute path to [this] vimscript
:help filename-modifiers
For example (with a vim function), to resolve() and expand() any symlinks to the absolute path to the current script <sfile>:p (instead of %:p), and then exec to source the fnameescape-ed filename contained in a function-local vim variable l:vimrcfilename:
" g:__sfile__dirname -- directory containing this vimrc script
" after symlinks
" ~dirname(abspath(realpath(__file__)))
let g:__sfile__dirname=fnamemodify(resolve(expand("<sfile>:p")), ":h")
" Source_dotvim(filename) -- source dirname(this_vimrc)/filename
function Source_dotvim(filename)
let l:vimrcfilename=g:__sfile__dirname . "/" . a:filename
if filereadable(l:vimrcfilename) && !empty(l:vimrcfilename)
"source s:vimrcfilename "this doesn't work, so exec:
exec "source " . fnameescape(l:vimrcfilename)
else
echo l:vimrcfilename . " empty or not found."
endif
endfunction
call Source_dotvim("vimrc.local.01-env.vimrc")
Notes:
:help fnamemodify
"current file": %:p (buffer file) / <sfile>:p (script file)
expand('<sfile>') does not work from within a function
source l:varname does not work; so, exec - string concatenation - and fnameescape
:messages prints the most-recent 200 vim [error,] messages
References
annika-backstrom's answer
umber-ferrule's answer
tito-11's answer

Get the name of the current file
http://vim.wikia.com/wiki/Get_the_name_of_the_current_file
Set_working_directory_to_the_current_file
http://vim.wikia.com/wiki/Set_working_directory_to_the_current_file

If you want to use the full path in your vimrc, you can use something like this:
let vimFiles = '$HOME/.vim'
let absPath = expand(vimFiles . '/subDir')
This will give you a path with backslashes on Windows.

Related

How can I source files relative to file?

I'm trying to split my vimrc up into multiple files - init.vim, keybindings.vim, ui.vim, etc. - but I can't get Vim to source files relative to init.vim (it instead sources relative to where I launch Vim from.
This is what I've got at the top of init.vim:
source keybindings.vim
source ui.vim
If I run vim from the same directory as those files, it works fine; if I run it from any other directory, I get the following errors:
Error detected while processing /path/to/vimrc:
line 1:
E484: Can't open file keybindings.vim
line 2:
E484: Can't open file ui.vim
Press ENTER or type command to continue
Edit: It's worth noting that I'm using NixOS, so I don't know what the absolute paths will be, nor if they would be constant if I found out.
I think you can use
runtime keybindings.vim
Source needs the full path, you can however simplify it using something like this :
let path = expand('%:p:h')
exec 'source' path . '/keybindings.vim'
You can have a look at mine here - https://github.com/dhruvasagar/dotfiles/blob/master/vim/vimrc for reference.
If the order is not important, you can just put your scripts into ~/.vim/plugin/, and they will be sourced after ~/.vimrc. You can check :scriptnames output to see what gets sourced when.
You can influence the ordering somewhat via the plugin filenames. For example, I have a ~/.vim/plugin/00plugin-configuration.vim that configures Vim plugins; the 00... ensures this is sourced first.
To get finer control, I would instead put the scripts into ~/.vim/. Vim will ignore them there, but they can easily be addressed via :runtime, which looks in all runtimepaths, and ~/.vim/ typically is included in 'runtimepath':
# .vimrc
runtime init.vim
runtime keybindings.vim
...
Relevant help pages: :help .vimrc and :help load-plugins.
Building on Dhruva's answer, you can make a function to help out with this
function! SourceLocal(relativePath)
let root = expand('%:p:h')
let fullPath = root . '/'. a:relativePath
exec 'source ' . fullPath
endfunction
You then use it like
call SourceLocal ("yourScript.vim")
I have met exactly the same issue with you in Neovim. I split my large init.vim file into several small vim scripts and I want to source them inside init.vim.
This is what I get finally based on #Dhruva Sagar's links:
let g:nvim_config_root = stdpath('config')
let g:config_file_list = ['variables.vim',
\ 'options.vim',
\ 'autocommands.vim',
\ 'mappings.vim',
\ 'plugins.vim',
\ 'ui.vim'
\ ]
for f in g:config_file_list
execute 'source ' . g:nvim_config_root . '/' . f
endfor
As none of the solutions works as a real substitute for source working globally (on any script, even sourced from vimrc), I ended up with this solution and decided to share here, which can be used as a substitute for source with relative support, as simple as:
Rsource /home/me/.vim/your/file/path
Rsource $HOME/.vim/your/file/path
Rsource your/file/path
Rsource ../your/file/path
To use it, this must be defined on your vimrc or any file sourced by it before you can use Rsource:
if !exists('g:RelativeSource')
function! g:RelativeSource(file)
let file = expand(a:file)
" if file is a root path, just source it
if stridx(file, '/') == 0
exec 'source ' . file
return
endif
let sfile = expand('<sfile>:p:h')
" If this is called outside this script, it will contains this script
" name, this function name, a script_marker then the executing script name
" In this case we extract just the last part, the script name which called
" the this function
let script_marker = '..script '
let path_index = strridx(sfile, script_marker)
if path_index == -1
let path_index = 0
else
let path_index += len(script_marker)
endif
let path = strpart(sfile,path_index)
let absolute_path = resolve(path . '/'. file)
exec 'source ' . absolute_path
endfunction
command! -nargs=1 Rsource :call g:RelativeSource(<q-args>)
endif
This is safe to be used in any script or plugin.
These are all great solutions, and this is what I ended up using.
let home = expand('~')
exec 'source' home . '/.config/nvim/prettierConfig.vim'

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

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.

Expand relative path wrt containing file

I am reading a config file, foo.yml with Vimscript. This file contains a parameter that is the relative path to a directory. This path is relative to the foo.yml file, not my current working directory.
I need to expand this relative path to an absolute path.
I have tried using fnamemodify(path, ':p') and expand(path) without luck. I think these functions seem to get confused because from the current working directory the relative path is invalid. So it keeps the path as is.
Is there a way to make Vim use the foo.yml as the point-of-reference when resolving relative paths? Or any other function that can do the same?
Thanks for your help.
In order to expand relative to the file's directory, it's easiest to temporarily :cd into that directory. Here's some sample code that does this for the current file (%); you have to adapt this to work with a passed filespec.
if expand('%:h') !=# '.'
" Need to change into the file's directory first to get glob results
" relative to the file.
let l:save_cwd = getcwd()
let l:chdirCommand = (haslocaldir() ? 'lchdir!' : 'chdir!')
execute l:chdirCommand '%:p:h'
endif
try
" Get the full path to a:filespec, relative to the current file's directory.
let l:absoluteFilespec = fnamemodify(a:filespec, ':p')
finally
if exists('l:save_cwd')
execute l:chdirCommand fnameescape(l:save_cwd)
endif
endtry
How about
:let dir = expand('%:p:h')
:let absolute_path = dir . '/' . path
You will have to work harder if you want it to work on Windows, too.
:help expand()
:help filename-modifiers
:help file-functions

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

Getting relative paths in Vim

Say I am running Vim and pwd returns
/home/rafid/myproject
And say I am currently editing the file
/home/rafid/myproject/website/editpage.php
Is there any command that returns this for me?
website/editpage.php
That is, the path of the file relative to the current folder.
Although expand('%') often works, there are rare occasions where it does not. But you can force Vim to always present the relative path by calling fnamemodify:
:echo fnamemodify(expand("%"), ":~:.")
From the manual:
:. Reduce file name to be relative to current directory, if
possible. File name is unmodified if it is not below the
current directory.
For maximum shortness, use ":~:.".
The :~ is optional. It will reduce the path relative to your home folder if possible (~/...). (Unfortunately that only works on your home; it won't turn /home/fred into ~fred if you aren't logged in as fred.)
As Adam pointed out the comments, this can be shortened to:
:echo expand("%:~:.")
Reference: :h expand<Tab> and :h fnamem<Tab>
If you are limited for space (e.g. using this in your statusline), and can manage with "fuzzy" information about where the file is located, then check out pathshorten() which compresses folder names down to one character:
:echo pathshorten('~/.vim/autoload/myfile.vim')
~/.v/a/myfile.vim
Reference: :h pathsh<Tab>
Another option would be to write a vim function. Here's my humble attempt:
function! Relpath(filename)
let cwd = getcwd()
let s = substitute(a:filename, l:cwd . "/" , "", "")
return s
endfunction
You call Relpath with any full path name, and it will strip the current directory name from its argument.
For example, try :echo Relpath(expand("%:p")) (the :p modifier asks Vim to return the full path). Obviously, this is not necessary in your case, since % by itself returns relative path. However, it might come in handy in other cases.
This works for me :
:echo expand("%")
if you use autocmd to always set the current directory of the buffer that you are working on ( cd %:p:h ) then you can just type :cd
Blockquote
This works for me :
:echo expand("%")
This is only working if you opened that file with a relative file:
for vi ./foo, expand("%") will be ./foo
but
for vi /tmp/foo expand("%") will be /tmp/foo
Yes, you can use
:args
This will give you the filename of the current file, for informational purposes.
A workaround can be :cd . which seems to re-evaluate the path relative-ness. I agree this is very annoying though.

Resources