1

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?

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278

7 Answers7

5

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
  • 3,917
  • 29
  • 30
2
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
  • 52,536
  • 7
  • 114
  • 135
1

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
  • 7,926
  • 3
  • 27
  • 38
0

By all means heed the previous answers and get your vim-fu in shape. Or install/modify/poke-the-author of this plugin:

http://www.vim.org/scripts/script.php?script_id=4727

From the plugin's page:

This script converts multi-line text in a C++ file to a multi-line string literal, escaping the quote and tab characters. It also does the reverse conversion, un-escaping some characters. It's not too complete for now, but it will be someday if needs come.

If you need to make changes use the source-code repository:

https://bitbucket.org/dsign/stringliteral.vim

dsign
  • 12,340
  • 6
  • 59
  • 82
0

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
Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
0

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
  • 7,926
  • 3
  • 27
  • 38
-1

In two passes:

let l = max(map(getline("'<", "'>"), 'strwidth(v:val)'))
'<,'>s/.*/\=('"'.submatch(0).repeat(' ', l-strwidth(submatch(0)) )).'"'
Luc Hermitte
  • 31,979
  • 7
  • 69
  • 83
  • Replace `len(submatch)` with `len(split(submatch, '\zs'))` in order to handle multibyte strings. Do this with `len(v:val)` too. – ZyX Jun 30 '10 at 23:08
  • 6 years latter note...: We can also use `strlen(substitute(submatch, '.', 'a', 'g'))` or simply `strdisplaywidth()` (or `strwidth()` if tab size doesn't matter) with recent versions of Vim. – Luc Hermitte Apr 14 '16 at 16:26