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
I am trying to create a VIM function that will allow me to find words in my code repository
by using the external grep function of linux then highlight all occurences of that word.
Also the silent on the execute is suppose to be suppressing the output of the external program but it is not working..
set grepprg=grep\ -rns\ -C\ 1\ "
set grepformat=%f:%l:%m
function! WordFind()
let l:word = input("Find:")
execute 'silent grep ' . l:word . ' .'
:redraw!
:copen
execute '/' . l:word
endfunction
Are you looking for words or symbols? If you are looking for symbols (functions, variables, arrays...) you might find ctags and/or cscope to be a better fit.
See :help tags for more information.
But... you didn't ask anything so, what do you want from us? A code critique?
The last line of your function seems useless because every line in the quickfix window contains your word at least once. It will just move the cursir horizontally on the first line which is of no use.
I would use :cwindow instead of :copen.
Other than that, your function seems ok.
EDIT
Well no, it is not OK, the last line wouldn't work anyway. The revised function below works for me using your grepprg and grepformat settings:
function! WordFind()
let l:word = input("Find:")
execute 'silent grep ' . l:word . ' .'
redraw!
cwindow
wincmd p
let #/ = l:word
endfunction
The wincmd p line may not be necessary after all.
I don't believe the output of grep can be completely hidden in CLI Vim.
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>
Basically I need to create a quickfix buffer listing all lines that match a regex. What is the best way?
The global command may not be the best, but I think it should be usable. The output of the global command is perfect, but I need it to hyperlink the matching lines.
You can also not use the global command and stick with what is built-in.
Use the vimgrep command as below:
:vimgrep /pattern/ %
And then :copen or :cwindow
Use caddexpr:-
g/<pattern>/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
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