Context
I’m working on an 'omnifunc'
function for Markdown editing. It’s supposed to help me link to other Markdown files while I’m typing a link – based on file content though, not file name.
Suppose I’m typing a link like [favorite editor](Vim|
(where |
marks my cursor). Once I hit ^X^O
, my omnifunc
grabs everything after the opening bracket (i.e. Vim
) and basically runs :lvimgrep /\VVim/j **/*.md
in order to find all Markdown files below the current directory (e.g. the project root) that contain the string Vim
.
Then, I’m iterating over getloclist('.')
to get the names of all matching files. The location list doesn’t contain file names though, it contains buffer numbers. However, they can easily be resolved to file names using getbufinfo()
.
This is how it looks like at the moment:
exec 'lvimgrep /\V' . escape(a:base, '/\') . '/j **/*.md'
let l:results = []
for l:match in getloclist('.')
let l:fpath = getbufinfo(l:match['bufnr'])[0]['name']
Problem
l:fpath
is an absolute path though. However, the link obviously should be relative to the file that contains it, which might very well be in a subdirectory of the current working directory (CWD). (It could also be outside of it, which is another can of worms entirely.)
I know that I can use fnamemodify(l:fpath, ':.')
to convert the path to one relative to the CWD (if the path is inside of the CWD), but I need a path relative to the file I’m editing.
Rejected approaches and ideas
:cd
to the file’s directory
I could try :lcd
'ing to the file I’m editing while my omnifunc
is running, but the warnings at :h current-directory
makes me concerned about whether that’s a smart approach:
Changing directory fails when the current buffer is modified, the
'.'
flag is present in'cpoptions'
and"!"
is not used in the command.[…]
After using
:cd
the full path name will be used for reading and writing files. On some networked file systems this may cause problems.
It simply smells like there could be unexpected side effects of changing the CWD. Also, even if there weren’t, this still will not result in relative links going upwards, i.e. something like ../../editors.md
.
Also, I know about 'autochdir'
. It has all of the disadvantages mentioned in the previous paragraphs and thus doesn’t help either.
Using :grep
instead
I’ve attempted to use :lgrep
instead of :lvimgrep
. In addition to being less cross-platform compatible, it also doesn’t improve the output format: It still contains buffer IDs, which still resolve to absolute paths.
Questions
- Is there a builtin function that will help me convert the path to a relative one, including
../
components, and I’ve simply missed it? - Is there an external tool that could do the conversion, and that can be expected to be preinstalled on modern Unixes (possibly even POSIX)?
- Do you know of any other Vim scripts that either
- do what I’m trying to do (auto-complete a Markdown link based on a string to search for)
- or at least contain a path conversion function like the one I’m afraid I have to write myself?
- Would
fzf
be able to help me here? I’ve seen it being mentioned all over the place, but have never used it. - Any other helpful remarks?
Thanks in advance for your time.
realpath
which is used to resolve paths into fully realized absolute paths. But it has a "relative to" flag so you can dorealpath --relative-to /some/path/to/foo /some/path/to/foo/bar/baz.txt
and it will returnbar/baz.txt
(after fully realizing the absolute). It also has--relative-base=FILE
that will print relative paths for paths below FILE.realpath
might be compatible enough for my purposes. Let’s see whether someone comes up with an even better suggestion.