How can I source files relative to file? - vim

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'

Related

Automatically append current date to file name when saving in Vim

I'm using vim to take notes while reading academic articles. I prefer to have a new text file for each note I've taken, but organizing them becomes tedious.
What I would like to do is set an autocommand to detect if I'm in a certain directory, writing to a newfile and then appened the current date and time to whatever filename I write.
So if I have:
:pwd
/path/to/projects
When I type
:w Notes
I would like vim instead to save the file as
"Notes - <CURRENT DATE - TIME >.txt"
I believe it involves declaring something like the following in my vimrc:
autocmd BufNewFile,BufWrite "/path/to/projects/*" <command involving strftime("%Y-%m-%d_%H-%M")>
But I can't figure out what. Any Ideas? I'm using Vim 7.3 on Debian Linux.
You're very close. I think it's best to rename the file as it is created; messing with the file name during writes makes this more difficult (e.g. what if you re-open an existing note, or just write the buffer again?)
The :file command can be used to rename the current file; the current filename is in the % special identifier. Triggered when a new file is created, this does the job:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(expand('%') . strftime(" - %Y-%m-%d_%H-%M.txt"))
If you don't want to consider the original filename, this becomes even easier:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(strftime("Notes - %Y-%m-%d_%H-%M.txt"))
You may be looking for something along the lines of:
function! SaveWithTS(filename) range
execute "save '" . a:filename . strftime(" - %Y-%m-%d_%H-%M.txt'")
endfunction
command! -nargs=1 SWT call SaveWithTS( <q-args> )
With the above in your .vimrc, executing :SWT Note will save your file as Note - YYYY-MM-DD_HH-MM.txt. This has the disadvantage of not happening automatically, so you have to remember to use :SWT instead of :w the first time your write your file, but it does let you wait until you are ready to save to decide what your filename should be (i.e. you aren't stuck with Note every time).
Edit: The above version of SaveWithTS actually saves to the filename with single quotes around it (bad testing on my part). Below is a version that should fix that and also lets you specify an extension to your file (but will default to .txt)
function! SaveWithTS(filename) range
let l:extension = '.' . fnamemodify( a:filename, ':e' )
if len(l:extension) == 1
let l:extension = '.txt'
endif
let l:filename = escape( fnamemodify(a:filename, ':r') . strftime(" - %Y-%m-%d_%H-%M") . l:extension, ' ' )
execute "write " . l:filename
endfunction

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

How to make Syntastic search for javac config file in project

I'm using Syntastic with vim and I've added external libraries to its classpath (using SyntasticJavacEditClasspath). This creates a file in the current working directory (which was my project folder). This is all fine. However, whenever I restart vim, Syntastic seems to search for the .syntastic_javac_config file in the current working directory only, and the cwd is, of course, randomly whereever I left it in my last operation. So it doesn't find the file and I get a bunch of incorrect import errors. Can Syntastic be told to search the file's parents for the config file? If not, is there a way of using vim where this typically works? (I've only been using vim for a couple of months so I might be clueless.)
According to the official manual, the snippet should solve the problem. Put it in your .vimrc, change the javascript, jscs, etc. to required values
function! FindConfig(prefix, what, where)
let cfg = findfile(a:what, escape(a:where, ' ') . ';')
return cfg !=# '' ? ' ' . a:prefix . ' ' . shellescape(cfg) : ''
endfunction
autocmd FileType javascript let b:syntastic_javascript_jscs_args =
\ get(g:, 'syntastic_javascript_jscs_args', '') .
\ FindConfig('-c', '.jscs', expand('<afile>:p:h', 1))

NerdTree copy command in Windows 7

I don't see the menu option for Copy command. Here is the menu that I see on my Windows 7 machine:
NERDTree Menu. Use j/k/enter and the shortcuts indicated
==========================================================
> (a)dd a childnode
(m)ove the curent node
(d)elete the curent node
According to the plugin documentation, the Copy command is not supported on all platforms.
A textual filesystem menu is provided which allows you to create/delete/move file
and directory nodes as well as copy (for supported OSs)
Has anybody managed to get this to work in Windows?
I got it working by installing Gow
choco install -y gow
Then adding this line to vim
let g:NERDTreeCopyCmd= 'cp -r'
Thanks: https://github.com/scrooloose/nerdtree/issues/152
PS: The choco command comes from https://chocolatey.org/
The root cause for the issue is discussed in detail(rather colorfully) in this blog post.(ht romainl).
I managed to find a solution by using the cp.exe shipped with msygit.
Ensure cp.exe is in your path
The cp.exe file can be found in <GIT_HOME>\bin directory. My path didn't not contain the <GIT_HOME>\bin directory. So I copied cp.exe and msys-1.0.dll to a directory in my path.
Set the g:NERDTreeCopyCmd variable
Add the line below to the end of the _vimrc file
let g:NERDTreeCopyCmd= 'cp -r '
Fix the implementation of s:Path.copy function.
Replace the lines 2297-2299 of ~/vimfiles/bundle/nerdtree/plugin/NERD_tree.vim (assuming you used pathogen for managing vim plugins)
Replace the lines 2297-2299
let dest = s:Path.WinToUnixPath(a:dest)
let cmd = g:NERDTreeCopyCmd . " " . escape(self.str(), s:escape_chars) . " " . escape(dest, s:escape_chars)
With the lines below
let dest = a:dest
let cmd = 0
if s:running_windows
let cmd = g:NERDTreeCopyCmd . '"' . self.str() . '" "' . dest . '"'
else
let cmd = g:NERDTreeCopyCmd . " " . escape(self.str(), s:escape_chars) . " " . escape(dest, s:escape_chars)
endif
What I did was I added the following to my vimrc
if (has('win32'))
" let g:NERDTreeCopyCmd= 'copy '
let g:NERDTreeCopyCmd= 'Copy-Item -Recurse '
endif
First one works, but I set it to the second, I think you need that for copying directories properly..
I just tried delete, and that also fails. Guess we need a similar workaround.
EDIT: Sorry, you will have to set your shell to run PowerShell for the second command to work!

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

Resources