How to run vim commands/scripts from project root? - vim

I have a multitude of commands I'd like to run not in the current directory, but in the project root directory. i.e. going up directories until I reach some indication of a root, like .git directory for example.
For example running vimgrep -r (recursive) on all my project, or running tags generation recursively on whole project.
How do I get that path? The only close indication I found is this:set tags=./tags;~/Projects
But that just saves the string as it is into tags. Assigning something similar but for the use case described, gets me the string verbatim.
Any help is greatly appreciated!
Thanks!

Avoid the idea of "changing the working directory" or distinguishing between "working directory and project root", because almost no tool is prepared to properly handle those concepts.
The only tools that do (e.g. git) are those that don't care about the current directory to begin with.
Otherwise, it's madness to try to get everything working without bad side effects. "Working directory" is a concept too fundamental to even attempt to change within a running program.
The best approach is open a new Vim sessions inside directories where you want to do "local" things - and switch back to the "project" session to run project commands. Vim will protect you from accidentally overwriting changes in another session.
The alternative is to wrap commands in shells so they can have their "own" working directories, e.g.:
:!cd ../../..; ctags -R
(Which would allow you to regenerate tags file for the project, and not just the current dir)
or:
:!cd ../../..; grep -r foo **/*
But any output with file names would be relative to that root directory, and not the current one.
So you may prefer to do:
:!cd ../../..; vim
which creates a new Vim session within the current one, but in the context of the root directory.
Or, you may prefer the reverse (assuming Vim is running in the project root):
:!cd $(dirname %); vim
Which lets you work in the directory of the current file - and you'd have to exit to the main session to run project-wide tools again.
So instead of "changing" directories, you're "changing vim sessions" (either by having 2 sessions or "nesting" one in another like above).

I use a local_vimrc plugin to set project related variables. Then, I use (/write) plugins that rely on (buffer/project)-local variables to do stuff. (Unfortunately, most plugin out there rely of global variables which is not the best choice to specialize their behaviour to the current project in multi-projects sessions)
Regarding ctags generation, in use lh-tags that requires a few variables to be set.
Regarding grepping, as other, I usually start from the current directory which is often the root directory of my project. But you could also easily have a plugin/command that would run something like:
exe "vimgrep -r ".pattern." ".
\ map(file_extensions,
\ string(lh#option#get('project_root_directory').'/**').'v:val')
EDIT:
If you don't want other configuration files, and if the .git/ directory is enough to identify your project root directory, then, you can have this kind of function to get your project root directory:
function! ProjectRootDirectory()
return fnamemodify(finddir('.git', '.;'), ':h')
endfunction
Then when you'll run ctags, you'll have to execute ctags from the result of this vim function. I don't know which plugin you use to run ctags, at this moment. My lh-tags doesn't support functions through its configuration variables. It can easily been added if need be.
However, I don't see a simple way to configure &tags once from the .vimrc in order to configure this setting on a per project basis.

I personally like to keep it simple and have my current directory be my project root. I can use % to represent the current file in commands, e.g. :!git add %. Along with % you can use filename modifiers, e.g. :e %:h/foo.txt. See :h filename-modifiers.
However you mentioned that you use CtrlP and you like the way it changes the current directory. This means you need to be a bit more creative.
Here is what I would recommend:
Update your tags via git hooks. See Effortless Ctags with Git
If your tags are in .git/tags fugitive.vim will automatically setup these tags for you.
Use :Ggrep which uses git grep so it already know about your project root directory and as a bonus is much faster than :vimgrep.
Running arbitrary commands from the root is trickier. I use projectionist to manage my projects. Projectionist provides the command, :ProjectDo, which does exactly what you want.
Now a word of caution: Vim has no understanding of a "project". The closest thing to is 'exrc' option (See :h 'exrc') which is pretty lame.

Related

How to get to long directory quickly when writting code in VIM

I am writing Bash script using VIM. I need to cd to a directory and run the command tool. The command tool is deep inside the directory. How do I quickly cd to that directory instead of manually typing the directory out in VIM ? In terminal prompt, I can get to the directory quickly using tab. It does not work in VIM.
Thanks
ffl3883
You can change to the currently edited file's directory with :cd %:h; see :help filename-modifiers. Likewise, if you trigger the tool from Vim :! % can do this quickly (and repeat with :!!). Or just :set autochdir, so that the current directory within Vim always follows the currently edited file (and you can then just reference the file via ./).
When typing file paths in vim (as I often do for shell scripts), I find filename-complete invaluable. Simply type <C-X><C-F> in insert mode.
N.B. It does not work in all cases (generally vim prefers the path to be a separate WORD), but a quick edit-complete-fixup isn’t terrible.

How to prevent syntastic from creating a directory for every vim instance?

When using the syntastic plugin with vim, I see a new /tmp/vXXXXXXX directory every time I open a new vim instance. When the syntastic plugin gets disabled, no such directories are created.
When I ran inotify, I found that a numeric file is created in that directory every time I save a file. Is it possible to make syntastic (or vim) create a temporary directory on demand? Failing on that, can I make it use a single directory instead? For example, /tmp/vim-syntastic/vXXXXXXX/?
According to the developer, syntastic does not create temporary directories by itself, that is handled by vim. Looking a bit further, I found that vim uses $TMPDIR to set a temporary directory. If the directory is unwritable, then it gets ignored.
So, as a solution, the following lines set the temporary directory to /tmp/vim-USERNAME, and then create it (ignoring errors that normally occur when the directory exists):
" Keep all vim-related temp files in a single directory
let $TMPDIR = '/tmp/vim-' . $USER
silent! call mkdir($TMPDIR, '', 0700)
Now, I do not have a lot of /tmp/vXXXXXX/ directories anymore. Instead, they appear in /tmp/vim-peter/vXXXXXX/ which is great.
If you look in the syntastic helpfiles, you'll see that syntastic uses a 'tail' file for storing the output of a given make program. You can override the default tail for a given filetype and subchecker by adding the following to your vimrc:
let g:syntastic_<filetype>_<subchecker>_tail = "> /tmp/vim-syntastic/your-file-here"
So for example if you wanted mri to output to /tmp/vim-syntastic/ruby-mri, you would write:
let g:syntastic_ruby_mri_tail = "> /tmp/vim-syntastic/ruby-mri"
See :help syntastic-config-makeprg for more info. Here's a direct link on git. As far as I know there's no built-in way to set the default directory for all syntastic output, unfortunately.
Edit: Lekenstein found another solution, which he posted in the linked Github issue.
let $TMPDIR = '/tmp/vim-' . $USER
silent! call mkdir($TMPDIR, '', 0700)
This will make a special directory for all vim-related temporary files. That means it will also affect temporary files not related to syntastic.

ctags, generate tags using multiple paths?

When I build/update my tags file, ctags starts at the current directory and works its way down recursively.
I would like for it to also include a completely different search path, a mapped network drive, and add those results to my tags file as well.
Is there any way to do that?
When the files in the other directory are related and often change together with the current directory hierarchy, I'd write a custom :Ctags command that supplies the other path to the :!ctags call.
If the other files are unrelated, and rarely update (as based on your comments seems to be the case), I'd run ctags there separately and include them via
:set tags+=/path/to/other/dir/tags
NOTE: Add the tag filename at the end, else there will be "tag not found" error. By default the name is tags but it could be renamed with -f option as below.
ctags -f my_tags -R
:set tags+=/path/to/other/dir/my_tags

No tags file in GVim on some file but not on others

I just installed ctags via homebrew and appended the following line in my ~/.vimrc:
set tags=./tags,tags;$HOME
And then I ran /usr/local/bin/ctags -R . on some of my directories and opened some files stored in the directories, then some of those scripts succeeded in importing tags file but others didn't.
For example, I opened up test.py on my Python workspace, which I already run the above command in, and then I tried to put Ctrl+] on my GVim, it looks like successfully imported the tags file.
I also opened up hello.go located in ~/go/src/github.com/user/hello, in which I already executed the above ctags command, successfully imported the tags file. However, my test.rb file, which I just put on the Go's directory in order to do test purpose, didn't import the tags file correctly.
Also, when I executed the ctags command on ~/another_go_workspace/src, and then opened up the file located in ~/another_go_workspace/src/hello/hello.go, then the file didn't import the tags file... However, since I appended set tags=./tags,tags;$HOME on my ~/.vimrc, doesn't it automatically look for higher directories, right?
So what am I missing?
And if it doesn't import the tags file in higher directories, do I have to execute the ctag command on EVERY directory, i.e. on ~/go/src/soccer_analysis, ~/go/src/coffee, ~/go/src/utility, etc, etc... ?
Thanks.
Your value for the tags option is correct and your assumptions about its behaviour are correct too.
With your setting, set tags=./tags,tags;$HOME, Vim will search for a tags file in the directory of the current file first then for a tags file from the working directory upward to $HOME.
This allows you to generate a tags file at the root of your project and be sure that Vim will pick it up wherever you are in your project and whatever the working directory is.
With the following structure and your current settings:
project/
bar/
bar.js
foo/
foo.js
project.js
tags
Vim should find tags in all the following scenarios and their variants:
$ vim project.js
$ cd foo && vim foo.js
$ cd bar && vim bar.js
$ vim foo/foo.js
$ vim bar/bar.js
$ cd bar && vim bar.js ../project.js
Every time you add a new file to your project or write to an existing file, you must re-index your whole project. From what you wrote about the ruby file, it looks like you didn't run ctags after adding the file. Try this for a selection of files in your project: :echo tagfiles().
No, vim doesn't go up directories to find tags files. I recommend you start vim from the top level directory (where you generated your tags), then traverse to whatever file you want.
vim go/src/coffee
Vim is capable of navigating filesystems nicely with commands like :Explore.
EDIT: I was wrong, semicolon can be used to search upwards. See :help file-searching
Also, I noticed that you tried to add $HOME to your tags, which isn't going to work for a number of reasons.
Documentation (:help 'tags') says:
Filenames for the tag command, separated by spaces or commas.
Therefore:
The delimiter is incorrect
$HOME is going to be treated like a tags file
So the "correct" way of doing this would be:
set tags=./tags,tags,$HOME/tags
Even if you do that though, I don't think it's going to work. Tags files comprise primarily of 2 elements, a search pattern and a filename. If you generated the file from the top, all filenames will be relative to that directory.
So if you are deep down in some subdir, vim will try to open the file using the relative filepath from the top, starting at that subdir.
The problem may have been caused by a typo. I think
set tags=./tags,tags;$HOME
should be
set tags=./tags;,tags;$HOME

gvim: change the default working directory

when I open gvim using Alt+F2 it takes as its default working directory my home folder.
How can I change the working folder after or while running gvim? can i pass that folder as a parameter when open gvim?
You could use a shortcut.
The simplest way, though, would be to
:edit $MYVIMRC
append a line
cd /home/user/my/work/dir
save (optionally execute :w|source % to immediately reload)
Inside vim
use
:pwd
:cd some/other/dir
To view/change current working directory.
Use e.g.
:cd %:h
to change to the directory containing the file loaded in the active window.
If you need/want to do this often, consider just setting 'autochdir'
:se autochdir
From the docs:
When on, Vim will change the current working directory
whenever you open a file, switch buffers, delete a
buffer or open/close a window. It will change to the
directory containing the file which was opened or
selected. This option is provided for backward
compatibility with the Vim released with Sun ONE
Studio 4 Enterprise Edition.
Note: When this option is on some plugins may not work.
You can pass an a folder to gvim (when you have NERDTree then it will be a file tree) You can cd before start to begin in directory you want or use :cd <path> command to change current working directory, which can be passed to -c flag when running Vim:
$ [g]vim -c 'cd <path>'
You can also check current dir using :pwd command.
You can change the working directory with the :cd command. You can also pass this in a command-line option like this:
vim -c "cd wherever"
If you like the working directory to always be the file you're currently editing you can use the set autochdir option. Put that in your ~/.vimrc or see :help autochdir.
I know I'm late, but I started using CDargs which is a bash tool to mark certain directories as bookmarks, then use cdb and press tab to list all the bookmarked directories.
There is a vim plugin that interacts with the settingsfile of this tool: vim-cdargs.
This combo works really nice for me to switch between projects.
Or after opening gvim to go quickly to some bookmarked folder, then use Ctrl-p plugin to quickly find the file I want to edit.
extra hint: I don't even want to type :Cdb so I abbreviated c to expand to :Cdb by adding this to my vimrc:
cnoreabbrev c Cdb
after which typing :c followed by a space, will expand into :Cdb.
EDIT: I now use vim-startify which provides a start page for vim that shows the most recent used files. And with the option let g:startify_change_to_vcs_root = 1 it will change the working directory to the outermost vcs root folder of the file you opened. Which is almost always what I want.
Furthemore, I created my own 'plugin' with some key mappings that will switch to the closest or furthest directory, in the path of the current buffer, containing a .git directory or file. In order to easily switch between searching for files in the current git submodule or in the overal supermodule.
Also I switched to fzf with fzf-vim instead of Ctrl-p, which works significantly faster and is more highly configurable.

Resources