Vim - Increase number without changing column width - vim

I'm dealing with a fixed-width file format and I need to increase all of the numbers in some columns. I have a simple macro that adds a value to a number, moves to the next line and repeats (like 2aj) However, these numbers start from 1 and usually end above 10000, so the column widths get messed up, e.g. (underscores as spaces, this example only covering the jump from 9 to 10)
FOO_7_BAR
FOO_8_BAR
FOO_9_BAR
becomes
FOO_9BAR
FOO_10_BAR
FOO_11_BAR
(note the new column of text that will break my program)
when I need
FOO_9_BAR
FOO10_BAR
FOO11_BAR
I have been manual going through and deleting a space from the first 9 columns, then 90, then 900, but I am looking for a more robust way to handle this without dealing with the first 10, 100, 1000, etc. with different macros or any manual input.
Thanks.

I come up with this way, I think the animation explains itself:
The final result is:
FOO 3 BAR
FOO 4 BAR
FOO 5 BAR
FOO 6 BAR
FOO 7 BAR
FOO 8 BAR
FOO 9 BAR
FOO10 BAR
FOO11 BAR

This requires a bit of manual hackery, but it's still better than manually deleting spaces.
You could also probably write a function that does this automagically, via Vimscript, though!
First, find the length of the shortest line. You can do this via ex. :echo col("$") on the shortest line.
Then, run the following command:
:g/.\{NUM\}/exec "norm! /[0-9]\<cr>X"
Replace NUM in the above command with the original number you got in the previous step.
Here's an explanation of how it works:
:g/.\{NUM\}/ Find lines that are too long
exec "norm! A common idiom: build a string to execute in normal mode
/[0-9]<cr> Find the first number on the line
X Delete the space before it (equivalent to "hx")
Then simply repeatedly run the same command (you can do this by pressing :UpReturn) until all the lines are the same length—it will result in an error once this is the case (since it won't find any matching lines).
Here's a short animation of the entire process:

This can be done with :s and a sub-replace expression.
:%s/\v([^0-9]*)(\d+)/\=strpart(submatch(1), 0, 5 - len(submatch(2))).submatch(2)
The idea is we capture the portion before the digits and the digits themselves. The replacement execute an vim expression via \= which put the two capture groups back together. However slice the first capture group via strpart() to a fixed width (5 in this example) minus the length of our second capture group, len(submatch(2)).
For more help see:
:h sub-replace-expression
:h strpart()
:h len()

Related

What is the difference between number command motion and command number motion

I'm learning vim with vimtutor. I was wondering if there is a difference between command motion number and number command motion.
For example:
2dw seems to me to work exactly like d2w, similarly 2dd does the same as d2d.
The two numbers are both called [count], in your example, indeed, they do same job. But the two counts come from different concept.
[count]command
this will do the command [count] times, 2dd does dd twice; 2dw does dw twice.
The second is from the {motion}, 2w, 2j etc.
If you want to see some differences, here are two I can think of:
Some commands don't support {motion}. For example, the X, you press 2X, will remove 2 characters before the cursor. However, you cannot do X{motion}. other commands that don't support {motion} p (paste), s etc. You can do 2p, 2s, but you cannot do p2w s3w
You get same result from 2dw and d2w, but the two 2 have different meaning, understanding what the number does is ok. you can combine the count and motion, like 2d3w.
The number command motion can use on all command, but number motion only in a few.
The most important is that repeat-action(.) redo the previous action.
Example:
2dd->. = 2dd->dd
d2d->. = d2d->d2d
Usually, I suggest use the first command. Because it is easier to be repeated.

Vim - Delete til last occurrence of character in line

I'm trying to figure out how to dt or df the last occurrence of a character in a string.
For example, let's say I have the following line:
foo not.relevant.text.bar
If I f df. I expectedly get foo relevant.text.bar but I would like to get foo bar. Using f 3df. is not an option as I don't know how many of that character will be in the string. Additionally, I may want to get foo .bar (f 3dt.), or if the line ends with a dot, I may want to get foo .. I want to always find the last one regardless of how many there are.
Is this possible without a regex? I suppose I could use a regex but I was hoping there was a simple vim command that I'm missing. I find myself trying to do something like this often.
one way without using regex, without counting "dot" (could be other letters)... see if others have better way..
foo[I]not.relevant.text.bar ([I] is cursor)
you could try:
lmm$T.d`m
or in this format, may look better?
lmm$T.d`m
this will do the job. you could create a mapping if you use that often.
EDIT
I add a GIF animation to show it works. :)
note
I typed #= in normal mode after moving my cursor to the right starting point (by f(space)), to display the keys I pressed in command line.
You can use my JumpToLastOccurrence plugin. It provides ,f / ,F / ,t / ,T commands that do just that.
I would use f df...
It is not necessarily shorter to type, but I find it easier to use "repeat last command" than counting in advance the number of word/sentence I want to delete.
Then you can adjust the number of . you type to adjust the length of the string you want to delete.
For your example: ET.dB
foo not.relevant.text.bar
And it works, as long as the cursor is anywhere within the text following "foo".
Strip Path from Path+Filename: ET/dB
I use it for stripping a pathname of all but the trailing filename.
Strip the path from /some/long/path/filename.ext leaving only the filename.
Just as long as:
The cursor is anywhere within the bold word
There are no spaces in that word
E Go to the end (since there are no spaces - also works if not the last thing on the line)
T/ Find the last / (stop just after it, so it will be deleted, as well)
dB Delete to the beginning of the word
In visual mode:
$F.d^
The $ goes to the end of the current line, F searches backward for a period and d^ deletes till the beginning of the line.

Tabular.vim : how to align on the first occurrence of 2 different delimiters placed at the beginning of Words?

I have installed the Tabular plugin, which works very well for me, as long as there are no complicated regexes involved…
But I have this list :
one #abc #rstuvw &foo
three #defg &bar
four #mn #opq &kludge &hack
twelve #hijkl &baz &quux
I wish to align it that way (on #… first, then on &…) :
one #abc #rstuvw &foo
three #defg &bar
four #mn #opq &kludge &hack
twelve #hijkl &baz &quux
which means I have 3 problems at the same time :
align on the first occurrence
of 2 different delimiters (# and &)
which are not really delimiters but "special characters" at the beginning of Words
This is far beyond my understanding of both regexes and Tabular.vim
How should I proceed ?
Align on the first occurrence
The help file explains this problem, you can use this command:
:Tabularize /^[^#]*\zs#/l1l0
A little explaination:
^ means the begin of the line
[^#]* match everything that isn't a #. The * means 0 or more times, as much as you can
\zs put the start of the regex here (everything from this point is matched)
# the 'this point' in the previous sentence means the # symbol
/l1l0 means align the 1st block to the left and add 1 space (l1) and align the 2nd block to the left and add 0 spaces (l0)
Align 2 different delimiters
You need to do this in 2 commands. To make your life easier you can name the pattern and use that name:
:AddTabularPattern f_at /^[^#]*\zs#/l1l0
:AddTabularPattern f_and /^[^&]*\zs&/l1l0
Now you can run
:Tabularize f_at
:Tabularize f_and
Map the commands
You can even map these methods to generate easy shortcuts. Read more about this here

Moving between lines in VIM

Let's say I have a file with N lines. I'm at line X and I'd like to move to line Y, where both X and Y are visible on screen. I can do that by typing :Y<cr>, but if Y>99 that's a lot of typing. I can also do abs(Y-X)[kj] (move up or down by abs(Y-X)), but for big X,Y computing this difference mentally isn't so easy.
Is there a way to exploit the fact, that both X,Y are visible on screen and move between X and Y fast?
You can :set relativenumber which does that Y-X computing for you (only in Vim >= 7.3).
You can use H, M or L to go the top, middle and bottom of the screen.
Perhaps you can make use of H, M, or L.
These keys jump the cursor to display lines:
H "Home" top of screen
M "Middle" middle of screen
L "Last" last line of screen
With a count, they offset: 4L would go to the third line above the last (1L is the same as just L).
Personally, I make heavy use of the m command to mark a line for navigation. From where I am now, hit mq to mark the position with label q; then navigate to another line, and ma to mark it with label a; and from then on I can hit 'q to jump to position q and 'a to jump to position a. (q and a are arbitrary; I use those mostly due to their position on a QWERTY keyboard.)
One you have the marks, you can use them for commands. To delete from the current position to the line marked with q, you just use: d'q
There is a variant, where instead of single quote you use back quote. This takes you to the exact position on the line where you placed the mark; the single quote uses the start of the line.
Those marks work even for ex (command line) commands. To limit search and replace to a specific set of lines, I mark the beginning and end lines respectively with labels b and e, and then do my search and replace like so:
:'b,'es/foo/bar/g
Dropping my dime in the pond:
I find that traversing code is exceptionally easy with text objects. I rarely do use jk/JK for larger jumps any more. Instead I navigate for whitespace lines using { and }
Since on any one screen there are usually only so-many whitespace delineations (and they are very easily visually recognized and counted), I find that e.g.
3}j
lands me on the intended line a lot more often than, e.g., a guesstimated
27j
To top it all, many 'brace-full' programming languages have opening braces at the start of functions. These can be reached with [[ resp. ]]. So sometimes it is just a matter of doing, e.g.
2[[}
(meaning: go to start of previous function, after the first contiguous block of lines)
My version of VIM lets you guestimate a number immediately before hitting J or K to go that many lines.
15K goes up 15 lines
The tougher vimmer you are becoming, the bigger amount of lines you can count at first glance.
Don't know, maybe there are some clever techniques, but I just type something like 17k/23j and so on.
also, searching some word on the string you want to jump works.
also, zz (center screen) is sometimes helpful in this cases.

Search for string and get count in vi editor

I want to search for a string and find the number of occurrences in a file using the vi editor.
THE way is
:%s/pattern//gn
You need the n flag. To count words use:
:%s/\i\+/&/gn
and a particular word:
:%s/the/&/gn
See count-items documentation section.
If you simply type in:
%s/pattern/pattern/g
then the status line will give you the number of matches in vi as well.
:%s/string/string/g
will give the answer.
(similar as Gustavo said, but additionally: )
For any previously search, you can do simply:
:%s///gn
A pattern is not needed, because it is already in the search-register (#/).
"%" - do s/ in the whole file
"g" - search global (with multiple hits in one line)
"n" - prevents any replacement of s/ -- nothing is deleted! nothing must be undone!
(see: :help s_flag for more informations)
(This way, it works perfectly with "Search for visually selected text", as described in vim-wikia tip171)
:g/xxxx/d
This will delete all the lines with pattern, and report how many deleted. Undo to get them back after.
Short answer:
:%s/string-to-be-searched//gn
For learning:
There are 3 modes in VI editor as below
: you are entering from Command to Command-line mode. Now, whatever you write after : is on CLI(Command Line Interface)
%s specifies all lines. Specifying the range as % means do substitution in the entire file. Syntax for all occurrences substitution is :%s/old-text/new-text/g
g specifies all occurrences in the line. With the g flag , you can make the whole line to be substituted. If this g flag is not used then only first occurrence in the line only will be substituted.
n specifies to output number of occurrences
//double slash represents omission of replacement text. Because we just want to find.
Once got the number of occurrences, you can Press N Key to see occurrences one-by-one.
For finding and counting in particular range of line number 1 to 10:
:1,10s/hello//gn
Please note, % for whole file is repleaced by , separated line numbers.
For finding and replacing in particular range of line number 1 to 10:
:1,10s/helo/hello/gn
use
:%s/pattern/\0/g
when pattern string is too long and you don't like to type it all again.
I suggest doing:
Search either with * to do a "bounded search" for what's under the cursor, or do a standard /pattern search.
Use :%s///gn to get the number of occurrences. Or you can use :%s///n to get the number of lines with occurrences.
** I really with I could find a plug-in that would giving messaging of "match N of N1 on N2 lines" with every search, but alas.
Note:
Don't be confused by the tricky wording of the output. The former command might give you something like 4 matches on 3 lines where the latter might give you 3 matches on 3 lines. While technically accurate, the latter is misleading and should say '3 lines match'. So, as you can see, there really is never any need to use the latter ('n' only) form. You get the same info, more clearly, and more by using the 'gn' form.

Resources