Yank column number in Vim - vim

I'd like to be able to easily make pretty code titles in vim, by making a macro. I'd like them to like something like this:
################################################################################
### Preamble ###
################################################################################
To make these I'd like to start with a line with just:
Preamble
Then the macro will make the surrounding hashes and spaces. To do this, I need to somehow yank the number for characters in the title. So in the case of preamble; I'd like to copy 8, its length, to some register.
Any ideas how to do this?

Instead of counting the length, I would paste using Replace mode, as #FDinoff said. First, yank the following line into a register, for example the t register: "tyy
### ###
Next, grab the Preamble line without the endline: 0vg_d
Then, paste our line from t and move to the appropriate spot: "tP4l
Finally, paste Preamble using Replace mode: RCtrl+r"

I would not recommend this style, as it's maintenance requires high(er) effort (and not everybody is using a powerful editor like Vim, or has your macros), but you can do this with the strdisplaywidth() function:
:echo strdisplaywidth(getline('.'))
Older Vim versions don't have this; strlen() is a replacement that will only handle normal ASCII letters.
Oh, and before you ask, you can create the header lines with repeat('#', num)

Related

Vim advanced multiline edits

I'm trying to getting into using more advanced vim features.
How would people go about for the following edit?
from this:
ssn=token_payload.fnr,
fname=token_payload.displayName,
email=token_payload.email,
login=token_payload.username,
to this:
ssn=token_payload['fnr'],
fname=token_payload['displayName'],
email=token_payload['email'],
login=token_payload['username'],
Command line :norm command
I would apply the following normal commands to all lines in the file:
" note that in the real command, <Esc> would be a literal
" press of the escape key (see explanation below)
:%norm f.s['<Esc>f,i']
apply to the whole file: %
the following normal mode commands:norm
move to the period: f.
substitute with opening square bracket and quote: s['
escape insert mode (press ctrl+v to enter a literal character, then escape -
you'll see a gray symbol appear): ^[
move to the comma: f,
insert the quote and closing square bracket: i']
I started using the command line way instead of macros recently since I find
that you can think it over more easily (particularly if you compose the command
in the command buffer with q: - see :help command-buffer).
Use a macro
Another way is to record a macro:
qa0f.s['<Esc>f,i']<Esc>jq
Which you can then deploy on the current line with #a and repeat with ##.
Or use :%norm #a to run the macro on each line.
It's basically the same as above, but instead of :%norm you use qa to
record into the a register (you can use any letter). Then perform the edit. I
added a drop down one line with j before stopping the recording with q.
You can edit the macro after recording it by pasting the contents of the
register ("ap), edit them, and yank them back ("ay$) before replaying it.
Using an external tool
If I wanted to perform multiple substitutions with a single command, I would
filter the text through an external program like sed:
:%!sed "s/\./['/; s/,$/'],/"
One more g[ood] thing
An extremely powerful tool is the :g[lobal] command! (see :help :g) I've
been using it a lot in combination with the norm command. For example, if I
wanted to get all the paragraphs in a document formatted nicely, but not affect
indented text (which could be code blocks, or tables etc.) I would do:
:%g/^\w/norm gqap
This means, for any line with a letter at the very start of the line, apply the
command gqap which applies the normal mode command gq to 'a paragraph'.
You might also want to capitalise the first word and increase the header level
of all the markdown headings like so:
:%g/^#/norm w~I#
This would change this:
# a heading
some text.
## another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
## a further heading
some text
# conclusion
into this:
## A heading
some text.
### Another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
### A further heading
some text
## Conclusion
see these videos for 'advanced' vim stuff
I'd implement this as an :s command. For example, this command would make the requested changes:
:%s/\.\(.*\),/['\1'],/
That operates on all lines %, matches the dot and comma and puts everything in between into a group (\(.*\)), and then replaces it with the desired value, matching the first group (\1).
If you want to operate on a different set of lines, you can write :1,4 instead of :%, or write :'<,'> to operate on the visual selection.

Editing multiple lines of same parameter

I'm working with a large dataset and multiple lines need change on a certain parameter. Something like this for example:
funct(35, circle, square, triangle);
funct(42, sphere, cube, prism);
funct(74, disc, rhombus, rectangle);
needs to become:
funct(35, circle, my_square, other_triangle);
funct(42, sphere, my_cube, other_prism);
funct(74, disc, my_rhombus, other_rectangle);
How could I go about doing this?
An additional vim approach. Visually select all of the lines you would like to change, and then type this:
:norm 2Wimy_<C-v><esc>Wiother_
Note that <C-v> and <esc> are ctrl-v and esc, not text. Or, if you want to do this on every line, do this:
:%norm 2Wimy_<C-v><esc>Wiother_
You could also do this on lines in a certain range. For example, lines 3-100:
:3,100norm 2Wimy_<C-v><esc>Wiother_
I'll speak about emacs because I just love emacs !
to enter commands in the prompt : Alt + x
For small datasets
You can install the multiple-cursors package. It's like the Ctrl+D in SublimeText. Here is the doc.
For big datasets with some controls
You can use built-in package query-replace. It will replace the string/regex you want with another. Documentation.
The advantage of this one is that you can choose if you want to replace or not each occurence.
For big datasets without control
The built-in packages replace-string and replace-regexp will replace the pattern of your choice with a string or another pattern.
replace-regexp
replace-string
If you just want to rename some variables, multiples-cursors, replace-string and query-replace are fine.
If you want to replace patterns in huge datasets, replace-regexp is fun, but nothing's better than a script (bash, js, python ...).
There are amazing books about sed and awk bash commands (sed & awk, 2nd Edition)
With Vim:
:%s/\vfunct\(([^,]+,\s*){2}\zs/my_/
The 2 above is the number of arguments you want to skip.
The catch: this breaks if some parameters are themselves function calls. There are plugins that allows you to cope with that situation.
Using vim's regex engine:
:%s/\v(%(\s*\w+,){PARAMETER_NUMBER})(\s+)(\w+)/\1\2NEW_PREFIX\3
Substitute PARAMETER_NUMBER with the index (starting from 0) of the parameter you'd like to change and NEW_PREFIX with the prefix you'd like to add to it.
For instance, running:
:%s/\v(%(\s*\w+,){2})(\s+)(\w+)/\1\2my_\3
Changes your example code into:
funct(35, circle, my_square, triangle);
funct(42, sphere, my_cube, prism);
funct(74, disc, my_rhombus, rectangle);
You can modify this to customize it in other ways by changing the order of the backreferences (the \1, \2 and \3) at the end of the command. \1 refers to the parameters before the one you're changing, \2 refers to the whitespace just before the parameter you're changing and \3 to the parameter itself.
I'm aware of multiple-cursors, and they look really cool, but I've been to lazy to learn them. Here's a solution using only Emacs built ins.
(defun align-regexp-comma (beginning end)
"Align columns using comma as a delimiter."
(interactive "*r")
(align-regexp beginning end
",\\(\\s-*\\)" 1 1 t))
(global-set-key (kbd "C-<return>") 'cua-rectangle-mark-mode)
Then select the region you of interest, do M-x align-regexp-comma, which should align the arguments, then you can use C-<return> to start the rectangle mark, which lets you edit several lines at once. If you then want to remove the aligning spaces, use query-regexp-replace (M-%) with ", +" -> ", ".

reformat in vim for a nice column layout

I have this dataset in a csv file
1.33570301776, 3.61194e-06, 7.24503e-06, -9.91572e-06, 1.25098e-05, 0.0102828, 0.010352, 0.0102677, 0.0103789, 0.00161604, 0.00167978, 0.00159998, 0.00182596, 0.0019804, 0.0133687, 0.010329, 0.00163437, 0.00191202, 0.0134425
1.34538754675, 3.3689e-06, 9.86066e-06, -9.12075e-06, 1.18058e-05, 0.00334344, 0.00342207, 0.00332897, 0.00345504, 0.00165532, 0.00170412, 0.00164234, 0.00441903, 0.00459294, 0.00449357, 0.00339737, 0.00166596, 0.00451926, 0.00455153
1.34808186291, -1.99011e-06, 6.53026e-06, -1.18909e-05, 9.52337e-06, 0.00158065, 0.00166529, 0.0015657, 0.0017022, 0.000740644, 0.00078635, 0.000730052, 0.00219736, 0.00238191, 0.00212762, 0.00163783, 0.000750669, 0.00230171, 0.00217917
As you can see, the numbers are formatted differently and misaligned. Is there a way in vim to quickly align the columns properly, so that the result is this
1.33570301776, 3.61194e-06, 7.24503e-06, -9.91572e-06, 1.25098e-05, 0.0102828, 0.010352, 0.0102677, 0.0103789, 0.00161604, 0.00167978, 0.00159998, 0.00182596, 0.0019804, 0.0133687, 0.010329, 0.00163437, 0.00191202, 0.0134425
1.34538754675, 3.3689e-06, 9.86066e-06, -9.12075e-06, 1.18058e-05, 0.00334344, 0.00342207, 0.00332897, 0.00345504,0.00165532, 0.00170412, 0.00164234, 0.00441903, 0.00459294, 0.00449357, 0.00339737, 0.00166596, 0.00451926, 0.00455153
1.34808186291, -1.99011e-06, 6.53026e-06, -1.18909e-05, 9.52337e-06, 0.00158065, 0.00166529, 0.0015657, 0.0017022, 0.000740644,0.00078635, 0.000730052,0.00219736, 0.00238191, 0.00212762, 0.00163783, 0.000750669,0.00230171, 0.00217917
That would be great to copy and paste sections with ctrl-v. Any hints?
If you're on some kind of UNIX (Linux, etc), you can cheat and filter it through the column(1) command.
:%!column -t
The above will parse on delimiters inside string literals which is wrong, so you will likely need pre-processing steps and specifying the delimiter for this file for example:
%!sed 's/","/\&/' | column -t -s '&'
Sometimes we want to align just two columns. In that case, we don't need any plugins and can use pure Vim functionality like this:
Choose a separator. In OP's post this is a comma, in my example this is =.
Add spaces before/after it. I use s/=/= ...spaces... / in visual selection for this.
Locate to the longest word and place cursor after it.
Remove all the extra whitespace using dw and vertical movement.
Example of this technique demonstrated below:
I don't find myself needing to align things often enough to install another plugin, so this was my preferred way of accomplishing it - especially that it doesn't require much thinking.
As sunny256 suggested, the column command is a great way of doing this on Unix/Linux machines, but if you want to do it in pure Vim (so that it can be used in Windows as well), the easiest way is to install the Align plugin and then do:
:%Align ,
:%s/\(\s\+\),\s/,\1/g
The first line aligns the entries on the commas and the second moves the comma so that it's flush with the preceding value. You may be able to use AlignCtrl to define a custom mapping that does the whole lot in one go, but I can never remember how to use it...
Edit
If you don't mind two spaces between entries and you want to do this in one command, you can also do:
:%Align ,\zs
This is a great answer using vim macros: https://stackoverflow.com/a/8363786/59384 - basically, you start recording a macro, format the first column, stop recording then repeat the macro for all remaining lines.
Copy/pasted from that answer:
qa0f:w100i <Esc>19|dwjq4#a
Note the single space after the 100i, and the <Esc> means "press escape"--don't type "<Esc>" literally.
Translation:
qa -- record macro in hotkey a
0 -- go to beginning of line
f: -- go to first : symbol
w -- go to next non-space character after the symbol
100i <Esc> -- insert 100 spaces
19| -- go to 19th column (value 19 figured out manually)
dw -- delete spaces until : symbol
j -- go to next line
q -- stop recording macro
4#a -- run the macro 4 times (for the remaining 4 lines)
We now also have the fabulous EasyAlign plugin, written by junegunn.
Demonstration GIF from its README:
Also, Tabularize is quite good http://vimcasts.org/episodes/aligning-text-with-tabular-vim/
You could use the csv.vim plugin.
:%ArrangeColumn
However, this will not do exactly what you have asked: it will right adjust the contents of cells, whereas you have your values aligned by the decimal point or by the first digit.
The plugin has many other useful commands for working with CSV files.
also if you have very long columns it can be handy to disable default wrapping
:set nowrap
:%!column -t
(note in debian you also have a further option for column -n which if you want to split multiple adjacent delimiters)
Here’s a pure Vim script answer, no plugins, no macros:
It might be most clear to start out with my problem’s solution as an example. I selected the lines of code I wanted to affect, then used the following command (recall that entering command mode from visual mode automatically prepends the “'<,'>”, so it acts on the visual range):
:'<,'>g``normal / "value<0d>D70|P`
Except I did NOT actually type “<0d>”. You can enter unprintable characters on the command line by pressing ctrl-v, then the key you want to type. “<0d>” is what is rendered on the command line after I typed ‘ctrl-v enter’. Here, it’s parsed by the “normal” command as the exit from “/” search mode. The cursor then jumps to “ value” in the current line.
Then we simply [D]elete the rest of the line, jump to column 70 (or whatever you need in your case), and [P]ut what we just deleted. This does mean we have to determine the width of the widest line, up to our search. If you haven’t put that information in your statusline, you can see the column of the cursor by entering the normal mode command ‘g ctrl-g’. Also note that jumping to a column that doesn’t exist requires the setting 'virtualedit'!
I left the search term for the :g(lobal) command empty, since we used a visual block and wanted to affect every line, but you can leave off using a visual selection (and the “'<,'>”) and put a search term there instead. Or combine a visual selection and a search term to narrow things more finely/easily.
Here’s something I learned recently: if you mess up on a complex command mode command, undo with ‘u’ (if it affected the buffer), then press “q:” to enter a special command history buffer that acts much like a conventional buffer. Edit any line and press enter, and the changed command is entered as a new command. Indispensable if you don’t want to have to stress over formulating everything perfectly the first time.
I just wrote tablign for this purpose. Install with
pip3 install tablign --user
Then simply mark the table in vim and do
:'<,'>:!tablign
Pretty old question, but I've recently availed myself of an excellent vim plugin that enables table formatting either on the fly or after-the-fact (as your use case requires):
https://github.com/dhruvasagar/vim-table-mode
I have this in my .vimrc.
command! CSV set nowrap | %s/,/,|/g | %!column -n -t -s "|"
This aligns the columns while keeping the comma, which may be needed later for correct reading. For example, with Python Pandas read_csv(..., skipinitialspace=True), thanks Pandas guys for this smart option, otherwise in vim %s/,\s\+/,/g. It may be easier if your column has the option --output-separator I guess, my doesn't and I'm not sure why (my man page for column says 2004, on ubuntu 18.04, not sure ubuntu will get a new version). Anyway, this works for me, and comment if you have any suggestions.
I made a cli tool written in Perl.
You can find it here: https://github.com/bas080/colcise

How to remove quotes surrounding the first two columns in Vim?

Say I have the following style of lines in a text file:
"12" "34" "some text "
"56" "78" "some more text"
.
.
.
etc.
I want to be able to remove the quotes surrounding the first two columns. What is the best way to do this with Vim (I'm currently using gVim)?
I figured out how to at least delete the beginning quote of each line by using visual mode and then enter the command '<,'>s!^"!!
I'm wondering if there is a way to select an entire column of text (one character going straight down the file... or more than 1, but in this case I would only want one). If it is possible, then would you be able to apply the x command (delete the character) to the entire column.
There could be better ways to do it. I'm looking for any suggestions.
Update
Just and FYI, I combined a couple of the suggestions. My _vimrc file now has the following line in it:
let #q=':%s/"\([0-9]*\)"/\1/g^M'
(Note: THE ^M is CTRLQ + Enter to emulate pressing the Enter key after running the command)
Now I can use a macro via #q to remove all of the quotes from both number columns in the file.
use visual block commands:
start mode with Ctrl-v
specify a motion, e.g. G (to the end of the file),
or use up / down keys
for the selected block specify an action, e.g. 'd' for delete
For more see
:h visual-mode
Control-V is used for block select. That would let you select things in the same character column.
It seems like you want to remove the quotes around the numbers. For that use,
:%s/"\([0-9]*\)"/\1/g
Here is a list of what patterns you can do with vim.
There is one more (sort of ugly) form that will restrict to 4 replacements per line.
:%s/^\( *\)"\([ 0-9]*\)"\([ 0-9]*\)"\([ 0-9]*\)"/\1\2\3\4/g
And, if you have sed handy, you can try these from the shell too.
head -4 filename.txt | sed 's/pattern/replacement/g'
that will try your command on the first 4 lines of the file.
Say if you want to delete all columns but the first one, the simple and easy way is to input this in Vim:
:%!awk '{print $1}'
Or you want all columns but the first one, you can also do this:
:%!awk '{$1="";$0=$0;$1=$1;print}'
Indeed it requires external tool to accomplish the quest, but awk is installed in Linux and Mac by default, and I think folks with no UNIX-like system experience rarely use Vim in Windows, otherwise you probably known how to get a Windows version of awk.
Although this case was pretty simple to fix with a regex, if you want to do something even a bit more advanced I also recommend recording a macro like Bryan Ward. Also macros come easier to me than remembering which characters need to be escaped in vim's regexes. And macros are nice because you can see your changes take place immediately and work on your line transformation in smaller bits at a time.
So in your case you would have pressed qw to start recording a macro in register w (you can of course use any letter you want). I usually start my macros with a ^ to move to the start of the line so the macro doesn't rely on the location of the cursor. Then you could do a f" to jump to the first ", x to delete it, f" to jump to the next " and x to delete that too. Then q to finish recording.
Instead of making your macro end on the next line I actually as late as today figured out you can just V (visually line select) all lines you want to apply your macro to and execute :normal #w which applies your macro in register w to each visually selected line.
See column editing in vim. It describes column insert, but basically it should work in the same way for removing.
You could also create a macro (q) that deletes the quotes and then drops down to the next line. Then you can run it a bunch of times by telling vi how many times to execute it. So if you store the macro to say the letter m, then you can run 100#m and it will delete the quotes for 100 lines. For some more information on macros:
http://vim.wikia.com/wiki/Macros
The other solutions are good. You can also try...
:1,$s/^"\(\w\+\)"/\1/gc
For more Vim regex help also see http://vim.wikia.com/wiki/Search_patterns.
Start visual-block by Ctrl+v.
Jump at the end and select first two columns by pressing: G, EE.
Type: :s/\%V"//g which would result in the following command:
:'<,'>s/\%V"//g
Press Enter and this will remove all " occurrences in the selected block.
See: Applying substitutes to a visual block at Vim Wikia

How to add line numbers to range of lines in Vim?

How can I add line numbers to a range of lines in a file opened in Vim? Not as in :set nu—this just displays line numbers—but actually have them be prepended to each line in the file?
With
:%s/^/\=line('.')/
EDIT: to sum up the comments.
This command can be tweaked as much as you want.
Let's say you want to add numbers in front of lines from a visual selection (V + move), and you want the numbering to start at 42.
:'<,'>s/^/\=(line('.')-line("'<")+42)/
If you want to add a string between the number and the old text from the line, just concatenate (with . in VimL) it to the number-expression:
:'<,'>s/^/\=(line('.')-line("'<")+42).' --> '/
If you need this to sort as text, you may want to zero pad the results, which can be done using printf for 0001, 0002 ... instead of 1, 2... eg:
:%s/^/\=printf('%04d', line('.'))/
Anyway, if you want more information, just open vim help: :h :s and follow the links (|subreplace-special|, ..., |submatch()|)
cat -n adds line numbers to its input. You can pipe the current file to cat -n and replace the current buffer with what it prints to stdout. Fortunately this convoluted solution is less than 10 characters in vim:
:%!cat -n
Or, if you want just a subselection, visually select the area, and type this:
:!cat -n
That will automatically put the visual selection markers in, and will look like this after you've typed it:
:'<,'>!cat -n
In order to erase the line numbers, I recommend using control-v, which will allow you to visually select a rectangle, you can then delete that rectangle with x.
On a GNU system: with the external nl binary:
:%!nl
With Unix-like environment, you can use cat or awk to generate a line number easily, because vim has a friendly interface with shell, so everything work in vim as well as it does in shell.
From Vim Tip28:
:%!cat -n
or
:%!awk '{print NR,$0}'
But, if you use vim in MS-DOS, of win9x, win2000, you loss these toolkit.
here is a very simple way to archive this only by vim:
fu! LineIt()
exe ":s/^/".line(".")."/"
endf
Or, a sequence composed with alphabet is as easy as above:
exe "s/^/".nr2char(line("."))."/"
You can also use a subst:
:g/^/exe ":s/^/".line(".")."^I/"
You can also only want to print the lines without adding them to the file:
"Sometimes it could be useful especially be editing large source files to print the line numbers out on paper.
To do so you can use the option :set printoptions=number:y to activate and :set printoptions=number:n to deactivate this feature.
If the line number should be printed always, place the line set printoptions=number:y in the vimrc."
First, you can remove the existing line numbers if you need to:
:%s/^[0-9]*//
Then, you can add line numbers. NR refers to the current line number starting at one, so you can do some math on it to get the numbering you want. The following command gives you four digit line numbers:
:%!awk '{print 1000+NR*10,$0}'
The "VisIncr" plugin is good for inserting columns of incrementing numbers in general (or letters, dates, roman numerals etc.). You can control the number format, padding, and so on. So insert a "1" in front of every line (via :s or :g or visual-block insert), highlight that column in visual-block mode, and run one of the commands from the plugin.
If someone wants to put a tab (or some spaces) after inserting the line numbers using the this excellent answer, here's a way. After going into the escape mode, do:
:%s/^/\=line('.').' '/
^ means beginning of a line and %s is the directive for substitution. So, we say that put a line number at the beginning of each line and add 4 spaces to it and then put whatever was the contents of the line before the substitution, and do this for all lines in the file.
This will automatically substitute it. Alternatively, if you want the command to ask for confirmation from you, then do:
:%s/^/\=line('.').' '/igc
P.S: power of vim :)
The best reply is done in a duplicate question.
In summary:
with CTRL-V then G I 0 You can insert a column of zero.
Then select the whole column and increment:
CTRL-V g CTRL-A
See also: https://vim.fandom.com/wiki/Making_a_list_of_numbers#Incrementing_selected_numbers

Resources