72

In my vim plugin, I have two files:

myplugin/plugin.vim
myplugin/plugin_helpers.py

I would like to import plugin_helpers from plugin.vim (using the vim python support), so I believe I first need to put the directory of my plugin on python's sys.path.

How can I (in vimscript) get the path to the currently executing script? In python, this is __file__. In ruby, it's __FILE__. I couldn't find anything similar for vim by googling, can it be done?

Note: I am not looking for the currently edited file ("%:p" and friends).

dreftymac
  • 31,404
  • 26
  • 119
  • 182
gfxmonk
  • 8,614
  • 5
  • 42
  • 53

4 Answers4

111
" Relative path of script file:
let s:path = expand('<sfile>')

" Absolute path of script file:
let s:path = expand('<sfile>:p')

" Absolute path of script file with symbolic links resolved:
let s:path = resolve(expand('<sfile>:p'))

" Folder in which script resides: (not safe for symlinks)
let s:path = expand('<sfile>:p:h')

" If you're using a symlink to your script, but your resources are in
" the same directory as the actual script, you'll need to do this:
"   1: Get the absolute path of the script
"   2: Resolve all symbolic links
"   3: Get the folder of the resolved absolute file
let s:path = fnamemodify(resolve(expand('<sfile>:p')), ':h')

I use that last one often because my ~/.vimrc is a symbolic link to a script in a git repository.

Zenexer
  • 18,788
  • 9
  • 71
  • 77
  • 2
    Thanks! The question was already long answered, but I'll take this one now since the extra info is likely to be useful. – gfxmonk Sep 12 '13 at 10:53
39

Found it:

let s:current_file=expand("<sfile>")
gfxmonk
  • 8,614
  • 5
  • 42
  • 53
  • 24
    Incase it helps anyone else. Make sure to do this at the top level scope. If you try to run it inside of a function you'll end up getting the function name rather than the path to the file containing the function. – Mat Schaffer Feb 12 '12 at 21:54
  • 4
    I'm amazed how hard it was to find this information on the internet, thanks a bunch! – Jesse the Game Jan 15 '13 at 06:53
  • 3
    `:p` for an absolute path. `:p:h` for the directory in which the script resides. – Zenexer May 10 '13 at 12:56
  • 3
    Another note: You might want to enclose this in `resolve()`, as `` could be a symbolic link. – Zenexer Jun 11 '13 at 03:38
18

It is worth mentioning that the above solution will only work outside of a function.

This will not give the desired result:

function! MyFunction()
let s:current_file=expand('<sfile>:p:h')
echom s:current_file
endfunction

But this will:

let s:current_file=expand('<sfile>')
function! MyFunction()
echom s:current_file
endfunction

Here's a full solution to OP's original question:

let s:path = expand('<sfile>:p:h')

function! MyPythonFunction()
import sys
import os
script_path = vim.eval('s:path')

lib_path = os.path.join(script_path, '.') 
sys.path.insert(0, lib_path)                                       

import vim
import plugin_helpers
plugin_helpers.do_some_cool_stuff_here()
vim.command("badd %(result)s" % {'result':plugin_helpers.get_result()})

EOF
endfunction
edthedev
  • 187
  • 2
  • 8
  • 1
    Thanks! After reading a few 'sources', this finally gave me enough insight to get it right. I used it in ruby, and use `load` rather than `require` to reload the script every time, which makes it much easier when making changes to the lib. – Emile Vrijdags Jul 30 '17 at 10:44
0

If you really want to get the script path inside a function (which is what I'd like to), you can still use <sfile>'s second semantic, or its equivalent <stack> inside expand().

   <sfile>    ...
              When executing a legacy function, is replaced with the call
              stack, as with <stack>
              ...
                                                   :<stack> <stack>
   <stack>    is replaced with the call stack, using
              "function {function-name}[{lnum}]" for a function line
              and "script {file-name}[{lnum}]" for a script line, and
              ".." in between items.  E.g.:
              "function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
              If there is no call stack you get error E489 .

However you possibly don't want to use it in a plugin, as you can use autoload functions in plugin, using this relative#path#to#plugin#root#script notation.

I use this for sourcing purpose:

function! s:SourceLocal(script)
  let l:callstack = expand("<stack>")
  let l:list = split(l:callstack, '\.\.')
  " list[-1] is SourceLocal function itself
  " list[-2] is the calling script
  let l:script_name = matchstr(l:list[-2], '^\(script \)\=\zs.\+\ze\[\d\+\]$')
  let l:script_path = fnamemodify(l:script_name, ":p:h")
  " l:script_path is the path where the script calling this function resides
  execute printf("source %s/%s", l:script_path, a:script)
endfunction

command! -nargs=1 SourceLocal :call s:SourceLocal(<f-args>)

Then you can SourceLocal inside any script to source another script relative to it.

Masquue
  • 166
  • 8