Vim 自动命令autocmd详解

回忆

自动命令autocmd

  • 在当前用户的插件目录编辑插件文件
mkdir -p ~/.vim/ftplugin
vi ~/.vim/ftplugin/log.vim
  • 定义函数
function DateInsert()
    $delete
    read !date
endfunction
  • 新建并打开log文件
    • vi a.log
  • 调用函数
    • :call DateInsert()
    • 但是他好像不认识log文件一样
    • :function中没有DateInsert()

设置filetype文件类型

  • :filetype

    • 查看文件类型检测情况
  • :set filetype=log

    • 强制设置文件类型为log
  • :function DateInsert

    • 观察DateInsert函数
  • :call DateInsert()

    • 调用DateInsert()
  • :visual

    • 回到编辑模式 图片描述
  • 但是每次都要手动设置filetype么?

  • 能否自动检测呢?

自动检测文件类型filetype

  • :h filetype

图片描述

  • mkdir ~/.vim/ftdetect
  • vi ~/.vim/ftdetect/log.vim
  • 在文件里面写上
    • echo "i am a log!"
    • set filetype=log
  • vi a.log

图片描述

  • 检测成功
  • :call DateInsert()
  • 函数调用成功
  • 我想把这个函数做成一个命令可以么?

制作命令

  • :nnoremap Di :call DateInsert()<CR>
    • nnoremap
      • n normal 正常模式
      • noremap 不再递归映射
    • Di 指定的命令Command
    • :call DateInsert() 调用函数
    • carriage 回车
  • Di 可以用
  • 再次Di可以刷新时间
  • 我想保存的时候自动调用可以么?

自动命令

  • :autocmd BufWritePre *.log call DateInsert()
    • autocmd 是命令的名字
    • BufWritePre 是{events} 是触发条件 保存buffer之前
    • *.log 是{file_pattern} 是文件模式为*.log
    • call DateInsert() 是{cmd} 具体执行的命令
  • :wq
  • cat a.log
  • 最新的时间已经写在最后一行了
  • 不过这个自动命令最好写在filetype的插件里面

删除自动命令

  • :autocmd
    • 可以查看所有的自动命令autocmd
  • :autocmd! FileWritePre *.log
    • 把他定义为啥都没有
    • 也就删除了

配置插件

  • vi ~/.vim/ftplugin
  • :autocmd BufWritePre *.log call DateInsert()
    • 为什么强调*.log
    • 因为如果是*的话
    • 一旦打开了log文件
    • 保存任何其他文件时都会执行DateInsert()

查看帮助

  • :h autocmd

图片描述

  • {group} 成组 自动命令成组并命名 可省
  • {events} 具体的触发事件
  • {pattern} 文件模式
  • 具体这都啥意思?

成组自动命令

  • 成组是可选的
  • 可以成组也可以不成组
  • 这是绕口令么?
  • 我们来看看
augroup cprograms
    autocmd BufReadPost *.c *.h :set sw=4 sts=4
    autocmd BufReadPost *.cpp :set sw=3 sts=3
augroup END
  • 上面说的是在读取.c、.h 之后
    • 自动设置缩进宽度为4
  • 在读取.cpp之后
    • 自动设置缩进宽度为3
  • 总而言之这两自动命令成为了一个组叫做cprograms
  • 也可以写成
    autocmd cprograms BufReadPost *.c *.h :set sw=4 sts=4
    autocmd cprograms BufReadPost *.cpp :set sw=3 sts=3
  • 下面这样可以删除组中所有的自动命令
  • :autocmd! cprograms

触发事件events

  • :autocmd BufReadPost *.gsm set filetype=asm
    • BufReadPost是读取之后
    • set filetype=asm
      • 把gsm文件的文件格式filetype设置为asm
      • gsm是gnu的assemble language
  • :autocmd Filetype text source ~/.vim/abbrevs.vim
    • Filetype text
      • 是检测到文件类型为文本的时候
      • vi a.txt可以触发Filetype text
    • source ~/.vim/abbrevs.vim
      • 加载一些缩写
  • :autocmd BufNewFile *.[ch] 0read ~/sktletons/skel.c
    • BufNewFile *.[ch]
      • BufNewFile 新建缓存文件的时候
    • *.[ch]
      • 文件类型对应*.c或者*.h
    • 0read ~/sktletons/skel.c
      • 加载c一个骨架文件作为框架
      • 然后添皮加肉

文件模式patterns

  • 通配符wild character
    • *任意多个字符或数字
      • *.c
      • *.h
    • ?一个字母
      • D???.c
    • .
      • 对应一个点dot
    • []或者关系
      • *.[ch]
    • {}或者关系
      • a{b,c}
      • 对应ab或ac
    • /
      • 在对应路径中使用,比如:
      • ~/.vim/ftplugin/*
      • /home/oeasy/*.txt

        自动命令的嵌套

  • 一般来说自动命令的执行结果不会触发另一个自动命令
  • 但是,如果你偏要触发
  • 可以加上 nested
  • :autocmd FileChangedShell * nested edit
    • 比如你打开了文件时触发了自动命令
    • 自动命令做出了一些修改
    • 这些修改就也会触发这个事件

使用文件名和扩展名

  • touch oeasy.txt oeasy.txt.new
    • 新建两个文件
  • vi
  • :echo expand("%:t")
    • 输出文件名
    • 因为现在啥都没有所以啥都没输出
  • :e oeasy.txt
    • 打开oeasy.txt进入缓存buffer
  • :echo expand("%:t")
    • 输出当前文件名
  • :echo "hello i am " . expand("%:t")
    • 输出一句话

图片描述

  • autocmd BufReadPost * echo "hello i am " . expand("%:t")
    • 定义自动命令
    • 读取任何文件格式的文件之后
    • 输出hello 和 当前文件名
  • :h expand
    • 显示hello i am eval.txt
  • 成功

强制触发

  • :doautocmd BufReadPost oeasy.txt
    • 虽然后没有打开oeasy.txt
    • 但是他强制执行了BufReadPost oeasy.txt对应的自动命令autocmd
  • :autocmd BufReadPost *.new excute "doautocmd BufReadPost " . expand("<afile>:r")
    • autocmd BufReadPost *.new
      • 定义*.new打开之后对应的自动命令
    • excute "doautocmd BufReadPost " . expand("<afile>:r")
      • 执行 "doautocmd BufReadPost " . expand(":r")

图片描述

  • 如果我们打开oeasy.txt.new
  • 强制执行oeasy.txt打开后的自动命令
  • 也就是excute "doautocmd BufReadPost oeasy.txt"

图片描述

执行正常模式命令

  • 刚才执行的都是命令行模式的命令
  • 如果我想执行正常模式的命令应该如何呢?
  • :autocmd BufReadPost *.log normal G
    • 读取*.log的时候
    • normal G 在正常模式下执行G
    • 跳到最后一行,查看最新的日志
  • normal进入正常模式,在正常模式下
    • i进入插入模式 esc退出
    • :进入命令模式 esc退出
    • /进入搜索模式 esc退出
  • :autocmd BufReadPost *.txt excute "normal ggONew entry:\<Esc>" | 1read !date
    • autocmd BufReadPost *.txt
      • 制作读取txt文件后对应自动命令
    • excute "normal ggONew entry:\<Esc>" | 1read !date
      • 执行normal ggONew entry:\<Esc>在第一行写字
      • | 然后执行
      • 1read !date 在第二行写日期

图片描述

总结

  • 这个自动命令还是很方便的
  • 打开时、保存时就会有自动执行的操作
  • 自动命令有这么几大元素
    • {event} 触发事件
    • {pattern} 文件模式
    • {cmd} 具体执行命令
    • {augroup} 命令组
  • 自动命令可以新建、删除、列表、查询
  • 还可以强制执行
  • 有这个我们可以
    • 针对每种不同的文件的类型
    • 定义相应的触发事件
    • 然后执行各种各样的命令
    • 方便操作
  • 不过关于文件类型的高亮显示还是没有讲的特别清楚
    • 为什么publicjava文件里面就可以改变颜色呢??🤔
  • 下次再说!

教程来源于Github,感谢overmind1980大佬的无私奉献,致敬!

技术教程推荐

程序员的数学基础课 -〔黄申〕

SQL必知必会 -〔陈旸〕

To B市场品牌实战课 -〔曹林〕

小马哥讲Spring AOP编程思想 -〔小马哥〕

朱涛 · Kotlin编程第一课 -〔朱涛〕

网络排查案例课 -〔杨胜辉〕

中间件核心技术与实战 -〔丁威〕

LangChain 实战课 -〔黄佳〕

互联网人的数字化企业生存指南 -〔沈欣〕