Vim 多行 If 语句转换为单行 If 语句

Vim Multi-Line If-Statements into Single-Line If-Statements

是否可以使用 vim 绑定将多行 if 语句转换为单行 if 语句,反之亦然?

转这个

if () {
    statement_1;
    statement_2;
} else if () {
    statement_3; statement_4;
} else if () {
    statement_5;
} else {

}

进入这个

if ()      { statement_1; statement_2 }
else if () { statement_3; statement_4; }
else if () { statement_5; }
else       { }

或任何接近上述行为的行为?我正在考虑在视觉上选择要转换的块然后使用搜索 else if 并输入新行等来执行命令。但我的问题是确定代码中有多少 else if

通过在可视模式下选择它们 V 并按 J 将所有行合并为一行;然后在每个 else:s/else/\relse/ 之前添加一个换行符。你最终会得到:

if () { statement_1; statement_2; } 
else if () { statement_3; statement_4; }
else if () { statement_5; } 
else { }

replacement pattern中的\r是一个换行符(你需要使用\n和search,replace中使用\r;不要问我为什么)。

下一步是将所有开始大括号放在同一列中。为此,我会使用 tabular plugin,这非常简单:

:%Tabularize /{/

使用 % 我们对整个缓冲区进行操作,在 "real" 文件中您可能希望使用限制性更强的范围或可视模式。还有一些其他插件也可以做类似的事情。

你现在应该有你想要的输出。


如果不想使用插件,可以使用column命令:

:%!column -ts{ -o{

如果你想要一个"Vim-only"的解决方案,那就有点复杂了:

:let b:column = 10
:%s/^\(.\{-}\) {/\=submatch(1) . repeat(' ', b:column - len(submatch(1))) . ' {'/

分解:

  • 我使用了 b:column 变量来指定要对齐的列。您不需要这个,但它可以让以后编辑这个数字更容易一些。

  • ^\(.\{-}\) {{ 之前的所有内容放入子组中。

  • 在替换中我们使用了一个表达式(如 \= 所示)。参见 :help sub-replace-\=
  • 首先我们用 submatch(1)
  • 放回 if ...
  • 然后我们用 repeat(' ', b:column - len(submatch(1)))
  • 插入我们需要的空格
  • 最后我们插入文字{

我告诉过你它有点复杂 ;-) 如果你不想要表格。就个人而言,我只是启动插入模式来插入空格,这比编写和调试这个要快得多(relevant xkcd)。


请注意,我没有使用 "magic" 命令来重新排列所有文本,只需按一下键即可。我不认为这样的命令是个好主意。在实践中,会有很多这样的命令无法处理的边缘情况。完全 "parsing" 一种带有临时编辑命令的编程语言 and/or 正则表达式并不是那么好用。

Vim 真正出色的地方在于为用户提供了强大的文本编辑命令,可以轻松应用和组合这些命令,这正是我在上面所做的。还有其他几种方法可以用来获得相同的效果。

但如果你真的想要,你当然可以将以上所有内容组合在一个命令中:

fun! s:reformat(line1, line2)
    " Remember number of lines for later
    let l:before = line('$')

    " Join the lines
    execute 'normal! ' . (a:line2 - a:line1 + 1) . 'J'

    " Put newline before else
    :s/else/\relse/

    " Run tabular; since the number of lines change we need to calculate the range.
    " You could also use one of the other methods here, if you want.
    let l:line2 = a:line2 - (l:before - line('$'))
    execute a:line1 . ',' . l:line2 . 'Tabularize /{/'
endfun

command! -range Reformat :call s:reformat(<line1>, <line2>)