vim学习经验 09

我想补充gu,gUg~,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() .. "_"映射普通模式的逐行操作符(类似guugUU)...Vim的串插值操作符._用作带操作符的动作.
_表示向下计数1行.gU_d_gUUdd意思一样.
<expr>参数允许指定计数.
vimrcplugin/前运行,如果不想用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")
//输出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'
然后是,

    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中,某些动作会在更改,跳转和标记列表中自动记录.这可以避免.使用noautocmdkeepjumps目的是防止副作用.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! 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)
//保证首字母总大写.如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创建新的标签文件,这样就可搜索了.

posted @   zjh6  阅读(12)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示