我想补充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() .. "_"
endif
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() .. "_"
然后这样,就可以了.
function! 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
endfunction
opfunc 是什么?为什么它返回g@ ? Vim 有个特殊操作符,g@ 操作符函数.允许把 函数分配给opfunc 选项.如果把Foo() 函数赋值给opfunc ,运行g@w 时,会在下个单词上运行Foo() .如果运行g@i( ,则我在内圆括号 中运行Foo() .该操作符 函数对创建你自己的Vim 操作符至关重要 .
set opfunc=ToTitle
return g@
工作原理:假设有如下映射:
nnoremap <expr> gt ToTitle()`
通过按gtw ,Vim 会检查opfunc 是否为空.如果为空 ,则Vim 会给它分配ToTitle .然后它返回g@ ,本质上又一次调用ToTitle ,即可工作了. 最开始时,opfunc 为空,因而a:type 为'' ,这样第1段 为真.再调用,赋值,并返回.
set opfunc=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")
接着,
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
默认为包含,见:h'clipboard' 和:h'selection' 然后是,
let l: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("$")
再复制进尾行:
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 norm! VGg@
递归调用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>"
最后,删除.清理,回到原位. 然后是行/符 代码.
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>"
@@ 包含无名 寄存器文本.l:WORD_PATTERN 是单个关键字 匹配.l:UPCASE_REPLACEMENT 调用capitalize() 命令,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, "")
这里用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 创建新的标签文件,这样就可搜索 了.
|