vim - non rectangular visual block - vim

Is there a way to select the second column in the following code,
which turns out to be non rectangular.
I tried "CTRLv 3jE" , but that doesn't work.
int var_one = 1;
int var_two = 2;
int var_three = 3;
int var_very_long = 4;

You could use one of the Align plugins to align your column, select and copy it and afterwards undo the alignment (or leave it aligned)

Based on the comments, I think the way to go is writing a custom function that
passes the task to awk. It could be done with some regex also, splitting each
line on spaces, but awk should be easier. Here is my first try:
function! ExtractColToRegister(reg, ...) range
let input = join(getline(a:firstline, a:lastline), "\n")
if a:1 | let column = a:1
else | let column = 1 | endif
exec "let #". a:reg . " = system(\"awk '{ print $" .
\ column . " }'\", input)"
endfunction
You should have no trouble understanding it if you're already writing Vim
scripts :-) however let me know if some part of it is unclear, and if there is
something to improve as well.
Basically what the function does is saving a specific column to a register. If
you visually select the example code given in the question, and then:
:'<,'>call ExtractColToRegister("a", 2)
Register a will now contain:
var_one
var_two
var_three
var_very_long
And you can easily "ap somewhere else. Notice the column defaults to 1 if the
argument was omitted.
Creating a custom command "Column to Register" makes the process even nicier to
use outside of Vim scripts:
:command! -range -nargs=+ CTR <line1>,<line2>call ExtractColToRegister(<f-args>)

use the CopyMatches function from http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard
then select the lines and do something like
:'<,'>CopyMatches .*=

Related

Resize vim split based on number of visible lines

I am trying to write a function in my vimrc which lets me create a small split which looks as though it is inside the current file but is, in fact, a different file.
To do this cleanly, I want this split to appear immediately below the cursor which means I need to resize my splits appropriately. Unfortunately, I cannot find a way to measure the number of visible lines between my cursor and the top of the window. In files where I have folded code, the approach of using line(".") - line("w0") ("." gives line number of cursor; "w0" gives line number of topmost visible line) does not work since this calculation includes the hidden lines inside the folds. Does anybody know how this could be achieved?
Images for reference before and after inserting the split:
line(".") - line("w0")
gives you the number of physical lines between the top of the window and the current line. As you figured out, this method doesn't account for folds.
You can count the number of folds within a range with something like this:
function! CountFolds(top_line, bottom_line)
let folded_lines = []
for line_nr in range(a:top_line, a:bottom_line)
if foldlevel(line_nr) == 0
call add(folded_lines, 0)
else
call add(folded_lines, 1)
endif
endfor
return count(uniq(folded_lines), 1)
endfunction
and then remove it from your initial line count:
let top_line = line("w0")
let bottom_line = line(".")
let physical_lines = bottom_line - top_line
let screen_lines = physical_lines - CountFolds(top_line, bottom_line)
Reference:
:help range()
:help foldlevel()
:help add()
:help uniq()
:help count()
Note that you may need your script to account for soft-wrapped lines as well, a topic that is well worth another question.

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)

How to quickly edit determinate part of code inside different similar lines

I have this problem I'm adjusting a code I've made I have a structure like this:
Apple1 = Fruit("ss","ss",[0.1,0.4],'w')
PineApple = Fruit("ss","ss",[0.315,0.4],'w')
Banana = Fruit("ss","ss",[0.315,0.280],'w')
...
...
Instead of "ss"I would like to type further information like "Golden Delicious". For the moment I'm simply deleting "ss"clicking over it and then replacing it with the information I want to insert. I'm sure there is a faster way to do it, I've tried something with VIM macros but I can't figure out how to "Raw input" my data.
I've try simply to substitute it with Spyder, but is slow because I have to click substitute every time, with VIM for what I've try is the same.
Then I wonder how insert something else after 'w'...
This is an example of an final output only to understand better the question :
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit(" Cavendish banana","yellow",[0.315,0.280],'w')
...
...
I reformulate the question: which is the faster way to change "ss", for the moment I'm clicking over "ss" delate "ss" and write e.g "Golden Delicous" but is very slow. What I would like is that for every single ss the editor ask me to insert something to replace the single ss.
e.g. first ssin the fist line: I want to replace it typing something else e.g. "Golden Delicous" second ssin the first line I want to replace it typing somethingelse e.g. red. First ssin the second line I want to replace it with s.e. e.g. Ananas comosussecond ssin the second line I want to replace with s.e. e.g. green and so on.
I'm sure there is an answer for this somewhere but I can't find it!
Please if you down vote explain me why so I can improve it!
As far as I understand, the data that you want to substitute for "ss" does not have regular structure, so you will need to enter it by hand.
In Vim you would do it like this:
Place the cursor over the first "ss", then press * and then N.
Press ce, enter the new data (e.g. "Golden Delicious"), then leave Insert mode by pressing Escape.
Press n to jump to the next instance of "ss".
Repeat steps 2 and 3 ad libitum.
Look up :h * and :h n for more information.
I would do it like that:
:%s/ss/\=input('Replacement: ')/gc
This queries you for each occurrence. With the /c flag, the display is even updated during the loop (at the cost of having to additionally answer y for each occurrence); without the flag, you would need to keep track of where you are yourself.
You can use a function that searches the whole file substituting all "ss" strings with values from arrays populated with the replacement data:
function! ChangeSS()
let ss1 = ['Golden Delicous', 'Ananas comosus', 'Cavendish banana']
let ss2 = ['red', 'green', 'yellow']
call cursor(1, 1)
let l = "ss2"
while search('"ss"', 'W') > 0
if l == "ss1"
let l = "ss2"
else
let l = "ss1"
endif
execute 'normal ci"' . remove({l}, 0)
endwhile
endfunction
It uses a reference variable (l) that exchanges which array you want to extract data from. ss1 is for first appearance of "ss" in the line and ss2 for the second one.
Run it like:
:call ChangeSS()
That (in my test) yields:
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit("Cavendish banana","yellow",[0.315,0.280],'w')

How to repeatedly add text on both sides of a word in vim?

I have a bunch of local variable references in a Python script that I want to pull from a dictionary instead. So, I need to essentially change foo, bar, and others into env['foo'], env['bar'] and so on. Do I need to write a regular expression and match each variable name to transform, or is there a more direct approach that I could just repeat with the . command?
You can use a macro: type these commands in one go (with spacing just to insert comments)
" first move to start of the relevant word (ie via search)
qa " record macro into the a register.
ienv['<esc> " insert relevant piece
ea'] " move to end of word and insert relevant piece
q " stop recording
then, when you're on the next word, just hit #a to replay the macro (or even ## to repeat the last replay after that).
There's an easier way - you can use a regex search and replace. Go into cmdline mode by typing a colon and then run this command:
%s/\\(foo\|bar\|baz\\)/env['\1']/
Replacing foo, bar, and baz with whatever your actual variable names are. You can add as many additional variables as you'd like, just be sure to escape your OR pipes with a backslash. Hope that helps.
you could write a function that would do this pretty well, add this to your .vimrc file:
function! s:surround()
let word = expand("<cword>")
let command = "%s/".word."/env[\'".word."\']/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
This will surround every occurance of the word currently under the cursor.
If you wanted to specify what went before and after each instance you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
If you only want to confirm each instance of the variable you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/c"
execute command
endfunction
map cx :call <SID>surround()<CR>
I figured out one way to do what I need. Use q{0-9a-zA-Z"} to record key strokes into a buffer. Position the cursor at the begging of the variable name, then cw and type env['']. Next move the cursor back one space to the last quote and paste the buffer filled from the cw command with P. Finally, reuse the recording with #{0-9a-z".=*} for each variable.

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