开发者

Vim: creating a C string literal around a block of text with vertically aligned quotes?

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 ri开发者_Python百科ght 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?


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.


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.


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/


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


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.


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


In two passes:

let l = max(map(getline("'<", "'>"), 'strwidth(v:val)'))
'<,'>s/.*/\=('"'.submatch(0).repeat(' ', l-strwidth(submatch(0)) )).'"'
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜