我想补充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操作符至关重要.
set opfunc=ToTitle
return
工作原理:假设有如下映射:
nnoremap <expr> gt ToTitle()`
通过按gtw,Vim会检查opfunc是否为空.如果为空,则Vim会给它分配ToTitle.然后它返回g@,本质上又一次调用ToTitle,即可工作了.
 最开始时,opfunc为空,因而a:type为'',这样第1段为真.再调用,赋值,并返回.
=ToTitle
return "g@"
刚按下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个块.然后
keepj $
keepj pu _
$移动到文件最后一行.pu_在光标位置下方插入一行,keepj不会改变跳转历史.
let l:lastLine = line("$")
//行号存储在`lastLine`变量中
再复制进尾行:
sil!
然后,存储光标所在当前行位置:
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 norm!
递归调用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创建新的标签文件,这样就可搜索了.
                









