Vim: Quickly select rectangular blocks of text in visual-block mode - vim

I'm looking for a fast way to select a block of text in visual-block mode. I deal with files of this nature:
aaaa bbbb cccc
aaaa bbbb cccc
aaaa bbbb cccc
dddd Xeee ffff
dddd eeee ffff
dddd eeee ffff
gggg hhhh iiii
gggg hhhh iiii
gggg hhhh iiii
My goal is to select the middle block in visual-block mode. I would do:
Navigate to the corner (where the X is)
Ctrl-V
'e' to extend selection to the end of block
'jj' or '2j' to extend the selection downward to the bottom of the block.
I'm looking for an alternative to (4) that, similar to 'e', would move to the last row of the block. In this simple example 'jj' is not too inconvenient, but sometimes these are large blocks.
There's a similar question here , but that involves jumping a pre-determined number of lines. Is there a way to do this, again an analog to 'e', but moving row-wise instead of column-wise? Thanks!

Starting on the X, you could do this with <C-v>}kee:
<C-v> – start blockwise visual mode
} – go to the end of the paragraph (that motion supposedly provides the benefit of this rather involved combo)
k – one above to exclude the empty line
ee – move the cursor from the first column to the end of the inner block.

I had some fun trying to make a function "select Visual block around cursor".
function! ContiguousVBlock()
let [lnum, vcol] = [line('.'), virtcol('.')]
let [top, bottom] = [lnum, lnum]
while matchstr(getline(top-1), '\%'.vcol.'v.') =~# '\S'
let top -= 1
endwhile
while matchstr(getline(bottom+1), '\%'.vcol.'v.') =~# '\S'
let bottom += 1
endwhile
let lines = getline(top, bottom)
let [left, right] = [vcol, vcol]
while len(filter(map(copy(lines), 'matchstr(v:val,"\\%".(left-1)."v.")'),'v:val=~#"\\S"')) == len(lines)
let left -= 1
endwhile
while len(filter(map(copy(lines), 'matchstr(v:val,"\\%".(right+1)."v.")'),'v:val=~#"\\S"')) == len(lines)
let right += 1
endwhile
call setpos('.', [0, top, strlen(matchstr(lines[0], '^.*\%'.left.'v.')), 0])
execute "normal! \<C-V>"
call setpos('.', [0, bottom, strlen(matchstr(lines[-1], '^.*\%'.right.'v.')), 0])
endfunction
nnoremap <Leader>vb :<C-U>call ContiguousVBlock()<CR>
You can try it with <Leader>vb: It should select any contiguous non-whitespace rectangular block around the cursor. The vertical axis is preferred.
Maybe I'll improve it later, but for now you can try if it solves your problem, if you like.
As an alternative to my homegrown attempt, you could try the popular plugin textobj-word-column. It gives you text objects ac ic aC iC to select a column of words or WORDs.

Start visual mode with v. Then select inner paragraph with ip. Enter visual block mode with <C-v>. Now just go to the end of the block with es as required.
Starting from the bottom right of the block is the same thing, but instead of e, use w.

Related

Match any character (including whitespace) until the LAST bunch of whitespaces

I've got such text:
0000 10 [STUFF] Text ("TOTAL,SOME RANDOM TEXT") (558b6a68)
The first two column is pretty static. The third is optional. The last is optional and if exists, then always covered between parenthesis.
My issue is with the forth column, which can have spaces or actually any character inside (except newline of course).
My current regex looks like this:
^([a-fA-F0-9]{4,})\s+[a-fA-F0-9]+\s+(?:\[[^\]]*\]\s+)?
It matches all until the beginning of the fourth column.
Please note that space might exist anywhere, I can't define exact locations, like "always before parenthesis" or "may be between quotation marks".
I know for sure that this is the column before the last. So I'd like to capture them like this:
0000 10 [STUFF] Text("TOTAL,SOME RANDOM TEXT") (558b6a68)
^ ^ ^ ^ ^ ^
CAPTURE C A P T U R E C A P T U R E
I'd like to capture the texts marked between ^ ^ characters mentioned in the previous code block.
So, I'd like to grab any character UNTIL the last bunch of whitespace but also I don't want to include them into the final match group.
I hope I described it well :) Is it posssible with regex at all?
Here is some more sample text to test on:
0000 10 Text("TOTAL,SOME RANDOM TEXT") (1122aabb)
0010 5 D==1122aabb (1122aabb)
0015 17 Text("AND,SOME,MORE") (00000001)
002c 5 D==1 (1)
0031 1 !D (ccdd3344)
0032 5 D==ccdd3344 (ccdd3344)
0037 2 !1 (1)
0039 0 [AAAA] Fff
0039 1 [BBBB] Aaa
003a 6 N(05, eeff5566) (eeff5566)
0040 1 Qq
0041 2 $ab ([String]:"Unknown")
0043 f Call A/SomeFunc-X
0052 1 cd
I'd also start similar like your pattern with something like ^(\w+) +\w+ +(?:\[[^\]]+\] *)?
From here (start of 4th column) capture the first \S non white space followed by .*? lazily any amount of any character until an optional parenthesized part at the $ end can be captured. If not, the full line is consumed by group two.
^(\w+) +\w+ +(?:\[[^\]]+\] *)?(\S.*?)(?: +(\([^)]+\)))?$
See this demo at regex101
Feel free to adjust the parenthesis of the third group to only capture what's inside if needed.

Vim: unindent multiple lines to the beginning

Suppose I have something like this:
line 1 with text
line 2 with text
line 3 with text
line 4 with text
I want to unindent all of these lines to the beginning, like this:
line 1 with text
line 2 with text
line 3 with text
line 4 with text
Shift + V < gives me ONE level of un-indentation. How can I get them all to the beginning? Sorry, I'm having trouble phrasing this...
There are two different ways you could do this:
Visually select all of the lines, press <, and then press . as many times as you need until there is no indent left. Or if there are a specific number of lines you would like this on, you could do something like
5<< (unindent 5 lines)
<j (unindent this line and the next)
<ip (unindent inside this paragraph)
followed by as many . as you need.
Select all of the lines, and then type either :norm d^ or :s/^\s*
Also, Shift-V + V + < is basically the same as <<.

How to wrap each line in quotes in SublimeText?

Input:
boston beach summer figural yellow blue
boston floral flowers still still-life food pink figural
boston horse pink purple house flowers floral figural
Expected output:
"boston beach summer figural yellow blue"
"boston floral flowers still still-life food pink figural"
"boston horse pink purple house flowers floral figural"
The actual input file has 600+ lines, and I'm looking at a quick way to wrap each line in quotes? Does the method involve using multiple cursors? How about macros?
I would use a multiple cursors approach like this:
Windows
Ctrl + A (Select everything)
Ctrl + Shift + L (Split into lines)
End (Put the cursor at the end of the line)
" (Add the quote at the end of the line)
Home (Go to the first character of the line)
Home (Go to the beginning of the line... like if you have tabs or spaces)
" (Add the quote at the beginning of the line)
Mac
Cmd + A (Select everything)
Cmd + Shift + L (Split into lines)
Cmd + → (Put the cursor at the end of the line)
" (Add the quote at the end of the line)
Cmd + ← (Go to the first character of the line)
Cmd + ← (Go to the beginning of the line... like if you have tabs or spaces)
" (Add the quote at the beginning of the line)
Method 1:
no multiple cursors
+ best performance (use for large files)
- slightly clumsy
Replace (.*) with "\1"
Method 2:
multiple cursors
+ best in regards to usability/comfort
- slower for bigger files
- wont work if the file has empty lines
Ctrl+a, Ctrl+Shift+l, "
Method 3:
multiple cursors
+ close to #2 as usability, but works always
- slower for bigger files
Ctrl+a, Ctrl+Shift+l, End, ", Home, "
None of these worked in Sublime Text 3 for a multi-column TSV file with tab spacing.
I found this worked for column 1:
Find: ^\s*\S+
Replace: "$0"

Writing text blocks using nested iteration in Vim

Let's say I'd like to write a 5x5 text block, such as
aaaaa
aaaaa
aaaaa
aaaaa
aaaaa
And I want to do it using nested iteration.
In pseudocode it would look like
do five times ((do five times (type 'a')) change line)
So my first guess was to simply convert that as
5 ((5 (i a esc)) enter)
But I can't do that, because Vim doesn't support use of parentheses for specifying execution order. And simply typing
5 5 i a esc enter
will of course not work, since that will just produce a single line with 55 'a's and a newline.
So my question is: Is there a way to write text blocks using nested iteration in Vim? I know that there are other ways to write text blocks, but I want to know if this is possible, just out of curiosity.
You cannot do this directly, you need to use a register, expression, or macro:
qq5aa<Esc>a<CR><Esc>q4#q
qq - record macro
5aa<Esc> - insert 5 a's
a<CR><Esc> - insert line break
q4#q - stop recording, repeat 4 more times
I do not normally like one-liners, but this seems to work:
:for i in range(5) | for j in range(5) | execute 'normal ia' | endfor | execute "normal A\<CR>" | endfor
and this is a lot shorter:
:for i in range(5) | execute 'normal 5aa' | put='' | endfor
<esc> i a <esc> x 5p dd 5P
esc - switch to normal mode
i - switch to insert mode
a - print "a"
esc - switch to normal mode
x - deleta "a"
5p - paste a 5 times ("aaaaa")
dd - delete line "aaaaa"
5P - paste line "aaaaa" 5 times
:norm 5oaaaaa
is the simplest way I could think of to obtain a 5x5 matrix of a's but I don't think it satisfies your curiosity.
One could also do:
:norm Oaaaaa
5#:
but that's not really recursive either.
So… I don't know!

how to move a block or column of text

I have the following text as a simple case:
...
abc xxx 123 456
wer xxx 345 678676
...
what I need to move a block of text xxx to another location:
...
abc 123 xxx 456
wer 345 xxx 678676
...
I think I use visual mode to block a column of text, what are the other commands to move the block to another location?
You should use blockwise visual mode (Ctrl+v).
Then d to delete block, p or P to paste block.
Try the link.
Marking text (visual mode)
v - start visual mode, mark lines, then do command (such as y-yank)
V - start Linewise visual mode
o - move to other end of marked area
Ctrl+v - start visual block mode
O - move to Other corner of block
aw - mark a word
ab - a () block (with braces)
aB - a {} block (with brackets)
ib - inner () block
iB - inner {} block
Esc - exit visual mode
Visual commands
> - shift right
< - shift left
y - yank (copy) marked text
d - delete marked text
~ - switch case
Cut and Paste
yy - yank (copy) a line
2yy - yank 2 lines
yw - yank word
y$ - yank to end of line
p - put (paste) the clipboard after cursor
P - put (paste) before cursor
dd - delete (cut) a line
dw - delete (cut) the current word
x - delete (cut) current character
One of the few useful command I learned at the beginning of learning VIM is
:1,3 mo 5
This means move text line 1 through 3 to line 5.
In VIM, press Ctrl+V to go in Visual Block mode
Select the required columns with your arrow keys and press x to cut them in the buffer.
Move cursor to row 1 column 9 and press P (thats capital P) in command mode.
Press Ctrl+Shift+b to get in and out of it.
(source)
Using an external command "awk".
%!awk '{print $1,$3,$2,$4}' test.txt
With pure vim
:%s,\v(\w+) (\w+) (\w+) (\w+),\1 \3 \2 \4,g
Another vim solution using global command
:g/./normal wdwwP

Resources