Getting relative paths in Vim - 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.

Related

Displaying the relative path of the currently edited file in Vim's statusline?

:help statusline claims %f will render as:
Path to the file in the buffer, as typed or relative to current directory.
When I set statusline=%f, the path in the statusline is sometimes relative, but often absolute.
Is there a way to make sure the path displayed is always relative?
There may be a better way, but you could try this:
set stl+=%{expand('%:~:.')}
The expression inside %{} should be evaluated and added to your statusline. Here the expression is:
expand('%:~:.')
... which expands the name of the current file, but prevents the expansion of the tilde (:~), and makes the path relative to the current working directory (:.).
Just below the explanation of %f, you will find explanation of F. Using %F instead of %f will give you the desired display.
item meaning ~
f S Path to the file in the buffer, as typed or relative to current
directory.
F S Full path to the file in the buffer.
you can type :!echo % to view path in shell
and :pwd shows the current working directory of vim

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

Search whole project by default with Vim/Ack

I would like to use Ack (or similar plugin if something else can do the job) to search my whole project in Vim by default, rather than just the current directory. Ideally I'd end up with a process that works like using Cmd+Shift+F in Sublime. How can I do this?
An option like CtrlP's let g:ctrlp_working_path_mode = 'r' that makes it search within the nearest parent directory that contains a file like .git would be perfect. (https://github.com/kien/ctrlp.vim#basic-options)
I think Rooter is what you want. For example:
let g:rooter_patterns = ['Rakefile', '.git/']
I don't think Ack (or grep/vimgrep) can detect your "project root". If you often work on several projects, you could add this block in your vimrc:
let g:projectA_path="/path/to/A"
let g:projectB_path="/path/to/B"
let g:projectC_path="/path/to/C"
also define some functions/commands, like AckA, AckB, AckC...
basically the func/command just does:
exec 'Ack! '. pattern . " " . g:projectA_path
the pattern is the argument you passed in. then, in future, you could do:
:AckA foo
or
:call AckA("foo")
for quick grepping/acking in projectA.
I didn't think of a simpler way to do it. glad to see if there is better solution.
Most of the time I don't need to cd the project root, but stay in the same working directory.
So there is a simpler solution, based on answer of Kent, without cd'ing the project root, installing additional plugins and using ag:
let g:ackprg = 'ag --vimgrep --smart-case'
function! Find_git_root()
return system('git rev-parse --show-toplevel 2> /dev/null')[:-2]
endfunction
command! -nargs=1 Ag execute "Ack! <args> " . Find_git_root()
And to use it call :Ag <keyword>
I have this line in my .vimrc:
cnoreabbrev ack cd ~/your-project <bar> Ack! <Space>
Whenever you type :ack and hit the space the rest will be added to the command line and you can add the keyword.

How to source all vim files in directory

I have split my .vimrc into several files and placed them into ~/vimfiles/vimrc.d/.
Currently I source each file in that directory using exact name:
source ~/vimfiles/vimrc.d/file1.vim
source ~/vimfiles/vimrc.d/file2.vim
etc.
How to make a loop thourgh all files in that directory so i could only have to do such loop in my .vimrc:
for file in ~/vimfiles/vimrc.d/*.vim
source file
enfor
As mb14 has already said, if you put them in ~/.vim/plugin they will be sourced automatically. For information, however, if you want to source all of the files in your vimrc.d directory, you could do this (requires a relatively recent Vim):
for f in split(glob('~/vimfiles/vimrc.d/*.vim'), '\n')
exe 'source' f
endfor
You may also be interested in the autoload mechanism, described in :help 41.15: if you're defining a lot of functions, this can make start-up a bit quicker as the functions are only loaded the first time they're used.
You can just put your files in the plugins directory (~/.vim/plugin). They will be automatically loaded.
mb14 gave you the best answer. You want something automatically executed? Then use the standard organization: here the plugin/ subdirectory.
Otherwise, :runtime would have been your friend:
:runtime! vimrc.d/*.vim
:source barks when its parameter doesn't exist while :runtime silently source nothing.
:source can source only one file while :runtime! can source any number of files.
:source takes an absolute pathname, or a pathname relative to the current directory while :runtime takes a pathname relative to the 'runtimepath' option, which shouldn't be a problem as long as you respect vim conventions.
The example from DrAl did not work for me, this is how I achieved it:
for fpath in split(globpath('~/.vimrc.d/', '*.vim'), '\n')
exe 'source' fpath
endfor
The following snip is what I use within my ~/.vimrc file to source scripts within the ~/.vimrc.d/ directory and sub-directories...
for f in glob('$HOME/.vimrc.d/**/*.vim', 0, 1)
execute 'source' f
endfor
Check vim -c ':help glob' for details about additional glob arguments.
TLDR
glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()*
{nosuf} set to False allows 'suffixes' and 'wildignore' options to apply
{list} set to True causes glob to return a list that respects new-lines within file names

How can I expand the full path of the current file to pass to a command in 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.

Resources