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!
Related
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 <<.
This question already has answers here:
VIM: Finding and replacing first N occurrences of a word
(4 answers)
Closed 4 years ago.
Suppose, I have
type,type,type,type,description,description,description.
type,type,type,type,description,description.
I want to -
type|type|type|type|description,description,description.
type|type|type|type|description,description.
Where I have no I idea about the content of the file. Just I know that I have to substitute first n occurrence of , with | for each line.
Nothing sofisticated but it get's the job done
:%norm f,r|;.;.;.
breakdown
:%norm start a normal command on all lines
f,r| f(ind) a ',' and r(eplace) with '|'
;.;.;. ';' jumps to next match and '.' repeats the change
I guess it could solve your problem
:%s/\v(type)./\1|/g
\v ......... very magic to avoid "\"
(type) ..... group 1
. ........ any char (in our case ,)
\1 ......... back reference to group 1
| .......... literal |
Use a macro.
Create a macro. Repeat it on all lines. Done.
In vim, in a Windows machine (with no access to "unix"-like commands such command column) I want to reformat this code to make it more readable:
COLUMN KEY_ID FORMAT 9999999999
COLUMN VALUE_1 FORMAT 99
COLUMN VALUE_2 FORMAT 99
COLUMN VALUE_3 FORMAT 999
COLUMN VALUE_4 FORMAT 999
And I want to have this using as less commands as possible:
COLUMN KEY_ID FORMAT 9999999999
COLUMN VALUE_1 FORMAT 99
COLUMN VALUE_2 FORMAT 99
COLUMN VALUE_3 FORMAT 999
COLUMN VALUE_4 FORMAT 999
Note this is just an excerpt, as there many more lines in which I must do the same.
You could use the following command:
:%s/\w\zs\s*\zeFORMAT/^I
The pattern will match the whitespaces between FORMAT and the end of the previous word and replace it by a tab:
\w Any 'word' character
\zs Start the matching
\s* Any number of whitespace
\ze End the matching
FORMAT The actual word format
\zs and \ze allow to apply the substitution only on the whitespaces see: :h /\zs and :h /\ze
Note that ^I should be inserted with ctrl+vtab
The tabular plugin recommended by #SatoKatsura would be a good way to do it too.
You can also generalize that. Let's say you have the following file:
COLUMN KEY_ID FORMAT 9999999999
COLUMN VALUE_1 FOO 99
COLUMN VALUE_2 BAR 99
You could use this command:
:%s/^\(\w*\s\)\{1}\w*\zs\s*\ze/
Were the pattern can be detailed like that:
^ Match the beginning of the line
\(\w*\s\)\{1} One occurrence of the pattern \w*\s i.e. one column
\w* Another column
\zs\s*\ze The whitespaces after the previous column
You could change the value of \{1} to apply the command on the next columns.
EDIT to answer #aturegano comment, here is a way to align the column to another one:
%s/^\(\w*\s\)\{1}\w*\zs\s*\ze/\=repeat(' ', 30-matchstrpos(getline('.'), submatch(0))[1])
The idea is still to match the whitespaces which must be aligned, on the second part of the substitution command we use a sub-replace-expression (See :h sub-replace-expression).
This allows us to use a command from the substitution part, which can be explained like this:
\= Interpret the next characters as a command
repeat(' ', XX) Replace the match with XX whitespaces
XX is decomposed like this:
30- 30 less the next expression
matchstrpos()[1] Returns the columns where the second argument appears in the first one
getline('.') The current line (i.e. the one containing the match
submatch(0) The matched string
[1] Necessary since matchstrpos() returns a list:
[matchedString, StartPosition, EndPosition]
and we are looking for the second value.
You then simply have to replace 30 by the column where you want to move your next column.
See :h matchstrpos(), :h getline() and :h submatch()
For alignment, there are three well-known plugins:
the venerable Align - Help folks to align text, eqns, declarations, tables, etc
the modern tabular
the contender vim-easy-align
Posting an answer as requested:
:g/^COLUMN / s/.*/\=call('printf', ['%s %-30s %s %s'] + split(submatch(0)))/
Explanation:
g/^COLUMN / - apply the following command to lines matching /^COLUMN / (cf. :h :global)
\= - replace with the result of evaluating an expression, rather than with a fixed string (cf. :h s/\=)
submatch(0) - the line being matched
split(...) - split line into words
printf(...) - format the line
call(...) - we'd like to have printf('%s %-30s %s %s', list), but printf() doesn't take "real" lists as arguments, so we have to unfold the list with a call(...) (cf. :h call()).
Yet another solution:
:%s/ \{2,}/ /g
This solution is not perfect because the result will have an extra single space on the first line. To fix this problem:
:%s/\%>15c \{2,}/ /g
Explanation of pattern:
%>15c\s\{2,}
%>15c Matches only after column 15
\s\{2,} Matches two or more white spaces
I'm working with a large text file and need to be able delete lines based on the value of the 25th character on the line, i.e. if it is equal to H, K or Z. Is this possible, either just by matching one of the letters and running 3 commands or (even better) by all 3 in one command? Any help greatly appreciated!
You can use global to find a regex and then execute a command on the line that regex was found.
In this case it looks for any character 24 times from the beginning of the line and if the character after it matches H, K, or Z delete that line. (d at the end of the command stands for delete).
:g/^.\{24\}[HKZ]/d
Edit: as Peter Ricker points out \%25c would also work.
:g/\%25c[HKZ]/d
\%25c matches the 25th column then preforms the regex from there.
You could also use \%v if you wanted to match virtual columns instead.
You can try following ex command:
:if match( "HKZ", strpart( getline("."), 24, 1) ) != -1 | delete | endif
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