Case insensitive f key in vim? - vim

Does anyone know of any way to get the 'f' key in vim normal command mode to operate case insensitive
Example
function! OpenCurrentFileInMarked()
In the above line, if I am at the start of the line I want to be able to type 'fi'and get the first 'i' and then ';' to toggle to the capital 'I' in the middle of the function name. I would prefer that the 'f' key is always bound to case insensitivity. Would work much better for me as a default.

The easy answer here is: use a plugin. Others have had the idea before.
Fanf,ingTastic;: Find a char across lines
The main purpose of this plugin is to make f and t cross the line boundary.
To make them ignore case you need to let g:fanfingtastic_ignorecase = 1 in your vimrc.
ft_improved: improved f/t command
Same here.
To make it ignore case you again need to set a variable in your vimrc, this time let g:ft_improved_ignorecase = 1.

The first part of this (case insensitive f) is actually in the reference manual as an example of how to use the getchar() function:
This example redefines "f" to ignore case:
:nmap f :call FindChar()<CR>
:function FindChar()
: let c = nr2char(getchar())
: while col('.') < col('$') - 1
: normal l
: if getline('.')[col('.') - 1] ==? c
: break
: endif
: endwhile
:endfunction
See :help getchar().
You'll need to save the character returned and write a similar map for ; if you want that to work too.

You can't do this with regular vim commands. However, you can write your own function and bind the f key to it.

Related

In vim, how to split a word and flip the halves? FooBar => BarFoo

I sometimes write a multi-word identifier in one order, then decide the other order makes more sense. Sometimes there is a separator character, sometimes there is case boundary, and sometimes the separation is positional. For example:
$foobar becomes $barfoo
$FooBar becomes $BarFoo
$foo_bar becomes $bar_foo
How would I accomplish this in vim? I want to put my cursor on the word, hit a key combo that cuts the first half, then appends it to the end of the current word. Something like cw, but also yanking into the cut buffer and then appending to the current word (eg ea).
Nothing general and obvious comes to mind. This is more a novelty question than one of daily practical use, but preference is given to shortest answer with fewest plugins. (Hmm, like code golf for vim.)
You can use this function, it swaps any word of the form FooBar, foo_bar, or fooBar:
function! SwapWord()
" Swap the word under the cursor, ex:
" 'foo_bar' --> 'bar_foo',
" 'FooBar' --> 'BarFoo',
" 'fooBar' --> 'barFoo' (keeps case style)
let save_cursor = getcurpos()
let word = expand("<cword>")
let match_ = match(word, '_')
if match_ != -1
let repl = strpart(word, match_ + 1) . '_' . strpart(word, 0, match_)
else
let matchU = match(word, '\u', 1)
if matchU != -1
let was_lower = (match(word, '^\l') != -1)
if was_lower
let word = substitute(word, '^.', '\U\0', '')
endif
let repl = strpart(word, matchU) . strpart(word, 0, matchU)
if was_lower
let repl = substitute(repl, '^.', '\L\0', '')
endif
else
return
endif
endif
silent exe "normal ciw\<c-r>=repl\<cr>"
call setpos('.', save_cursor)
endf
Mapping example:
noremap <silent> gs :call SwapWord()<cr>
Are you talking about a single instance, globally across a file, or generically?
I would tend to just do a global search and replace, e.g.:
:1,$:s/$foobar/$barfoo/g
(for all lines, change $foobar to $barfoo, every instance on each line)
EDIT (single occurrence with cursor on the 'f'):
3xep
3xep (had some ~ in there before the re-edit of the question)
4xea_[ESC]px
Best I got for now. :)
nnoremap <Leader>s dwbP
Using Leader, s should now work.
dw : cut until the end of the word from cursor position
b : move cursor at the beginning of the word
P : paste the previously cut part at the front
It won't work for you last example though, you have to add another mapping to deal with _ .
(If you don't know what Leader is, see :help mapleader)

VIM: Replace [aeiou]' with the respective accented letter

I know that VIM support digraph, and it would be perfect if it's possible to use with :s command, but I can't find a way to use it!
I think something like:
:%s/\([aeiouAEIOU]\)'/\=digraph(submatch(1)."!")/g
Would be perfect, but I didn't find a digraph function.
Thanks in advance.
EDIT
Ok, after a bit of diggin in the built-in VIM's functions, I've found tr and a first solution to the problem:
:%s/\([aeiouAEIOU]\)'/\=tr(submatch(1), 'aeiouAEIOU', 'àèìòùÀÈÌÒÙ')/g
However, I still want to know if there's a way to use digraph in expressions :)
function! Digraph(letter, type)
silent exec "normal! :let l:s = '\<c-k>".a:letter.a:type."'\<cr>"
return l:s
endfunction
This function will allow you to generate any digraph you want.
It simulates typing <c-k><char><char> by running it with the normal command and assigning it to the local variable s. And then it returns s.
After this function is defined and you can use it like this.
:%s/\([aeiouAEIOU]\)'/\=Digraph(submatch(1), "!")/g
Note: This was based off of the source code for EasyDigraph
Here's another approach using a hand-coded vim function (add to your vimrc):
" get a matching digraph for a given ASCII character
function! GetDigraph(var1)
"incomplete dictionary of digraphs, add your own....
let DigDict = {'a': 'à', 'e': 'è', 'i': 'ì'}
"get the matching digraph. If no match, just return the given character
let DictEntry = get(DigDict, a:var1, a:var1)
return DictEntry
endfunction
Call it like this :%s/\([aeiouAEIOU]\)'/\=GetDigraph(submatch(1))/g

Mapping/macro to 'smartly' auto-create pairs of apostrophes in vim (and ignore contractions)

I'm currently using closepairs for my auto-closing needs, and it works pretty well. However, there is one caveat -- apostrophes. Don't get me wrong, I need apostrophes closed all the time. I don't want to just disable them. But whenever I type in plain text, whenever there are any contractions (I'm, Don't, Can't)...these apostrophes get made.
Now I could just type to delete them as soon as they can, but doing it every time is a bit impractical.
Does anyone know how I can possibly modify the closepairs script to only autoclose single quotes/apostrophes if they are the start of a word? That is, they are preceded by a whitespace character?
Here is the current code:
inoremap <expr> " <SID>pairquotes('"')
inoremap <expr> ' <SID>pairquotes("'")
function! s:pairquotes(pair)
let l:col = col('.')
let l:line = getline('.')
let l:chr = l:line[l:col-1]
if a:pair == l:chr
return "\<right>"
else
return a:pair.a:pair."\<left>"
endf
I don't know closepairs, but the AutoClose - Inserts matching bracket, paren, brace or quote plugin handles this well. You'll find a list of plugin alternatives on the Vim Tips Wiki.
Are you sure you want to autocomplete only after whitespace? In that case, something like function('string') would not autocomplete after the parenthesis.
Regardless, you can check the previous character against some regex. For example, to avoid autocompletion after letters:
function! s:pairquotes(pair)
let l:line = getline('.')
let l:col = col('.')
let l:chr = l:line[l:col - 1]
let l:prev = l:line[l:col - 2]
if l:chr == a:pair
return "\<right>"
elseif l:prev !~ "[A-Za-z]"
return a:pair . a:pair . "\<left>"
else
return a:pair
endif
endfunction
Note that there are exceptions even with this conservative example, like typing r'regex' in Python, so it might also make sense to define filetype-specific behavior.

CamelCase Expansion in Vim like Intellij Idea?

In Intellij Idea, there's a feature. Let's say I have used a variable myCamelCase somewhere in my code. Then if I type mCC and press Ctrl-Enter or some such key combination, it expands to myCamelCase. Is there something similar in Vim?
Okay, forgive me for answering twice, but since my first attempt missed the point, I'll have another go. This is more complicated than I thought, but possibly not as complicated as I have made it (!).
This is now modified to suggest all matching variable names.
First of all, here's a function to generate the 'mCC' abbreviation from the 'myCamelCase' string:
function! Camel_Initials(camel)
let first_char = matchstr(a:camel,"^.")
let other_char = substitute(a:camel,"\\U","","g")
return first_char . other_char
endfunction
Now, here's a function that takes an abbreviation ('mCC') and scans the current buffer (backwards from the current line) for "words" that have this abbreviation. A list of all matches is returned:
function! Expand_Camel_Initials(abbrev)
let winview=winsaveview()
let candidate=a:abbrev
let matches=[]
try
let resline = line(".")
while resline >= 1
let sstr = '\<' . matchstr(a:abbrev,"^.") . '[a-zA-Z]*\>'
keepjumps let resline=search(sstr,"bW")
let candidate=expand("<cword>")
if candidate != a:abbrev && Camel_Initials(candidate) == a:abbrev
call add( matches, candidate )
endif
endwhile
finally
call winrestview(winview)
if len(matches) == 0
echo "No expansion found"
endif
return sort(candidate)
endtry
endfunction
Next, here's a custom-completion function that reads the word under the cursor and suggests the matches returned by the above functions:
function! Camel_Complete( findstart, base )
if a:findstart
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '[A-Za-z_]'
let start -= 1
endwhile
return start
else
return Expand_Camel_Initials( a:base )
endif
endfunction
To make use of this, you must define the "completefunc":
setlocal completefunc=Camel_Complete
To use insert-mode completion, type CTRL-X CTRL-U, but I usually map this to CTRL-L:
inoremap <c-l> <c-x><c-u>
With this code in your vimrc you should find that typing mCC followed by CTRL-L will make the expected replacement. If no matching expansion is found, the abbreviation is unchanged.
The code isn't water-tight, but it works in all the simple cases I tested. Hope it helps. Let me know if anything needs elucidating.
There is a plugin for this in Vim called vim-abolish. Use the map crc to expand to camel case.

What is the easiest way to swap occurrences of two strings in Vim?

What is the easiest way to replace all occurrences of string_a with string_b while at the same time changing anything that was already string_b into string_a? My current method is as follows:
:s/string_a/string_c/g
:s/string_b/string_a/g
:s/string_c/string_b/g
Although this works, it requires extra typing and seems inefficient. Does anybody know of a better way to do this?
I'd do it like this:
:%s/\v(foo|bar)/\={'foo':'bar','bar':'foo'}[submatch(0)]/g
But that's too much typing, so I'd do this:
function! Mirror(dict)
for [key, value] in items(a:dict)
let a:dict[value] = key
endfor
return a:dict
endfunction
function! S(number)
return submatch(a:number)
endfunction
:%s/\v(foo|bar)/\=Mirror({'foo':'bar'})[S(0)]/g
But that still requires typing foo and bar twice, so I'd do something like this:
function! SwapWords(dict, ...)
let words = keys(a:dict) + values(a:dict)
let words = map(words, 'escape(v:val, "|")')
if(a:0 == 1)
let delimiter = a:1
else
let delimiter = '/'
endif
let pattern = '\v(' . join(words, '|') . ')'
exe '%s' . delimiter . pattern . delimiter
\ . '\=' . string(Mirror(a:dict)) . '[S(0)]'
\ . delimiter . 'g'
endfunction
:call SwapWords({'foo':'bar'})
If one of your words contains a /, you have to pass in a delimiter which you know none of your words contains, .e.g
:call SwapWords({'foo/bar':'foo/baz'}, '#')
This also has the benefit of being able to swap multiple pairs of words at once.
:call SwapWords({'foo':'bar', 'baz':'quux'})
You can do this easily with Tim Pope's Abolish plugin
:%S/{transmit,receive}/{receive,transmit}
Here is how I swap two words skip & limit:
%s/skip/xxxxx/g | %s/limit/skip/g | %s/xxxxx/limit/g
Pretty sure someone could turn it into a shorter command which accepts two arguments.
The swapstrings plugin provides a handy command for this:
:SwapStrings string_a string_b
You can do it with a single command as shown in my code below:
:%s/\<\(string_a\|string_b\)\>/\=strpart("string_bstring_a", 8 * ("string_b" == submatch(0)), 8)/g

Resources