基础
c{motion} c会将{motion}指定的片段删除,然后进入插入模式。
. 会记住c插入的词语,然后进行重放
比d{motion}i更加快一点
as, is a sentence/inner sentnce
(
) -> 移动到句子的开始或结束
{} 段落移动
]] section 移动
几个移动命令的具体区别
^ 第一个非空字符
0 第一个字符
g_ 最后一个非空字符
g0 屏幕的第一个字符
g^ 屏幕的第一个非空字符
f<char> char的第一个,right方向
F<char> char的第一个,left方向
t,T 类似,但是字符会放在char的前面(相对左右方向前面) , 注意fFtT不会跨行
; 重复fFtT命令
, 重复fFtT命令,相反方向
:[count]% 进入文件的百分比
wW
bB backward, word
eE forward,end of work
gE backword,go to end of word(next)
从命令行使用vim
Plugin
:profile file <file>
:profile func <pattern>
--startuptime <file> Write startup timing messages to <file>
插件规则:主要代码写在autoload中,必须的启动代码写在plugin.vinm中
:packadd
添加插件
vim和neovim都支持feedkeys,但是只有vim支持term_sendkeys
subsitute
统计: s/…//gn
n: nop,显示匹配的数量
marks
大写的Marks是跨文件,跨Session的。
autocmd
在调试autocmd时,可通过:set verbose=9
来进行调试
buffer-local
:help autocmd-local 查看<buffer>
修饰的autocmd
所谓的buffer-local就是指仅仅针对当前buffer生效,一旦buffer被移除,相应的命令就会失效。
通过:set verbose=6来进行调试,这对于测试autocmd命令是非常有效的
autocmd BufEnter <buffer> echo "hi"
这条命令在进入buffer时显示hi.
定义能够接受范围的命令
像c,d,y,~这样能够在normal模式中改变文本的键叫做操作符,通过:help operator查看帮助。
像这样的操作符可以和motion移动命令结合在一起使用。
= 将文本传递给eqaulprg执行(filter)
> < shift
g@ 调用operatorfunc
通过:omap 查看operator对应的map。
在operator后面可以使用:命令来定位光标的位置
d:call search("f")<CR>
在这种模式下,操作是exclusive的(也就是光标的末尾不包含在操作内),也是按字符的(而不是按行)
仅能用于operator的选择命令
aw 一个word,无论在词的哪里,都可以选中这个词 "*yaw
将一个词语复制到系统粘贴板.
iw 和aw相同,但是指定count时,iw会将空格也计算在内
space between words
3yiw
-> space between
3yaw
-> space between words
aW
iW 类似
as
is sentence
通过vis
查看选中的句子
ap
ip 段落
a[
a] 两个都是一样的,最终广告都停留在末尾]处
i[
i] 在[]内部
a) a( ab 在()内部
i) i( ib
a>
a<
i>
i<
a} a{ aB
i} i{ iB
a" a’ a`
i" i’ i`
组合
dl 等价于x,删除一个单词
diB 删除{}之间的内容
Marks和jumplist
m’ m` 标记上一次的位置,非常有用
g’a 跳转,但是不改变jumplist
jumplist 可以使用C-I, C-O进行跳转
当你使用split开启新的窗口时,jumplist会被复制
通过jumps命令可以看到跳转栈,你可以通过C-O 向上移动
变量名和映射
@@ 对应unamed_register
g@ 定义模式
nmap <silent> <F4> :set opfunc=CountSpaces<CR>g@
vmap <silent> <F4> :<C-U>call CountSpaces(visualmode(), 1)<CR>
function! CountSpaces(type, ...)
let sel_save = &selection
let &selection = "inclusive"
let reg_save = @@
if a:0 " Invoked from Visual mode, use gv command.
silent exe "normal! gvy"
elseif a:type == 'line'
silent exe "normal! '[V']y"
else
silent exe "normal! `[v`]y"
endif
echomsg strlen(substitute(@@, '[^ ]', '', 'g'))
let &selection = sel_save
let @@ = reg_save
endfunction
上面的命令都是将<F4>映射到统计函数,但是支持不同的模式,当在正常模式中使用g@时,实际上type这些参数就是自动设置的,会根据上下文进行设置
注意上面的函数中,a:0表示第一个可变参数
type是line时表示按行执行, char表示字符. 一般而言,类型会根据移动的motion来进行判断
在正常模式下,[和]的mark表示的上一次yank或者changed的文本段的开始或结尾,但当使用g@{motion}时,[和]则是{motion}指示的范围
编写DoComment
我们希望根据文件类型来实现IDEA的 <C-/>注释和去掉注释的快捷键.
我们通过operator来做有很大的灵活性。因此,需要能够通过mark拿到lnum(VIM术语)以及对应的行
line(expr) 获取行号
getline(lnum,end) 获取行内容,如果不存在就返回0. 注意:end也会包含在结果中
setline(lnum,text) 来设置
" set comment prefix
augroup comment-maker
autocmd!
autocmd BufEnter *.go,*.c,*.c++,*.java let b:commentPrefix="//"
autocmd BufEnter *.sh,*.zsh,*.csh,*.ruby let b:commentPrefix="#"
autocmd BufEnter *.vim let b:commentPrefix='"'
augroup END
" NOTE do not use \<SID>, <SID> is replaced by command,not by string
" Usage: <Leader>/4j = comment following 5 lines
" <Leader>/l = comment current line
nnoremap <silent> <Leader>/ :set operatorfunc=<SID>ToggleCommentOperator<CR>g@
vnoremap <silent> <Leader>/ :<C-U>call <SID>ToggleCommentOperator(visualmode())<CR>
function! s:ToggleCommentOperator(type)
" do not do this when there is no comment prefix
if !exists("b:commentPrefix") || len(b:commentPrefix)==0
return
endif
let start = 0
let end = 0
if a:type ==# 'v'
" todo visual mode
let start = line("'<")
let end= line("'>")
else
" line or char
"use '[ and '] to get motion range
let start = line("'[")
let end = line("']")
endif
let lines = getline(start,end)
if len(lines)==0
return
endif
" judge should we comment or uncomment based on first line's
" start
let commentLen = len(b:commentPrefix)
let firstline = lines[0]
let commented = 0
" startswithA commentPrefix
if len(firstline) >= commentLen && firstline[0:commentLen-1] ==# b:commentPrefix
let commented = 1
endif
if commented
" must all line be commented to be safely uncommented
let i = 0
let size = end - start + 1
while i < size
let cur = lines[i]
if len(cur) < commentLen || cur[0:commentLen-1] !=# b:commentPrefix
echoerr "line ".(i+start)." does not start with ".b:commentPrefix.", aka not commented"
return
endif
let i=i+1
endwhile
let i=0
while i < size
let cur = lines[i][commentLen:]
if len(cur) > 0 && cur[0] ==# " "
let cur = cur[1:]
endif
call setline(start+i,cur)
let i=i+1
endwhile
else
" all line can be commented
let i = 0
let size = end - start + 1
while i < size
call setline(start+i,b:commentPrefix . " " .lines[i])
let i=i+1
endwhile
endif
endfunction
quickfix
关键词: getqflist setqflist getloclist setloclist
vim中关于 edit-compile-edit的编程循环有两个支持:quickfix和locationlist
每个窗口都有一个附属的quickfix,当你使用:clist时就会展示所有相关的错误
使用 :vimgrep /patter/ file 即可打开一个:clist
echo getqflist({'lines':["F1:20:Line10"]})
--> {'items': [{'lnum': 20, 'bufnr': 22, 'col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'Line10'}]}
errorformat选项:这个选项指定了一个列表,定义编译器产生的错误该如何解析。例子:
%*[^"]"%f"%*\D%l: %m,"%f"%*\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,"%f"\, line %l%*\D%c%*[^ ] %m,%D%*\a[%*\d]: Entering directory %*[`']%f',%X%*\a[%*\d]: Leaving directory %*[`']%f',%D%*\a: Entering directory %*[`']%f',%X%*\a: Leaving directory %*[`']%f',%DMaking %*\a in %f,%f|%l| %m
其中,逗号用于分隔多个format,逗号之后的空格忽略。
注意: errorformat也可以简写为efm.
如果某一行不能被匹配,则:cn, :cp时会被跳过,也会被标记为not valid.
%f 文件名
%\ \符号
%. .符号
管理quickfix list
使用:cexpr system(‘grep -n xyz’) 可以创建一个quickfix list, 并且跳转到第一个错误
:lexpr … 则可以创建一个跳转的list
跳转: :cc[!] [nr] 展示nr或当前错误,如果错误在另外的buffer,则指定了!时进行跳转
与quickfix list相关的命令有哪些呢?
autocmd的event: QuickFixCmdPre QuickFixCmdPost 用于在执行QuickFix类命令前后执行
相关命令:help QuickFixCmdPre -> [l]make,[l]grep,[l]vimgrep,cbuffer,cfile,cexpr
cgetexpr和cexpr一样,但是不跳转到第一个错误
caddfile file 将错误从文件添加过来
与系统交互
关键词:system systemlist shellescape expand
shellescape 基本上就是将字符串使用单引号引起,然后将字符串中的单引号换成’
但是注意,与grep等系统命令交互时,需要将|进行转义,避免被当成管道。
Command-Editing 模式
在normal模式下输入:即可进入command-editing模式,相应的按键使用cmap, cnoremap。
此外, /和?也可以进入command模式
<C-\> e
可以计算表达式的值
类readline的交互
:cnoremap <C-A> <Home>
:cnoremap <C-F> <Right>
:cnoremap <C-B> <Left>
:cnoremap <Esc>b <S-Left>
:cnoremap <Esc>f <S-Right>
窗口
z{nr}<CR>
设置窗口的大小
<C-W> t
top的window
<C-W> z
关于preview窗口
关于window title
选项title, titlestring ,t_ts 用于决定一个vim与terminal的交互。
首先:set title会将title打开
然后 set titlestring=… 会设置terminal的窗口名称(其实就是整个应用的标题,在MacOS上桌面预览时会展示这个作为标题)
但是,仅在t_ts选项非空的情况下才会用作用。例子:
:auto BufEnter * let &titlestring = hostname() . "/" . expand("%:p")
:set title titlestring=%<%F%=%l/%L-%P titlelen=70
[]命令
Square Brackets又称为方括号命令,主要用于与语言相关的移动。
Tab
<C-W> gt
同gt下一个tab
<C-W> gT
同gT,上一个Tab
help
:help subject
<C-D>
可用于列出所有可帮助的主体补全。
help ^V
CTRL-V的帮助
help i^V
insert模式下 CTRL-V的帮助,等价于help i_CTRL-V
:helpgrep pattern 用于在所有的帮助文件中搜索正则表达式, 搜索内容在quickfix window中打开,你可以通过:cnext等命令进行移动
最有用的几个主题:
:help howto
:help quickref
命令重复(Power of g)
g& 重复 对所有的行重复": s"命令
在shell脚本中使用vim
vim -e -s “$file” < commands.vim
-e: 在ex模式下启动vim, 在这种模式下,只能对command-line进行编辑,不能对文件进行直接的编辑。通过输入visual可进入normal模式。
其实ex模式就是normal模式输入:之后不再退出的状态。
在这个模式下默认有一个buf和file可以直接写入
-s: 告诉vim使用silent,不输出
vim -e -s
normal! iheel
file x.txt
wq
上面就是-s模式启动的例子,这种模式下vim不会启动一个新的窗口,而是如同repl一样接收输入,但不展示命令结果。
在这种模式下执行 echo “msg” 不会有任何展示。
echo hello|vim -
-作为文件参数时,表示从stdin读取内容。vim中作为脚本时需要从stdin读取
这种情况下该怎么设置脚本内容呢?通过-S scriptname指定。
vim -s source file
当-s不与-e出现时,-s表示source的脚本。
vim -w scriptrecord file
-w用于指定记录输入的命令。
gv + s组合
为了完成多次替换,可以使用gv来重复选择之前的文本,然后执行s替换指令。
CTRL_A 对数字进行递增
如果你有一个列表
1
2
3
5
6
7
你想对1,2,3都增加1,办法就是选择1,2,3,然后执行<C-A>
就会递增1.还可以结合visual + /搜索模式框定要改变的文本。
<C-X>
减少1.
do系列命令
:argdo %s… 在每个文件上执行命令
:windo cmd 执行完命令后,光标将会处于最后一个window。
:bufdo cmd
:tabdo cmd
preview窗口
每个tab只能有一个preview窗口,&previewindow属性用于识别一个preview窗口。这就意味着使用split和vsplit时,新的窗口previewwindow属性未设置.
previewheight用于设置preview窗口的高度。
:ptag tag 在preview窗口中打开tag
C-W z
:pclose 关闭任何已经打开的preview窗口
:ppop 类似:pop, 跳转到tagstack中前面的位置
C-W } 在当前word上执行ptag
C-W g} 当前word执行ptjump
:psearch pattern 搜索tag,在preview中打开
:pedit 在preview window中编辑
:wincmd P 跳转到preview window
CursorHold + ptag
自动在preview window中展示tag
:au! CursorHold *.[ch] ++nested exe "silent! ptag " . expand("<cword>")
++nested表示在preview窗口中其他autocmd也能执行,比如语法高亮。
CursorHold受updatetime
选项的影响。
:tags 用于展示
:jumps 用于展示jump list, 当你使用C-O C-I进行移动时 jumpstack会保留,但是使用C-] 时,jump的头部会被替换。
Tag
map进阶
通过使用<Cmd> : noremap x <Cmd>echo mode(1)<cr>
可以避免切换到command-lin 状态,这比:C-U(visual 模式)和C-O:(insert 模式)具有更好的通用性。
relativenumber
由于d6j是如此常用,我们需要知道目标和现在的位置,使用
set relativenumber即可获取当前行的相对位置
如何改变光标的形状?
如果你的光标不幸被改变了默认值,执行下面这条命令恢复它!
:set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor,sm:block-blinkwait175-blinkoff150-blinkon175