How can I CamelCase-enable Vim search? - vim

Is there a Vim plugin, script, or function out there that allows Vim search to be extended in order to match camel-cased words when I type its capital letters in succession?
Here is an example to clarify. Let’s say I am looking for WordInQuestion. I would like to be able to just type /wiq in order to find it.
As an added bonus, it would be nice if I could find getWordInQuestion by typing /gwiq which means the first letter of the word I am looking for may be lower case.

The described functionality can be easily implemented by means of Vimscript.
Let us consider the following custom mappings.
nnoremap <expr> <leader>/ SearchCamelCase('/')
nnoremap <expr> <leader>? SearchCamelCase('?')
function! SearchCamelCase(dir)
call inputsave()
let ab = input(a:dir)
call inputrestore()
let l = filter(split(toupper(ab), '\zs'), 'v:val =~ "\\w"')
if len(l) > 0
let l[0] = '[' . l[0] . tolower(l[0]) . ']'
end
let #/ = '\C\<' . join(map(l, 'v:val . "[0-9a-z_]*"'), '') . '\>'
return a:dir . "\r"
endfunction

There is a nice plugin called fuzzy finder that may be useful.
http://www.vim.org/scripts/script.php?script_id=1984
FuzzyFinder provides convenient ways to quickly reach the
buffer/file/command/bookmark/tag you want. FuzzyFinder searches with a
fuzzy/partial pattern such as camel case.

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 do I use :iabbr with backslash followed by two or more letters as abbreviation in vim? [duplicate]

I want to be able to write \bit and have it expand to something in vim. How do I encode a backslash in the left-hand side of an abbreviation, though?
I tried all of these:
:iab \bit replacement_text
:iab <Bslash>bit replacement_text
:iab <bs>bit replacement_text
but got E474: Invalid argument for all of these.
The map_backslash help-topic suggests <Bslash>, but this doesn't seem to work.
You can define your abbreviation on "bit", and then test if it is preceded by "", if so, return the new text, or "bit" otherwise.
function! s:Expr(default, repl) expr
if getline('.')[col('.')-2]=='\'
return "\<bs>".a:repl
else
return a:default
endif
endfunction
:inoreab bit <c-r>=<sid>Expr('bit', 'foobar')<cr>
That's the kind of tricks I used in MapNoContext().
EDIT: see :h abbreviations for the reasons why what you asked can't be achieved directly.
EDIT2: It can be easily encapsulated this way:
function! s:DefIab(nore, ...) abort
let opt = ''
let i = 0
while i != len(a:000)
let arg = a:000[i]
if arg !~? '<buffer>\|<silent>'
break
endif
let opt .= ' '.arg
let i += 1
endwhile
if i+2 != len(a:000)
throw "Invalid number of arguments"
endif
let lhs = a:000[i]
let rhs = a:000[i+1]
exe 'i'.a:nore.'ab'.opt.' '.lhs.' <c-r>=<sid>Expr('.string(lhs).', '.string(rhs).')<cr>'
endfunction
command! -nargs=+ InoreabBSlash call s:DefIab('nore', <f-args>)
And used with a simple:
InoreabBSlash <buffer> locbit foobar
or
InoreabBSlash bit foobar
I suggest using backslash on both sides, vim is happy that way:
inoreabbr \bit\ replacement_text
Note that I am using the "nore" version of abbr, better to be clear if you don't intend a recursive expansion. I have been using the below abbreviations for a long time and they work great:
inoreabbr \time\ <C-R>=strftime("%d-%b-%Y # %H:%M")<CR>
inoreabbr \date\ <C-R>=strftime("%d-%b-%Y")<CR>
:set iskeyword+=\
in vimrc_tex (or just vimrc) works perfectly.
you could
inoremap \bit replacementtext
Also if you dont like the lag an alternative leader like backtick ` (above the tab for me)
:iab `f foobar
if you are not using them in your code often
You can only use a backslash as a prefix for an abbreviation if it's only got a single character following it, so :iab \b replacementtext will work.

Is there a way to use abbreviation in vim that ignore special characters?

I'm writing an SQL database script, and I use iab to upper case special words such as DATABASE or TABLE. I have three questions:
Is there a better tool to use? I prefer to avoid using a plugin as they usually come with functionality that bothers me.
Is there a way to make iab be case insensitive? I mean that it will correct both set and Set into SET?
Is there a way to make iab work when there are special or certain characters following/preceding the word? For example, I want exec and exec( to be changed into EXEC and EXEC( respectively. I can create two entries, but the question is if I can do this in a single line.
Better ways: No, short of a plugin I can't see what else would be as useful as abbreviations in this context. I note that sql_iabbr.vim is at least easy to read, and doesn't seem to contain much in the way of hidden gotchas. It's essentially a whole bunch of this:
inoreabbr <silent> <buffer> select <C-R>=SqlIab_ReplaceConditionally('select', 'SELECT')<CR>
with the following function:
function! SqlIab_ReplaceConditionally(original, replacement)
" only replace outside of comments or strings (which map to constant)
let elesyn = synIDtrans(synID(line("."), col(".") - 1, 0))
if elesyn != hlID('Comment') && elesyn != hlID('Constant')
let word = a:replacement
else
let word = a:original
endif
let g:UndoBuffer = a:original
return word
endfunction
I think it's also worth pointing out Abolish for more powerful abbreviations.
Case insensitive: again, I think the answer is 'No' (based on a reasonably thorough check of the help.
Special/certain characters: sorry, if you need that done in one abbreviation I think you're out of luck again!
So many questions:
Is there a better tool to use? I prefer to avoid using a plugin as they usually come with functionality that bothers me.
Tim Pope's Abolish.vim is the closest plugin which fits your needs. However with the more advanced expansions I feel like it too will fall short. I am not sure about your past plugin experiences, but Tim's plugins are usually well behaving and narrowly focused.
Is there a way to make iab be case insensitive? I mean that it will correct both set and Set into SET?
No, there is no native way that I am aware of. I would recommend using Abolish if you do this regularly and have simpler expansions. Or go all the way and use a snippet plugin.
Is there a way to make iab work when there are special or certain characters following/preceding the word? For example, I want exec and exec( to be changed into EXEC and EXEC( respectively. I can create two entries, but the question is if I can do this in a single line.
This can be done with a fancier expansion. It is in fact very similar to what Rails.vim does with it's abbreviations.
Add the following to your ~/.vim/after/ftplugin/sql.vim:
function! s:selective_expand(root, good, ...)
let [pat, extra; _] = a:0 ? a:000 : ['', '']
let c = nr2char(getchar(0))
if c == "" || c == "\t"
return a:good
elseif c =~ '\s'
return a:good . c
elseif pat != '' && c =~# pat
return a:good . extra
else
return a:root . c
endif
endfunction
function! s:paren_expand(root, good)
return s:selective_expand(a:root, a:good, '[(]', "()\<left>")
endfunction
function! s:sql_expand(root, ...)
let good = a:0 ? a:1 : toupper(a:root)
let good = substitute(good, '[\"|]', '\\&', "g")
let good = substitute(good, '<', '\\<lt>', "g")
let f = 'selective_expand'
let root = a:root
if root =~ '($' && (!a:0 || good !~ '($')
let root = substitute(root, '($', '', '')
let good = substitute(good, '($', '', '')
let f = 'paren_expand'
endif
let root = substitute(root, '[\"|]', '\\&', "g")
let root = substitute(root, '<', '\\<lt>', "g")
let roots = [root]
if root !~# '\u' && root =~# '^\l'
call add(roots, substitute(root, '^\l', '\u&', ''))
endif
for r in roots
execute "iabbr <buffer> " . r . " <c-r>=<SID>" . f . "(\"" . r . "\", \"" . good . "\")<cr>"
endfor
endfunction
command! -nargs=* Sqlabbrev call <SID>sql_expand(<f-args>)
Now you can create abbreviations in your ~/.vim/after/sql.vim file like so:
Sqlabbrev select
Sqlabbrev join INNER\ JOIN
Sqlabbrev exec(
Sqlabbrev Takes 2 arguments similar to iabbrev. However behaves slightly differently:
Abbreviations will always be local to the buffer
If only 1 argument then the expansion will be the same as the first argument but made uppercase
Using <tab>/<c-]> will make the expansion without any following spaces
Using abbreviation followed by space will expand and add space
If the abbreviation ends with ( then expand with ending of () and place cursor inside the parens
You must escape spaces via \ or <space>. Can use keycodes
Will create some case expansions if no uppercase letter is found in abbrevation. e.g. Sqlabbrev select will expand select and Select.

Understand :iabbrev <buffer> iff if:<left>

I typed :autocmd FileType python :iabbrev <buffer> iff if:<left> as this tutorial told.
The output was
if :
Why is there a space between if and ":"?
I assume you're using the space bar after you type iff? If so, it's because of the <left>. This is positioning your cursor one to the left, i.e. between the f and the ":". Once the space bar is accepted your cursor is in between the two characters so it puts a space between them. You can try the command without the <left> and see if that does what you need. If not, you'll need to let us know exactly what output you're looking for us to be able to help you. Also see: :help abbrev if you haven't already.
Abbreviation's are triggered by non-keyword (e.g. ., <cr>, <space>, etc), <esc>, or <c-]>. Typing iff alone will is not enough to expand the abbreviation. You typed iff<space> which is enough to expand the abbreviation and puts the <space> inside your expanded abbreviation. You can use <c-]> to expand abbreviations without inserting any extra characters. e.g. iff<c-]>
Eatchar
I however find using <c-]> to be unappealing. Vim's documentation gives us an alternative, the Eatchar function. This function will consume a key matching some pattern and not output it.
function! Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunction
iabbr <buffer> iff if:<left><c-r>=Eatchar('\s')<cr>
Rails.vim like abbreviations
You can take this even further and make Rails.vim-esque abbreviations which only expand on <tab> or a supplied pattern. Think of these as lightweight snippets.
function! RailsExpand(root, good, ...)
let c = nr2char(getchar(0))
if c == "" || c =~ (a:0 ? a:1 : "\t")
return a:good
else
return a:root . c
endif
endfunction
iabbr <buffer> iff <c-r>=RailsExpand('iff', "if:\<left>")<cr>
Now iff<tab> will expand properly. However defining abbreviations like this is a mess.
function! Railsabbrev(root, good)
let good = substitute(a:good, '[\"|]', '\\&', "g")
let good = substitute(good, '<', '\\<lt>', "g")
let root = substitute(a:root, '[\"|]', '\\&', "g")
let root = substitute(root, '<', '\\<lt>', "g")
execute "iabbr <buffer> " . a:root . " <c-r>=RailsExpand(\"" . root . "\", \"" . good . "\")<cr>"
endfunction
command! -nargs=* Railsabbrev call Railsabbrev(<f-args>)
Now you can use :Railsabbrev to define your <tab> expanding abbreviation. Example:
Railsabbrev iff if:<left>
Snippets
Sometimes abbreviations are just too simple or too tricky to maintain for multiline expansions. If this is the case I suggest you look for a good snippet plugin. Good choices are UltiSnips or vim-snipmate. Look at their documentation on how to expand and create your own snippets.
More help
:h Abbreviations
:helpg Eatchar

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>

Resources