Ignore node_modules with vim-fzf - vim

I have defined a function to search for filename (<C-P>), and a string (<C-F>) from git root directory asynchronously with fzf.vim plugin (I also have Ag installed). However, I can not manipulate the definition to ignore node_modules directory. The vim script is too hard to debug, there is no console to print anything.
Is there any expert in vim script that can help me sort this out. Many thanks in advance
let s:git_path = substitute(system("git rev-parse --show-toplevel 2>/dev/null"), '\n', '', '')
function! s:ag_git_root(query, ...)
if type(a:query) != type('')
return s:warn('Invalid query argument')
endif
let query = empty(a:query) ? '^(?=.)' : a:query
let args = copy(a:000)
let ag_opts = len(args) > 1 && type(args[0]) == s:TYPE.string ? remove(args, 0) : ''
let command = ag_opts . ' ' . fzf#shellescape(query) . ' ' . s:git_path
return call('fzf#vim#ag_raw', insert(args, command, 0))
endfunction
command! -bang -nargs=* A
\ call s:ag_git_root(<q-args>, <bang>0)
command! -bang -nargs=? F
\ call fzf#vim#files(s:git_path, <bang>0)
silent! nmap <C-P> :F<CR>
silent! nmap <C-F> :A<CR>

Finally, I figured out a workaround by replacing fzf#vim#files with :Gfiles
New configuration is silent! nmap <C-P> :GFiles<CR>
<C-F> mapping is kept the same as in the question.
The demerit of this :GFiles solution is that files not added to git (untracked files) are not included in the search results. They all can be added via git add . -A
The merit of this solution is that all files in .gitignore is ignored in the search results

Related

Vim. set command line from a function

I'm trying to write a function that replaces text in all buffers. So I call Ack to search all the matches and next step I want to set into Quickfix command line this code
:QuickFixDoAll %s/foo/boo/gc
Seems like I can only call 'exec' function which runs this command immediately and there is no ablility to edit it or cancel at all
I also tried "input" function to read user input but got this error at runtime
not an editor command
Any ideas?
.vimrc:
function! ReplaceInFiles(o, n)
exec "Ack '" . a:o . "'"
exec "QuickFixDoAll %s/" . a:o . "/" . a:n . "/gc"
endfunction
" QuickFixDoAll
function! QuickFixDoAll(command)
if empty(getqflist())
return
endif
let s:prev_val = ""
for d in getqflist()
let s:curr_val = bufname(d.bufnr)
if (s:curr_val != s:prev_val)
exec "edit " . s:curr_val
exec a:command
endif
let s:prev_val = s:curr_val
endfor
exec "quit"
endfunction
command! -nargs=+ QuickFixDoAll call QuickFixDoAll(<f-args>)
Using input()
This queries both values interactively:
function! ReplaceInFiles()
let l:o = input('search? ')
let l:n = input('replace? ')
exec "Ack '" . l:o . "'"
exec "QuickFixDoAll %s/" . l:o . "/" . l:n . "/gc"
endfunction
nnoremap <Leader>r :call ReplaceInFiles()<CR>
Incomplete mapping
nnoremap <Leader>r :let o = ''<Bar>exec "Ack '" . o . "'"<Bar>exec "QuickFixDoAll %s/" . o . "//gc"<Home><Right><Right><Right><Right><Right><Right><Right><Right><Right>
This one puts the cursor on the right spot for the search. As this value is used twice (Ack and QuickFixDoAll), it is assigned to a variable. After that, move to the end of the command and fill in the replacement in between the //gc.
Custom parsing
The most comfortable option would be a custom command :AckAndSubstAll/search/replacement/. For that, you'd need to parse the two parts in the custom command (like :s does). You could do that with matchstr(), or use ingo#cmdargs#substitute#Parse() from my ingo-library plugin.
First use vim-qargs to copy all files from the quickfix window into Vim's arglist by calling :Qargs.
Then run your replace on all arguments in the arglist by doing :argdo %s/search/replace/gc

Vim language: send current word to CtrlP

I know how to use CtrlP. I type ctrl+p, then I start to write file name, ... and so on. But, ... I am very lazy developer. I want to directly send to CtrlP current word. I know how to get current word:
let l:currentWord = expand('<cword>')
In Vim Language, ... I How can I send l:currentWord to CtrlP?
map <F6> :call ComposerKnowWhereCurrentFileIs()<CR>
function! ComposerKnowWhereCurrentFileIs()
let l:currentWord = expand('<cword>')
let l:command = "grep " . l:currentWord . " ../path/to/composer -R | awk '{print $6}' | awk -F\\' '{print $2}'"
let l:commandFileFound = l:command . ' | wc -l'
let l:numberOfResults = system(l:commandFileFound)
if l:numberOfResults == 1
let l:fileName = system(l:command)
let l:openFileCommand = 'tabe /path/to/project' . l:fileName
exec l:openFileCommand
else
echo "Too many files :-( - use CtrlP ;-) "
endif
endfunction
<C-P><C-\>w
See :h ctrlp-mappings. You may map this combination:
map <F6> <C-P><C-\>w
In a function:
exe "normal \<C-P>" . expand('<cword>')
The whole point of CtrlP and similar plugins is to provide an alternative command-line where you can refine your search as you type.
If you don't need fuzzy search and you already have the filename under the cursor… why not simply use the built-in gf?
-- edit --
In the gif below:
I jump to /path/not/knowable/BlaBlaClassName.php with gf,
I jump back to the previous buffer with <C-^> (unrelated to your question),
I jump to the declaration of BlaBlaClassName in /path/not/knowable/BlaBlaClassName.php again with <C-]> thanks to a tagsfile generated with ctags.
function! LazyP()
let g:ctrlp_default_input = expand('<cword>')
CtrlP
let g:ctrlp_default_input = ''
endfunction
command! LazyP call LazyP()
nnoremap <C-P> :LazyP<CR>
(this could probably be simplified but I suck at vim syntax)
For that, you wouldn't use the <C-P> mapping, but the :CtrlP command, as that one takes parameters.
To build a mapping that passes the current word to the command, there are two approaches. Either directly insert the current word into the command-line (via :help c_CTRL-R_CTRL-W):
:nnoremap <Leader>p :CtrlP <C-r><C-p><CR>
Or, in order to use expand(), build the Ex command via :execute:
:nnoremap <Leader>p :execute 'CtrlP' expand('<cword>')<CR>

Using Uncrustify with VIM

In my vimrc I call Uncrustify by this command:
%!uncrustify -l CPP -c D:\uncrustify\default.cfg
After that on some code I get a Windows Fatal error:
But when I call uncrustify on the same code in the console using the -f option, there is no error.
How can I change my vimrc to avoid such errors in the future? What can invoke this error?
In order to integrate Uncrustify with Vim properly, add the following to your .vimrc:
" Restore cursor position, window position, and last search after running a
" command.
function! Preserve(command)
" Save the last search.
let search = #/
" Save the current cursor position.
let cursor_position = getpos('.')
" Save the current window position.
normal! H
let window_position = getpos('.')
call setpos('.', cursor_position)
" Execute the command.
execute a:command
" Restore the last search.
let #/ = search
" Restore the previous window position.
call setpos('.', window_position)
normal! zt
" Restore the previous cursor position.
call setpos('.', cursor_position)
endfunction
" Specify path to your Uncrustify configuration file.
let g:uncrustify_cfg_file_path =
\ shellescape(fnamemodify('~/.uncrustify.cfg', ':p'))
" Don't forget to add Uncrustify executable to $PATH (on Unix) or
" %PATH% (on Windows) for this command to work.
function! Uncrustify(language)
call Preserve(':silent %!uncrustify'
\ . ' -q '
\ . ' -l ' . a:language
\ . ' -c ' . g:uncrustify_cfg_file_path)
endfunction
Now you can either map this function (Uncrustify) to a combination of keys or you could do the convenient trick that I use. Create a file ~/.vim/after/ftplugin/cpp.vim where you can override any Vim settings particularly for C++ and add the following line there:
autocmd BufWritePre <buffer> :call Uncrustify('cpp')
This basically adds a pre-save hook. Now when you save the file with C++ code it will be automatically formatted by Uncrustify utilizing the configuration file you supplied earlier.
For example, the same could be done for Java: in ~/.vim/after/ftplugin/java.vim add:
autocmd BufWritePre <buffer> :call Uncrustify('java')
You got the point.
NOTE: Everything presented here is well-tested and used every day by me.
I have found the placing the following code into your .vimrc to be sufficient:
let g:uncrustifyCfgFile = '~/.uncrustify.cfg'
function! UncrustifyFunc(options) range
exec a:firstline.','.a:lastline.'!uncrustify '.a:options
\.' -c '.g:uncrustifyCfgFile.' -q -l '.&filetype
endfunction
command! -range=% UncrustifyRange <line1>,<line2>call UncrustifyFunc('--frag')
command! Uncrustify let s:save_cursor = getcurpos()
\| %call UncrustifyFunc('')
\| call setpos('.', s:save_cursor)
Note this does assume that you have "uncrustify" binary in your $PATH.
It also assumes your configure file is ~/.uncrustify.cfg however you can change that by modifiying the g:uncrustifyCfgFile variable.
To call run
:Uncrustify
It also works on ranges (which was what promoted me to make this function). Visual selection example:
:'<,'>UncrustifyRange
I have only tired it with C, CPP and JAVA (I assume others will work as well)
In addition to #Alexander Shukaev's answer, adding the following will perform a check for uncrustify config correctness and not auto format if error is detected:
let output = system('uncrustify -q -c ' . a:cfgfile)
if v:shell_error != 0
echo output
endif
return v:shell_error
endfunction
" Don't forget to add Uncrustify executable to $PATH (on Unix) or
" %PATH% (on Windows) for this command to work.
function! Uncrustify(language)
if CheckUncrustifyCfg(g:uncrustify_cfg_file_path)
echo "Config file" g:uncrustify_cfg_file_path "has errors"
echo "No formatting will be performed"
return
endif
call Preserve(':silent %!uncrustify'
\ . ' -q '
\ . ' -l ' . a:language
\ . ' -c ' . g:uncrustify_cfg_file_path)
endfunction

How to delete multiple buffers in Vim?

Assuming I have multiple files opened as buffers in Vim. The files have *.cpp, *.h and some are *.xml. I want to close all the XML files with :bd *.xml. However, Vim does not allow this (E93: More than one match...).
Is there any way to do this?
P.S. I know that :bd file1 file2 file3 works. So can I somehow evaluate *.xml to file1.xml file2.xml file3.xml?
You can use <C-a> to complete all matches. So if you type :bd *.xml and then hit <C-a>, vim will complete the command to :bd file1.xml file2.xml file3.xml.
:3,5bd[elete]
Will delete buffer range from 3 to 5 .
You also can use alternatively use:
:.,$-bd[elete] " to delete buffers from the current one to last but one
:%bd[elete] " to delete all buffers
You can use this.
:exe 'bd '. join(filter(map(copy(range(1, bufnr('$'))), 'bufname(v:val)'), 'v:val =~ "\.xml$"'), ' ')
It should be quite easy to add it to a command.
function! s:BDExt(ext)
let buffers = filter(range(1, bufnr('$')), 'buflisted(v:val) && bufname(v:val) =~ "\.'.a:ext.'$"')
if empty(buffers) |throw "no *.".a:ext." buffer" | endif
exe 'bd '.join(buffers, ' ')
endfunction
command! -nargs=1 BDExt :call s:BDExt(<f-args>)
Try the script below. The example is for "txt", change it as needed, e.g. to "xml".
Modified buffers are not deleted. Press \bd to delete the buffers.
map <Leader>bd :bufdo call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
if (matchstr(bufname("%"), ".".a:strExt."$") == ".".a:strExt )
if (! &modified)
bd
endif
endif
endfunction
[Edit]
Same without :bufdo (as requested by Luc Hermitte, see comment below)
map <Leader>bd :call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
let s:bufNr = bufnr("$")
while s:bufNr > 0
if buflisted(s:bufNr)
if (matchstr(bufname(s:bufNr), ".".a:strExt."$") == ".".a:strExt )
if getbufvar(s:bufNr, '&modified') == 0
execute "bd ".s:bufNr
endif
endif
endif
let s:bufNr = s:bufNr-1
endwhile
endfunction
I too had a need for this functionality all the time. This is the solution I have in my vimrc.
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 BW call WipeMatchingBuffers('<args>')
Now, I can just do :BW regex (e.g. :BW \.cpp$ and wipe all matching buffers that have match that pattern in their pathname.
If you want to delete rather than wipe, you can of course replace exec 'bw ' . join(l:matchList, ' ') with exec 'bd ' . join(l:matchList, ' ')
TAB will only autocomplete one file for you as of Vim 7.4.282
use <c-a> to autocomplete all files.
You can just use:
bd filetype
then just use <c-a> to facilitate the completion of all open files of specified filetype.
for example, you have 1.xml, 2.xml, 3.xml, and 4.xml,
you can do:
bd xml
then press <c-a>
vim will autocomplete for you as follow:
bd 1.xml 2.xml 3.xml 4.xml
you can just press enter to complete the command.
if you have made changes in one of the files mentioned above, do remember to do:
bd! xml
Very simply: use the :bd[elete] command. For example, :bd[elete] buf#1 buf#5 buf#3 will delete the buffers 1, 3, and 5.

How do you exit vimdiff mode in vim, specifically, for Fugitive?

I am using vim with the fugitive extension. It has a :Gdiff command which brings you into vimdiff mode, but what is the right/quick way to close/quit vimdiff mode?
I.e., let's say I am editing the file FooBar.txt under Git repository. I fire up :Gdiff, review my changes in vimdiff, and then I want to get back and continue editing FooBar.txt or any other file :)
UPDATE1: I'm going to give these quick combos a try next working day :)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD :diffoff!<cr><c-w>h:bd<cr>
UPDATE2: My current mappings (closes diff window only!)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD <c-w>h<c-w>c
Also, please help me decide if the following should be an anwser: https://stackoverflow.com/a/15975201/275980
You can execute windo set nodiff noscrollbind and then close the second window.
Update: there is a diffoff command. Use windo diffoff, not what I wrote in previous line.
According to: https://github.com/tpope/vim-fugitive/issues/36
Close the other window. The easiest way to do this if you haven't shifted focus to it is <C-W><C-O>, which means "make this Window the Only window."
I had no luck with diffoff, but I just learned that :Gedit with no argument will bring you back to the working-directory version of the file, as opposed to some earlier version you were reviewing.
And as q (no need for :q) will close the diff sidebar, you can do q followed by :Gedit to get rid of the sidebar and then go back to the current version of the file.
None of the above solutions worked for me. Ended up doing this instead:
nnoremap <Leader>D :Gedit<CR><C-w>h :q<CR><C-w>k
This works fine for me, combining some of the existing ideas here:
function! MyCloseDiff()
if (&diff == 0 || getbufvar('#', '&diff') == 0)
\ && (bufname('%') !~ '^fugitive:' && bufname('#') !~ '^fugitive:')
echom "Not in diff view."
return
endif
" close current buffer if alternate is not fugitive but current one is
if bufname('#') !~ '^fugitive:' && bufname('%') =~ '^fugitive:'
if bufwinnr("#") == -1
b #
bd #
else
bd
endif
else
bd #
endif
endfunction
nnoremap <Leader>gD :call MyCloseDiff()<cr>
An alternative to <C-W><C-O>, if you have multiple windows, would be move to the other diff window and do <C-W>c, which close only one window.
If you close the wrong diff window do a :Gedit
Be careful and don't confuse <C-W>c with <C-W><C-C>
I've found a simple solution for this. You can check it here: https://gist.github.com/radmen/5048080
" Simple way to turn off Gdiff splitscreen
" works only when diff buffer is focused
if !exists(":Gdiffoff")
command Gdiffoff diffoff | q | Gedit
endif
Check the vimdiff toggling between diffthis and diffoff here
at this page.
The code:
nnoremap <silent> <Leader>df :call DiffToggle()<CR>
function! DiffToggle()
if &diff
diffoff
else
diffthis
endif
:endfunction
Method 1:
open a compare by:
:windo diffthis
close it by:
:windo diffoff
Method 2:
I recommend just using the most simple command: :q<CR>
when you want to do it quickly, add the mapping:
" Set mapleader
let mapleader = ","
let g:mapleader = ","
and
" Quickly close the current window
nnoremap <leader>q :q<CR>
It works well for me. Exit vimdiff just by ,q, because normally your cursor in the old file.
this is what I have to leave the vimdiff windows after using :Gdiff
nnoremap gD :q!<CR> :Gedit!<CR>
noremap <leader>do :diffoff \| windo if &diff \| hide \| endif<cr>
Quite diff mode and close other diff windows. (Note: fugitive will auto delete its hidden buffers.)
My function will work both from diff window and file window. But probably won't handle itself with multiple diffs opened. For that you'll need to use fugitive#buffer(n).path() to scan and match.
command! Gdiffoff call Gdiffoff()
function! Gdiffoff()
let diffbufnr = bufnr('^fugitive:')
if diffbufnr > -1 && &diff
diffoff | q
if bufnr('%') == diffbufnr | Gedit | endif
setlocal nocursorbind
else
echo 'Error: Not in diff or file'
endif
endfunction
Add a key binding:
nnoremap <silent> <leader>gD :Gdiffoff<CR>
Yet another way. What I have in fugitive.vim - first save some info (s:gitbufname) when diff starts:
function! s:Diff(vert,...) abort
call sy#toggle()
let s:startcol = winwidth(0)
let &columns=(winwidth(0) * 2 - 20)
...
if getwinvar('#', '&diff')
let s:gitbufname = bufname("%")
wincmd p
call feedkeys(winnr."\<C-W>w", 'n')
endif
...
endfunction
and later when leaving the buffer switch window to the saved buffer and restore:
augroup fugitive_diff
autocmd!
autocmd BufWinLeave *
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
\ if exists('s:gitbufname') && winnr() != bufwinnr(s:gitbufname) |
\ let nr = bufnr("%") | exe bufwinnr(s:gitbufname).'wincmd w' | exe 'buf'.nr |
\ endif |
\ call s:diffoff_all(getbufvar(+expand('<abuf>'), 'git_dir')) |
\ call sy#toggle() |
\ call airline#load_theme() | call airline#update_statusline() |
\ let &columns=s:startcol |
\ endif
...
Was using the code below based on https://stackoverflow.com/a/15113951/10999673 :
if !exists(":Gdiffoff")
command Gdiffoff bw! fugitive://*
endif
but it gave me an error "E93: more than one match for ..." in a 3 way diff, so i instead used the answer from https://stackoverflow.com/a/4867969/10999673 and finally have this:
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 Gdiffoff call WipeMatchingBuffers('fugitive://')
I just tweaked, copied and pasted the code into my .vimrc
Running :Gwrite after merging to your satisfaction will close the other two diff panes in addition to updating the git cache to mark the file as merged.

Resources