协慌网

登录 贡献 社区

Vim 最有效的捷径是什么?

我听过很多关于Vim 的消息 ,包括优点和缺点。看起来你应该(作为开发人员)使用 Vim 比使用任何其他编辑器更快。我正在使用 Vim 来做一些基本的东西,而且我最好用 Vim 的效率低 10 倍。

当你谈论速度时你应该关心的两件事(你可能不太关心它们,但你应该这么做)是:

  1. 使用左手和右手交替使用键盘是最快捷的方式。
  2. 永远不要触摸鼠标是第二种尽可能快的方法。移动手,抓住鼠标,移动鼠标并将其带回键盘需要很长时间(而且您经常需要查看键盘才能确保将手正确地移到正确的位置)

这里有两个例子,说明为什么我对 Vim 的效率低得多。

复制 / 剪切和粘贴。我一直这样做。使用所有当代编辑器,您可以用左手按Shift 键 ,然后用右手移动光标以选择文本。然后按 Ctrl + C复制,移动光标并按Ctrl + V粘贴。

有了 Vim,它太可怕了:

  • yy复制一行(你几乎从不希望整个行!)
  • [number xx]yyxx行复制到缓冲区中。但你永远不知道你是否选择了你想要的东西。我经常要做[number xx]dd然后u撤消!

另一个例子? 搜索和替换。

  • PSPad 中按 Ctrl + f,然后键入要搜索的内容,然后按Enter 键
  • 在 Vim 中: / ,然后键入要搜索的内容,那么如果有一些特殊的字符放在\ 每个特殊字符前,然后按Enter 键

与 Vim 的一切都是这样的:似乎我不知道如何以正确的方式处理它。

NB: 我已经阅读过 Vim 备忘 单了 :)

我的问题是:

你使用 Vim 的方式是什么让你比现代编辑更有效率?

答案

你和Vim 的问题在于你没有理解vi

你提到切割与yy并抱怨你几乎从不想切割整条线。事实上,编程源代码的程序员经常想要在整行,行范围和代码块上工作。但是, yy只是将文本拖入匿名复制缓冲区(或者在vi 中调用 “注册”)的众多方法之一。

vi的 “禅” 是你说的一种语言。最初的y是动词。声明yyy_的同义词。 y加倍,以便更容易键入,因为它是如此常见的操作。

这也可以表示为dd P (删除当前行并将副本粘贴回原位; 将副本留在匿名寄存器中作为副作用)。 yd “动词” 将任何运动视为 “主题”。因此, yW “从这里(光标)猛拉到当前 / 下一个(大)单词的y'a ” 并且y'a是 “从这里y'a包含名为 ' a '的标记的行。”

如果您只了解基本的向上,向下,向左和向右光标移动,那么vi将不会比您的 “记事本” 副本更高效。 (好吧,你仍然会有语法高亮和处理文件大于大约 45KB 左右的文件; 但是在这里和我一起工作)。

vi有 26 个 “标记” 和 26 个 “寄存器”。使用m命令将标记设置为任何光标位置。每个标记由单个小写字母指定。因此, ma将 ' a ' 标记设置为当前位置, mz设置 ' z ' 标记。您可以使用' (单引号)命令移动到包含标记的行。因此'a移动到包含 ' a ' 标记的行的开头。您可以使用` (反引号)命令移动到任何标记的精确位置。因此`z将直接移动到 ' z ' 标记的确切位置。

因为这些是 “运动”,所以它们也可以用作其他 “陈述” 的主题。

因此,削减任意文本选择的一种方法是删除标记(我通常使用 ' a '作为我的 “第一” 标记,' z '作为我的下一个标记,' b '作为另一个标记,' e ' 作为另一个(我不记得曾经在使用vi 的 15 年中交互式地使用了超过四个标记; 一个人创建了一个关于如何不干扰一个人的交互环境的宏使用标记和寄存器的约定。)然后我们去到我们想要的文本的另一端; 我们可以从任何一端开始,没关系。然后我们可以简单地使用d`a来剪切或者y`a来复制。因此整个过程有 5 个击键开销(6 个如果我们以 “插入” 模式开始并且需要Esc out 命令模式)。一旦我们剪切或复制,然后粘贴副本就是一次击键: p

我说这是剪切或复制文本的一种方法。但是,它只是其中之一。通常,我们可以更简洁地描述文本范围,而无需移动光标并删除标记。例如,如果我在文本段落中,我可以分别使用{}移动到段落的开头或结尾。因此,为了移动一段文本,我使用{ d} (3 次击键)剪切它。 (如果我碰巧已经在段落的第一行或最后一行,那么我可以简单地分别使用d}d{

“段落” 的概念默认为通常直观合理的东西。因此它通常适用于代码和散文。

我们经常知道一些模式(正则表达式),它标记了我们感兴趣的文本的一端或另一端。向前或向后搜索是vi中的移动。因此,它们也可以在我们的 “陈述” 中用作 “主语”。所以我可以使用d/foo从当前行切换到包含字符串 “foo” 和y?bar的下一行,从当前行复制到包含 “bar” 的最新(上一行)行。如果我不想要整行,我仍然可以使用搜索动作(作为他们自己的语句),删除我的标记并使用前面描述的`x命令。

除了 “动词” 和 “主语” 之外, vi还具有 “对象”(在术语的语法意义上)。到目前为止,我只描述了匿名寄存器的使用。但是,我可以使用 “对象” 引用前缀 " (双引号修饰符)来使用 26 个” 命名 “寄存器中的任何一个。因此,如果我使用"add我将当前行切换到 ' a ' 寄存器中,如果我使用"by/foo然后我将文本的副本从这里拖到包含”foo“的下一行到' b ' 寄存器。要从寄存器粘贴,我只需在粘贴前加上相同的修饰符序列: "ap将 “ a ” 寄存器内容的副本粘贴到光标后面的文本中, "bP将副本从 ' b ' 粘贴到当前行之前。

这种 “前缀” 概念还将语法 “形容词” 和 “副词” 的类似物添加到我们的文本操作语言中。大多数命令(动词)和动作(动词或对象,取决于上下文)也可以使用数字前缀。 3J表示 “加入接下来的三行”, d5}表示 “从当前行到第五段末尾从此处删除”。

这都是中级vi 。它们都不是特定于Vim 的 ,如果您准备好学习它们,那么vi 中有更多高级技巧。如果您只掌握这些中间概念,那么您可能会发现很少需要编写任何宏,因为文本操作语言足够简洁和富有表现力,可以使用编辑器的 “本机” 语言轻松完成大多数事情。


一系列更高级的技巧:

有许多:命令,最值得注意的是:% s/foo/bar/g全局替换技术。 (这不是先进的,而是其他:命令可以)。整体:命令集在历史上由vi以前的版本继承为ed (行编辑器)和后来的ex (扩展行编辑器)实用程序。实际上vi是如此命名的,因为它是ex的可视化界面。

:命令通常在文本行上运行。 edex是在终端屏幕不常见且许多终端都是 “电传打字”(TTY)设备的时代写的。所以通常使用文本的打印副本,通过极其简洁的界面使用命令(常见的连接速度为 110 波特,或大约每秒 11 个字符 - 这比快速打字员要慢; 通常滞后很常见)多用户交互式会话; 另外,通常有一些节省纸张的动机)。

因此,most :命令的语法包括地址或地址范围(行号),后跟命令。当然,人们可以使用文字行号: :127,215 s/foo/bar在 127 和 215 之间的每一行上将 “foo” 的第一次出现更改为 “bar”。还可以使用一些缩写,例如.$分别为当前和最后一行。也可以使用相对前缀+-分别表示在临界线之前或之前的偏移。因此:: :.,$j表示 “从当前行到最后一行,将它们全部连接成一行”。 :%代表:1,$ (所有行)。

:... g:... v命令有一些解释,因为它们非常强大。 :... g是 “全局” 的前缀,用于将后续命令应用于与模式(正则表达式)匹配的所有行,而:... v将此类命令应用于与给定模式不匹配的所有行(“v” “来自”conVerse“)。与其他ex命令一样,这些命令可以通过寻址 / 范围引用作为前缀。因此:.,+21g/foo/d意味着 “删除包含字符串”foo“的任何行,从当前一行到下一行 21 行”,而:.,$v/bar/d表示 “从这里到结尾文件,删除任何不包含字符串 “bar” 的行。

有趣的是,常见的 Unix 命令grep实际上是受这个ex命令启发的(并且以它的记录方式命名)。 ex命令:g/re/p (grep)是他们记录如何 “全局”“打印” 包含 “正则表达式”(re)的行的方式。当使用edex时, :p命令是任何人学习的第一个命令,通常是编辑任何文件时使用的第一个命令。这是你打印当前内容的方式(通常只使用以下一页:.,+25p或其他一些内容)。

请注意:% g/.../d或(它的 reVerse / conVerse 对应:: :% v/.../d是最常见的使用模式。但是还有一些其他ex命令值得记住:

我们可以使用m来移动线,并使用j来连接线。例如,如果你有一个列表并且想要将所有匹配的东西分开(或者相反地不匹配某些模式)而不删除它们,那么你可以使用类似:% g/foo/m$ ... 以及所有 “foo“行将被移动到文件的末尾。 (注意关于使用文件末尾作为临时空间的另一个提示)。这将保留所有 “foo” 行的相对顺序,同时从列表的其余部分中提取它们。 (这相当于做了类似的事情: 1G!GGmap!Ggrep foo<ENTER>1G:1,'ag/foo'/d (将文件复制到自己的尾部,通过grep过滤尾部,并删除所有东西从头部)。

为了连接线,通常我可以找到需要连接到其前任的所有线的模式(例如,所有以 “^” 开头的行而不是某些项目符号列表中的 “^ *”)。对于这种情况,我会使用:: :% g/^ /-1j (对于每个匹配的行,上一行并加入它们)。 (顺便说一句:对于试图搜索子弹线并加入到下一个子弹线的子弹列表,由于几个原因不起作用...... 它可以将一个子弹线连接到另一个子弹线,它不会加入任何子弹线到所有它的延续; 它只会在匹配上成对使用)。

几乎不用提,您可以使用我们的老朋友s (替代)与gv (全球 / 逆全局)命令。通常你不需要这样做。但是,请考虑某些情况,您只想在匹配其他模式的行上执行替换。通常,您可以使用带有捕获的复杂模式,并使用返回引用来保留您不想更改的行的部分。但是,通常更容易将匹配与替换分开:% g/foo/s/bar/zzz/g - 对于包含 “foo” 的每一行,用 “zzz” 替换所有 “bar”。 (例如:% s/\(.*foo.*\)bar\(.*\)/\1zzz\2/g仅适用于那些由 “foo” 预先设置的 “bar” 实例的情况同一条线; 它已经足够笨拙,并且必须进一步修复以捕获所有 “bar” 在 “foo” 之前的情况

关键是ex命令集中不仅仅有psd行。

:地址也可以指标记。因此,如果它位于 ' a '和' b ' 标记之间的行之间,则可以使用:: :'a,'bg/foo/j将包含字符串 foo 的任何行连接到其后续行。 (是的,所有前面的ex命令示例都可以通过添加这些类型的寻址表达式来限制到文件行的子集)。

这是非常模糊的(在过去的 15 年里,我只使用了几次这样的东西)。但是,我会自由地承认,如果我花时间思考正确的咒语,我经常以迭代和交互方式做事,这可能会更有效率地完成。

另一个非常有用的viex命令是:r读取另一个文件的内容。因此:: :r foo在当前行插入名为 “foo” 的文件的内容。

更强大的是:r!命令。这将读取命令的结果。它与暂停vi会话,运行命令,将其输出重定向到临时文件,恢复vi会话以及从 temp 中读取内容相同。文件。

更强大的是! (砰)和:... !爆炸)命令。这些也执行外部命令并将结果读入当前文本。但是,他们还通过命令过滤我们文本的选择!我们可以使用1G!Gsort来对文件中的所有行进行排序1G!GsortGvi “goto” 命令; 它默认转到文件的最后一行,但可以用行号作为前缀,例如 1,第一行)。这相当于ex变体:1,$!sort 。作家经常使用!使用 Unix fmtfold实用程序进行重新格式化或 “自动换行” 选择文本。一个非常常见的宏是{!}fmt (重新格式化当前段落)。程序员有时会使用它来通过缩进或其他代码重新格式化工具来运行代码,或者只是部分代码。

使用:r!而且!命令意味着可以将任何外部实用程序或过滤器视为编辑器的扩展。我偶尔使用这些脚本从数据库中提取数据,或者使用从网站上提取数据的wgetlynx命令,或从远程系统中提取数据的ssh命令。

另一个有用的ex命令是:so (简称:source )。这将读取文件的内容作为一系列命令。当您启动正常六世 ,含蓄,执行:source~/.exinitrc文件(和Vim通常不会在这个~/.vimrc ,很自然)。使用此方法的是,您可以通过简单地获取一组新的宏,缩写和编辑器设置来动态更改编辑器配置文件。如果您偷偷摸摸,您甚至可以将其用作存储编辑命令序列的技巧,以便按需应用于文件。

例如,我有一个七行文件(36 个字符),它通过wc运行一个文件,并在包含该字数统计数据的文件顶部插入一个 C 风格的注释。我可以使用如下命令将 “宏” 应用于文件: vim +'so mymacro.ex' ./mytarget

viVim+命令行选项通常用于在给定的行号处开始编辑会话。但是,一个鲜为人知的事实是,可以通过任何有效的ex命令 / 表达式跟随+ ,例如 “source” 我在这里完成的命令; 对于一个简单的例子,我有脚本调用: vi +'/foo/d|wq!' ~/.ssh/known_hosts以非交互方式从我的 SSH 已知主机文件中删除条目重新映像一组服务器。

通常使用 Perl,AWK, sed编写这样的 “宏” 要容易得多(事实上,就像grep是受ed命令启发的实用程序)。

@命令可能是最模糊的vi命令。偶尔教授近十年的高级系统管理课程,我遇到的人很少见过。 @执行寄存器的内容,就像它是viex命令一样。
示例:我经常使用:: :r!locate ...在我的系统上找到一些文件并将其名称读入我的文档。从那里我删除任何无关的命中,只留下我感兴趣的文件的完整路径。而不是费力地Tab -ing 通过路径的每个组件(或者更糟糕的是,如果我碰巧卡在没有 Tab 完成的机器上支持vi 的副本我只是使用:

  1. 0i:r (将当前行转换为有效的:r命令),
  2. "cdd (删除行进入”c“寄存器)和
  3. @c执行该命令。

这只有 10 次击键(表达式"cdd @c实际上对我来说是一个手指宏,因此我几乎可以像任何常见的六个字母一样快速输入它)。


一个发人深省的想法

我只是抓住了vi的力量,我在这里所描述的一切都不是vim命名的 “改进” 的一部分!我在这里描述的所有内容都应该适用于 20 或 30 年前的任何旧版本的vi

有些人比以往任何时候都更多地使用了vi的力量。

你在谈论文本选择和复制,我认为你应该看看Vim 可视模式

在可视模式下,您可以使用 Vim 命令选择文本,然后您可以使用选择执行任何操作。

请考虑以下常见情况:

您需要选择下一个匹配的括号。

你可以这样做:

  • v%如果光标在开始 / 结束括号上
  • 如果光标在括号内,则vib

您想要在引号之间选择文本:

  • vi“双引号
  • vi'单引号

你想选择一个花括号块(在 C 风格的语言中很常见):

  • viB
  • vi{

您想要选择整个文件:

  • ggVG

可视块选择是另一个非常有用的功能,它允许您选择文本的矩形区域,您只需按Ctrl - V启动它,然后选择所需的文本块并执行任何类型的操作,如 yank,删除,粘贴,编辑等。编辑面向列的文本非常棒。

一些生产力提示:

聪明的动作

  • *#搜索光标向前 / 向后的单词。
  • w到下一个字
  • W到下一个以空格分隔的单词
  • b / e到当前单词的开头 / 结尾。 (仅限空格的B / E
  • gg / G跳转到文件的开头 / 结尾。
  • %跳转到匹配的 {..} 或(..)等。
  • { / }跳到下一段。
  • '.跳回到最后编辑的行。
  • g;跳回最后编辑的位置。

快速编辑命令

  • I在开头插入。
  • A追加结束。
  • o / O在当前 / 之前打开一个新行。
  • v / V / Ctrl+V可视模式(选择文字!)
  • Shift+R替换文本
  • C改变线的剩余部分。

组合命令

大多数命令接受数量和方向,例如:

  • cW = 改变直到单词结尾
  • 3cW = 改变 3 个字
  • BcW = 开始全字,改变全字
  • ciW = 改变内在的词。
  • ci" = 改变内在之间”..“
  • ci( = 改变文本之间的(..)
  • ci< = 在 <..> 之间更改文本(需要在 vimrc 中set matchpairs+=<:>
  • 4dd = 删除 4 行
  • 3x = 删除 3 个字符。
  • 3s = 替换 3 个字符。

有用的程序员命令

  • r替换一个字符(例如rdd替换当前字符)。
  • ~改变案例。
  • J加入两行
  • Ctrl + A / Ctrl + X 递增 / 递减数字。
  • .重复上一个命令(一个简单的宏)
  • ==修复行缩进
  • >缩进块(在可视模式下)
  • < unindent block(在可视模式下)

宏录制

  • q[ key ]开始录制。
  • 然后按q停止录音。
  • 可以使用@[ key ]播放宏。

通过使用非常具体的命令和动作,VIM 可以为下一行重播那些确切的动作。 (例如 A 用于追加到结尾, b / e分别将光标移动到单词的开头或结尾)

精心设置的设置示例

# reset to vim-defaults
if &compatible          # only if not set before:
  set nocompatible      # use vim-defaults instead of vi-defaults (easier, more user friendly)
endif

# display settings
set background=dark     # enable for dark terminals
set nowrap              # dont wrap lines
set scrolloff=2         # 2 lines above/below cursor when scrolling
set number              # show line numbers
set showmatch           # show matching bracket (briefly jump)
set showmode            # show mode in status bar (insert/replace/...)
set showcmd             # show typed command in status bar
set ruler               # show cursor position in status bar
set title               # show file in titlebar
set wildmenu            # completion with menu
set wildignore=*.o,*.obj,*.bak,*.exe,*.py[co],*.swp,*~,*.pyc,.svn
set laststatus=2        # use 2 lines for the status bar
set matchtime=2         # show matching bracket for 0.2 seconds
set matchpairs+=<:>     # specially for html

# editor settings
set esckeys             # map missed escape sequences (enables keypad keys)
set ignorecase          # case insensitive searching
set smartcase           # but become case sensitive if you type uppercase characters
set smartindent         # smart auto indenting
set smarttab            # smart tab handling for indenting
set magic               # change the way backslashes are used in search patterns
set bs=indent,eol,start # Allow backspacing over everything in insert mode

set tabstop=4           # number of spaces a tab counts for
set shiftwidth=4        # spaces for autoindents
#set expandtab           # turn a tabs into spaces

set fileformat=unix     # file mode is unix
#set fileformats=unix,dos    # only detect unix file format, displays that ^M with dos files

# system settings
set lazyredraw          # no redraws in macros
set confirm             # get a dialog when :q, :w, or :wq fails
set nobackup            # no backup~ files.
set viminfo='20,\"500   # remember copy registers after quitting in the .viminfo file -- 20 jump links, regs up to 500 lines'
set hidden              # remember undo after quitting
set history=50          # keep 50 lines of command history
set mouse=v             # use mouse in visual mode (not normal,insert,command,help mode


# color settings (if terminal/gui supports it)
if &t_Co > 2 || has("gui_running")
  syntax on          # enable colors
  set hlsearch       # highlight search (very useful!)
  set incsearch      # search incremently (search while typing)
endif

# paste mode toggle (needed when using autoindent/smartindent)
map <F10> :set paste<CR>
map <F11> :set nopaste<CR>
imap <F10> <C-O>:set paste<CR>
imap <F11> <nop>
set pastetoggle=<F11>

# Use of the filetype plugins, auto completion and indentation support
filetype plugin indent on

# file type specific settings
if has("autocmd")
  # For debugging
  #set verbose=9

  # if bash is sh.
  let bash_is_sh=1

  # change to directory of current file automatically
  autocmd BufEnter * lcd %:p:h

  # Put these in an autocmd group, so that we can delete them easily.
  augroup mysettings
    au FileType xslt,xml,css,html,xhtml,javascript,sh,config,c,cpp,docbook set smartindent shiftwidth=2 softtabstop=2 expandtab
    au FileType tex set wrap shiftwidth=2 softtabstop=2 expandtab

    # Confirm to PEP8
    au FileType python set tabstop=4 softtabstop=4 expandtab shiftwidth=4 cinwords=if,elif,else,for,while,try,except,finally,def,class
  augroup END

  augroup perl
    # reset (disable previous 'augroup perl' settings)
    au!  

    au BufReadPre,BufNewFile
    \ *.pl,*.pm
    \ set formatoptions=croq smartindent shiftwidth=2 softtabstop=2 cindent cinkeys='0{,0},!^F,o,O,e' " tags=./tags,tags,~/devel/tags,~/devel/C
    # formatoption:
    #   t - wrap text using textwidth
    #   c - wrap comments using textwidth (and auto insert comment leader)
    #   r - auto insert comment leader when pressing <return> in insert mode
    #   o - auto insert comment leader when pressing 'o' or 'O'.
    #   q - allow formatting of comments with "gq"
    #   a - auto formatting for paragraphs
    #   n - auto wrap numbered lists
    #   
  augroup END


  # Always jump to the last known cursor position. 
  # Don't do it when the position is invalid or when inside
  # an event handler (happens when dropping a file on gvim). 
  autocmd BufReadPost * 
    \ if line("'\"") > 0 && line("'\"") <= line("$") | 
    \   exe "normal g`\"" | 
    \ endif 

endif # has("autocmd")

设置可以存储在~/.vimrc ,也可以存储在/etc/vimrc.local系统范围内,然后使用以下命令从/etc/vimrc文件中读取:

source /etc/vimrc.local

(你必须更换#用注释符" ,使其在 VIM 工作,我想给正确的语法高亮显示在这里)。

我在这里列出的命令非常基本,到目前为止我使用的主要命令。他们已经让我更有成效,而不必知道所有花哨的东西。