tags:

views:

120

answers:

6

I would like to create a macro or a script in Vim that does the following:

  1. wrap a block of text in double quotes
  2. escaping any quotes that appear in the text itself
  3. have the quotes on the right side in vertical alignment

For example:

<html>
<head></head>
<body>
<h1>High Score Server</h1>
<table>
ROWS
</table>
</body>
</html>

would become:

"<html>                    "
"<head></head>             "
"<body>                    "
"<h1>High Score Server</h1>"
"<table>                   "
"ROWS                      "
"</table>                  "
"</body>                   "
"</html>                   ";

I am able to achieve this with a macro, but without the vertical alignment of the quotes on the right side. Can anyone help me with this one?

A: 

Is it possible to make two passes over the list of lines in vim script? Then you can do something like this (pseudocode):

let N = length of longest line
for each line L:
    insert a " character at the beginning
    append N - len(L) spaces
    append a " character
Kristo
+2  A: 
function Enquote()
    let [startline, endline]=sort([line("'<"), line("'>")])
    let lines=getline(startline, endline)
    let lengths=map(copy(lines), 'len(split(v:val, ''\zs''))')
    let maxlen=max(lengths)
    call map(lines, '''"''.v:val.repeat(" ", maxlen-lengths[v:key]).''"''')
    return setline(startline, lines)
endfunction

Explanation:

line("'<") and line("'>") get the line numbers of start and end of last visual selection.

sort([...]) sorts this line numbers since you may have started selecting lines from the end of the selection.

let [a, b]=[c, d] is a parallel assignment: sort will produce a sorted list of two items, where first item is lesser or equal to second item. Obviously, lesser is a first selected line.

len(split(v:val, '\zs')) is an advanced strlen() which supports unicode.

max(list) finds a maximum value. Obvious.

So, map(copy(lines), 'len(split(v:val, ''\zs''))') applies this strlen to all items in list. copy() is required since we do not want our list to be modified.

map(lines, '''"''.v:val.repeat(" ", maxlen-lengths[v:key]).''"''') modifies an lines in a way you require. I switched from printf to repeat because printf does not handle multibyte characters correctly (by «correctly» I mean that «¥» is one character long, while printf considers it two bytes long).

setlines(linenumber, listoflines) actually modifies buffer.

ZyX
Can you add an example of how you might `vmap` this?
too much php
@too much php `vnoremap \eq :<C-u>call Enquote()<CR>` to map this to `\eq` for visual mode. And omit using `*map` if you can.
ZyX
A: 

best i got is a 3-pass regex.

select block in visual mode, then use:

:'<,'>s#^#"#
:'<,'>s#$#                                                #
:'<,'>s#\(.\{28\}\).*#\1"

with the 28 being the length of your longest line.

sleepynate
A: 

In two passes:

let l = max(map(getline("'<", "'>"), 'len(v:val)'))
'<,'>s/.*/\=('"'.submatch(0).repeat(' ', l-len(submatch(0)) )).'"'
Luc Hermitte
Replace `len(submatch)` with `len(split(submatch, '\zs'))` in order to handle multibyte strings. Do this with `len(v:val)` too.
ZyX
+5  A: 

What I'd do :

With "surround" and "Align" plugins :

1) with cursor on first line (0,0), type <C-V>)$s" 2) then <S-V>):Align " and <Enter>.

Another solution without plugins :

1) set virtual mode

:set ve=all

2) <C-V> to go in block-wise selection, with cursor at the position 0,0

3) go down to the bottom of the text, then Shift-I, type " and Esc. This should prepend the quotes.

4) now go on the left end (since ve=all, you can go where there is no text) 5) <C-V>, go down to bottom, type r"

This is long to explain, but easy to do and reproduce. Also useful in lots of case.

Drasill
The second solution is awesome!
StackedCrooked
+1 for `virtualedit`, I thought there was a built-in feature that would do this easily.
too much php
+1  A: 

Making use of the unix program "par" to do this may well solve your problem. There's a Vimcast showing how to integrate it into vim over at http://vimcasts.org/episodes/formatting-text-with-par/

sleepynate