Skip to content

Latest commit

 

History

History

qcmotion

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

基于上下文的快速移动方式

quickly motion in c language file

Summary

Try to do smart motion depended on the differnet C entity under current cursor. The default banding map is 'Q', which is left near 'w' character. It may override the macro key, but you can custom this maping directly edit the 'plugin/qcmotion.vim' or a copy of it and rename to 'setlocal.vim'. (The source comment and doc, if provided, is written in english, and the left of this page is written in chinese.)

使用方式

autoplug 已加载的情况下执行 :PI qcmotion 命令,然后在移动或需要操作块时 按 q 键,或许有奇效。(现在已将默认快捷键改为大写 Q

背景介绍

移动操作是 vim 的基本功,故 vim 本身也提供了诸多丰富的移动功能。最无脑的字符移 动是 hjkl ,嫌长按 hl 移动太慢?可用词间移动键 web ,咦,没错,就是 web 这三个键,词的界定具体由 set iskeyword 选项设定,一般就是不包括符号的 字母。另外大写版的 WEB 就是按空格分词的,移动距离应该比 web 大些。

首尾移动键 ^$ ;屏幕行 HML 上中下移动;全局行移动 gg G,可带前导数字表示 行号哦。命令模式 : 纯数字命令也是移至指定行,普通模式 | 是去指定列呢。

搜索式移动功能,行内局部搜索有 ft,及基重复上次 ft 的快捷命令 ,; 。也有 大写版 FT 命令。全局搜索键 /? ,及其下(上)一处键 nN

块级匹配移动功能,% (vim 的标准插件 matchit),此外 {} () [] 及其组合也 是移动命令哦。

以上,是 vim 内置移动的小小总结。在寻求一个功能前,先看看 vim 本身的相关功能是 很有必要的。

社区提供的三方插件,移动相关的有闻名的 easy-motion。 其工作模式是通过标记法使后续命令快捷定位。不过我对此插件运用不熟,因为似乎与我 个人工作习惯不是很吻合,几经纠结,还是没常装呢。

我目前的主要工作是 C++ 码匠,在工作中常想要个更快速的移动功能,故而写了这个插 件。

如 C 的编程语言源文件是非常结构化的文本,大致来说有不同的语法或语义部分。比如 宏观上讲,分代码、注释、预处理指令、空行(空行很重要呢);微观上讲,一条语句似 乎可分为操作符(各种运算符号)与操作数(变量标识符);中观上讲,或许就是大括号 块,及表示流程控制的关键词。

在编辑源文件时,根据光标所处的位置,期望移动的方式可能不同,一般是期望移动到下 一处相同或相似的实体处。比如你在修改注释时,希望移到下一处注释。有时候一条语句 只想修改操作数,而其间的操作符(加减乘除之类的)“骨架”是正确的不必修改;另一时 刻可能操作数写对了,只想改操作符,比如把一些点(.)改为箭头(->)。这时如果用 w 移动键,得按两次,而我只想用一个键移动,跳过暂不关注的操作符或操作数。

键盘资源是有限的,我希望绑定一个键根据上下文环境作不同的移动处理。我选了 q 这 个键,因为它在 w 附近,它在很多情况下可能做着类似 w 的增强工作,同时也是 quick 的首字母便于记忆。

不过 q 似乎有个重要的内置功能,记录宏。个人使用 vim 经验,记录宏的操作远没有移 动操作频繁呢。而且宏这么强大的功能,在记录宏前一般都要稍停片刻以思考如何组织操 作以达到循环宏的功能,为此复杂的操作绑定更复杂些的按键,应该是值得的。所以当把 q 用于快速移动时,可以绑定另一个键用于恢复记录宏的功能,比如 Q 键(Q的原生功能 又是什么呢,你是否会常用到?)或者也可按惯例将 Q 用于 q 的反向移动,不过反向移 动的需求没正向那么多,故暂不提供了。

主要功能

普通模式 q

目前,qcmation 识别以下类型,处理不同方式的移动(大致按优先级):

  • 空行:移动到下一个空行,同内置命令 }

  • 注释:移动到下一处注释,可识别 C++ // 行注释与 C 式块注释 /* */

  • 特殊定界符:#{},; 当光标处于这几个字符上时,搜索移动到下一个对应字符

  • 函数头:当光标处于第一列时,认为关注函数定义,将搜索下一个函数定义或声明。 vim 内置移动命令 [[ ]]就是移动到上或下一个第一列的 {,认为是函数定义的开 始,而这里的 q 大部分情况是移动到首列 { 的上一行。

  • 操作符:当光标处于操作符之上时,q 移动到下一个操作符

  • 流程跳转关键字:break/contine/return,当光标位于这三个关键字之上时,q移动到 下一处这三个关键字的任一个。此外,相对于在首列假定为搜索函数名,若光标在尾列 ,也假定要搜索这三个跳转关键字。不过大部分尾列是分号,搜索分号的优先级更高。

  • 分支关键字:当位于 case 时,移动到下一个 case

  • 选择关键定:当位于 if/else 时,移动到下一个 if/else

  • 长变量名:能识别以下划线或驼峰型大小写混合的长变量名,当光标位于长变量名的中 间时,q 可以在长变量名的内部分词移动。

  • 操作数:如果光标不在变量名中间,而在变量名开始或结尾(we可定位到词首尾),认为 是普通操作符,q 将移动到下一个操作符。

在移动前,会设置 m' 以保存上次位置,所以若移动非所要,可用 '' 返回上次的位置。

选择模式 vq

在选择模式下设定为与普通模式下移动方式完全一样,内部调用相同的方法实现。扩展选 区至一次 q 普通移动后的位置。建议结合其他内置移动命令再微调选区。

命令后缀模式 dq cq

vim 术语是 Operater-Pending Map,就是像删除(d)、修改(c)以及其他许多命令, 需要后续再按一个键表示操作对象(text object),或移动命令将其移动扫描过的区间 当作该命令的操作范围。dc 是最常用的操作命令,但不限于此,甚至可以自定义映射命令。

虽然可以将直接普通模式的移动方式直接套用到命令后缀模式,但这用选择模式也可能达 到相同的目的,只不过多按了一个 v 键而已。所以我想将纯粹的后缀模式改成略有不同 的更快捷的操作方式。

目前,在命令后缀模式下,q 键映射将按优先级作以下操作:

  • 操作配对块:配对符包括 {} () [] "" '' <> ,当光标恰好位于这些符号之上时,如 ( 或 ) ,q 将执行类似 a(a) 的操作,其效果是一样的,都表示将操作运用于这 对括号及其内容。如果光标恰好位于左括号右边一个字符位置上,或右括号左边一个字 符位置上时,q 将执行类似 i(i) 的操作,即运用于括号内容但不包括括号本身。

  • 操作子语句:包括逗号子语句(或函数调用参数表)或分号子语句(如 for 三重词)。 如果光标恰好定位于逗号上,则表示操作这个子语句,往前搜索前一个逗号或左括号, 操作这之间范围内的文本,不包括这个逗号本身与前一个逗号或左括号。如果光标恰好 位于逗号右边一个字符上,则表示操作下一个子语句,直到下一个逗号或右括号。一个 字符的偏移请用 hl 命令,逗号本身可用 ft 定义或普通模式下 q 定位。分号类似。

  • 空行:兼容普通模式的移动方式,即操作至下一空行。

  • 注释:如果光标位于被注释字符串内,则仅操本条注释内的所有文本,不包括注释符 (//) 本身;如果光标位于注释符上,则操作包括注释符。

  • 函数:普通模式下认为是搜索函数时,后缀模式将操作本行的函数名。

  • 长变量名:将操作部分分词。

  • 定界符与操作符:空操作,C 的大部分操作符只是单个字符,用 x 或 s 更方便。

  • 操作数与跳转关键字:类似 iw ,操作当前单词

  • case 关键字:直接操作其后冒号前的内容。

  • if 或 if else 关键字:直接操作其后条件判断的括号内容。

注意事项

  • 所编辑的 C 源文件尽可能规范,为什么用 vim 写程序还能不规范?还有什么是两个等 号(==格式化当前行)不能解决的事,如果有,那就一个等号(=格式化范围区间)

  • 不要忘了配置内置移动命令,尤其是 hl 微调光标位置,可以改变 q 移动方式。q 在 左手,hl 在右手,左右开弓多配合。

  • 连续的 q 移动不一定始终操持相同的移动模式,不建议长按 q 键。

  • 命令后缀的 q 操作,不确定结果是否正确时,记得还有 u 命令 (undo 撤销)。

  • 不支持嵌套的 C 式注释,后缀 q 操作逗号分号子语句时,目前也不支持解析嵌套括号 。

实现要点

plugin.vim 脚本只是简单的 map 设置,作为用户界面公开接口。实际实现功能的方 法、函数都写在 func.vim 文件中,既隐藏实现细节,更利用 vim 的自动加载机制,最 大化地不影响插件对 vim 启动的速度,延迟到第一次按激活加载脚本。

脚本中,主要用 match 方法按优先级检测可能的移动方式,然后根据不同移动方式调用 search 移动光标。除了需要用于键映射的入口方法用 qcmation# 前缀定义函数名外,其 他都用 s: 前缀限定脚本作用域。

插件定制

plugin/qcmation.vim 只用简单的用户键设置,有意义的语句就如下几句:

: nnoremap Q q
: nnoremap <silent> q :call qcmotion#func#NormalMove()<CR>
: vnoremap <silent> q :<C-u>call qcmotion#func#VisualMove()<CR>
: onoremap <silent> q :call qcmotion#func#OpendMove()<CR>

如果不喜欢用 q 作为该快捷键,可希望保留 q 的记录宏功能,将其中的 q 改为其他想 要的键即可。

  • 不介意直接修改 plugin.vim 的键映射语句
  • 或者在相同目录下复制副本并改名为 setlocal.vim ,再按自己喜好修改
  • 或者将要设置语句拷贝至总配置文件 .vimrc(或其 source 调用的),再在 plugin.vim 同级处 touch setlocal.vim 建一个空文件。

当 plugin.vim 发现身边有 setclocal.vim 时,只会执行 setlocal.vim。若 好奇这点奇淫技巧,也可直接看源文件。

实在不想用 q 键的用户,可考虑其他不常用的单键。但不甚建议 <leader>q ,如果要按 两次键,就不够快了,如此, easy-mation 可能更适合了。

全局插件局部化

虽然基于 C 语言考虑,但应该也可用于其他类似编程语言,所以将插件放在 plugin/ 目 录下,如果只想用于 C 语言而不想妨碍全局,则可将 plugin.vim 移至 ftplugin/c/qcmation.vimftplugin/cpp/qcmation.vim,并在 map 设置语句中 添加 参数。

反馈意见

欢迎体验意见,QQ 或 issue 。