Is there any way to search a directory recursively for a file (using wildcards when needed) in Vim? If not natively, is there a plugin that can handle this?
You can use wildcards with the :edit command. So,
:e **/test/Suite.java
will open test/Suite.java no matter where it is in the current directory hierarchy. This works with tab-completion so you can use [tab] to expand the wildcards before opening the file. See also the wildmode option for a way to browse through all possible extensions instead.
Another trick is to use
:r! find . -type f
to load a list of all files in the current directory into a buffer. Then you can use all the usual vim text manipulation tools to navigate/sort/trim the list, and CTRL+W gf to open the file under the cursor in a new pane.
There is a find command. If you add ** (see :help starstar) to your 'path' then you can search recursively:
:set path
will show you your current path, add ** by doing something like
:set path+=**
then you can just type
:find myfile.txt
and it opens magically!
If you add the set command to your .vimrc it'll make sure you can do recursive search in future. It doesn't seem to search dot directories (.ssh for example)
I'd recommend ctrlp.vim. It's a very good plugin, ideal to work inside large projects. It has search by file name or full path, regexp search, automatic detection of the project root (the one with the .git|hg|svn|bzr|_darcs folder), personalized file name exclusions, and many more.
Just press <c-p> and it will open a very intuitive pane where you can search what you want:
It's possible to select and open several files at once. It also accepts additional arbitrary commands, like jump to a certain line, string occurrence or any other Vim command.
Repo: https://github.com/kien/ctrlp.vim
vim as a builtin find command (:help find) but only open the first found file. However you can use this amazing plugin : FuzzyFinder which does everything you want and even more
You can browse the file system with :ex ., but I do not know how to search recursively (I am a Vim novice — I have been using it only ten years).
There are a few popular file browsers plug-ins:
NERD tree
Lusty explorer
vtreexplorer
See also this thread on SuperUser.
Command-T lets you find a file very fast just by typing some letters. You can also open the file in a new tab, but it need vim compiled with ruby support.
You can use ! to run shell commands :
:! find . -name *.xml
vim has bild in commands named grep, lgrep, vimgrep or lvimgrep that can do this
here is a tutorial on how to use them
http://vim.wikia.com/wiki/Find_in_files_within_Vim#Recursive_Search
you can also use an external command like find or grep from vim by executing it like this
:!find ...
Quickfix-like result browsing
Usage:
Find my.regex
Outcome:
a new tab opens
each line contains a relative path that matches a grep -E regex
hit:
<enter> or <C-w>gf to open the file on the current line in a new tab
gf to open the file on the current tab and lose the file list
Find all files instead:
Find
Alternative methods:
Gfind my.regex: only search for Git tracked files (git ls-files). Fugitive request: https://github.com/tpope/vim-fugitive/issues/132#issuecomment-200749743
Gtfind my.regex: like Gfind, but search from the git Top level instead of current directory
Locate somefile: locate version
Code:
function! Find(cmd)
let l:files = system(a:cmd)
if (l:files =~ '^\s*$')
echomsg 'No matching files.'
return
endif
tabedit
set filetype=filelist
set buftype=nofile
" TODO cannot open two such file lists with this. How to get a nice tab label then?
" http://superuser.com/questions/715928/vim-change-label-for-specific-tab
"file [filelist]
put =l:files
normal ggdd
nnoremap <buffer> <Enter> <C-W>gf
execute 'autocmd BufEnter <buffer> lcd ' . getcwd()
endfunction
command! -nargs=1 Find call Find("find . -iname '*'" . shellescape('<args>') . "'*'")
command! -nargs=1 Gfind call Find('git ls-files | grep -E ' . shellescape('<args>'))
command! -nargs=1 Gtfind call Find('git rev-parse --show-toplevel && git ls-files | grep -E ' . shellescape('<args>'))
command! -nargs=1 Locate call Find('locate ' . shellescape('<args>'))
Depending on your situation (that is, assuming the following command will find just a single file), perhaps use a command like:
:e `locate SomeUniqueFileName.java`
This will cause Vim to open, in the current tab (the e command) a file that is the result of running (in this example),
locate SomeUniqueFileName.java
Note that the magic here is the backticks around the command, which will convert the output from the shell command into text usable in the Vim command.
You don't need a plugin only for this function, below code snippet is enough.
function! FindFiles()
call inputsave()
let l:dir = input("Find file in: ", expand("%:p:h"), "dir")
call inputrestore()
if l:dir != ""
call inputsave()
let l:file = input("File name: ")
call inputrestore()
let l:nf = 'find '.l:dir.' -type f -iname '.l:file.' -exec grep -nH -m 1 ".*" {} \;'
lexpr system(l:nf)
endif
endfunction
nnoremap <silent> <leader>fo :call FindFiles()<CR>
Run:
:args `find . -name '*xml'`
Vim will run the shell command in backticks, put the list of files to arglist and open the first file.
Then you can use :args to view the arglist (i.e. list the files found) and :n and :N to navigate forward and bacwards through the files in arglist.
See https://vimhelp.org/editing.txt.html#%7Barglist%7D and https://vimhelp.org/editing.txt.html#backtick-expansion
You can find files recursively in your "path" with this plugin. It supports tab completion for the filename as well.
I am surprised no one mentioned Unite.vim yet.
Finding files (fuzzily or otherwise) is just the very tip of the iceberg of what it can do for a developer. It has built in support for ag, git, and a myriad of other programs/utilities/vim plugins. The learning curve can be a bit steep, but i cannot imagine my life without it. User base is big, and bugs are fixed immediately.
ag tool and corresponding Ag vim plugin solves this problem perfectly:
To find a file using some pattern use:
AgFile! pattern
It will open quickfix window with results where you can choose.
You can add vim keybinding to call this command using selected word as a pattern.
nnoremap <silent> <C-h> :AgFile! '<C-R><C-W>'<CR>
vnoremap <silent> <C-h> y :AgFile! '<C-R>"'<CR>
Related
I want to be able to search files that only reside in the directory of the file that I opened inside vim.
The documentary of Ack says:
:Ack[!] [options] {pattern n} [{directory}] *:Ack*
Search recursively in {directory} (which defaults to the current
directory) for the {pattern}. Behaves just like the |:grep| command, but
will open the |Quickfix| window for you. If [!] is not given the first
occurrence is jumped to.
On VimFandom I found that I could get the current directory of the file with
echo expand('%:p:h') but how could I get this to evaluate in the Ack command?
I'd need something like this:
:Ack searchpattern expand('%:p:h')
The expression register, "=, will let you evaluate an expression and put/paste the output. Using <c-r> on the command-line will insert content from a register.
:Ack pat <c-r>=expand('%:p:h')<cr>
For more help see:
:h "=
:h i_CTRL-R
Using :grep instead of :Ack
You can set 'grepprg' to use the silver searcher or other grep-like tool, e.g. ripgrep.
set grepprg=ag\ --vimgrep\ $*
set grepformat=%f:%l:%c:%m
:grep understands % and :h as parameters. This means you can do:
:grep pat %:h
For more help see:
:h 'grepprg'
:h :grep
If directory has no further children (otherwise is recursive search):
nnoremap <leader>f <Esc>:cd %:p:h<CR><Esc>:Ag<CR>
Where,
:cd %:p:h changes directory to the location of current file
:Ag<CR> directly goes to the interactive search window if you have fzf-vim
By "interactive search" I mean customizing your search pattern dynamically (try wildcard, test if adding more keywords, ...)
On the other hand, if you don't need the interactive search, you are sure what you look for, then:
nnoremap <leader>f <Esc>:cd %:p:h<CR><Esc>:Ag<Space>
Use :exe[cute]:
:exe 'Ack searchpattern ' . expand('%:p:h')
. (dot) means string concatenation.
I have a little mapping for cases like this: %% inserts the directory of the current file.
cnoremap <expr> %% filename#command_dir('%%')
And the function definition:
function filename#command_dir(keymap) abort
if getcmdtype() isnot# ':'
return a:keymap
endif
let l:dir = expand('%:h')
return empty(l:dir) ? '.' : (dir.'/')
endfunction
So far I always used EasyGrep for replacing text in multiple files. Unfortunately it is quite slow when a project gets bigger. One thing that seems to be amazingly fast is Ggrep of fugitive.vim that only search my version controlled files. All results are also stored in the quickfix list.
How can I use the results of Ggrep for doing a simple replace over all those found files? Is it somehow possible to use %s/foo/bar/cg on all files in the quickfix list or are there any better ways?
Update:
Vim now has cdo, see Sid's answer.
Original Answer:
Vim has bufdo, windo, tabdo and argdo, which let you perform the same command in all open buffers, windows or files in the argument list. What we really need is something like quickfixdo, which would invoke a command on every file in the quickfix list. Sadly, that functionality is lacking from Vim, but here's a solution by Al that provides a home-rolled solution. Using this, it would be possible to run:
:QFDo %s/foo/bar/gc
And that would run the foo/bar substitution on all files in the quickfix list.
The bufdo, windo, tabdo and argdo commands have some common behaviour. For example, if the current file can't be abandoned, then all of these commands will fail. I'm not sure if the QFDo command referenced above follows the same conventions.
I've adapted Al's solution to create a command called Qargs. Running this command populates the argument list with all of the files listed in the quickfix list:
command! -nargs=0 -bar Qargs execute 'args ' . QuickfixFilenames()
function! QuickfixFilenames()
" Building a hash ensures we get each buffer only once
let buffer_numbers = {}
for quickfix_item in getqflist()
let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
endfor
return join(values(buffer_numbers))
endfunction
Using this, you could follow these steps to do a project-wide search and replace:
:Ggrep findme
:Qargs
:argdo %s/findme/replacement/gc
:argdo update
Edit: (with a hat tip to Peter Rincker)
Or you could join the last 3 commands together in a single line:
:Ggrep findme
:Qargs | argdo %s/findme/replacement/gc | update
cdo command has now been added! After you grep, you can use cdo to execute the given command to each term in your quickfix list:
cdo %s/<search term>/<replace term>/cg
(Take a look at this git commit and this vim developers google group discussion for more information on cdo and the motivations behind adding it.)
nelstrom's answer is quite comprehensive and reflects his brilliant contributions to vimdom. It also goes a bit beyond what is strictly needed here; the quickfix step can be omitted in favor of populating args with the result of a shell command:
:args `git grep -l findme`
:argdo %s/findme/replacement/gc
:argdo update
should be all you need.
Edit: as Domon notes, :set hidden must be done first if it's not already set!
Using quickfix-reflector.vim, you can edit your search results in the quickfix window. The write command will then save the changes to your files.
:copen
:%s/foo/bar/cg
:write
External grep
(uses grepprg, grepformat like in makeprg/errorformat; if grepprg=='internal' this is identical to internal grep)
:grep fopen *.c
:copen
:cnext
Internal grep
:vimgrep /\<myVimregexp\>/ **/*.c
:copen
:cnext
etc.
Location list internal grep
:lvimgrep /\<myVimregexp\>/ **/*.c
:lopen
:lnext
etc.
Bonus: doing external grep for the loaded buffers:
:silent bufdo grepadd fstream %
:copen
:cnext
etc.
External for all arguments:
:silent argdo grepadd fstream %
:copen
:cnext
There's a patch to add the cdo (Quickfix do) command to vim, but it has not been pulled yet (as of 2015-03-25):
https://groups.google.com/forum/#!topic/vim_dev/dfyt-G6SMec
You may want to patch vim yourself to get this patch:
brew install hg # install mercurial, e.g. with homebrew
hg clone https://vim.googlecode.com/hg/ vim
cd vim
# copy/download patch to . folder
patch -b -p1 < cdo.diff
./configure
make && make install
In a given working directory, if I do
:tabe **/test*.py
vim complains with E77: Too many file names. What if I want it to open every matching file in a separate tab? There must be a way to do it, but I can't find it.
You could use the args list and argdo like so:
:args **/test*.py
:argdo tabe %
However, the syntax event is turned off by argdo (to speed up the normal use case), so the files will be loaded without syntax at first. You could follow it up with a :syntax on to force the syntax event on all loaded buffers. Compressed into one line (need to wrap argdo in execute so it doesn't absorb the following |):
:args **/test*.py | execute 'argdo tabe %' | syntax on
Alternately, you can open vim from the command line via:
vim -p **/test*.py
But that will max out at 10 tabs.
You can use the following:
:next **/test*.py
It opens all the files.
To map it
nmap <c-d> :args **/*.tpl<bar>execute 'argdo tabe %'<bar>syntax on<cr>
But still it displays list of files, you have to press enter few times (depending of number of files).
This functionality can be included as a command in your .vimrc file:
"open all files in seperate tabs
command -nargs=1 OpenAll call <SID>openAll(<f-args>)
function! s:openAll(dir)
execute 'args ' . a:dir
silent argdo tabe %
syntax on
endfunction
With this function running :OpenAll **/*.py from vim will quickly open all files into new tabs
None of the other answers works for me, but this is fine:
find <path> -iname <pattrn> | xargs -o vim -p
all files are visible in different tabs
file lookup is recursive
Note, vim can limit tabs - to be changed by set tabpagemax=42.
Also, if you wonder how to close all tabs at once, use :qa
In vim, I do search with vimgrep frequently. I have mapping like below:
map <leader>s :execute "noautocmd vimgrep /\\<" . expand("<cword>") . "\\>/gj **/*.*" <Bar>
cw<CR> 5
The problem is that there are some temporary subfolders (like obj, objd) that I don't want to search for. How can I exclude subfolders matching given patterns. For example, subfolders with prefix "objd" should not be included in searching.
As of Vim 7.3.570, you can use wildignore to exclude patterns with vimgrep.
For example, to ignore the objd subfolder:
:set wildignore+=objd/**
Additional exclusions can be added by separating patterns with a comma:
:set wildignore+=objd/**,obj/**,*.tmp,test.c
See Vim's help documentation for a few more details.
:help wildignore
As showed in http://vimcasts.org/blog/2013/03/combining-vimgrep-with-git-ls-files/ you could instead of exclude files, include the files you want to search. So you can search in the files tracked by Git with
:noautocmd vimgrep /{pattern}/gj `git ls-files`
In this way you are not searching the files stated in the .gitignore.
I use it so much I created a command for that, so I just need to
:Sch {pattern}
and I did it by adding the following line to my .vimrc
command -nargs=1 Sch noautocmd vimgrep /<args>/gj `git ls-files` | cw
You could try ack instead. It integrates nicely with vim and has lots of options for doing the sort of thing you want to do.
There are several ack-vim integrations on GitHub. For example: here.
For example in Ubuntu just
sudo apt-get install ack-grep
sudo ln -s /usr/bin/ack-grep /usr/bin/ack
then install http://www.vim.org/scripts/script.php?script_id=2572
and now add next line to your .vimrc
noremap <C-f> :copen<CR>:Ack --ignore-dir #first_ignore_dir# --ignore-dir #second_ignore_dir# -ai
its open search frame by Ctr+F, have fun
My problem is simple. I search a specific pattern in a file (let's say label in a Tex file)
:g/label/#
but there are lots of occurrences. So I'd like to redirect this output to another file to be able to work easily with it.
Do you have a trick or a command that I don't know?
it's not clear from the original post what you mean by "work easily with it" but it's often useful to see and quickly jump between all of the matches in a buffer without "extracting" the matches to a separate buffer.
vim has an internal grep built in. your example would be something like this (in vim, % denotes the current file)
:vimgrep /label/ %
This will take you to the first occurrence and report how many matches there were. What's cool is that you can look at all of the matches listed by opening up the quickfix error list using
:cope
Now you can just scroll around and press enter on a line to jump to the exact position of the match.
The quickfix error list is exactly the same buffer you use if you run make from inside vim and your compiler throws errors: it gives you a list of what and where the errors are.
After you've jumped to one location pointed by quickfix, you can go to forwards and backwards in the list via :cn and :cp. :ccl closes the error list.
You can also expand your "error" list via :vimgrepa /newpattern/ % or :vimgrepadd
The (documented) caveat is that vim's internal grep is slower than most native grep implementations (but you do get it "for free" in windows, for example). If you do have a grep installed, you can use :grep instead of :vimgrep for similar results.
quoting :help grep
Vim has two ways to find matches for a
pattern: Internal and external. The
advantage of the internal grep is that
it works on all systems and uses the
powerful Vim search patterns. An
external grep program can be used when
the Vim grep does not do what you
want.
The internal method will be slower,
because files are read into memory.
The advantages are:
- Line separators and encoding are automatically recognized, as if a file
is being edited.
- Uses Vim search patterns. Multi-line patterns can be used.
- When plugins are enabled: compressed and remote files can be searched.
You can also use the location list if you're already using the error list for dealing with compilation errors. just add l (for location) to the beginning of the grep command (:lvimgrep,:lvimgrepa :lgrep, :lgrepa) and use :lopen :ln :lp :lcl instead of the :c* ones.
For more commands consult
:help grep
:help quickfix-window
:help quickfix
:help quickfix-error-lists
:redir > matches.txt|execute 'g/foo/#'|redir END
See :h :redir, you can also redirect to registers, variables, the clipboard etc.
What you're doing is essentially 'grep -n label file' from command line. So you can run that command and > it into a file easily enough.
The derivation of 'grep' is even from basically the same source.
I've gotten this of the net at some point:
function GoToLine(mainbuffer)
let linenumber = expand("<cword>")
silent bd!
silent execute "buffer" a:mainbuffer
silent execute ":"linenumber
silent nunmap <Enter>
endfunction
command -nargs=1 GoToLine :call GoToLine(<f-args>)
function GrepToBuffer(pattern)
let mainbuffer = bufnr("%")
silent %yank g
enew
silent put! g
execute "%!egrep -n" a:pattern "| cut -b1-80 | sed 's/:/ /'"
silent 1s/^/\="# Press Enter on a line to view it\n"/
silent :2
silent execute "nmap <Enter> 0:silent GoToLine" mainbuffer "<Enter>"
" silent nmap <C-G> <C-O>:bd!<Enter>
endfunction
command -nargs=+ Grep :call GrepToBuffer(<q-args>)
Put it in your .vimrc, then :Grep Foo
Requires external grep program to work properly.
(Just an idea -- untested.)
You can delete all the lines with your pattern in it, write to another file, and undo the delete.
:g/label/d
:w matches
u