Vim language: send current word to CtrlP - vim

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>

Related

How to make f and t wrap around the current line in vim

Is there a way to make the 'f' and 't' command wrap around the line? For example, if I have
Hello, my name is _intz,
where _ denotes my cursor position, I would like to be able to press fl for vim to place my cursor on the first l on the line.
Similarly, I would ideally like the , and ; commands to also wrap on the current line.
Thank you
No, this is not possible without implementing the feature yourself.
Note that fF are universally expected to mean "next on the line" and tT to mean "previous on the line", both of which being extremely useful in their own right. Instead of changing their meaning, and thus reducing the overall usefulness of Vim, you should consider making new commands.
Something like these quick and dirty mappings:
" move the cursor on first occurrence of character on the line
nnoremap <expr> <key> '0f' . nr2char(getchar())
" move the cursor before first occurrence of character on the line
nnoremap <expr> <key> '0t' . nr2char(getchar())
See :help <expr>, :help nr2char(), :help getchar().
With the help of this answer https://vi.stackexchange.com/questions/29167/determine-if-there-is-a-matching-character-on-the-current-line-past-the-cursor, the following maps <c-f> to allow that gives it the functionality of f with same line wrapping.
function!Neweff()
let character = nr2char(getchar())
let beforejump = getpos('.')
execute 'norm! f'.character.''
let afterjump = getpos('.')
if beforejump == afterjump
let firstcharacter = getline(".")[0]
execute 'norm! 0'
if character !=# firstcharacter
execute 'norm! f'.character.''
endif
endif
endfunction
nnoremap <c-f> :call Neweff()<CR>

How to use vim variables in an external filter command in visual mode?

I'm trying to make a code pretty printer filter (e.g. perltidy) accept arbitrary options depending on vim variables. My goal is to pass project specific options to an external command used as a filter (:!) in visual mode.
The following expresses my intention (the last line is problematic):
" set b:perltidy_options based on dirname of the currently edited file
function! SetProjectVars()
if match(expand("%:p:h"), "/project-foo/") >= 0
let b:perltidy_options = "--profile=$HOME/.perltidyrc-foo --quiet"
elseif match(expand("%:p:h"), "/project-bar/") >= 0
let b:perltidy_options = "--profile=$HOME/.perltidyrc-bar --quiet"
else
let b:perltidy_options = "--quiet"
endif
endfunction
" first set the project specific stuff
autocmd BufRead,BufNewFile * call SetProjectVars()
" then use it
vnoremap ,t :execute "!perltidy " . b:perltidy_options<Enter>
However, the last line (vnoremap) is an error in vim, because it expands to:
:'<,'>execute "!perltidy " . b:perltidy_options
and the execute command cannot accept a range.
But I'd like to have this:
:execute "'<,'>!perltidy " . b:perltidy_options
How can I do this?
p.s. My perltidy is configured to act like a unix filter and I use vim 7.3.
You can use <C-\>e and getcmdline() to preserve command-line contents:
vnoremap ,t :<C-\>e'execute '.string(getcmdline()).'."!perltidy " . b:perltidy_options'<CR><CR>
, but in this case I would suggest simpler <C-r>= which purges out the need for :execute:
vnoremap ,t :!perltidy <C-r>=b:perltidy_options<CR><CR>
If you ever want to get rid of a range in command (ex) mode,
CRL-u will do just that.
vnoremap ,t :execute "!perltidy " . b:perltidy_options<Enter>
becomes
vnoremap ,t :<C-u>execute "!perltidy " . b:perltidy_options<CR>
:h c_CTRL-u
Happy vimming,
-Luke

vim: jump to buffer that contains /string/

I normally have quite a few buffers opened, which I navigate using combination of Bufexplorer and FuzzyFinder. Finding the right buffer still involves going through file names. But often, it could be much easier to say something like 'jump to buffer that contains "wip"'. Anyone knows how?
I am using a small function I put inside my .vimrc:
function! s:GrepOpenBuffers(search, jump)
call setqflist([])
let cur = getpos('.')
silent! exe 'bufdo vimgrepadd /' . a:search . '/ %'
let matches = len(getqflist())
if a:jump && matches > 0
sil! cfirst
else
call setpos('.', cur)
endif
echo 'BufGrep:' ((matches) ? matches : 'No') 'matches found'
endfunction
com! -nargs=1 -bang BufGrep call <SID>GrepOpenBuffers('<args>', <bang>0)
You could use something like the above to grep for a search term in all opened buffers.
Check out buffer grep: http://www.vim.org/scripts/script.php?script_id=2545

How do I write a vim function to output the result of a system command?

What I have so far:
function! GetMarker()
return system('echo $random `date` | md5sum | cut -d" " -f1')
endfunction
I would like to be able to do a :getmarker and have it insert the output of that system command at my cursor, with no new lines.
Also what is the difference between function! and function?
Edit: before any of you ask, I need the random string to mark sections in my code so I can find them again by referencing my notes in my todo wiki.
Edit1. Take two. Trying to absorb the feedback from Luc. Without temp file (readfile() turned out to be not available in VIM 6.x I have on some systems).
:function InsertCmd( cmd )
: let l = system( a:cmd )
: let l = substitute(l, '\n$', '', '')
: exe "normal a".l
: redraw!
:endfunction
:imap <silent> <F5> <C-O>:call InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
:map <silent> <F5> :call InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
:put can't be used because it works line-wise. I replaced <Esc>...<Insert> with the all better <C-O>. I left redraw in, as it helps for the cases of called command produces output to the stderr.
Or using <C-R>=:
:function InsertCmd( cmd )
: let l = system( a:cmd )
: let l = substitute(l, '\n$', '', '')
: return l
:endfunction
:imap <silent> <F5> <C-R>=InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
Also what is the difference between function! and function?
Exclamation on the end of command most of the time means force to execute. (Looking in the :help is advised since different commands use ! differently, but VIM tries to document all forms of the commands.) In the case of the function it tells VIM to override previous definition of the function. E.g. if you put the code above into the func1.vim file, first time :source func1.vim would work fine, but the second time it would fail with error that function InsertCmd is already defined.
I did once before try to implement something similar here. I'm not good at VIM programming, thus it looks lame and the suggestion from Luc should take precedence.
Here it goes anyway:
:function InsertCmd( cmd )
: exe ':silent !'.a:cmd.' > /tmp/vim.insert.xxx 2>/dev/null'
: let l = readfile( '/tmp/vim.insert.xxx', '', 1 )
: exe "normal a".l[0]
: redraw!
:endfunction
:imap <silent> <F5> <Esc>:call InsertCmd( 'hostname' )<CR><Insert>
:map <silent> <F5> :call InsertCmd( 'hostname' )<CR>
Despite being lame, it works though.
You can trim/chomp the last newline with matchstr(), substitute, [:-2], etc
function s:GetMarker()
let res = system('echo $random `date` | md5sum | cut -d" " -f1')
" then either
let res = matchstr(res, '.*\ze\n')
" or
let res = res[:-2]
" or
let res = substitute(res, '\n$', '', '')
return res
endfunction
command! -nargs=0 GetMarker put=s:GetMarker()
Banging the function/command definition (with '!') will permit you to source the script where it is defined several times and thus to update the function/command you are maintaining without having to exit vim.
I was running into similar problems with trying to map a hotkey to insert the current date and time. I solved the newline problem by just including a <backspace>, but this still inserted newlines when I was indented (backspace would kill the last character, but when I was indented I got newline+tab and only the tab would go away).
So I did this -- just turned smartindent off, insert the string, then turn it back on:
imap <F5> <esc>:set nosmartindent<CR>a<C-R>=system('echo $random `date` \| md5sum \| cut -d" " - f1')<CR><Backspace><esc>:set smartindent<CR>a
...which works, but it gets un-indented if you're sitting on a new, auto-indented line. To get around that, insert a character to hold your place, then escape, turn off smartindent, get rid of the extra character, and do the rest:
imap <F5> x<esc>:set nosmartindent<CR>a<backspace><C-R>=system('echo $random `date` \| md5sum \| cut -d" " -f1')<CR><Backspace><esc>:set smartindent<CR>a
This seems to work.

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.

Resources