Align text on an equals sign in vim - vim

I tend to align code on equal signs for better readability. From this:
$ = jQuery.sub()
Survey = App.Survey
Sidebar = App.Sidebar
Main = App.Main
To this:
$ = jQuery.sub()
Survey = App.Survey
Sidebar = App.Sidebar
Main = App.Main
Is there an easy way to do this in vim?

The best plugin I found so far is Tabular.vim.
Easiest way to install it is by using the Pathogen plugin, and then cloning the Tabular git repository to ~/.vim/bundle/tabular. Full instructions in the Pathogen README.
After it's installed, using it is just a matter of putting your cursor somewhere in the paragraph you want to align and running:
:Tab /=

For a simple solution that does not involve installing a plugin, just filter through the Unix column command.
Note there are two ways to do this depending on whether your column command supports -o.
GNU column command (Linux etc)
:% ! column -t -s= -o=
That's it.
BSD column command (Mac OS X etc)
Step one, filter through column -t:
:% ! column -t
Step two, remove the padding around the delimiter:
:%s/ = /=/
Initial text is
$ = jQuery.sub()
Survey = App.Survey
Sidebar = App.Sidebar
Main = App.Main
After step one it becomes
$ = jQuery.sub()
Survey = App.Survey
Sidebar = App.Sidebar
Main = App.Main
And after step two
$ = jQuery.sub()
Survey = App.Survey
Sidebar = App.Sidebar
Main = App.Main
Or, if you want to do it in one step:
:% ! column -t | sed 's/ = /=/'
For more info, man column.

This isn't the easiest way to do things, but it can be done without any plugins.
Use V and :s/=/ =/ to insert a bunch of spaces before each equals sign.
Use Ctrl-V to select the column that you want the equals signs to be moved into.
Press << to "unindent" the right hand side of each equation towards the column you selected, then press . repeatedly until the equals signs are lined up in that column.

I believe this is easily done with the Tabular plugin. Here it is in action.
Select the range in Visual mode (not actually necessary), and do:
:Tabularize /=
The plugin can actually find the correct range on its own often, without needing to select it visually or specify a range to the ex command.

An alternative plugin to Tabular:
https://github.com/tommcdo/vim-lion
From the docs:
For example, glip= will turn
$i = 5;
$username = 'tommcdo';
$stuff = array(1, 2, 3);
into
$i = 5;
$username = 'tommcdo';
$stuff = array(1, 2, 3);

You can use the Align Vim plugin to align such blocks, e.g. via typing
vip:Align =
in command mode, when the cursor is placed inside the to be aligned block.
Where vip enters virtual mode and selects the current paragraph. The Align command is quite powerful, e.g. you can also specify multiple patterns, patterns are interpreted as regular expressions etc.

The vim-easy-align plugin does that as well. Here is one of the many examples given:

(From this answer in Vi & Vim Stack Exchange:)
If you're in a pinch and want to get the expressions aligned, without having to install and learn any plug-ins, here is a quick way to do it.
Select the lines on a visual selection. For example, if this is your whole buffer, you could use ggVG, but if these lines are in the middle of a file, just select appropriately. Perhaps V4j?
Insert enough whitespace before the =, which you can do with :normal f=9i . (Note the "space" at the end.) This will add 9 spaces before each = in the lines of the visual selection. If 9 is not enough, add more (like 99 or 999, as much as you want.) Note that when you type : with the visual selection, Vim will automatically insert the range, so the actual command is :'<,'>normal f=9i , but you don't need to type those characters.
Move to the column where you want the =s to be flushed to. In this case, line 2 has the longest variable name, so move to two spaces after the end of that name, which is where the =s should be at the end. If this is the whole buffer, you could use 2G2e2l to get there.
Staying on that same column, move to the first line of the block. In this case, you're moving from line 2 to line 1, so k is enough.
Start visual-block selection, pressing Ctrl-V.
Move to the last line of the block. If this is the whole buffer, you could use G, if this is the middle of a file, you could use 4j to go four lines down, etc.
Now you can use the < command to shift the lines left, but until they hit the left of the visual block. Each < will shift them by one 'shiftwidth' only, so you're likely to need more than one. So, to be sure, use 9< (or 99<, or 999<, if you added tons of spaces in step 2.)
Voilà!
This is a pretty cool technique and it can be helpful when you need more flexibility than plug-ins can afford you. It's a good one to learn and keep on your Vim toolbox.
It is also quite flexible if you want to align on a different criteria other than a =, for example, to align on the third column (third word) of each line, use :normal 2W9i on step 2.

By adding a single line to your vimrc all you need is to select the text and type
:Aleq
Put this in your vimrc:
command! -range Aleq execute <line1>.",".<line2> . "! sed 's/=/PXXXQYYY/'| column -t -s 'PXXX'| sed 's/QYYY\s*/= /'"
Explanation:
The above is equivalent to typing
'<,'>! ! sed 's/=/PXXXQYYY/'| column -t -s 'PXXX'| sed 's/QYYY\s*/= /'
-range gives which is the current selection
Sub the first = for PXXXQYYY leaving other = untouched.
make columns based on -s 'PXXX' removing PXXX leaving QYYY
Put back the first = by substituting QYYY. Remove unwanted whitespace
Update: Mulitple = is now supported.

Install tabularize plugin and modify gist by tpope to something like this :
inoremap <silent> : :<Esc>:call <SID>align(':')<CR>a
inoremap <silent> = =<Esc>:call <SID>align('=')<CR>a
function! s:align(aa)
let p = '^.*\s'.a:aa.'\s.*$'
if exists(':Tabularize') && (getline(line('.')-1) =~# p || getline(line('.')+1) =~# p)
let column = strlen(substitute(getline('.')[0:col('.')],'[^'.a:aa.']','','g'))
let position = strlen(matchstr(getline('.')[0:col('.')],'.*'.a:aa.':\s*\zs.*'))
exec 'Tabularize/'.a:aa.'/l1'
normal! 0
call search(repeat('[^'.a:aa.']*'.a:aa,column).'\s\{-\}'.repeat('.',position),'ce',line('.'))
endif
endfunction

Related

How do I re-select a range in vim? [duplicate]

Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.

How to unwrap text in Vim?

I usually have the tw=80 option set when I edit files, especially LaTeX sources. However, say, I want to compose an email in Vim with the tw=80 option, and then copy and paste it to a web browser. Before I copy and paste, I want to unwrap the text so that there isn't a line break every 80 characters or so. I have tried tw=0 and then gq, but that just wraps the text to the default width of 80 characters. My question is: How do I unwrap text, so that each paragraph of my email appears as a single line? Is there an easy command for that?
Go to the beginning of you paragraph and enter:
v
i
p
J
(The J is a capital letter in case that's not clear)
For whole document combine it with norm:
:%norm vipJ
This command will only unwrap paragraphs. I guess this is the behaviour you want.
Since joining paragraph lines using Normal mode commands is already
covered by another answer, let us consider solving the same issue by
means of line-oriented Ex commands.
Suppose that the cursor is located at the first line of a paragraph.
Then, to unwrap it, one can simply join the following lines up until
the last line of that paragraph. A convenient way of doing that is to
run the :join command designed exactly for the purpose. To define
the line range for the command to operate on, besides the obvious
starting line which is the current one, it is necessary to specify
the ending line. It can be found using the pattern matching the very
end of a paragraph, that is, two newline characters in a row or,
equivalently, a newline character followed by an empty line. Thus,
translating the said definition to Ex-command syntax, we obtain:
:,-/\n$/j
For all paragraphs to be unwrapped, run this command on the first line
of every paragraph. A useful tool to jump through them, repeating
a given sequence of actions, is the :global command (or :g for
short). As :global scans lines from top to bottom, the first line
of the next paragraph is just the first non-empty line among those
remaining unprocessed. This observation gives us the command
:g/./,-/\n$/j
which is more efficient than its straightforward Normal-mode
counterparts.
The problem with :%norm vipJ is that if you have consecutive lines shorter than 80 characters it will also join them, even if they're separated by a blank line. For instance the following example:
# Title 1
## Title 2
Will become:
# Title 1 ## Title 2
With ib's answer, the problem is with lists:
- item1
- item2
Becomes:
- item1 - item2
Thanks to this forum post I discovered another method of achieving this which I wrapped in a function that works much better for me since it doesn't do any of that:
function! SoftWrap()
let s:old_fo = &formatoptions
let s:old_tw = &textwidth
set fo=
set tw=999999 " works for paragraphs up to 12k lines
normal gggqG
let &fo = s:old_fo
let &tw = s:old_tw
endfunction
Edit: Updated the method because I realized it wasn't working on a Linux setup. Remove the lines containing fo if this newer version doesn't work with MacVim (I have no way to test).

Using Vim, isn't there a more efficient way to format LaTeX paragraphs according to this best practice?

The best practice mentioned in the title is the one suggested by Uri:
When writing paragraphs, start each
sentence at the beginning of a line,
and if it spills over, each subsequent
line is tabbed.
I use gVim with Vim-LaTeX, which comes with an indent/tex.vim file, to edit LaTeX files. The way I currently implement the practice mentioned above is as follows:
I :set textwidth=79 to automatically break lines before they become too long.
I manually hit Enter after I finish inserting each sentence.
If I'm done with revising and editing a sentence, I manually shift any spillovers using >>, prefixing it with a count if necessary.
Occasionally, that last step will make one or more spillovers go over the maximum line width. In this case, I
gqq the faulty line.
J my way through to the end of the sentence.
repeat steps 1 and 2 as necessary.
As you can imagine, this can become tedious. Isn't there a more efficient way to achieve the same result? Ultimately, I want to be able to write the sentences without worrying about their format, and then use gqap, or gqip, to automatically produce the result that I currently produce manually.
To do that, I suspect that I will need to write a formatexpr of my own, but I'm not sure how to proceed. I have found a number of plugins, Latex Text Formatter and Text (Especially LaTeX) Formatter, and a tip, but none of them seem to suit my needs, and I'm not sure how to modify them to do so.
I may well be oversimplifying the problem, but does this mapping do what you want?
nnoremap \z (j>>gq)
So pressing \z in normal mode will do the following: From the cursor position, jump to the start of the sentence. Then go to the next line and indent it. Then reformat from this line to the end of the sentence. Reformatting sentence-wise is the way to go, rather than reformatting each line individually, as your method seems to do.
Of course you can use an insert-mode mapping if you prefer, or even try redefining the behaviour of the Enter key to do this automatically (although I don't know if this will have unintended consequences...).
One way to do this is not by actually breaking the lines in the file but instead doing the following:
set wrap linebreak
let &showbreak='===> '
The wrap option makes long lines wrap instead of extending off the screen and linebreak makes the line breaks happen only at characters specified in the breakat option.
You can set showbreak to anything that is pleasing to your eye. My favorite when I'm using vim where unicode characters work right is:
let &showbreak="\u21aa "
This puts a ↪ symbol at the beginning of each wrapped line.
I also like to turn on line numbers (set number) to give another indicator of what the actual lines in the file are.
To make navigating the file easier you might want to use
noremap j gj
noremap k gk
noremap gj j
noremap gk k
This makes k and j move up and down by displayed lines not file lines. To affect the cursor keys as well replace k with <Up> and j with <Down>.
One option that takes different tack than tabbing subsequent lines would be to set the w flag in formatoptions. When you do that it changes the way Vim identifies new paragraphs, and lines ending in a space are understood to continue on a new line as part of same paragraph. See :h fo-table.
If you set the w flag and enter your text so that continued sentence lines are the only ones ending in a space (and abandon completely practice of entering tabs at beginning of any text lines) then I think you should be able to use gqap to format text paragraphs as you want. To get visual cues to logical structure you can then set listchars to display the eol (i.e., <cr>) character and set different highlightings for <space><cr> and for <non-space><cr> so that sentence/paragraph ends are easily spotted.
Another benefit of this method is that you can just type your text naturally and let line breaks be entered automatically by textwidth setting. (Just make sure that LaTeX formatting lines don't break automatically in textwidth area; you want them to have non-space char as last char in line.)
That tip also caught my eye. Here's how I solved the problem (a diff of the changed lines in tex.vim):
*** tex.vim.old 2011-08-16 08:26:56.845046457 +0200
--- tex.vim 2011-08-16 08:59:14.736306930 +0200
***************
*** 90,95 ****
--- 90,96 ----
" LH modification : \begin does not always start a line
if line =~ '\\begin{\(.*\)}' && line !~ 'verbatim'
\ && line !~ 'document'
+ \ || line =~ '^\s*[A-Z].*[a-zA-Z0-9,]\s*$\C'
let ind = ind + &sw
***************
*** 105,110 ****
--- 106,112 ----
" Subtract a 'shiftwidth' when an environment ends
if cline =~ '^\s*\\end' && cline !~ 'verbatim'
\&& cline !~ 'document'
+ \|| line =~ '\.\s*$'
if g:tex_indent_items == 1
" Remove another sw for item-environments
Basically it indents new lines when the previous line starts with a capital letter and ends with a letter, digit, or comma, and "unindents" new lines with the previous line ends with a period.
There is definitely room for improvement (better criteria) but for me it works all right so far.
I find the suggestion from #kev (and the people commented) at this post to be the most satisfying.
There, it is explained that by setting
:set fo+=n
followed by either
:let &flp='^\s*\\(item\|end\|begin)*\s*'
or
:let &l:flp='^\s*\\\(item\|end\|begin\)\s*'
lets you type gggqG to reformat the entire file.
I use the vim-textobj-usr plugin to define a "LaTeXPar" text-object. Then I can use gwal to format.
There is already a vim-textobj-latex plugin, but the biggest text-object it defines is "environment". This is not what I (and OP) want.
A "LaTeXPar" is delimited by
an empty line
a line begin with \[, \], \begin, \end, }
a line end with {
this is adapted to my writing habit: I always have an empty line after \section, always use \[ \] on a single line, and so on. You can easily write one for yourself.
Here is the relative part in my ~/.vim/ftplugin/tex.vim.
call textobj#user#plugin('latexpar', {
\ 'par': {
\ 'select-a-function': 'LaTeXPar',
\ 'select-a': 'al',
\ },
\ })
function! LaTeXPar()
let pattern='\v^$|^\s*(\\\[|\\\]|\\begin|\\end|\})|\{$'
if search(pattern,"bW")
normal! j
else
normal! gg
endif
let head_pos = getpos('.')
if search(pattern,"W")
normal! k
else
normal! G
endif
let tail_pos = getpos('.')
" echo head_pos[2]
" echo tail_pos[2]
return ["V", head_pos, tail_pos]
endfunction

How to diff two lines in an open file in vim?

I occasionally see very long lines in my code that I need to check if they are the same. Is there a way in vim to select two lines and diff them to show any differences between the two?
For example, given the two lines in vim:
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *lists);
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *1ists);
I would like vim to tell me that the two lines are in fact different because each spells "lists" differently. Is this possible, and if so, how do I do it?
A quick and dirty solution is to just select both lines and sort them while removing duplicates:
select lines
":sort u"
if only one line remains, both were equal
if both remain, there most be some difference
An undo recovers everything again.
An alternative to #sehe's approach would not require the use of temp files:
funct! DiffTwoTexts(text1, text2)
new
put =a:text1
normal ggdd
diffthis
new
put =a:text2
normal ggdd
diffthis
endfunct
funct! DiffTwoLines(line1, line2)
let text1 = getline(a:line1)
let text2 = getline(a:line2)
call DiffTwoTexts(text1, text2)
endfunct
comma! DiffWithNext call DiffTwoLines('.', line('.') + 1)
This will still be pretty hard to read, since it keeps everything on a single line, so I came up with this modification:
funct! EvalTextPreprocessor(expr, text)
let text = a:text
return eval(a:expr)
endfunct
comma! -nargs=1 DiffWithNextPre call DiffTwoTexts(
\ EvalTextPreprocessor(<q-args>, getline('.')),
\ EvalTextPreprocessor(<q-args>, getline(line('.') + 1)))
This new command takes a vimscript expression as its argument, wherein the variable text refers to whichever line is being preprocessed. So you can call, e.g.
DiffWithNextPre split(text, '[(,)]\zs')
For your sample data, this gives the two buffers
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *lists)
;
and
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *1ists)
;
Only the lines that start with Parameter are highlighted.
You can even build up from there, creating a command
comma! DiffTwoCFunctionSigs DiffWithNextPre split(text, '[(,)]\s*\zs')
Notice that I modified the regexp a bit so that it will keep trailing spaces at the end of lines. You could get it to ignore them entirely by moving the \s* to after the \zs. See :help /\zs if you're unfamiliar with what that vim-specific RE atom does.
A nicety would be to make the command take a range (see :help command-range), which you could use by diffing the first line of the range with the last line. So then you just visual-select from the first line to the second and call the command.
I used linediff.vim.
This plugin provides a simple command, ":Linediff", which is used to diff two separate blocks of text.
That is not a feature, however it is easily scripted, e.g. in your vimrc:
function! DiffLineWithNext()
let f1=tempname()
let f2=tempname()
exec ".write " . f1
exec ".+1write " . f2
exec "tabedit " . f1
exec "vert diffsplit " . f2
endfunction
This will open the current and next lines in vertical split in another tab.
Note that this code is a sample
it doesn't check whether next line exists (there are any following lines)
it doesn't cleanup the tempfiles created
a nice improvement would be to take a range, or use the '' mark to select the other line
You can leave off the 'vert' in order to have a horizontal split
Map it to something fancy so you don't have to :call it manually:
:nnoremap <F10> :call DiffLineWithNext()^M
you could also just create a new empty window buffer and copy line, then make command:
:windo diffthis
this should open a new window showing the differences of those 2 lines

Notepad++ like "multi editing" in Vim?

I’m switching from Notepad++ to Vim as my main text editor.
In Notepad++, you can have multiple cursors by holding down Ctrl and clicking anywhere in the text, so that if you type, the text appears in multiple locations.
Is it possible in Vim? Something like insert after selecting multiple rows in Visual mode, but with the possibility to have cursors anywhere in the text.
It’s a feature I rarely use, and it’s also quite easily avoidable; I’m just curious, since it’s the only one I could’t find a replacement for in Vim yet.
There is not a built-in feature of that kind.
Let me suggest a function that repeats command (for example . repeating last
change command) at the positions of given marks. Both marks and command are
specified as string arguments. Marks specified in the way ranges in regular
expressions or scanf-format specifier are defined. For example, za-dx
means marks z, a, b, c, d, x.
function! MarksRepeat(marks, command)
let pos = 0
let len = strlen(a:marks)
let alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let beta = '1234567899bcdefghijklmnopqrstuvwxyzzBCDEFGHIJKLMNOPQRSTUVWXYZZ'
while pos < len
if a:marks[pos + 1] != '-'
exe 'norm `' . a:marks[pos] . a:command
let pos += 1
elseif a:marks[pos] <= a:marks[pos+2]
let mark = a:marks[pos]
let stop = a:marks[pos+2]
if mark =~ '[0-9a-zA-Z]' && stop =~ '[0-9a-zA-Z]'
while 1
exe 'norm `' . mark . a:command
if mark == stop
break
endif
let mark = tr(mark, alpha, beta)
endwhile
endif
let pos += 3
endif
endwhile
endfunction
In your case, the function could be used as follows.
Mark all places for simultaneous insertions (except one) using Vim
marks (by means of m command).
Actually insert text in the one place that has not been marked.
Run the function:
:call MarksRepeat(‹marks›, '.')
You could insert the text in one place, in a single operation, then use . to repeat that insertion at each other place you want the text.
It's the converse of what you asked for, because you wanted to mark the locations before entering the text, but it gives you the same result in the same number of keystrokes :).
Check multi select vim plugin: http://www.vim.org/scripts/script.php?script_id=953
ib's response and the multi select vim plugin are interesting, but the following is a suggestion that does not require a special function or plugin.
Temporarily set foldmethod=manual, then mark the blocks you want to operate on with zf.
Finally, use the ex command :folddoclosed to do ex commands on the folded blocks.
For example: :folddoclosed norm Iinsert some text at the front
Note, you can use :folddoclosed on any folded groups of lines, so you could use other foldmethods... but usually it makes sense to manually create the folds.
You can also use visual markers, followed by :norm which gives you :'<,'>norm... But visual markers only let you select a continuous range of lines. Using folds and :folddoclosed you can operate on multiple ranges of lines at once.
Another tip... to save time having to type out :folddoclosed, I will type :fo<shifttab><shifttab><shifttab>

Resources