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
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
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
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'])['name']
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
"!"is not used in the command.
:cdthe 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
Also, I know about
'autochdir'. It has all of the disadvantages mentioned in the previous paragraphs and thus doesn’t help either.
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.
- 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?
fzfbe 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.
realpathwhich is used to resolve paths into fully realized absolute paths. But it has a "relative to" flag so you can do
realpath --relative-to /some/path/to/foo /some/path/to/foo/bar/baz.txtand it will return
bar/baz.txt(after fully realizing the absolute). It also has
--relative-base=FILEthat will print relative paths for paths below FILE.
realpathmight be compatible enough for my purposes. Let’s see whether someone comes up with an even better suggestion.