I am using function below to search content in files with ripgrep using fzf in vim
function! RipgrepFzf(query, fullscreen)
let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case -- %s || true'
let initial_command = printf(command_fmt, shellescape(a:query))
let reload_command = printf(command_fmt, '{q}')
let spec = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command]}
call fzf#vim#grep(initial_command, 1, fzf#vim#with_preview(spec), a:fullscreen)
endfunction
command! -nargs=* -bang FRG call RipgrepFzf(<q-args>, <bang>0)
I am able to perform a simple search with it, However i am not able to use RG flags to make my search more precise (search in filetype, exclude directory, or search in sub-directory etc.)
I found some articles suggest small changes in function to get what i am looking for are below:
Remove -- before %s
let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case %s || true'
Remove shellescape function
let initial_command = printf(command_fmt, a:query)
After these changes i am able to use flags initially, However while updating my search it's not working as expected.
I would like to use RG as it is (same way it works in terminal) within fzf in vim
to search for files only by extension/filetype, try this in command line:
rg . --files -g "*.{py}"
with this im searching only for python files
my output:
> rg . --files -g "*.{py}"
./another.py
./colors_do_not_apply.py
to exclude a directory from ripgrep
if you want to search all content from files at current directory:
> rg --column --line-number --no-heading --color=always --smart-case --hidden -g "\!.git" .
notice that:
--hidden is for hidden files
-g "\!.git" is for ignoring git folder (you can do the same with others)
note:
i used "\!.git", that is only used in terminal (my zsh explodes without ! escaped), in vim you can use it as "!.git"
if you want to search only for files and exlcude git folder at current dir:
> rg --hidden -g "\!.git" --files .
search in sub-directory is made automatically so you dont need to worry
finally
i saw you copy pasted a fancy function from fzf.vim on github, i use that too :)
here is my function:
" search for content in files in current project, including hidden ones and ignoring git
function! SearchContentInFiles(query, fullscreen)
let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case --hidden -g "!.git" -- %s || true'
let initial_command = printf(command_fmt, shellescape(a:query))
let reload_command = printf(command_fmt, '{q}')
let spec = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command, '--exact', '--pointer=#', '-i', '-m', '--cycle', '--border=none', '--marker=*', '--ansi', '--preview-window=left,50%', '--bind=alt-bspace:backward-kill-word,ctrl-x:beginning-of-line+kill-line,ctrl-a:select-all', '--color=16,fg+:bright-red,hl:bright-blue,hl+:green,query:blue,prompt:yellow,info:magenta,pointer:bright-yellow,marker:bright-blue,spinner:bright-blue,header:blue', '--prompt=content from files(+hidden) (git ignored) at . > ']}
call fzf#vim#grep(initial_command, 1, fzf#vim#with_preview(spec), a:fullscreen)
endfunction
command! -nargs=* -bang SCIF call SearchContentInFiles(<q-args>, <bang>0)
nnoremap <silent> <S-F> :SCIF!<CR>
well, its quite big, i know. this is the best i've got.
the only differences are:
in command_fmt i added extra args for hidden files and exclusion of git folder
the name of the function
fzf options (there are a lot of options), more exactly these options:
'--exact' - exact match
'--pointer=#',
'-i',
'-m',
'--cycle', (repeat after end)
'--border=none',
'--marker=*',
'--ansi', (enable colors)
'--preview-window=left,50%',
'--bind=alt-bspace:backward-kill-word,ctrl-x:beginning-of-line+kill-line,ctrl-a:select-all', (some keybindings)
'--color=16,fg+:bright-red,hl:bright-blue,hl+:green,query:blue,prompt:yellow,info:magenta,pointer:bright-yellow,marker:bright-blue,spinner:bright-blue,header:blue', (color scheme)
'--prompt=content from files(+hidden) (git ignored) at . > ' (prompt text)
ofc, you can integrate every above command in vim.
hope these help.
First, you need to remove the -- from the comand string because
a double dash (--) is used in most Bash built-in commands and many other commands to signify the end of command options, after which only positional arguments are accepted.original answer
And the second is, you're interested in reload_command because that's what's called whenever you change the string on preview window.
If you change your reload_command to be
let reload_command = printf(command_fmt, shellescape('{q}'))
then you should be able to pass flags on the preview window.
I wanted to see how fzf calls that reload command just to see the difference shellescape makes but I think that would take some time for me as I am not really familiar. If I do that I'll edit my answer as well.
Related
I use a convenience package fzf.vim to integrate fzf with vim. The file search works perfect and has completely replaced CtrlP plugin, but the functionality to grep files has unfortunate side-effect of fzf thinking the auto-appended filename (by ripgrep, but this would be the case with ag (silver-searcher) and regular grep as well) is part of the search string.
As a result, the best result doesn't always come to the top (or bottom in my case based on layout). Here is an example:
The result that appears last in this case should actually be first because it's the only result that matches by content (as intended by Rg) rather than file name. This problem is especially frustrating on large repos, where irrelevant results could litter your entire search space.
I should also clarify that the problem occurs because I invoke :Rg from a shortcut, so the filtering is done by fzf, not rg (which just dumps everything to fzf).
This problem occurs in default :Rg implementation bundled with fzf.vim as well as my attempt to roll my own:
command! -bang -nargs=* Rg
\ call fzf#vim#grep(
\ 'rg --column --line-number --no-heading --color=always --smart-case '.shellescape(<q-args>), 1,
\ <bang>0 ? fzf#vim#with_preview('up:60%')
\ : fzf#vim#with_preview('right:50%:hidden', '?'),
\ <bang>0)
I couldn't figure out how to remedy this issue. Can someone help?
Something that worked out for me is redefining Rg as follows:
command! -bang -nargs=* Rg call fzf#vim#grep("rg --column --line-number --no-heading --color=always --smart-case ".shellescape(<q-args>), 1, {'options': '--delimiter : --nth 4..'}, <bang>0)
I wish I came up with this myself, but honestly, after facing the same issue as you have, I was lucky to get this blog post in my search results. If you are like me and get confused how and why this works, this issue thread in fzf github repo describes the anatomy of the command above (and offers the same solution to the problem).
It sounds like you want your own ripgrep shortcut as called inside vim to not return the filename when searching, so that you can use your search results down the line.
man rg:
-I, --no-filename
Never print the file path with the matched lines. This is the default
when ripgrep is explicitly instructed to search one file or stdin.
This flag overrides --with-filename.
I'm trying to split my vimrc up into multiple files - init.vim, keybindings.vim, ui.vim, etc. - but I can't get Vim to source files relative to init.vim (it instead sources relative to where I launch Vim from.
This is what I've got at the top of init.vim:
source keybindings.vim
source ui.vim
If I run vim from the same directory as those files, it works fine; if I run it from any other directory, I get the following errors:
Error detected while processing /path/to/vimrc:
line 1:
E484: Can't open file keybindings.vim
line 2:
E484: Can't open file ui.vim
Press ENTER or type command to continue
Edit: It's worth noting that I'm using NixOS, so I don't know what the absolute paths will be, nor if they would be constant if I found out.
I think you can use
runtime keybindings.vim
Source needs the full path, you can however simplify it using something like this :
let path = expand('%:p:h')
exec 'source' path . '/keybindings.vim'
You can have a look at mine here - https://github.com/dhruvasagar/dotfiles/blob/master/vim/vimrc for reference.
If the order is not important, you can just put your scripts into ~/.vim/plugin/, and they will be sourced after ~/.vimrc. You can check :scriptnames output to see what gets sourced when.
You can influence the ordering somewhat via the plugin filenames. For example, I have a ~/.vim/plugin/00plugin-configuration.vim that configures Vim plugins; the 00... ensures this is sourced first.
To get finer control, I would instead put the scripts into ~/.vim/. Vim will ignore them there, but they can easily be addressed via :runtime, which looks in all runtimepaths, and ~/.vim/ typically is included in 'runtimepath':
# .vimrc
runtime init.vim
runtime keybindings.vim
...
Relevant help pages: :help .vimrc and :help load-plugins.
Building on Dhruva's answer, you can make a function to help out with this
function! SourceLocal(relativePath)
let root = expand('%:p:h')
let fullPath = root . '/'. a:relativePath
exec 'source ' . fullPath
endfunction
You then use it like
call SourceLocal ("yourScript.vim")
I have met exactly the same issue with you in Neovim. I split my large init.vim file into several small vim scripts and I want to source them inside init.vim.
This is what I get finally based on #Dhruva Sagar's links:
let g:nvim_config_root = stdpath('config')
let g:config_file_list = ['variables.vim',
\ 'options.vim',
\ 'autocommands.vim',
\ 'mappings.vim',
\ 'plugins.vim',
\ 'ui.vim'
\ ]
for f in g:config_file_list
execute 'source ' . g:nvim_config_root . '/' . f
endfor
As none of the solutions works as a real substitute for source working globally (on any script, even sourced from vimrc), I ended up with this solution and decided to share here, which can be used as a substitute for source with relative support, as simple as:
Rsource /home/me/.vim/your/file/path
Rsource $HOME/.vim/your/file/path
Rsource your/file/path
Rsource ../your/file/path
To use it, this must be defined on your vimrc or any file sourced by it before you can use Rsource:
if !exists('g:RelativeSource')
function! g:RelativeSource(file)
let file = expand(a:file)
" if file is a root path, just source it
if stridx(file, '/') == 0
exec 'source ' . file
return
endif
let sfile = expand('<sfile>:p:h')
" If this is called outside this script, it will contains this script
" name, this function name, a script_marker then the executing script name
" In this case we extract just the last part, the script name which called
" the this function
let script_marker = '..script '
let path_index = strridx(sfile, script_marker)
if path_index == -1
let path_index = 0
else
let path_index += len(script_marker)
endif
let path = strpart(sfile,path_index)
let absolute_path = resolve(path . '/'. file)
exec 'source ' . absolute_path
endfunction
command! -nargs=1 Rsource :call g:RelativeSource(<q-args>)
endif
This is safe to be used in any script or plugin.
These are all great solutions, and this is what I ended up using.
let home = expand('~')
exec 'source' home . '/.config/nvim/prettierConfig.vim'
I have a complected project hierarchy for my FPGA projects. I have written a passer which examines the "Vivado/ISE" project file and returns a file containing a list of all the source file the project users (I then run ctags over this list).
I would like to be able to search this list of files from vim 7.4 without needing to do a whole recursive search through the FPGA library. I can do this from the linux command line using something like:
cat ./files.txt | xargs grep -Hn my_function
Where ./files.txt contains the list of files I would to search over. However I would like to be able to use vim's internal grep function. I would also like to be able to run this from both Windows and Linux.
Is there an easy way to pass a list of files (contained within a file) to vim's grep ex function?
You should probably use a plugin such as CtrlSF. But if you insist to do it with Vim alone, you can do something like this:
function! Grep(what, where)
exec join(extend(['vimgrep', a:what],
\ map(filter(readfile(a:where), 'v:val !=# "" && filereadable(v:val)'),
\ 'fnameescape(v:val)')))
copen
endfunction
command! -nargs=+ Grep call Grep(<f-args>)
Then you'd just call :Grep /pattern/ filelist, instead of :vimgrep /pattern/ ....
First step, populate the argument list with all the files in your list:
" in UNIX-like environments
:args `cat files.txt`
" in Windows
:args `type files.txt`
Second step, search for pattern in the argument list:
:vim pattern ##
If you have opened all files in Vim—eg with
vim `<files.txt`—then you can search all of them with (e.g.)
:bufdo g/my_function/
If you :set nu you'll get line numbers as per
grep -nHth
I would like to use Ack (or similar plugin if something else can do the job) to search my whole project in Vim by default, rather than just the current directory. Ideally I'd end up with a process that works like using Cmd+Shift+F in Sublime. How can I do this?
An option like CtrlP's let g:ctrlp_working_path_mode = 'r' that makes it search within the nearest parent directory that contains a file like .git would be perfect. (https://github.com/kien/ctrlp.vim#basic-options)
I think Rooter is what you want. For example:
let g:rooter_patterns = ['Rakefile', '.git/']
I don't think Ack (or grep/vimgrep) can detect your "project root". If you often work on several projects, you could add this block in your vimrc:
let g:projectA_path="/path/to/A"
let g:projectB_path="/path/to/B"
let g:projectC_path="/path/to/C"
also define some functions/commands, like AckA, AckB, AckC...
basically the func/command just does:
exec 'Ack! '. pattern . " " . g:projectA_path
the pattern is the argument you passed in. then, in future, you could do:
:AckA foo
or
:call AckA("foo")
for quick grepping/acking in projectA.
I didn't think of a simpler way to do it. glad to see if there is better solution.
Most of the time I don't need to cd the project root, but stay in the same working directory.
So there is a simpler solution, based on answer of Kent, without cd'ing the project root, installing additional plugins and using ag:
let g:ackprg = 'ag --vimgrep --smart-case'
function! Find_git_root()
return system('git rev-parse --show-toplevel 2> /dev/null')[:-2]
endfunction
command! -nargs=1 Ag execute "Ack! <args> " . Find_git_root()
And to use it call :Ag <keyword>
I have this line in my .vimrc:
cnoreabbrev ack cd ~/your-project <bar> Ack! <Space>
Whenever you type :ack and hit the space the rest will be added to the command line and you can add the keyword.
I'm using vim -d file1 file2 in order to see the differences between them. This works fine, but I want to ignore whitespace changes - they are irrelevant for source code files.
Vim help states that the following command will do the magic:
set diffopt+=iwhite
But unfortunately, this command only adds -b to diff tool command line, and that only ignores trailing whitespaces. The correct command line key for diff should be -w, to ignore all whitespace changes. But I can't find how to modify the diff command line directly from Vim. Of course I can compile a custom diff, or replace diff with diff.sh, but that looks kinda ugly :(.
Is there a better way to modify how Vim interacts with the diff tool for displaying file differences?
This implements what you want (taken from the diffexpr docs with -b changed to -w):
set diffopt+=iwhite
set diffexpr=DiffW()
function DiffW()
let opt = ""
if &diffopt =~ "icase"
let opt = opt . "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt . "-w " " swapped vim's -b with -w
endif
silent execute "!diff -a --binary " . opt .
\ v:fname_in . " " . v:fname_new . " > " . v:fname_out
endfunction
... I'm still looking for a better diffexpr helper with respect to handling which lines map to which (GNU diff, even with -w instead of -b, is rather baffled by combining extra whitespace with minor edits like commented lines). Maybe diffchar?
Yes. Set the iwhite option as you did, but additionally, make diffexpr empty.
From the relevant section of the vim docs:
iwhite
Ignore changes in amount of white space. Adds
the "-b" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
of the "diff" command for what this does
exactly. It should ignore adding trailing
white space, but not leading white space.
Note also that you can provide a custom diff command line by setting diffexpr. See the discussion on the vimdiff man page, in particular:
The 'diffexpr' option can be set to use something else than the standard
"diff" program to compare two files and find the differences.
When 'diffexpr' is empty, Vim uses this command to find the differences
between file1 and file2:
diff file1 file2 > outfile
Thanks ire, that helped me. I now only need to have this (simpler than what is proposed by Adam K) in my ~/.vimrc :
set diffopt+=iwhite
set diffexpr=""
And it does it... That is still the most powerfull diff tool I know of, far better than any other.
I know it's an antique question but for others like me who didn't know, this is now available:
:set diffopt+=iwhiteall
Adds the "-w" flag to the "diff" command if 'diffexpr' is empty.
See :h 'diffopt'
For those hitting "Invalid argument" doing set diffopt+=iwhite, try without the + like so:
set diffopt=iwhite
However, a more robust approach would be to set ignore whitespace while preserving existing options. Beware though, that the "Invalid argument" error is likely caused by one of those existing options not being supported. In my case it was the "internal" option therefore I needed to set options in the following order:
set diffopt-=internal
set diffopt+=iwhite
Or add the following to your .vimrc:
if &diff
set diffopt-=internal
set diffopt+=iwhite
endif
Credit to https://www.micahsmith.com/blog/2019/11/fixing-vim-invalid-argument-diffopt-iwhite/
Addressing an issue brought up in the comments of Adam Katz's solution:
Depending on the vim version and setup of the user, a silent command can neglect to redraw the screen after it is issued. I also encountered this problem, which arose whenever I executed :diffo after using the suggested diffexpr. My solution was to change the silent execute command to the following:
silent execute "!diff -a --binary " . opt .
\ v:fname_in . " " . v:fname_new . " > " . v:fname_out | redraw!
This forces a redraw after the command is issued.