Vim 中你母亲从未告诉你的黑暗角落是什么?

829

有很多人在讨论常见技巧,特别是“Vim+ctags 技巧”。

然而,我不是指那些新手会觉得很酷的常用快捷键。我指的是经验丰富的 Unix 用户(无论是开发者、管理员还是两者兼备等),他们认为自己知道一些我们中绝大部分人从未听说或梦想过的东西。这些不仅能够使他们的工作更轻松,而且还很炫酷和有技巧性。毕竟,Vim 是运行于全球最黑暗角落的操作系统之一,因此它应该有一些只有少数特权人士知道并愿意与我们分享的复杂技巧。


12
分割窗口并打开集成文件浏览器(水平分割)。 - user3218088
3
这个问题似乎非常有益 - 从点赞数来看...,我投票支持重新开放 - 或许会有更棒的答案出现 - 如果有用的话可以被点赞 - 因此为 SOF 带来更多价值。通过按照得票数排序的答案,我在短短5分钟内学到了很多酷炫的东西...这里真的有很有价值的东西,为什么要关闭这么有价值的东西呢?这不是很有益吗? - jhegedus
7
很荒谬这个问题被关闭了。谈论法律主义。 - Lepidopterist
3
我认为这个问题非常有建设性,但更适合在 Quora 上讨论。 - Diogo Melo
3
为什么现在有两个投票要删除这个问题?这样做有什么好处吗? - Akavall
显示剩余4条评论
70个回答

102

我不确定这是否算作“黑暗角落”(dark-corner-ish),不过我刚学会了它...

:g/match/y A

将包含"match"的所有行复制到寄存器"a/@a"中。(使用大写字母A会使vim附加复制而不是替换之前的寄存器内容。) 最近我在制作Internet Explorer样式表时经常使用它。


计数,计数是我没有见过的。 :) - Sasha
不错啊。从没听说过……可能会有用 :P - skinp
7
你可以使用 :g! 命令来查找不匹配某个模式的行,例如:要删除所有不包含“set”的行,可以输入命令 :g!/set/normal dd。 - tsukimi
4
有时候最好按照Tsukimi说的那样,过滤掉不符合你模式的行。该命令的缩写版本是 :v/PATTERN/d。解释::v:g! 的缩写,:g 命令将任何ex命令应用于行。:y[ank]:normal 也可以使用,但在这里最自然的做法就是 :d[elete] - Pandu
你也可以使用 :g/match/normal "Ayy 命令 -- normal 关键字让你可以运行普通模式下的命令(这些命令可能更加熟悉)。 - Kimball Robinson

97

想查看您的命令历史记录吗?

q:

然后浏览、编辑,最后执行命令。

有没有做过对两个文件进行类似修改,来回切换的情况?(比如源代码和头文件)

:set hidden
:map <TAB> :e#<CR>

然后在这些文件之间切换标签页。


137
我经常不小心按到了“q:”键... - Josh Lee
12
另外,从ex编辑器(:)中,您可以按CTRL-f弹出命令历史记录窗口。 - Jason Down
6
我也是。我几乎讨厌这个命令,因为我经常不小心使用它。 - bradlis7
10
q/和q?可以用来对搜索模式执行类似的功能。 - bpw1621
6
在命令模式下,按下 ":" 或 "/" 后再按下 "<C-f>" 将弹出相同的历史记录菜单。因此,如果您经常意外按下 "q:",仍然可以重新映射它并访问这个很棒的模式。 - idbrii
显示剩余4条评论

90

Vim可以打开URL,例如:

vim http://stackoverflow.com/

需要参考页面源代码时非常好用。


2
对我来说,它没有打开源代码;相反,它似乎使用elinks将渲染页面转储到缓冲区中,然后打开该缓冲区。 - Ivan Vučica
执行此命令时出现错误。 - Dzung Nguyen
7
在末尾加上斜杠效果更佳。巧妙的技巧! - Thomas
@Vdt:如果您能发布您的错误信息,那将非常有用。如果您遇到了这个错误:“error (netrw) neither the wget nor the fetch command is available”,那么您显然需要将其中一个工具添加到您的PATH环境变量中。 - Isaac Nequittepas
当人们发送到粘贴服务的链接时,如果忘记选择语法高亮,我发现这个特别有用。通常只需在链接后添加“&raw”,然后在vim中打开链接即可。 - Dettorer

72

宏可以调用其他宏,也可以调用自身。

例如:

qq0dwj@qq@q

...将会删除文件末尾之前每行的第一个单词。

这是一个相当简单的示例,但它展示了vim的一个非常强大的特性。


3
我不知道宏可以自我重复,太酷了。注意:qx 开始录制到寄存器 x(他使用 qq 来表示寄存器 q)。0 移动到行首。dw 删除一个单词。j 向下移动一行。@q 将再次运行宏(定义循环)。但你忘记最后用 "q" 停止录制宏,并通过输入 @q 实际运行宏。 - Kimball Robinson
2
我认为这是有意的,作为一个嵌套和递归的宏。 - Yktula
7
抱歉,您提供的内容无法翻译成有意义的语句。它似乎包含了一些随机字符和符号。请提供更准确、完整的信息以便我能够帮助您进行翻译。 - Gerardo Marset
8
另一种实现这个目标的方法是在寄存器a中录制一个宏,对单行进行一些转换,然后使用V进行行选择,输入:normal! @a将你的宏应用到所选区域的每一行。 - Nathan Long
1
宏被递归使用绝对是一种设计选择,只有当它们到达缓冲区的末尾时才会停止。如果您的宏是递归的并且陷入无限循环,请按ctrl+c停止它,而不是杀死整个vim进程。 - Stun Brick
显示剩余3条评论

58
假设您已经编译了 Perl 和/或 Ruby 支持,:rubydo 和 :perldo 将在范围内的每一行上运行一个 Ruby 或 Perl 单行命令(默认为整个缓冲区),其中 $_ 绑定到当前行的文本(去除换行符)。通过操作 $_ 可以改变该行的文本。
您可以使用这个功能来执行一些在脚本语言中容易实现但在 Vim 内置命令中不太明显的操作。例如,要反转一行中单词的顺序:
:perldo $_ = join ' ', reverse split

在每一行的末尾插入一个由8个随机字符(A-Z)组成的字符串:

:rubydo $_ += ' ' + (1..8).collect{('A'..'Z').to_a[rand 26]}.join

在这里,您一次只能处理一行文本,并且无法添加新行。


如果我只想让perldo在指定的行上运行怎么办?或者只在选定的几行上运行? - Sujoy
5
您可以像其他命令一样给它一个范围。例如:1,5perldo 只会在第 1-5 行操作。 - Brian Carper
4
同样地,如果你已经编译好了必要的支持,pydopy3do 也可以用于 Python。 - Derecho
如果您已经编译支持Lua,那么可以使用 luado 来运行Lua。 - adam_0
红宝石,红宝石,dooooo...红宝石,红宝石,doooo。像Megan Draper一样唱出来。 - user1065985
显示剩余3条评论

58

^O和^I

向旧的/新的位置移动。当您通过搜索、移动命令等在文件中移动时,vim会记住这些“跳转”,因此您可以向后(^O - O代表旧的)和向前(^I - 键盘上紧挨着I的位置)重复这些跳转。在编写代码和执行大量搜索时,我发现它非常有用。

gi

返回到上次停止插入模式的位置。我经常编辑然后搜索某些东西。要返回到编辑位置,请按gi。

gf

将光标放在文件名上(例如包含头文件),按下gf即可打开文件。

gF

类似于gf,但识别格式“[文件名]:[行号]”。按gF将打开[文件名]并将光标设置为[行号]。

^P和^N

编辑时自动补全文本(^P - 上一个匹配项,^N - 下一个匹配项)。

^X^L

在编辑时完成相同的行(对于编程非常有用)。您编写代码,然后回想起您在文件中有相同的代码。只需按^X^L即可完成整行。

^X^F

自动补全文件名。您写入"/etc/pass"。嗯。您忘记了文件名。只需按下^X^F即可完成文件名。

^Z或:sh

临时进入shell。如果您需要快速bash:

  • 按^ Z(将vi放入后台)返回原始shell,然后按fg返回vim
  • 按:sh进入子shell,按^ D /退出返回vi

使用 ^X^F 时,我最讨厌的是文件名包含 = 符号,在许多情况下会导致它做出糟糕的事情(如 ini 文件、makefiles 等)。我使用 se isfname-== 来解决这个问题。 - sehe
3
内置的自动完成功能就在那里等待被发现,可以对其进行优化。 - joeytwiddle

52

输入==命令可以根据上一行调整当前行的缩进。

实际上,您可以使用一个等号和任何移动命令。={movement}

例如,您可以使用%移动命令在匹配的大括号之间移动。将光标定位于以下代码中的{处:

if (thisA == that) {
//not indented
if (some == other) {
x = y;
}
}

按下 = % 即可立即获得以下内容:

if (thisA == that) {
    //not indented
    if (some == other) {
        x = y;
    }
}

或者,您可以在代码块内使用=a{,而不是将光标定位在 { 字符上。


哦,我不知道缩进方面的这个问题。 - Ehtesh Choudhury
通常情况下,不需要完全精准地放置大括号。虽然经常我会使用=}vaBaB=,因为这样更加独立。另外,v}}:!astyle -bj 更符合我的代码风格,但是我可以通过简单的%!astyle -aj将其转换成你的风格。 - sehe
8
当粘贴内容时,gg=G 的功能非常方便。 - remmy
相关:重新缩进糟糕的缩进代码 在 Vim SE 上。 - kenorb
@kyrias 噢,我一直都是这样做的 ggVG= - Braden Best

51
" insert range ip's
"
"          ( O O )
" =======oOO=(_)==OOo======

:for i in range(1,255) | .put='10.0.0.'.i | endfor

2
除了看起来像是一个玩笑答案,我不明白这有什么用处。还有其他人能给我解释一下吗? - Ryan Edwards
2
打开vim,然后输入“:for i in range(1,255) | .put='10.0.0.'.i | endfor”。 - codygman
2
@RyanEdwards 填写 /etc/hosts 可能。 - Ruslan
28
这是一个很棒的回答,不是关于创建IP地址的部分,而是暗示了VIM可以在命令中使用循环。 - dotancohen
4
i10.0.0.1<Esc>Y254p$<C-v>}g<C-a> 的翻译内容。 - BlackCap
显示剩余3条评论

47

这是一个很好的技巧,可以使用不同的编码重新打开当前文件:

:e ++enc=cp1250 %:p

当你需要处理遗留编码时,它会非常有用。支持的编码在一个表格中列出,位于encoding-values下面(参见help encoding-values)。类似的事情也适用于++ff,因此如果第一次错误地获取了Windows / Unix行结束符,则可以重新打开文件(参见help ff)。


从未使用过这种东西,但肯定会加入我的技巧库... - Sasha
伟大的提示,谢谢。常见的有效编码列表:
  • UTF-8
  • UTF-16
  • ISO 8859-1
  • Windows-1252
  • ASCII
- Adriano Varoli Piazza
2
我今天用了这个,但我觉得我不需要指定“%:p”; 只需打开文件并使用“:e ++enc = cp1250”就足够了。 - Ivan Vučica
会有相同的效果吗?:set encoding=cp1250 - laz
@laz:不,'encoding'选项定义了Vim用于存储所有内部数据(如缓冲区、寄存器等)的编码方式。'fileencoding'定义了当前缓冲区的编码方式。但是,如果您在打开文件后设置了'fenc',Vim将会将文件从当前编码转换为新编码。因此,使用特定编码打开文件的唯一方法是使用++enc选项。 - Paul
1
":e +b %' 同样适用于以二进制模式重新打开文件(不会改变换行符)。" - intuited

40
imap jj <esc>

8
那么你要如何打出“jj”这个组合呢? :P - hasen
7
你有多频繁地输入"jj"这个词组?至少在英语中是这样吗? - ojblass
55
我将大写锁定键映射为Esc键,因为它除此之外没有什么用处。我的映射是全局的,所以它还有一个额外的好处,就是永远不用担心意外按到它。唯一的缺点是不太容易对别人喊话。 :) - Alex
8
@Alex:肯定的,大写锁定键就是死亡。 "等等,什么?哦,那是ZZ?...糟糕。" - intuited
8
不确定有多少人会在Vim中编写Matlab代码,但“ii”和“jj”通常用作计数器变量,因为“i”和“j”保留用于表示复数。 - brianmearns
显示剩余13条评论

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