I got the following hotkey mapping:
nnoremap <leader>f Unite file -path=~/Workspace
This works great, however, I want to make it so that path equals the current folder I'm in (which will be seperate from working directory).
Anyone know how I can make this happen? :S
You can use the expand() function to use %:p:h in places where a file name is not expected (these expansions are for file-name arguments, not others like what it appears to happen with your command)
:echo expand('%:p:h')
But you can't map that directly. It is a command that needs to be built "on-the-fly", so you can use :execute to build and execute an evaluated expression:
nnoremap <leader>f :exec "Unite file -path=" . expand('%:p:h')
How about using:
:UniteWithBufferDir file
or
:UniteWithCurrentDir file
(depending on what you want)
Unite allows dynamic argument by using backtick, as documented in the Unite help doc:
You don't have to use |:execute| for dynamic arguments.
You can use evaluation cmdline by ``.
Note: In the evaluation, The special characters(spaces, "\" and ":")
are escaped automatically.
>
:Unite -buffer-name=search%`bufnr('%')` line:forward:wrap<CR>
So in your case, the mapping will be:
:nnoremap <leader>f :Unite file -path=`expand('%:p:h')`
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
I use ack.vim, and have below keymap in my .vimrc,
nnoremap ;f :Ack! ^\"
to search for tags in my note files, which are lines that begin with double quote followed by non-space characters.
which works fine, since my note files lies in a common directory, say ~/notes.
Now, say at a git repo, eg, ~/code/some_repo, I want below keymap at that directory,
nnoremap ;f :Ack! ^\/\/\ *\"
I could manually set the keymap if work at given directory, but it soon become tedious.
So I wonder, how can I set keymap base on working directory when I start vim.
-- hopefully vimscript solution, with possible aid of bash command.
Have a look at https://stackoverflow.com/a/456889/15934
The solutions exposed can solve your question. Either by defining buffer specific mappings (:h :map-<buffer>, or by defining buffer variables that you could use in your mappings (:h b:var).
As #Luc suggested, I could use autocmd base on file name with BufEnter and BufCreate. Which works fine.
Then I ask myself, why stick to command only??
Just utilize vim builtin functions and simple vimscript statement!!
So, let's break my requirement to different parts.
get current working directory. -- getcwd().
pattern match on that. -- =~. What does =~ mean in VimScript?
do keymap base on match or not.
below is what's in my vimrc then,
"git, see also " grep
if getcwd() =~ "/code/repoA/"
nnoremap ;f :Ack! ^\/\/\ *\"
nnoremap ,a :!cd %:p:h && git add %<CR>
else
nnoremap ;f :Ack! ^\"
endif
I am trying to create a simple mapping in vim to execute a shell command. The command I want to execute is this:
ruby -e "Dir.glob('./spec/*_spec.rb').each {|f| require f}"
which works fine when I run it at the command line.
However, if I run the following in vim:
nmap ,t :!ruby -e "Dir.glob('./spec/*_spec.rb').each {|f| require f}"<cr>
I get the error:
E492: Not an editor command: require f}"<cr>
Note: I'm on windows, if that's relevant.
What am I doing wrong?
Bonus: How can alter the above command so that it does not depend on the current file being in the directory containing "spec"? Ideally, if the current file's directory did not contain "spec", it would check the parent directory, and so on, recursively, until it found a directory containing "spec". At that point it would run the command with "." replaced by the directory it found in my code above.
Final Solution
Based on Ingo's answer, my final solution was this:
nnoremap ,tt :call RunAllMinitestSpecs()<cr>
function! RunAllMinitestSpecs()
let l:dir = finddir('spec', '.;')
let l:dir = substitute(l:dir, '\', '/', 'g') " so it works on windows
let l:ruby_cmd = "\"Dir.glob('" . l:dir . "/*_spec.rb').each {|f| require f}\""
exe('!ruby -e ' . l:ruby_cmd)
endfunction
The | separates Vim commands; for Vim, the mapping ends at the first |, and Vim tries to interpret the remainder as a command (which obviously fails). You need to either escape via \ or (better) use the special <Bar> notation in mappings:
:nnoremap ,t :!ruby -e "Dir.glob('./spec/*_spec.rb').each {<Bar>f<Bar> require f}"<cr>
Tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Bonus answer
You can get an upward directory search (:help file-searching) via finddir(), then pass the result to glob(). See
:echo finddir('spec', '.;')
(I would then move the implementation into a :function, and invoke that from the mapping. This would have also avoided the | escaping problem.)
To use Vim/Python like a calculator one option is executing the following command in gVim:
:pyf[ile] P:\Computer Applications\Python\pi.py
I intend on storing all my py files in the directory P:\Computer Applications\Python\. Can I add something to my _vimrc file so that in the future gVim knows where all my python files are stored and all I need to write is the following?
:pyf[ile] pi.py
Why not use a mapping?
nnoremap <Leader>p :pyf P:\Computer Applications\Python\
You can then press \p (in normal mode) to get the :pyf P:\Computer Applications\Python\ prefix.
References
Mapleader
:noremap
Normal mode
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>