如何在Vim中定义换行符的异常?

5
我使用Vim编辑文件,记录了终端命令行及其描述。所有的命令行都以$开头,所以我的文件看起来像这样:
This is a description of what this
command does, it can be quite long and
should have line breaks.

$ ./the_command.sh
这些实际上是Viki文件,但我想此问题适用于任何文件类型。我已打开文件类型检测并正确识别文件。
现在的问题是:
我想在所有文本中插入(硬)换行,但不包括实际命令行副本,这些副本可以通过前导$轻松识别。
在Vim中,能否根据模式定义适用于换行规则的例外情况?我应该在viki文件的语法文件中进行吗?
更新 使用Herbert和Jefromi建议的组合,我现在有了以下内容在我的.vimrc文件中:
au CursorMovedI *.viki call SetTextWidth()

function! SetTextWidth()
    if getline(".")=~'^\$'
        set textwidth=1000
    else
        set textwidth=80
    endif
endfunction

它完全符合我的要求。谢谢大家!


只是想澄清一下:我希望在我输入时自动插入换行符。在我的 .vimrc 文件中,我有一行 "autocmd FileType viki setlocal textwidth=80" 专门为 viki 文件设置文本宽度,这个设置很好用,但是它也会将我的 "$ COMMAND" 行分割开来。 - tospo
3个回答

3

我了解当您说要“硬换行”时,您是想让Vim自动换行,例如到达textwidth列。我认为最好的方法是定义一个'au'命令,在以“$”开头的行上设置textwidth为一个高数字(大于最长可能的行)。

因此,每当您在一行上进入或退出插入模式时,类似以下内容将更改textwidth:

au InsertEnter call SetTextWidth()
au InsertLeave call SetTextWidth()

function! SetTextWidth()
    if getline(line('.')) =~ '^\$'
        " [edit: 'set textwidth = 0' is preferable to line below]
        set textwidth =1000
    else
        set textwidth=78
    endif
endfunction

你可能希望使用CursorMoved/CursorMovedI组,而不是InsertEnter/Leave,因为它们更加细粒度。每当你移动光标时,它们就会被触发,所以函数会被调用很多次,但是这个函数足够简单,可能不会在性能上引入任何明显的降级。
如果你想完全不使用函数,你可以尝试使用类似以下代码:
au InsertEnter exec "set tw=" . getline(line('.'))=~'^\$' ? 1000 : 78
au InsertLeave exec "set tw=" . getline(line('.'))=~'^\$' ? 1000 : 78

非常感谢这个解决方案。不过应该是简单的“getline('.')”而不是“getline(line('.'))”,对吧?这种方法的问题在于,它只有在你开始编辑一行、输入“$”,然后退出并重新进入插入模式时才起作用。否则,在进入INSERT模式时,该行开头没有“$”,这意味着会插入换行符,因此在退出INSERT模式时当前行中也没有前导的“$”。我现在必须尝试使用CursorMovedI,我想那应该可以。 - tospo
@tospo--是的,你正确指出了InsertEnter/Leave解决方案的限制。由于我很少在没有至少一次退出插入模式的情况下开始和完成78个或更多字符的文本行,所以这对我来说不是问题。此外,如果你意识到这个限制并输入一个长行,你可以确保<esc>退出插入,然后再进入插入以获取textwidth改变。我更喜欢这种方法,而不是每次移动光标时都运行事件代码,但对于这个特定的解决方案,CursorMoved的唯一问题可能只存在于我的想象中。 - Herbert Sitz
唯一还不起作用的情况是当我粘贴东西时——在这种情况下将不会应用换行符。这不是太大的问题,只是出于兴趣:我如何捕获那个事件?在 Vim 事件列表中没有找到任何内容。我认为 CurcorMoved 会解决问题,但我猜在 NORMAL 模式下设置 textwidth 也不会起作用,对吧?这就是你所说的手动实现换行吗? - tospo
@Herbert:在我的系统上使用CursorMovedI事件似乎没有任何明显的负面影响,因此我认为应该没问题。 - tospo
@tospo -- 对于粘贴,一个解决方案是重新映射 'p' (和 'P') 键到类似 'nmap p :call DoPaste()' 的内容,然后有一个 DoPaste 函数,它可以重置 tw 或使用 'paste' 选项,例如:'function DoPaste() | set paste | normal p | set nopaste | endfunction'。 - Herbert Sitz
显示剩余2条评论

2
我相信这会符合您的要求:
set textwidth=78
v/^$/normal gq/^$\|\%$^M

^Mctrl-v后跟着enter

让我们将其分解成更小的部分

/^$\|\%$是一个匹配所有不以$开头的行的模式。 \%$将包括从最后一个$开始的行到文件结尾之间的所有行。

gq/^$\|\%$格式化从当前行到模式/^$\|\%$的范围内的文本

:normal {cmd}在当前行上执行正常模式命令。

:v/pattern/等同于:g!/pattern/,它对每一行都执行一个不匹配/pattern/的命令。

这种解决方案不像@Herbert Sitz的解决方案那样实时对输入进行格式化。相反,它会在最后一次处理时一次性格式化文本。

您当然可以在每次写入之前应用此操作

au BufWritePre filename-pattern-here set textwidth=78 | v/^$/normal gq/^$\|\%$^M
au BufWritePost filename-pattern-here set textwidth=0

谢谢你提供的解决方案。我会选择Herbert的方法,因为我喜欢在输入时应用那些换行符,但还是非常感谢你的解释! - tospo

0

虽然我不确定你想要什么(你应该更具体地说明在哪里(为什么,如何)插入硬换行),但你可以发出像这样的命令

:v:^\s*$: <Your command to insert line break, e.g. s/something/\r/ >

以上命令搜索任何不以任何空格后跟$开头的行,然后执行您指定的命令。

希望对你有所帮助。


正如我所说,我希望在文件的任何地方都有硬换行,除了以$开头的行(命令行的副本)。我希望这些换行符是动态插入的,即通过设置textwidth变量来实现(我已经专门为viki文件类型这样做了)。因此,你建议的不完全是我想要的,还是谢谢! - tospo
好的,现在我明白了。在这种情况下,你应该考虑其他答案。 - Zsolt Botykai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接