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.
Related
I've got a textfile which I want to filter for a pattern, to get a quick overview. My normal approach in vim is to filter via :v/pattern/d. This works fine, but If I save the file accidentally, after I did the filtering, I loose the not filtered information.
To avoid this, I search for a solution where the orginal textfile cannot be destroy by accident.
My current solution is a function where I read the textile into a temporary file and run the filter on this file. But the function does not work if I try to search something like ^linestart. Furthermore I want to highlight the search pattern, which as well does not work as expected.
Here is my function in vimscript:
function! FilterJournal(pattern)
:exe 'e ~/tempfile'
" delete all existing lines
:1,$d
:exe 'r ~/journal.txt'
:exe 'silent v/ ' . a:pattern . '/d'
" to highlight the search pattern
:exe 'silent / ' . a:pattern
endfunction
:command! -nargs=1 Fijo :call FilterJournal("<args>")
When I run the command: Fijo foo I get the result, but the highlightning does not work.
When I run the command: Fijo ^foo I get some error messages and the tempfile is empty:
Error during execution of "function FilterJournal":
Line 6:
E486: Pattern not found: ^foo
How can I filter my textfile without destroy it by accident or get my function to work?
You can try this based on your function. Please note that
The space in v/ causes E486: Pattern not found
I tried set hlsearch but it did not work in this case. However, :match works well for highlighting in the current buffer
Leading : is not necessary
function! FilterJournal(pattern)
exe 'e ~/tempfile'
" delete all existing lines
1,$d
exe 'r ~/journal.txt'
exe 'v/' . a:pattern . '/d'
" to highlight the search pattern
exe 'match Search /' . a:pattern . '/'
endfunction
:command! -nargs=1 Fijo :call FilterJournal("<args>")
If you really want "to get a quick overview", you could simply do:
:g/pattern
which prints the matching lines without affecting the buffer.
Give this a try, it yanks the matched lines and show it in a vsplit buffer:
function! FilterIt(pat)
call setreg('x', '')
call setreg('/', a:pat)
exec 'g/'.a:pat.'/y X'
exec 'vsp /tmp/t'
exec 'norm! ggVG"xp'
endfunction
:command! -nargs=1 Fijo :call FilterIt("<args>")
I just started with vim script and try make translation of opencart language files easier. I want to have a function that looks for a given search pattern and selects it. If there is no match left in the file, it shall open the next file for editing. What I have so far:
function! Nextmatch()
normal /\(= *'\)\#<=.\{-\}\('\)\#=/
normal v//e
endfunction
function! Nextfile()
if !exists("s:filecounter")
execute "!find -iname *.php > files.txt"
normal <CR>
let s:filecounter=0
endif
let s:lines= system("wc -l < files.txt")
if s:filecounter<s:lines
w
let s:filecounter += 1
let s:sedcommand="sed '".s:filecounter."!d' files.txt"
let s:selectedfile=system(s:sedcommand)
execute 'edit' s:selectedfile
else
wq
endif
endfunction
How can I achieve that Nextfile() is called in Nextmatch() if the search pattern is not found between the cursor and the end of the current file? And is there something that you consider to be bad style in my snippet?
Quickfix commands are powerful and well integrated with some external plugins, but if you really need to use your own script, and if you need to check a match in an if statement, just do:
if search("=\\s*'\\zs[^']*\\ze", 'W') == 0
echo 'No match until the end of the buffer'
endif
See :h search(), and please note :
the double backslashes, due to the double quotes
the 'W' flag which forbids wrapping around the end of file
I simplified the pattern you gave
You could simply use the :vim command to get rid of all your script.
I think the following should do quite what you're expecting:
:noremap <f8> <esc>:cn<cr>gn
/\(= *'\)\#<=.\{-\}\('\)\#=
:vim //g *.php
Then, to go to the next pattern in all files while selecting it,
you just have to press the F8 key.
In the noremap line, gn let you select the next actual search.
You may need to do:
:set nohidden
to let you navigate threw modified buffers (but don't forget to save
them with :wa, or list them with :ls)
About your script:
It's a good habit in scripts to always use :normal! instead of :normal (unless you deliberately need it) : thus, your personnal mappings won't interfer in your scripts.
Let me jump right in.
What I'm trying to do is simply print out the file path of any open buffer when I exit vim. This is useful because I often open other files in vim buffers with a vim script I wrote that can search through my codebase for a specific function call.
I figure I can set up an autocommand, either for when I open a file or when I leave vim, and use the output from :ls to list all currently open buffers. The problem that I'm having is that I can't get any output to show up in terminal. I have tried various combinations of :!echo in my function, but to no avail. I have been trying something like the following in my .vimrc
function! PrintFileName()
:!echo "hello"
:exec "!echo world"
"... etc
endfunction
au BufRead * call PrintFileName()
Both :!echo foobar and :call PrintFileName() work for me if I do it from the command line. I also figure I might have to use some form of silent/redraw! so I don't have to hit enter to continue.
Really the main problem here is that I can't see a way to get output to stdout from inside my function and called by an autocommand.
Thanks for any help.
Okay, so I've found this solution, which works as long as I enter vim from the last line of my terminal. Otherwise this prints out a line below the current line and will get overwritten when you press enter. If anyone knows how to fix that let me know, otherwise I will use this.
function! PrintBuffers()
redir => files
:ls
redir END
" Regex to strip out everything from :ls but the buffer filenames
let files = substitute(files, '^[^"]*"', '', 'g')
let files = substitute(files, '"[^"]*\n[^"]*"', '\n', 'g')
let files = substitute(files, '"[^"]*$','','g')
" This is the magic line
exe '!echo; echo ' . shellescape(&t_te . files)
endfunction
au VimLeave * call PrintBuffers()
*Note - As I'm writing this, I realize that this won't display the right path if you did a :cd at some point. So I guess its pretty fragile, but it does the job.
As everyone knows, we can :source current buffer by :so % .
But sometimes I want to :source just a part of the buffer, not the whole buffer. Say, I just added something to my .vimrc, and want to source that part, but I don't want to re-source all the rest stuff.
I tried select text and :so (actually :'<,'>so ) , but it reported that range is not allowed. So, how could this be done?
Of course I can save needed part to the temp file and source it, but it is clearly annoying.
Why not have the following mappings for sourcing a file (or a range):
" source the current file
nmap <leader>vs :source %<CR>
" source a visual range
vmap <leader>vs y:#"<CR>
Now, you can press ,vs (if your mapleader is ,), and the selected range will be sourced or otherwise, in normal mode, current file will be sourced.
You can define the following command, which operates on the current line or passed range:
":[range]Execute Execute text lines as ex commands.
" Handles |line-continuation|.
command! -bar -range Execute silent <line1>,<line2>yank z | let #z = substitute(#z, '\n\s*\\', '', 'g') | #z
Thanks to Ingo Karkat, I have taken his main idea and improved it. What I wanted to improve:
We have to use additional user-specified command :Execute instead of standard :so (ok, we can name user-specified command :So, anyway it's annoying to use new capitalized version of the command)
There is little side effect: register #z is corrupted after executing the command.
With my script below, we can use :so {file} command as before, and we are also able to use it with range: :'<,'>so (which actually expands to :'<,'>Source)
Here:
" This script provides :Source command, a drop-in replacement for
" built-in :source command, but this one also can take range and execute just
" a part of the buffer.
"
" Sources given range of the buffer
function! <SID>SourcePart(line1, line2)
let tmp = #z
silent exec a:line1.",".a:line2."yank z"
let #z = substitute(#z, '\n\s*\\', '', 'g')
#z
let #z = tmp
endfunction
" if some argument is given, this command calls built-in command :source with
" given arguments; otherwise calls function <SID>SourcePart() which sources
" visually selected lines of the buffer.
command! -nargs=? -bar -range Source if empty("<args>") | call <SID>SourcePart(<line1>, <line2>) | else | exec "so <args>" | endif
" in order to achieve _real_ drop-in replacement, I like to abbreviate
" existing :so[urce] command to the new one.
"
" So, we can call :so % just as before, and we are also call '<,'>so
cnoreabbr so Source
cnoreabbr sou Source
cnoreabbr sour Source
cnoreabbr sourc Source
cnoreabbr source Source
The following works if you only selected one line:
yq:p<enter>
This will also work:
y:<control-r>"<enter>
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>