Practical Vim
每日Vim
规则:
操作 = 操作符 + 命令
一.模式
普通模式
无论光标在何处,删除一个单词最快
daw
,记忆delete a word
<C-a>
命令 对数字执行加法,<C-x>
命令 对数字执行减法, 在命令前加上count可以进行算数运算gu
修改为小写/gU
修改为大写(operator)ap
表示一段a paragraph
插入模式
backspace
的代替操作:<C-h>
删除一个字符<C-w>
删除前一个单词<C-u>
删除至行首
<C-o>zz
,进入插入-普通模式,zz重新绘制屏幕<C-r> {register}
,插入寄存器的内容,- 插入表达式寄存器的内容 = 利用表达式寄存器做计算
- 插入复制寄存器的内容 =
<C-r> 0
可视模式
v
:面向字符的可视模式
V
:面向行的可视模式
<C-v>
:面向列的可视模式
gv
:重新选择上次的high light区域
切换选区的活动端点,正常是左上的端点固定,右下的端点活动,
o
使得右下的端点固定,左上的端点活动使用
o
切换端点配合w
:下一个单词b
上一个单词e
单词的词尾<C-v>
切换到列模式,配合I
在行首插入,和r
单次替换同时修改列文本:
<C-v>
切换到列模式选中区域后,c
替换所有.在
<C-v>
模式中i
和a
不在是插入,请使用I
,和A
命令行模式
ex命令格式:
: [range] delete/substitute/ []/{}
命令:
:[range]delete [x]
删除指定行到寄存器x:[range]yank [x]
复制指定范围行到寄存器x:[line]put [x]
在指定行后粘贴寄存器x中的内容:[range] copy {address}
把指定范围内的行拷贝到{address}
所指定的行下:[range] move {address}
把指定范围内的行移动到{address}
所指定的行下:[range] join
连接指定范围内的行:[range] normal {commands}
对指定范围的每一行执行普通模式命令:[range] substitute/{pattern}/{string}/{flags}
把指定范围内出现的{pattern}
地方替换为{string}
:[range] global/{pattern}/[cmd]
对指定范围内出现匹配{pattern}
的所有行,在其上执行ex命令
特殊符号含义:
- 单个数字表示行,
$
表示文件尾部(在普通模式下表示行尾部),.
表示当前行%
表示文件中的所有行. 全局替换 :%s/待替换字符串/新字符串{address}+n
表示行偏移. 显示当前行,到偏移3行后的内容: .,.+3p
地址指定范围:
- 格式:
{start},{end}
- 格式:
normal命令,(指定范围上执行普通模式命令),一般配合
.
使用,进行大量重复操作,- 如果其他普通模式命令一定要和normal分开,
- normal命令会自动将光标移动到每一行的起始处
例如给指定行添加分号
:.+1,$ normal A;
<C-r><C-w>
将当前光标的单词插入到命令行,- 在执行ex命令时使 插入单词到command line
- 查看
vimrc
文档,:h <C-r><C-w>
完整的命令行窗口:
q:
打开ex命令历史的命令行窗口 (注意和退出命令是相反的,难怪有时候会按错按出来)q/
:打开查找命令历史的命令行窗口<C-f>
从command line mode切换到命令行窗口(保留当前输入的命令)
使用shell的几种操作
<C-z>
挂起vim,然后fg回到vim:shell
命令行启动shell,然后exit
回到vim:!{cmd}
命令行执行一条shell命令,%
表示当前文件名
有关标准输入输出
:read !{cmd}
将命令的标准输出插入到光标下:[range] write !{cmd}
,选中范围,让后将其作为命令的标准输入- 好用:
[range] write !tee xxx.file
:将当前选中的buffer的内容写入到外面的一个文件中
- 好用:
- 使用命令作为过滤器(filter),(选中输入->修改->写回).
[range] !{cmd}
二.文件
buffer基本管理:
vim中buffer是文件在内存的映像
- buffer列表的基本操作:
- 显示缓冲区列表
ls
, b{n}
切换到buffer n
,b {bufname}
等价于上面bp=bprev
切换到上一个buffer,bn=bnext
切换到下一个buffer,bf=bfirst
切换到第一个buffer,bl=blast
切换到最后一个Buffer%
表示当前显示的buffer,#
表示轮换的Buffer(使用<C-^>
可以快速切换到轮换buffer)、- 删除buffer(不影响文件,删除的内存的数据)
bdelete
- 显示缓冲区列表
- 参数列表(
args
)记录了启动时传递给vim的文件列表.这个不是固定的,是可以改变的.- glob模式打开文件
args *.*
打开当前模式的任意文件,arg **/*.css
,打开上一层目录中所有子目录包含的css文件,所以*
只能在当前目录用,**
可以递归进入子目录. - 参数列表执行shell命令 :args `pwd`
- glob模式打开文件
- 隐藏缓冲区
hidden buffer
:- 当修改当前buffer后不保存想要切换到另一个buffer中需要加
!
强制执行,此时被修改的Buffer有h
标记表示变成hidden buffer. edit!
重新将磁盘文件覆盖到当前被修改的Buffer中write
=w
将buffer内容写回到磁盘中qa!
强制退出所有的窗口wa!
所有buffer写回到磁盘
- 当修改当前buffer后不保存想要切换到另一个buffer中需要加
:argdo {cmd}
,对所有的缓冲区执行命令cmd
window(窗口)基本管理:
窗口是显示buffer的
打开新窗口默认的buffer是上一个窗口的buffer
创建窗口:
<C-w>s
:水平切割窗口 水平当做split
默认记<C-w>v
:竖直切割窗口vertical
edit
给窗口更换buffer
创建窗口+更换buffer(
<C-w>s+edit
):split {filename}
vsplit {filename}
窗口切换:
<C-w> {hjkl}
和normal mode下的移动是一样的<C-w>w
在窗口间循环切换
关闭窗口(buffer是仍然存在的)
:close
命令 关闭活动窗口:only
命令 关闭除活动窗口外的所有窗口<C-w>c
同上<C-w>o
同上
窗口大小设置
<C-w>=
窗口等宽,高.
类似
:argdo {cmd}
,:windo {cmd}
是对所有的窗口执行cmd命令.例如 设置所有窗口的当前工作目录
:windo lcd Documents/
标签页将窗口分组
vim的标签页是vim窗口的容器,将窗口组织到工作区中,类比桌面workspace
- 创建标签页
tabedit {filename}
- 关闭标签页
tabc{lose}
- 关闭除当前标签页外的所有标签页
tabo{nly}
- 标签页切换
- ex命令(不方便) 去第n个标签页:
:tabn {N}
, 下一个标签页:tabn
,上一个标签页tabp
- normal mode: 去第n个标签页
{N}gt
, 下一个标签页gt
, 上一个标签页gT
- ex命令(不方便) 去第n个标签页:
- 标签页移动
tabm{ove} {N}
:方便重新给标签页排序
基本文件管理
ex命令
edit
打开文件:edit {filename}
配置技巧: cmd mode下
:%%
得到当前文件所在目录(配合edit使用)cnoremap <expr> %% getcmdtype() == ':' ? expand('%:h').'/' : '%%'
vim默认的文件管理器,
edit {path}
打开文件管理器窗口:edit %
,%符号表示当前文件的完整路径:edit %:h
,:h
会去掉当前文件,只留下路径,vim打开一个不存在的文件后如何保存:
- 先创建此文件存在的目录:
!mkdir -p %:h
- 保存文件就行
:w
- 先创建此文件存在的目录:
三.快速移动和快速转跳
行之间的基本移动:
实际行和屏幕行之间的移动
j
和k
命令会根据实际行,gj
和gk
按照屏幕行来上下移动.移动到行首
0
,移动到屏幕行首g0
移动到行首
^
((自己单独做了映射)),移动到屏幕行首g^
移动到行尾
$
((自己单独做映射)),移动到屏幕行尾g$
基于单词和字符串的移动
- 单词(之间)的移动
w
下一个单词
b
上一个单词
e
单词的词尾
ge
上一个单词的词尾字符串对应的移动()
vim的字符串比单词更简单,非空白字符序列组成(以空白字符分割):
W
下一个字符串B
上一个字符串E
下一个字符串词尾gE
上一个字符串词尾
基于字符查找的移动
通过normal mode下的
f{char}
来快速移动,所以f{char}
配置,
和;
移动非常快N
,和n
是重复搜索模式的查找.;
和,
是重复f
命令的查找最好不要将
<Leader>
映射为,
通过normal mode下
t{char}
快速移动到查找字符的前一个字符,类似f{char}
.f记忆为find,t记忆为
till
,直到查找到指定的字符为止d/c
配合t
使用可以删除行中的指定的一段. 例如删除句子的后半句:f,dt.
通过查找进行移动
/
正向查找?
反向查找删除某段的基本操作:
- 利用可视模式选中某段然后d删除
v/xx<CR>d
,
删除某段的高级操作(利用操作符+动作,把查找看成动作理解)
d{motion}
+查找动作 =d/xxx<CR>
- 利用可视模式选中某段然后d删除
使用文本对象 (第一类: 操作分隔符或者分隔符内部文本对象)
使用模式: 在visual mode或者操作符之后(操作符待决模式 operator-pending)
永远以a
或者i(inner)
开头出现,
aB
/iB
选中区块内的文本,at
/it
选中tag标签内的文本a
/i
+{char}
,char表示想选中一对符号中的其中一个,a(
/a<
,a"
,a[
,a{
,a'
使用场景例如:
快速修改头文件
ci<
快速删除
const char *
的内容di"
快速复制
const char *
的内容yi"
d
配合a
类文本对象(因为删除前后的空格),c
配合i
类文本对象(只选中文本对象,不修改前后的空格)使用文本对象 (第二类: 操作文本块)
iw
/aw
当前单词, (iw不包含空格)iW
,aW
当前字符串is
,as
当前句子ip
,ap
当前段落
使用位置标记(局部/全局)
标记当前光标位置:
m{a-zA-Z}
,小写buffer内可用,大写全局可用.(m表mark)跳转到光标位置: 反引号调转到标记位置, 单引号跳转到标记的行的位置.
单标记技巧:
mm
来标记,``m`跳转回来vim默认的位置标记
- `` 两个反引号: 表示跳转之前的光标位置
- `. 上次修改的地方
- `^ 上次插入的地方
- `[ 上次修改或复制的起始位置
- `] 上次修改或复制的结束位置
- `< 上次高亮区的起始位置
- `> 上次高亮区的结束位置
%
在当前行找到和当前字符匹配的项目,{}
/[]
,()
等,利用`` 反引号快速回到另一个匹配项的位置,然后r修改遍历跳转列表 (each window)
遍历改变列表
changes
(each buffer)vim会记录每次修改的地方,
:changes
查看所有修改.g;
和g,
遍历每次修改的光标位置
查看光标下的指定文件
gf
,配合跳转命令<C-o>
,和<C-i>
跳去跳回
四.寄存器
常用寄存器
windows中的剪切(cut),复制(copy),粘贴(paste),
对应vim中的delete(剪切),yank(复制),put(粘贴).
windows使用的系统的剪贴板,vim默认使用的无名寄存器,
无名寄存器
""
x
,s
,d{motion}
,c{motion}
,y{motion}
命令即使指定了其他寄存器,但是无名寄存器也会有内容(无名寄存器发生总是缺省的)
黑洞寄存器
"_
真正的删除文本,因为没有把内容复制到其他的寄存器上去.
"_diw
,真正删除一个单词
复制专用寄存器
"0
``y{motion}`命令复制的文本不仅在无名寄存器中,也在复制专用寄存器中.
Numbered register 0 contains the text from the most recent yank command,unless the command specified another register with [“x].
26个有名寄存器
"a-"z
,- 如果使用大写字母引用,就是将新内容追加到原有内容之后.小写字母引用是覆盖.
系统剪切板
"+
或者"*
,- 通过外部寄存器可以与系统剪切板交换文本
表达式寄存器
"=
.- 技巧1:在插入模式中当做计算器使用:
<C-r> =
使用计算功能
- 技巧1:在插入模式中当做计算器使用:
只读寄存器
"%
当前文件名"#
上一个文件名".
上次插入的文件":
上次执行的ex命令
查找寄存器
"/
,- 保存的是上一次查找的内容
寄存器内容替换高亮区文本
- 基本操作:
yiw
->ve
->"0p
- 可视模式下的p会交换无名寄存器的内容,将寄存器内容填充到高亮区域,将高亮区域内容复制到无名寄存器中.
宏
宏的读取与执行
q{register}
开始录制宏,q
停止录制,@{register}
执行宏,@@
执行上一次执行的宏
- 录制宏的修改序列会保存到寄存器上.
加次数回放宏
f+
->s +<C-[>
->qq;.q
->22@q
在连续的文本行上修改
串行操作: 录制宏完毕后执行
n
次(宏里面有换行的操作)例如:
qa0f.r)w~jq
->再执行3次宏3@a
并行操作:宏录制完后,可视化选中然后执行·
:normal @{register}
(对所有的行进行操作)例如:
qa0f.r)w~q
->VG
->:normal @a
给宏追加命令
用大写字母引用寄存器录制宏就行:
qa0f.r)w~q
->追加j
,qAjq
就行.
在一组文件中执行宏(x)
给列表编号
使用let设一个变量后使用表达式寄存器插入.
:let i=0
qaI<C-r>=i<CR>) <C-[>
:let i+=1
//目的是标号+1q
然后并行执行宏
编辑宏的内容
- 先
"{register}p
粘贴到当前文档中, - 然后再文档中修改宏的内容
- 然后
V"ay
就能又覆盖到a寄存器中.完成修改宏内容.
五.模式
查找
1. 基本查找操作
/
正向查找,?
反向查找,n
跳转到下一处,N
跳转到上一处.
2. 高亮查找匹配
开启
hlsearch
可以高亮所有匹配项,:noh
暂时关闭高亮在
重新绘制屏幕的基础上增加 noh
功能的新映射项配置:nnoremap <silent> <C-l> :<C-u>noh<CR><C-l>
3. 预览第一处匹配
- 开启:
set incsearch
4. 统计匹配个数
% s///gn
5.将光标偏移到查找匹配的结尾
光标默认会匹配于首字母上,
/{pattern}/{offset}<CR>
,能够控制光标定位在匹配结果的附近,例如定位在匹配结果的结尾
/{pattern}/e<CR>
或者已经进行了查找操作
/{pattern}<CR>
,则//e<CR>
,也能重定位光标
6.对完整的查找匹配进行操作
global/{pattern}/{cmd} 可以做到,
7. 向前查找当前光标下的单词
*
替换 (substitute命令
1. 完整格式:
[range]substitute/{pattern}/{string}/{flags}
常用flags总结:
g
一行的所有匹配项都发生替换,而不仅是一行的第一项n
不进行替换,报告匹配项目数量c
确认每一匹配项是否需要替换e
没有匹配项时,不报告错误.&
重复上次使用的标志位
替换域的特殊字符
\r
换行符\t
制表符\\
反斜线\{n}
插入第n个子匹配\0
或&
插入匹配模式的所有内容~
上次的{string}
\={vim script}
,执行表达式
2. 全局替换
利用标志位
g
使得每行所有匹配项都替换,%
表示所有行的范围:% s/{pattern}/{string}/g
,就能实现全局替换.
3.手动控制每一次的替换
利用标志位
c
(confirm),同上:% s/{pattern}/{string}/gc
y替换此处,
n忽略此处
q退出此处
l 替换此处后退出
a 替换后面所有退出
4.重用上次查找模式
{pattern}
留空,将使用上一次的查找模式,(上一次的substitute
命令和/
和?
都算上一次查找模式)
5.用寄存器内容替换
查找域留空,使用上一次的,但是替换域留空,则空字符串替换,等价于删除
传寄存器的值,
:%s//<C-r>{register}/g
,寄存器的值会直接复制到命令行上.(特殊字符需要提前进行转义)引用寄存器.
:%s//\=@{register}/g
,如上,=表示执行vim脚本,@{register}在普通模式下是执行宏,在这里表示引用某个寄存器.固定场景(充分利用寄存器):
现在文档中复制替换的字符串(到”0寄存器中).
/
查找所有替换域:%s//\=@0
, 利用//
表示上一次查找域,@0
表示引用赋值寄存器. (充分利用了vim的特性)
6.重复上一次的substitute命令
- 重复上一次s命令:
:s//~/&
,//
表使用上一次的查找域,~
表使用上一次替换字符串,&
表使用上一次标志位. g&
快捷键对应:%s//~/&
,比上面多一个全局的范围,弥补没有加%
的单次替换
global命令
1.完整格式:
:[range] global[!]/{pattern}/ [range][cmd]
缺省的范围是
%
,查找域留空//
也是使用上一次的查找域. cmd缺省是print命令.还有
global!
和vglobal
命令,vglobal
在匹配的行的剩下的行上执行命令.(v表示invert)清空寄存器:
q{register}q
,没有指令就是空
2.利用global提取内容到寄存器
例:提取所有注释内容到寄存器B
:global/\/\//yank B
,为什么要用大写,(小写 覆盖只剩下第一条匹配的,大写追加)
例:提取所有注释内容到文件尾部
:global/\/\//t$
,
3.给指定区域属性排序
给单条规则(大括号内的属性)排序:
vi{:sort
,(利用文本对象)- 给所有规则排序
:global/{/ .+1,/}/-1 sort
解释:- ex命令会接受 范围 作为参数(也就是接受global传来的范围)
- [cmd],准确来说是
[range][cmd]
,也就是在传入的范围的基础上再次展开
.+1,/}/-1
符合/{pattern}/{offset}
模式,也就是在查找模式下偏移光标,.,/}/
去掉偏移表示 从当前行开始到}
行结束,
4. global的广义形式(global与任意ex命令组合范式)
:g/{start} .,{finish} [cmd]
例如给 {} 区域内文本缩进
:global/{/ .+1,/}/-1 >
基本操作-(单字母)
因为不熟悉单独拧出来
r
替代一个字符,R
替换模式(插入模式的一种特例)f{char}
行内字符查找,F{char}
行内字符反向查找s
是删除后插入,S
是删除整行后插入c
是d
是一样的类型(操作符),d是删除,c是修改,所以c
后面接命令/次数,C
修改至行尾- 和
A
对应的在 行首插入的是I
. t/T{char}
将光标定位到前/后字符,t{char}
可作为{motion}
使用.m{position}
在当前光标位置设置标记,:marks
查看设置的标记,`{position}定位到标记处
其他模式
- 普通,插入,命令,可视化,模式外,还有一种模式叫做**
operator pending mode
,操作符待决模式,意思就是输入了gu
/d
/c
/y
这个操作符,在状态栏可以看到情况,等待输入command来完成一个操作** - 在插入模式下,想执行一条普通模式的命令,使用
ctrl-o
进入插入-普通模式,然后执行一条命令后就自动回到了插入模式 <C-g>
在可视模式和选择模式切换,选择模式就是Windows的可视模式
,输入使得选中的文本被删除
奇怪场景解决办法:
(以超级用户权限保存文件)在修改权限不够的文件后无法保存,只能退出重新sudo编辑的解决办法:
:write !sudo tee %
原理:
write将当前Buffer内容作为 !{cmd}的标准输入,tee命令将标准输入转为文件,%表示当前文件名,所以就很好理解:将当前修改的Buffer的内容重新以root写到当前文件.
标准语法
操作=操作符+动作motion
,可以在前缀时用可视模式替换后面的动作.
我尝试做的vim配置
1 |
|
Surround.vim插件可以用用
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!