中级篇

Vim 编程

vim 有自身的脚本语言,同时也支持其他常见的语言,例如 python, tcl,perl等等。 当你熟悉这些之后,你基本上就 可以对vim二次开发了,例如一些插件。既使不写插件,这些同样有用。语言的支持 再加上那些丰富的命令,都可实现 支持只用命令很难实现或者实现不了功能。 就非常的容易了。

另一方便,对于初级篇中很东东,都不但知其然,并知其所以然。用起来就会更加的高效。当然对于 出现的问题的,也少一些的茫然,而不是只会google了。

resource
good start to learn the learning vimscript help eval.txt options.txt
Learn Vimscript the Hard Way http://learnvimscriptthehardway.stevelosh.com/
vim 下的unit测试工具 vimUnit

Vim shell

每一个语言都都有变量, vim 支持标量,以及列表以及字典。 然后就是内在结构。主要是寄存器。 各种的 options. 再有函数的支持,以及一些内建函数的支持。

基本语法就不讲了,可以参考 :command:` :help usr_41.txt`. 如何添加命令 :help usr_40.txt.

只讲一些主要的,vim的shell比较简单的,复杂的实现是可以用ptyhon等语言来实现的。对于vim shell 还有一个增强版本 https://github.com/xolox/vim-shell

变量的作用域

vim 的变量作用域是通过前缀来识别的简单的分为四类:

作用域
b:name variable local to a buffer
w:name variable local to a window
g:name global variable (also in a function)
v:name variable predefined by Vim
s:name script-variable local the script file

Vim 编程模型

特殊的变量

主要三种特殊的变量,环境变量,寄存器,options. 分别用不同前缀来表示。 而正常的变量是不需要前缀的。

寄存器

环境变量

$NAME 表示环境变量,是可读可写的,如果你运行时修改环境变量,就这样修改。 :set $NAME=VALUE

option

&name vim 里各种option, 例如 :set auto=,以及:command:set encoding=utf8 在代码中需要用 & 前缀来读取。

寄存器

@r 用到最多的寄存器。用@前缀来读取,你在初级篇的学习那些命令再加上寄存器,就可以实现十分强大的功能。

Vim 中大量的寄存器保存各种各样信息,以与CPU寄存器的作法,有过之而不及。

一共有九种寄存器 具体用法可以查看 :h registers

  1. The unnamed register “”
  2. 10 numbered registers “0 to “9
  3. The small delete register “-
  4. 26 named registers “a to “z or “A to “Z
  5. four read-only registers ”:, ”., “% and “#
  6. the expression register “=
  7. The selection and drop registers "*, "+ and "~
  8. The black hole register "_
  9. Last search pattern register "/

下面通过例子来讲解常用寄存器的用法, 当你需要用一些特殊的直接来查就行了。

查看当前所有寄存器的值 :reg

匿名寄存器

主要是 d c s x y p 等命令的默认寄存器,如果没有指令的寄存器的,最后一次的 d 删除的内容就放在 这里,同理p粘贴命令的内容也是从这里来的。

数字寄存器

这十个是 ring buffer, 主要用来 yank 与delete 命令。 0 最新一次操作内容,1 次新操作内容,以及类推。

这个对于常规剪切功能更强的应用,一般的编辑器的剪切功能只对最后剪切有效。vim 没有专门剪切命令,是通过这些数字寄存器再删除与粘贴命令实现的。 并且功能更加,那就是支持默认支持最近十次的剪切功能。

"1p 例如粘贴倒数二次的剪切的内容

注解

” 是双引号

如果想更多,利用命名寄存器了

26 个命名寄存器 a-z

命令行操作的需要用 来指定,例如

"ad 的删除的内容放在寄存器 a "byy 把当前行yank 到寄存器 b 中 "bp 把寄存器b中内容放在当前位置

在宏录制的时候也是可以可把命令存在命名的寄存器中

qc 开始宏的录制,并放在 寄存器c中。

“+ 系统剪贴板寄存器,如何用命令行vim外部的东东copy到vim里, "+p 就可以了。

剩下的这几个对象,在编程中会遇到比较多,不像前面的寄存器在平时命令也会用到很多。几个对象主要有 buffer object, Range object,Window object, Tab page objects, vim.bindeval object. 基本原理都在初级篇讲过了,但是只是讲其编程接口。

并且主要基于python 来讲吧,具体的可查看 help :python .

可以直接通过 pydo 直接执行 python的命令。
pydo <<EOF 直接代码块 pfile filename 直接执行python 脚本

来一个简单的例子

:pydo if line: return "%4d: %s"%(linenr,line)

:pydo 默认的输入参数是 line, linenr, 具体可考 :help :pydo

重要的时,可以利用 每条 :pydo 都在一个session里,第一条 :pydo 会建立这个session. 也就是说下面的代码是工作的。

:pydo import os
:pydo import os.listdir(.)

vim 的python会有一个vim module.

另一个方式那就是直接在 :ConqueTerm python ,打开一个python。如果直接import vim那就完美了。

vim module
Name Content Example
vim.windows all the windows vim.windows[0] 第一个窗口
vim.buffers all the buffers vim.buffers[0] 每一个buffer
vim.current current 指针 包含当前所有状态 vim.current.window当前的窗口 vim.current.buffer 当前的buffer
vim.current.buffer[m:n] 当前buffer中m到n行  
vim.comand 可以用来执行Ex命令 vim.command(“%s/lgw/zgg”)
vim.eval 执行vim命令并取得返回值 :py str = vi.eval("12+12")
vim.bindeval 执行vim命令并返回 ptyhon对象  
vim.tabpages vim中再tabpages.  
vim.vars vim.options vim中变量以及options  
vim.current.range 这个会经常用到对应就是visual选择模式。  

读写文件

writefile, readfile函数。当你生成一些固定长度的格式,就可以使用repeat函数来实现。

编程脚本在日常的应用

让 vim 像 sed,与awk 一样工作

如何加载脚本 :so[ource] scripts.vim 就像bash 中的 source 命令一样的。 这样就可以用 vim来代替 sed,与awk.

vim - 直接可以直接读标准输入的,

vim -c cmd 直接可以执行命令, 我们直接用上面的加载命令就不就可以像sed,awk 这样的加载脚本了。 你用命令行能做的,在脚本都能做。 甚至可以直接 vim -W 记录所有命令到文件,然后直接加载。当然也可以在vim q 来把命令都录制在寄存器,然后把寄存器内容写入到文件就行了。

如何插入序列

例如我们我们要一个case 编号要求 case_1-> case_1000 用如下方法 先把 case_XX 复制1000 行,然后替换

:let i = 1
:g/case_/s/XX/\=i/g| let i = i + 1

更好玩,利用vim 生成各种测试数据见 http://www.ibm.com/developerworks/cn/linux/l-cn-vimcolumn/index.html

如何插入模式使用寄存器

ctrl-r 就可以直接读取值了, ctrl-r# 例如现在当前位置插入本文件的文件名, # 是特殊寄存器的存当前文件名。

如何插入模式下直接调用vim的函数

这个就要用到 = 表达式寄存器了,例如现在我在此计算 cos(10)是多少。 ctrl-r=cos(10) 就是得到了-0.839072 ctrl-r=strfile("%c") 就是插入一个日期.

这里是有一个bug的,那就是表达式寄存器是不认的 :pydo命令的。 ctl-r=pydo os.listdir(".") 是会报错的

printf

在需要计算生成数据时,使用printf就会很方便,你把关键的数据用正则式表达出来,然后通过printf来调用户各种函数输出,就可以不需要太多转义字符

:%s/[0-9]\{2,5}/\=printf("A is %.2f,B is %.2f%,submatch(0)*0.2)/gc
字符串反序
join(reverse(split("ABCDE",'\zs')),"")
:s/.*/\=join(reverse(split(submatch(0),'\zs')),'')/g

plugin 插件开发

走到这里已经, vim shell 也会用了,也会用其他语言来操作 vim. 是不是有想写一些东东的欲望了。再动手之前,还是google,不要闭门造车,真的需要完全重新动手的时候不多,大部分的情况是已经有了现成的解决方案,或者有一个半成品不太符合你的需要。只需要动手小修就可以了。

真发现全新的东东需要你来写,机会不容易,好好珍惜吧。

基本知识可以参考 :help usr_40.txt usr_42.txt usr_43.txt usr_44.txt.

这里主要分析vim 扩展的开发框架。 各种键盘事件,以及autocomand(在其他的语言中叫event),一般地 commmand.

人机的接口,消息传递的机制。与外面的交互。

人机接口

Normal mode 命令大部分都有对应的 command line, 都是对 ex 命令做的wrapper. 如何实现各种键盘的消息。可以查看 :help map. 主要有

key map
command content
:map Normal Visual and Operator-pending
:vmap Visual
:nmap Normal
:omap Operator-pending
:map! Insert and Command-line
:imap Insert
:cmap Command-line

特殊字符用 <> 来括起来

特殊字符
<F1> 表示 F1.
<CR> 表示回车键
<ESC> 表示退出键

vim 中所有event定义可以查看 :help autocmd.txt, 查看当前vim 中的定义可以查看 :autocmd. 并且这些事件还是可以分组的,,然后按组来处理这些event,具体命令可以查看 :help autocmd-groups

command

:command 不加任何参数,就可以查看当前vim中所有command定义

:function 可以查看当前系统中有多少命令

对于 vim内置内对象操作,就是前面已经讲过的 buffer/range/window object. 可以OS的操作,也可以通过 管道,或者python来进行交互。

大部分插件开发流程

  1. 先用一堆的 :map 来定义各种 command 的key mapping 或者一堆 AutoCommnds hook 各种event, 例如 BufWritePre 等等。
  2. :command 定义相对应的 command 并调用各个函数的接口
  3. 实现各个函数,以及对应补全函数。

先分析一些现成plugin的开发,对于key mapping 最好玩例子那是 $VIMRUNTIME/macro 下那两个游戏了,maze,与hanoi. 完全用key mapping来做的。把这个看明白了,你对 vim 的key mapping就完全掌握了。

vim calender 颜色的配置 就是利用键盘map捕捉键盘并且可以抓取环境变量例如当下的字符等,然后实现消息拦截,利用syn match 实现各种颜色的不同

vim 消息hook 可以在用map来实现,并且控制各种环境,例如<Leader> <LocalLeader> <Plug><SID>

vim 实现弹出菜单可以用 popup File,tearoff

command complete there is three key para, the ArgLead,Comline,CursorPos. :help -complete

It is possible to define customized completion schemes via the "custom,{func}"
or the "customlist,{func}" completion argument.  The {func} part should be a
function with the following signature: >

     :function {func}(ArgLead, CmdLine, CursorPos)

The function need not use all these arguments. The function should provide the
completion candidates as the return value.

For the "custom" argument, the function should return the completion
candidates one per line in a newline separated string.

For the "customlist" argument, the function should return the completion
candidates as a Vim List.  Non-string items in the list are ignored.

The function arguments are:
     ArgLead         the leading portion of the argument currently being
                     completed on
     CmdLine         the entire command line
     CursorPos       the cursor position in it (byte index)

如何调试与开发

如何控制vim 的启动。 vim --noplugin 可以干净的vim 启动, :command:` vim -u <vimrc>` 加载指定的 .vimrc 配置文件。这个就像windows 的安全模式。 并且vim 本身还有一个debug mode, vim -D file.txt 就可以开始 debug. 并且在也可以其他语言一样加断点以及debug. 具体可以查看 :help debug-scripts` .

除了断点debug外,还有log的功能, 可以控制 log的输出等级 例如 :set verbose=20 .

vim 更强的是自带一个profiling 功能。 :command;`:prof start {fname}` 开始 profiling :prof pause/contine/stop 控制 profiling 的进度 prof func {attern} profiling 具体函数

具体详细的功能可以查看 help profiling

如果使vim 崩溃了,可以使用 gcc与gdb 来定位与调试 具体可以看 http://vimcdoc.sourceforge.net/doc/debug.html

如何查看map 的工作原理

当你的插件装多了,就会出现冲突,就时候就要诊断了。

 To see what it is mapped to, and who mapped it, simply ask Vim with:

 :verbose map ys

 This will tell you what the mapping invokes, and also which file set up the mapping. From there it should be trivial to figure out which plugin it belongs to.

 If it is an insert-mode mapping (for example, the CTRL+S mapping of surround.vim), use:

:verbose map! <C-S>

.. seealso::
   http://vim.wikia.com/wiki/Troubleshooting
   http://vim.wikia.com/wiki/Debug_unexpected_option_settings

vba

vimball 是针对插件制作一种封装工具,就像tar一样 vim-addon-manager 插件管理器,就像debian的包管理机制一样

http://stackoverflow.com/questions/2033078/how-to-install-a-vimball-plugin-with-vba-extension or use :help vba to see the document.

vim 的配色与语法文件

人眼对于彩色的敏感度是远高于纯黑白两色的。不同光照条件下以及不同显示器,对文本的显示效果也是不同的。如何达到最好显示效果提高你文本处理速度呢。

配色是基础,然后是语法配色

color scheme

colorscheme 就可以看到默认的配色。

vim 默认有17种配色. blue, darkblue, default, delek, desert, elflord, evening, koehler, morning, murphy, pablo, peachpuff, ron, shine, slate, torte, zellner . 至于选用哪一种好呢。 这个根据显示器来的 例如我一般用 darblue, 而在diff模式我一般用 blue .

可以用 :colorscheme <schemename> 就可以改变配色了。

在计算机里字符都是前景与背景。通过指定前景与背景。来不同的着色效果,这并且着色是可以基于字符的。 要想彩色,首先等硬件支持才行。gvim不用说了,肯定支持了。对于term来说,现在的term 大部分都是支持的,不能显示彩色一般都是配制有问题,一般只情况下,只要设置一下环境变量就可以了。

set-env TERM xterm-color 或者 TERM=xterm-color; export TERM

同时,在vim 里又分gui,与cterm,两种模式下前景与背景。在vim是这么几个概念。

Name Content
cterm 彩色终端
ctermfg 彩色终端的前景
ctermbg 彩色终端的背景
guifg 图形化界面的前景
guibg 图形化界面的背景

既然已经知道了每一个字符如何着色,一个个上色不就解决了。一个来太麻烦的,最简单的方式,那就是所有字符的前景与背景统一。也大家常见的方式。 另一种方式,对文本定义逻辑块,哪些逻辑结构是什么颜色,由vim来自动给你上色。

vim 中定义了72组逻辑块,大部分都能望文生义 也可以通过 :so $VIMRUNTIME/syntax/colortest.vim 查看vim 的支持的分组与本色,并且group也分major与minor,组是可有二层的嵌套的。大的分组可以包含小的分组。

11 major group
Comment
Constant
Identifier
Statement
PreProc
Type
Special
Underlined
Ignore
Error
Todo

Comment Constant Cursor CursorColumn CursorLine DiffAdd DiffChange DiffDelete DiffText Directory Error ErrorMsg FoldColumn Folded Function Identifier Ignore IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Number Operator Pmenu PmenuSbar PmenuSel PmenuThumb PreProc Question Repeat Scrollbar Search ShowMarksHL Special SpecialChar SpecialKey Statement StatusLine StatusLineNC StorageClass TabLine TabLineFill TabLineSel Tag Title Todo Type Underlined VertSplit Visual VisualNOS WarningMsg WildMenu cIf0 comment constant diffOnly hlight identifier lCursor label link operator preproc special statement title type

当然每一套的配色方案不需要定义的所有group的颜色,每一组都有默认的颜色, 就像blue配色来说,这就只定义 34 group 的颜色。

vim 主要三类高亮。一个是为特定语言的,一般做法是把这些link 到vim 的自身的分组的。 另个用于所有语言的,还有用 :hi 命令行的。

如何控制单个字符的颜色 可以利用 :syn keyword 来实现。 当然也可以用 :echoh 来控制输出。

语法高亮

usr_06.txt 是入门的介绍, usr_44.txt 是讲如何写语法文件的。 全面深入的讲解是 syntax.txt 然后就是定义逻辑块了,其实也就是编译原理中词法分析了,vim为了加快速度,并没有采用全文分析的方法,而是采用局部分析的方法。

三个基本要素

keyword 要根据 iskeyword 来定义 的关键字.

Match 用一个正则表式的匹配

Region 从两个正达式,一个匹配头一个匹配尾。

并且三者的优先级 keyword>Region>Match.

然后是把这些组 link 连接到 color中那些group上去。

对于简单的当然这样就够了,对于复杂的那还得用到上下文的信息的。这个就用 Nested Items,以及 Following groups,以及 提高复用性的 clusters 功能.

例如对于 if, while 语句的配色就需要用到这些了。用到这些就上升语法分析的水平。在 vim的syntax分析 中就是 contaninednextgroup 两者了。

如何调试

当发现在语法高亮非常慢时,你还可以 profiling 一把,看看到底是什么慢了。

syntime on 开始计时 syntime off 停止 syntime clar 清零 syntime report 生成report

Vim 是如何识别各个语法文件

在首行或者尾行用注释添加 modeline 就可以做到。 同时还是记得让vim enble 识别 modeline 的功能。 只要在 .vimrc 中加一条 :set modeline

Vim 的日常应用

用vim 来快速实现循环的功能

我们经常会一些一次性工作,但是量又有点大。写代码不值,不写的话,一个个手工删除太慢。那个时候就可以vim帮助你了。

问题

把windows 下所有的usb driver给删除了。在我们经常使用各种device的人,经常会遇到device认不出来的问题,原因一般会可能系统把usb 认错设备了,或者认没有了。也有需要配制 /home/.android.ini 中配制的由于安全原因。这里只讲删除 usb driver.

  1. 首先查看一下有多少,

    pnputil -e

    一下就有200多条怎么办, pnputil -d -f oem297.inf 一条一条来到时候才能完呢

    Published name :            oem297.inf
    Driver package provider :   VMware, Inc.
    Class :                     Universal Serial Bus controllers
    Driver date and version :   05/21/2009 4.0.4.0
    Signer name :               Microsoft Windows Hardware Compatibility Publisher
    
    Published name :            oem199.inf
    Driver package provider :   Intel
    Class :                     Universal Serial Bus controllers
    Driver date and version :   12/21/2010 9.2.0.1021
    Signer name :               Microsoft Windows Hardware Compatibility Publisher
    
    Published name :            oem289.inf
    Driver package provider :   STMicroelectronics
    Class :                     System devices
    Driver date and version :   11/04/2011 2.2.2.0
    Signer name :               Microsoft Windows Hardware Compatibility Publisher
    
    Published name :            oem298.inf
    Driver package provider :   VMware, Inc.
    Class :                     Network Service
    Driver date and version :   06/30/2011 4.1.2.0
    Signer name :               Microsoft Windows Hardware Compatibility Publisher
    
    Published name :            oem299.inf
    Driver package provider :   VMware, Inc.
    Class :                     Network adapters
    Driver date and version :   06/30/2011 4.1.2.0
    Signer name :               Microsoft Windows Hardware Compatibility Publisher
    

用vim 来搞定

  1. 建立一个 clean.bat 文件

    pnputil -f -d oemXX.inf
    
  2. 用vim 打开输入以下命令

    yy10p
    :let i=0
    :g/XX/s/XX/\=i/g |let i=i+1
    :wq
    
  3. 执行clean.bat 就OK了。

插入特殊字符

可以用输入模式下用,ctrl+k,同时可以用 :dig查看缩写。

How to get insert date or time

  1. one is use strftime(“%y-%m-%d”) [VimWiki]
  2. use the python time and calendar module time and calendar

查看代码中non-ascii 字符

这个在python的中经常会遇到。

:set hls
/[^\x00-\x7F]

快速实现多文件的替换

:args %s/pattern/replacement/gc |update

template

template 主要是解决就是复用问题。实现基本原理那替换,在C/C++所以使用的预处理一样。 处理合并替换可以换一个名词那是render.借用了graphic中原语。

最简单的template就是先写几个占位符,然后再加上一个全局替换就搞定了。

复杂的一些,加入一些嵌套与包含关系,例如include指定等等。

再复杂一些,支持 循环与条件。

再复杂一些,可以外界系统进行交互,例如 os, 或者数据库。 而这时候就需要一个egnine. 这个的实现,一般都是内嵌一个语言的解析器来搞定的。基本一个语言都有各种数据的接口。

list 的生成

这个最简单,只要加一个简单的计数,复杂一点再加上indent。 无非进去一层重新计数。 采用深度优先的来解决

table的生成

各个看起来有点难,其实也很简单,对于html,xml的表格就不用说了,直接生成指令就行了。 对于字符表格生成,看起来很难。只直接用一个列表来存储一个table,一行是一个元素。

单行表格生成

  1. 统计的各行各例的字符数,
  2. 找到各例的最大值
  3. 按照样式以及对齐方式逐行生成就行
#count
zip(*[[len(item) for item in row] for row in grid])

http://stackoverflow.com/questions/11347505/what-are-some-approaches-to-outputting-a-python-data-structure-to-restructuredte

并且在python 中有现成的 tabulate 包,可以直接生成各种表格。

对于编码的处理

对于vim, 有 encoding, fileencoding 等等。 对于python来编程语言来都说,在读写文件的时候都会有一个 encoding属性,平时大家都不写,对于C/C++来说,就是那些IOStream大显身手的 时候。 如何快速查看编码,直接用浏览器打开就查看了,它是支持编码最全面的。

同时需要转换的可以用iconv来实现批量的转化。