47

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?

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97
iblue
  • 29,609
  • 19
  • 89
  • 128

10 Answers10

48

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 /=
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
drrlvn
  • 8,189
  • 2
  • 43
  • 57
  • 3
    This plugin works great! You can also type Tab while in visual mode to align the highlighted text. – ericraio Aug 26 '13 at 15:37
23

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.

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97
  • 3
    this is great, no 3rd party tools required! dunno why someone voted this down as it totally works – tmck-code Mar 19 '19 at 23:38
  • 2
    It should probably be noted that as it's written in this answer, it will align the entire buffer/file. If you just want to align part of your buffer, you can replace `%` with a range (as in `:! column -t -s= -o=`) or just make your selection, press `:` and type `! column -t -s= -o=` (you should end up with `:'<,'>! column -t -s= -o=`). – Cybolic Mar 03 '20 at 11:57
  • Just throws an error for me on macos and bsd method leaves the file a mess. – StevieD Feb 22 '22 at 10:28
17

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.
Tanner Swett
  • 3,241
  • 1
  • 26
  • 32
  • I think I found an easier way. :) – Alex Harvey Jul 22 '18 at 07:04
  • @AlexHarvey You've found an easier way than the way I described here? If that's the case, could you post it here as an answer, please? – Tanner Swett Jul 22 '18 at 17:13
  • @AlexHarvey I'm assuming that what you mean is that you found one of the other answers to be easier than this one. In any case, if you've got any useful information that hasn't been posted here, please make sure to post it. Thanks. – Tanner Swett Jul 23 '18 at 01:05
  • No, I mean I found a new way of doing it without installing a plugin, as I explained in a new answer above. I am not sure why you aren't seeing that, but here is the direct link: https://stackoverflow.com/a/51462785/3787051 – Alex Harvey Jul 23 '18 at 04:25
  • @AlexHarvey Yup, I missed that. My bad, sorry. – Tanner Swett Jul 23 '18 at 12:16
  • I found that this is the best solution. The @AlexHarvey does not work properly if you have strings with spaces. – Hunaphu Mar 24 '21 at 10:33
  • @Hunaphu My solution will work fine if you have the GNU version of column. Otherwise try passing -s= to column. – Alex Harvey Mar 27 '21 at 18:09
  • All I can say is that it does not work for me. – Hunaphu Mar 29 '21 at 10:52
13

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.

Arturo Herrero
  • 12,772
  • 11
  • 42
  • 73
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
4

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);
Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
2

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.

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
2

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

Example of vim easy align command

Sardathrion - against SE abuse
  • 17,269
  • 27
  • 101
  • 156
2

(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.

  1. 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?
  2. 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.
  3. 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.
  4. 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.
  5. Start visual-block selection, pressing Ctrl-V.
  6. 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.
  7. 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.

filbranden
  • 8,522
  • 2
  • 16
  • 32
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*/= /'
  1. -range gives which is the current selection
  2. Sub the first = for PXXXQYYY leaving other = untouched.
  3. make columns based on -s 'PXXX' removing PXXX leaving QYYY
  4. Put back the first = by substituting QYYY. Remove unwanted whitespace

Update: Mulitple = is now supported.

Hunaphu
  • 589
  • 10
  • 11
0

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
rizkix
  • 9
  • 1