aturegano
aturegano

Reputation: 1036

Align text after n-th column in vim removing unnecessary blanks

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.

Upvotes: 1

Views: 182

Answers (4)

builder-7000
builder-7000

Reputation: 7627

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

Upvotes: 0

statox
statox

Reputation: 2886

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

Upvotes: 4

Sato Katsura
Sato Katsura

Reputation: 3086

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

Upvotes: 2

Ingo Karkat
Ingo Karkat

Reputation: 172540

For alignment, there are three well-known plugins:

Upvotes: 2

Related Questions