我想补充gu,gU
和g~
,gt(w,iw,$,t,i(,等等)
.
两个目录:doc/(文档)
和plugin/(插件)
开始:
if !exists("g:totitle_default_keys")
let g:totitle_default_keys = 1
endif
//文件底部,加上映射
if g:totitle_default_keys
nnoremap <expr> gt ToTitle()
xnoremap <expr> gt ToTitle()
//视觉模式
nnoremap <expr> gtt ToTitle() .. "_"
nnoremap <expr> gt ToTitle ()
映射普通模式*操作符*
.这样,可操作符+动作/文本块
.
nnoremap<expr>gtt ToTitle() .. "_"
映射普通
模式的逐行操作符(类似guu
和gUU
)...
是Vim
的串插值操作符._
用作带操作符
的动作.
_
表示向下计数1行
.gU_
或d_
与gUU
或dd
意思一样.
<expr>
参数允许指定计数
.
vimrc
在plugin/
前运行,如果不想用gt
(因为已有),而用gz
.
把let g:totitle_default_keys = 0
放在你的vimrc
中,这样,可在vimrc
中自定义
映射.因为!exists ("g:totitle_default_keys")
和if g:totitle_default_keys
都返回假.
let g:totitle_default_keys = 0
nnoremap <expr> gz ToTitle()
xnoremap <expr> gz ToTitle()
nnoremap <expr> gzz ToTitle() .. "_"
然后这样,就可以了.
! ToTitle(type = "")
if a:type ==# ""
set opfunc=ToTitle
return "g@"
endif
"细节"
if a:type != "block" && a:type != "line" && a:type != "char"
let l:words = a:type
let l:wordsArr = trim(l:words)->split("\s\+")
call map(l:wordsArr, "s:capitalize(v:val)")
return l:wordsArr->join(" ")
endif
"保存当前配置"
let l:sel_save = &selection
let l:reg_save = getreginfo('"')
let l:cb_save = &clipboard
let l:visual_marks_save = [getpos("'<"), getpos("'>")]
try
set clipboard= selection=inclusive
let l:commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
silent exe "noautocmd keepjumps normal! " .. get(l:commands, a:type, "")
let l:selected_phrase = getreg('"')
let l:WORD_PATTERN = "\<\k*\>"
let l:UPCASE_REPLACEMENT = "\=s:capitalize(submatch(0))"
let l:startLine = line("'<")
let l:startCol = virtcol(".")
"用户调用块操作"
if a:type ==# "block"
sil! keepj norm! gv"ad
"
keepj $
keepj pu_
let l:lastLine = line("$")
sil! keepj norm "ap
"
let l:curLine = line(".")
sil! keepj norm! VGg@
exe "keepj norm! 0\<c-v>G$h\"ad"
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>\"aP"
exe "keepj " . l:lastLine
sil! keepj norm! "_dG
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
"用户调用`符/行`操作
else
let l:titlecased = substitute(@@, l:WORD_PATTERN, l:UPCASE_REPLACEMENT, "g")
let l:titlecased = s:capitalizeFirstWord(l:titlecased)
call setreg('"', l:titlecased)
let l:subcommands = #{line: "'[V']p", char: "`[v`]p", block: "`[\<c-v>`]p"}
silent execute "noautocmd keepjumps normal! " .. get(l:subcommands, a:type, "")
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
endif
finally
"恢复设置"
call setreg('"', l:reg_save)
call setpos("'<", l:visual_marks_save[0])
call setpos("'>", l:visual_marks_save[1])
let &clipboard = l:cb_save
let &selection = l:sel_save
endtry
return
opfunc
是什么?为什么它返回g@
?
Vim
有个特殊操作符,g@
操作符函数.允许把
函数分配给opfunc
选项.如果把Foo()
函数赋值给opfunc
,运行g@w
时,会在下个单词上运行Foo()
.如果运行g@i(
,则我在内圆括号
中运行Foo()
.该操作符
函数对创建你自己的Vim
操作符至关重要
.
工作原理:假设有如下映射:
nnoremap <expr> gt ToTitle()`
通过按gtw
,Vim
会检查opfunc
是否为空.如果为空
,则Vim
会给它分配ToTitle
.然后它返回g@
,本质上又一次调用ToTitle
,即可工作了.
最开始时,opfunc
为空,因而a:type
为''
,这样第1段
为真.再调用,赋值,并返回.
刚按下gtw
后,gt
完成上述操作,并返回g@
.返回g@
后,变成g@w
.g@
为函数符号,因而把w
传递给g@
执行.就调用了ToTitle
.
三种动作类型,符/行/块
.g@w
操作符,g@j
操作行,列前后
,则是操作块.串
作为类型参数
传递给函数
.
可用
function! Test(some_arg)
echom a:some_arg
endfunction
//再
:set opfunc=Test
来测试.
接着:
if a:type != "block" && a:type != "line" && a:type != "char"
let l:words = a:type
let l:wordsArr = trim(l:words)->split("\s\+")
//分开空格
call map(l:wordsArr, "s:capitalize(v:val)")
return l:wordsArr->join(" ")
//元素大写并合并.
endif
//虽然违反了单一职责原则
你可这样:
:echo ToTitle("once on a time")
//输出Once Upon a Time
接着,
let l:sel_save = &selection
let l:reg_save = getreginfo('"')
let l:cb_save = &clipboard
let l:visual_marks_save = [getpos(""<"), getpos("">")]
临时变量,保存当前状态.
set clipboard= selection=inclusive
//`selection`为包含,`clipboard`为空
默认为包含,见:h'clipboard'
和:h'selection'
然后是,
:commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
silent exe "noautocmd keepjumps normal! " .. get(l:commands, a:type, "")
//安静,为静转执行,否则在屏幕底部显示通知.
#{}
为字典.
"l:commands"
局部变量为以"lines","char"
和"block"
为键的哈希
.
noautocmd
,执行后续命令
而不触发自动命令
.
keepjumps
,在移动
时不记录光标移动
.
在Vim
中,某些动作
会在更改,跳转和标记
列表中自动记录.这可以避免.使用noautocmd
和keepjumps
目的是防止副作用
.normal
按普通命令执行命令串
,..
是Vim
的串插值
语法.get()
是接受列表,blob
或字典的getter
方法.这里,传递给它的是l:commands
字典.
关键是a:type
.a:type
是:符行块
之一.因此,如果a:type
是'line'
,你执行"noautocmd keepjumps normal! '[V']y"
.更多信息,见:h silent,:h:exe,:h:noautocmd,:h:keepjumps,:h:normal
和:hget()
.
'[
和']
记住g@
命令的开始和结束
动作位置.
按'[
会移动光标到第一行
,这是运行g@
时开始地方.V
是逐行可视
模式命令.最后,']
移动光标
到先前更改或复制出
的文本末尾,但此时,它移动光标
到最后一次g@
操作末尾.最后y
复制出选定文本
.
上段,就是复制要执行文本
.其他命令类似,
let l:commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
//类似操作.
接着:
let l:selected_phrase = getreg('"')
取无名
寄存器内容.然后是正则
:
let l:WORD_PATTERN = "\<\k*\>"
\<
和\>
是单词边界,\k
是关键字模式
.这是匹配模式.最后有
let l:UPCASE_REPLACEMENT = "\=s:capitalize(submatch(0))"
模式.用\=
.submatch(0)
为整个匹配.
let l:startLine = line("'<")
//返回行号
let l:startCol = virtcol(".")
//光标列.先保存
处理块操作:
if a:type ==# "block"
sil! keepj norm! gv"ad
"
keepj $
keepj pu_
let l:lastLine = line("$")
sil! keepj norm "ap
"
let l:curLine = line(".")
sil! keepj norm! VGg@
exe "keepj norm! 0\<c-v>G$h\"ad"
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>\"aP"
exe "keepj " . l:lastLine
sil! keepj norm! "_dG
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
sil!
静默运行,keepj
在移动
时保留跳转历史
.然后执行普通gv"ad
命令.gv
选择最后一个可视高亮
显示文本(在pancakes
例中,它将重新高亮
显示所有三个'cakes'
)."ad
删除他们,并在a
寄存器存储
.结果,现在,a
中存储了3
个块.然后
$
移动到文件
最后一行.pu_
在光标
位置下方插入一行
,keepj
不会改变跳转历史
.
let l:lastLine = line("$")
//行号存储在`lastLine`变量中
再复制进尾行:
然后,存储
光标所在当前行
位置:
let l:curLine = line(".")
然后是:
sil! keepj norm! VGg@
exe "keepj norm! 0\<c-v>G$h\"ad"
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>\"aP"
exe "keepj " . l:lastLine
sil! keepj norm! "_dG
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
这里:
递归调用VGg@
,VG
可视块,g@
递归调用函数
.这样都大写了.
exe "keepj norm! 0\<c-v>G$h\"ad"
删除高亮,并存储在a
寄存器中.h
为右移,c-v
为可视,G
为尾.
exe "keepj " . l:startLine
光标移回起始行
.再粘贴:
exe "sil! keepj norm! " . l:startCol . "\<bar>\"aP"
<bar>
是|
动作,为跳至多少列的意思.再粘贴.
exe "keepj " . l:lastLine
sil! keepj norm! "_dG
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
最后,删除.清理,回到原位.
然后是行/符
代码.
:titlecased = substitute(@@, l:WORD_PATTERN, l:UPCASE_REPLACEMENT, "g")
//关键.
let l:titlecased = s:capitalizeFirstWord(l:titlecased)
call setreg('"', l:titlecased)
let l:subcommands = #{line: "'[V']p", char: "`[v`]p", block: "`[\<c-v>`]p"}
silent execute "noautocmd keepjumps normal! " .. get(l:subcommands, a:type, "")
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
@@
包含无名
寄存器文本.l:WORD_PATTERN
是单个关键字
匹配.l:UPCASE_REPLACEMENT
调用capitalize()
命令,g
替换所有.
let l:titlecased = s:capitalizeFirstWord(l:titlecased)
//保证首字母总大写.如an不会大写时.
接着
call setreg('"', l:titlecased)
放入
无名寄存器.
let l:subcommands = #{line: "'[V']p", char: "`[v`]p", block: "`[\<c-v>`]p"}
silent execute "noautocmd keepjumps normal! " .. get(l:subcommands, a:type, "")
这里用p
粘贴.
exe "keepj " . l:startLine
exe "sil! keepj norm! " . l:startCol . "\<bar>"
移回来.恢复设置:
call setreg('"', l:reg_save)
call setpos("'<", l:visual_marks_save[0])
call setpos("'>", l:visual_marks_save[1])
let &clipboard = l:cb_save
let &selection = l:sel_save
无名,位置,剪切板及选区
.
用:set ft=help
设置文档
类型.
关键字,*totitle*
这样写.也可用|
包围关键字
.这是vim
的内部链接.C-]
跳进,C-[
跳出.
:helptags ~/.vim/doc
创建新的标签文件,这样就可搜索
了.