Vim技能修炼教程(2) - 语法高亮速成

语法高亮速成

我们继续在人间修行Vim技能之旅。上一次我们学习了如何通过vundle安装插件,这次我们迅速向写插件的方向挺进。

我们先学习一个最简单的语法高亮插件的写法。
语法高亮基本上是由三部分组成:

  • 配色方案
  • 正则表达式
  • 配色方案和正则表达式的规则对应关系

简单的三步法写语法高亮

第一步,写匹配的正则表达式

我们举个最简单的例子,以Android的log为例,Android的log格式如下:

--------- beginning of system
05-05 17:55:48.909 I/ActivityManager( 2454): Start proc 15530:com.ss.android.article.lite:pushservice/u0a69 for service com.ss.android.article.lite/com.xiaomi.push.service.XMPushService
05-05 17:55:48.920 V/Build   (15530): clr
05-05 17:55:48.933 D/CompatibilityInfo( 2454): mCompatibilityFlags - 0
05-05 17:55:48.933 D/CompatibilityInfo( 2454): applicationDensity - 640
05-05 17:55:48.933 D/CompatibilityInfo( 2454): applicationScale - 1.0

从中可以看到,前面先是一个时间戳,然后是log的类型,接着是Tag,进程号和具体内容。

最简单的做法,我们就只取log类型和后面的"/"这两个特征,正则表达式这样写:

syn match LogF '\<F/.*'
syn match LogE '\<E/.*'
syn match LogW '\<W/.*'
syn match LogI '\<I/.*'
syn match LogD '\<D/.*'
syn match LogV '\<V/.*'

其中,'<'表示匹配一个单词的词首。详细信息可以通过:help \<来查询,在帮助的pattern.txt中。

第二步,为场景配色

下面,我们需要为这些匹配的场景定义颜色:
有四种属性可以使用:

  • ctermfg: 在终端时运行的前景色
  • ctermbg: 终端时的背景色
  • guifg: 图形界面的前景色
  • guibg: 图形界面的背景色
    定义格式:hi def 配色名 {颜色列表}
    hi def是highlight default的缩写

例:

hi def LogF_color ctermfg=white guifg=white ctermbg=red guibg=red
hi def LogE_color ctermfg=red guifg=red
hi def LogW_color ctermfg=brown guifg=brown
hi def LogI_color ctermfg=grey guifg=grey
hi def LogD_color ctermfg=darkcyan guifg=darkcyan
hi def LogV_color ctermfg=grey guifg=grey

第三步,将配色和正则表达式映射在一起

使用hi def link命令,将第一步和第二步的成果链接在一起就好了。

hi def link LogF LogF_color
hi def link LogE LogE_color
hi def link LogW LogW_color
hi def link LogI LogI_color
hi def link LogD LogD_color
hi def link LogV LogV_color

注:上述代码引用自:https://github.com/serpent7776/vim-logcat/blob/master/syntax/logcat.vim
非作者原创,版权归原作者所有。

更复杂一点的例子

看了最简单的一个实现,我们当然还可以做得更复杂一些:
我们参考一个更复杂一些的例子:https://github.com/gburca/vim-logcat/blob/master/syntax/logcat.vim

" Vim syntax file
" Language:     Android LogCat and aplogd log file syntax
" Maintainer:   Gabriel Burca <gburca dash vim at ebixio dot com>
"
" adb logcat -v time *:V
" 06-09 14:36:00.000 V/AlarmManager( 1484): sending alarm {957ff72 type 3 alarm:android.intent.action.TIME_TICK}
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"
" Or for aplogd logs (syntax group names end with '2'):
" 06-08 16:17:56.101   566   566 E NEW_BHD : Open /sys/class/power_supply/gb_battery
" 06-08 16:17:55.183 18677 20835 D ACDB-LOADER: ACDB -> ACDB_CMD_GET_AFE_COMMON_TABLE
" 06-08 16:17:55.183 18677 20835 D         : ACDBFILE_MGR:Read the devices count as zero, please check the acdb file

if exists("b:current_syntax")
  finish
endif

...

syn match   lcBegin       display '^' nextgroup=lcDate

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
" ^^^^^^
syn match   lcDate        '[0-1]\d-[0-3]\d '
                                \ nextgroup=lcTime

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"       ^^^^^^^^^^^^^
syn match   lcTime        '[0-1]\d:[0-5]\d:[0-5]\d\.\d\d\d '
                                \ nextgroup=lcTag,lcThread2

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"                    ^
syn match   lcPriority    '\(V\|D\|I\|W\|E\|F\)[\/ ]'me=e-1
                                \ containedin=lcTag nextgroup=lcTag2

" Must come after lcPriority so it has higher match priority
syn match   lcTagError    'E\/[[:alnum:]_-]\+'
                                \ containedin=lcTag
" Example:
" 06-08 16:17:56.101   566   566 E NEW_BHD : Open /sys/class/power_supply/gb_battery
syn match   lcTagError2   'E [^:]\+:'
                                \ nextgroup=lcMsgBody

" The component may be empty in some cases
" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"                      ^^^^^^
syn match   lcComponent   '\/[^[:space:](]\+'ms=s+1
                                \ containedin=lcTag

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"                    ^^^^^^^^^^
" 06-09 10:42:06.729 I/        ( 1484): Message with empty component
"                    ^^^^^^^^^^
syn match   lcTag         '\w\/[^(]\s'
                                \ nextgroup=lcThread contains=lcTagError,lcPriority,lcComponent,myTags

" Example:
" 06-08 16:17:55.183 18677 20835 D ACDB-LOADER: ACDB -> ACDB_CMD_GET_AFE_COMMON_TABLE
"                                 ^^^^^^^^^^^^
" 06-08 16:17:55.183 18677 20835 D         : ACDBFILE_MGR:Read the devices count as zero, please check the acdb file
"                                 ^^^^^^^^^
syn match   lcTag2        ' [^:]\s:'
                                \ nextgroup=lcMsgBody contains=myTags

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"                              ^^^^^^^^
syn match   lcThread      '(\s*\d\+):'he=e-1
                                \ nextgroup=lcMsgBody contains=lcNumber
" Example:
" 06-08 16:17:55.183 18677 20835 D ACDB-LOADER: ACDB -> ACDB_CMD_GET_AFE_COMMON_TABLE
"                    ^^^^^^^^^^^^
syn match   lcThread2     '\s*\d\+\s\+\d\+ '
                                \ nextgroup=lcPriority,lcTagError2 contains=lcNumber

" Example:
" 06-09 10:42:06.729 I/chatty  ( 1484): uid=1000(system) Binder:1484_5 expire 1 line
"                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
syn match   lcMsgBody     contained ' .*'
                                \ contains=myKeywords

syn match   lcNumber      contained '0x[0-9a-fA-F]\|\[<[0-9a-f]\+>\]\|\<\d[0-9a-fA-F]'

hi def link lcDate        Comment
hi def link lcTime        SpecialComment

hi def link lcTag         Statement
hi def link lcTag2        Statement
hi def link lcPriority    Identifier
hi def link lcTagError    Error
hi def link lcTagError2   Error
hi def link lcComponent   Normal

hi def link lcThread      Special
hi def link lcThread2     Special

hi def link lcMsgBody     Normal
hi def link lcNumber      Number

hi def link myTags        Function
hi def link myKeywords    Function

与上一个完全自定义颜色不同,这位作者直接将正则表达式映射到语言的预定义配色方案中。比如Comment是注释,Statement是语句,Identifier是标识符等等。具体可以通过:help syntax来学习,我们后面晋阶上仙的教程里也会有详细介绍。
总而言之,这个的正则表达式更复杂了,但是基本原理还是一样的。

例三

下面我们再趁热打铁,来看一个更复杂,也更人性化的例子:https://github.com/thinca/vim-logcat/blob/master/syntax/logcat.vim

我们来看下面一段,根据背景是不是暗的配色而设计两套配色方案,非常贴心:

function! s:define_color()
  if &background is 'dark'
    highlight default logcatLevelVerbose guifg=Gray   ctermfg=Gray
    highlight default logcatLevelDebug   guifg=Cyan   ctermfg=Cyan
    highlight default logcatLevelInfo    guifg=Green  ctermfg=Green
    highlight default logcatLevelWarning guifg=Yellow ctermfg=Yellow
    highlight default logcatLevelError   guifg=Red    ctermfg=Red
  else
    highlight default logcatLevelVerbose guifg=DarkGray   ctermfg=DarkGray
    highlight default logcatLevelDebug   guifg=DarkCyan   ctermfg=DarkCyan
    highlight default logcatLevelInfo    guifg=DarkGreen  ctermfg=DarkGreen
    highlight default logcatLevelWarning guifg=DarkYellow ctermfg=DarkYellow
    highlight default logcatLevelError   guifg=DarkRed    ctermfg=DarkRed
  endif
  highlight default logcatLevelFatal guifg=White ctermfg=White guibg=Red ctermbg=Red
endfunction

小结

小结一下,我们这节只学习三个命令:

  • syntax match: 正则表达式和场景匹配
  • highlight default: 为场景定义配色
  • highlight link: 将上两者联系在一起,也可以链接到预定义的一些标准配色方案上
时间: 2024-11-17 06:51:51

Vim技能修炼教程(2) - 语法高亮速成的相关文章

Vim技能修炼教程(3) - 语法高亮进阶

语法高亮进阶 首先我们复习一下上节学到的三个命令: syntax match用于定义正则表达式和规则的对应 highlight default定义配色方案 highlight link将正则规则和配色方案对应起来 但是,定义好了规则,如何让它自动生效呢?我们需要识别文件类型. 识别文件类型 我们创建一个新插件,建立一个ftdetect目录,然后为这个类型创建一个vim文件,比如之前logcat的例子,我们就建立一个logcat.vim. 例子我们还是找github上的:https://githu

Vim技能修炼教程(14) - 写个ex命令吧

写个ex命令吧 我们第二节开始就写了语法高亮的插件.这一节,我们学习第二种插件的写法,就是写个我们自己的ex命令. 自定义ex命令的命令是:command,我们在~/.vim/下建立一个plugin目录,然后创建一个test.vim之类的文件: :function Hello() :echom "Hello,World!" :endfunction :command Hello call Hello() 首先,我们定义一个函数.函数名的首字母必须大写.函数中调用:echom去显示一个字

Vim技能修炼教程(4) - 基本功

基本功 前面我们学会了插件管理器和如何实现语法高亮,相信大家已经从中体会到了vim插件的强大功能.现在,是时候回来补一补基本功了. Vi有三种主要模式,正常模式,插入模式和可视化模式.正常我们推荐的方式是所有的移动命令只在正常模式下进行,只有在修改的时候再进入插入模式. 这个切换是如此频繁,所以Vi提供了很多快速切换模式的命令.Vim的难学之处之一就是命令太多. 模式切换的好处 如<Practical Vim>中第8条所说,正常模式和插入模式的切换,可以为撤销编辑带来方便.在没有模式切换的文本

Vim技能修炼教程(8) - 多窗口

多窗口 如果一个vim只能开一个窗口,那肯定是有点low.尤其是写代码的时候,打开多个文件是经常的需求. 速成教程 横着切成两个 :split 文件名 上下切换窗口 Ctrl-W加上上下键,可以实现上下切换窗口. 关掉一个窗口 跟退出vi一样.比如正常模式下的ZZ,再比如:q. 竖着切成两个 :vsplit 文件名 左右切换窗口 Ctrl-W加上左右键 循环切换窗口 Ctrl-W w 好了,欢迎进入多窗口的世界.基本功能就这么多,大家先把多窗口用起来吧. 再学一点 分割窗口 打开只读文件并水平分

Vim技能修炼教程(15) - 时间和日期相关函数

Vimscript武器库 前面我们走马观花地将Vimscript的大致语法过了一遍.下面我们开始深入看一下Vimscript都给我们准备了哪些武器.如果只用这些武器就够了,那么就太好了,只用Vimscript会导致最佳的兼容性.否则不管是用:python还是使用扩展库,都给可移植性带来麻烦. 随着Vim的广泛移植,Vimscript带来的可移植性还是很可观的.这是一门跨相当多平台可用的语言. 之前我们为了区分normal命令和ex命令,在脚本中使用ex命令的前面都加上了":"前缀.从这

Vim技能修炼教程(11) - 代码折叠

上一讲我们是程序员篇的第一讲,关于代码跳转.代码跳转是一个付出很少收获很大的功能.这一节我们开始一个收获很多,但是付出也相对多一点功能:代码折叠. 代码折叠 折叠的类型 折叠有下面几种类型: Manual: 手动创建折叠.但是退出vim后就一切都没了. Marker: 基于特定的字符组合进行折叠. Expr: 定义正则表达式来处理折叠,这是最终的解决方案. Indent: 这个最简单也最基本,根据缩进来折叠. Syntax: 根据程序语义进行折叠. 我们先看一个例子,左边是采用syntax方式进

Vim技能修炼教程(1) - 使用vundle管理插件

世界上有两个伟大的编辑器:一个是emacs,一个是vi.它们从诞生以来,一直在Unix/Linux世界得到最广泛的支持. 尽管过了几十年,在Windows平台上和跨平台上有层出不穷的后起之秀不断挑战它们的地位,比如编辑器界的Sublime Text,Ultra Editor,Notepad++,pspad,Atom,Visual Studio Code,IDE界的Eclipse, IntellJ IDEA, NetBeans等等,但是它们的地位从未动摇过. 写C#的最好IDE一定是Visual

Vim技能修炼教程(10) - 代码跳转

程序员功能 前面我们用了5讲的篇幅来讲基本编辑的基本功:第4讲是基本操作,第5讲是操作符,第6讲行编辑ex命令,第7讲可视模式,第8讲多窗口,第9讲缓冲区和标签页. 从这一讲开始,我们从通用功能向程序员常用功能挺进.首先我们从浏览代码最常用的跳转功能开始. 代码跳转 代码跳转,需要传说中的Exuberant Ctags工具,地址在:http://ctags.sourceforge.net/. 当然大家根据系统不同各显神通吧.比如我是macOS,就通过Homebrew来安装了.大家手头有什么"ap

Vim技能修炼教程(17) - 编译自己的Vim

编译自己的Vim 前面我们已经对Vim有比较丰富的了解了.我们也知道Vim有很多编译时的选项,很多功能依赖于这些编译选项.其中最重要的就是脚本语言的支持,很多发行版本是不全的.为了支持我们所需要的功能,我们需要编译自己的Vim. 首先我们要下载Vim的源代码: git clone https://github.com/vim/vim.git Linux上编译Vim 以Ubuntu为例,首先要安装一些库的开发包,例: sudo apt-get install libncurses5-dev lib