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

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

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 it possible to use vimgrep for a visual selection of the current file?

I would like to search with vimgrep only within a visual selection of the current file and not the whole file. Is that possible and how? I couldn't find something for this case with Google or in vim help.
The reason why I want this is because I need the result in the quicklist (copen) and :g/FOO which is showing the matching lines at the bottom is not doing this job.
Yes, you can, as Vim has special regular expression atoms for mark positions, and the start and end of the visual selection is marked by '< and '>. As there are atoms for on / before / after a mark, we need to combine those to cover the entire range of selected lines:
On the selection start | after the selection start and before the selection end | on the selection end.
To limit the search to the current file, the special % keyword is used.
:vimgrep/\%(\%'<\|\%>'<\%<'>\|\%'>\)FOO/ %
You are on the right path with using :g command. The basic idea is do something like this:
:g/FOO/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
Now lets make it a command
command! -range -nargs=+ VisualSeach cgetexpr []|<line1>,<line2>g/<args>/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
Now you can do :VisualSearch FOO and it will add the searches to the quickfix list.
Note that the issue w/ this is only finds one match per line.
This is from Steve Losh's vimrc. It makes * work on the visual selection. I've gotten pretty dependent on it.
" Visual Mode */# from Scrooloose {{{
function! s:VSetSearch()
let temp = ##
norm! gvy
let #/ = '\V' . substitute(escape(##, '\'), '\n', '\\n', 'g')
let ## = temp
endfunction
vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR><c-o>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR><c-o>

How can I CamelCase-enable Vim search?

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.

Using backslashes in vim abbreviations

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.

Resources