很多人可能没有听过tab trigger这个功能,那么诸位可以google一把即可知道其为何物。
本来我是打算把这样的功能放到插件里面做的,可是一想到最后能为我的编辑器开发插件的,
在初期估计也没啥人,于是乎还是在内核上给予支持吧。
本篇文章即是分析如何去实现该功能的,事实上,我已经在MegaxEdit基本上实现了。不过相较于
TextMate功能有所缩水。缩水的原因主要是因为我采用了正则表达式去实现的,而绝大多数
正则表达式库是不支持嵌套的(本人使用了日本的鬼车正则库),所以在我的编辑器里变量里面
不可以再定义变量。
在我的编辑器里面,这个东西不叫TextMate上的Bundle,我给起了个名字叫
HotText, hoho~~~
首先我们看一下下面的语句:
for (${1:unsigned int} ${2:i} = ${3:0}; ${4:< ${5:count}}; ${6:++}) {
$0
\}
基于上面的形式我们作出如下定义:
变量: 变量以${数字:内容}的形式开头, 内容可以为空, 比如${1:name}, ${2:test}, ${3:}
引用: 这里面的引用是指对变量的引用, 比如 $数字, $1, $2, $3之类的
常量: 编辑器提供的常量,比如${FILE_NAME}, ${TIME}
规则: 变量不可嵌套变量, 变量必须顺序定义,顺序引用
经过上面的规则,这个for语句就变成了下面这样:
for (${1:unsigned int} ${2:i} = ${3:0}; ${4:} < ${5:count}}; ${6:++}) {
$0
\}
Ok, 我们看一下典型的效果,当用户输入for语句的时候,编辑器应该显示出:
for (<unsigned int> i = 0; i < count; i++) {
}
<>部分为光标选择区域。
聪明的读者头脑里面应该马上知道如何构造了,其实也就是几步正则替换,因为文本非常小,所以效率是非常
高效的。伪代码如下:
const int MAX_HT_CNT = 16;
for( int i=1; i<=MAX_HT_CNT; i++ ){
//首先替换变量
string strFind;
strFind.Format( "\$\{%d:(.*?)\}", i );
string strReplace;
strReplace.Format( "$1" );
//在进行替换的时候我们同时要保存该变量Pos和长度信息
//正因为如此,我们在定义了上面的规则,变量要顺序定义,否则就乱序了。
GetSearchPos&Length();
RegexReplace();
GetBackRef();//在这儿获取一下上面的()中内引用内容,在进行一次替换
//替换对变量的引用
string strRef;
strRef.Format( "\$%d", i );
RegexReplace();
//到这儿对变量1替换完毕
}
上面是非常简单的逻辑。
因为这东西要和编辑器结合,所以有一些数据结构是必要的。最重要的要属上面循环中变量的位置和长度信息了。
有了它我们才能够实现HotSpot(热点即变量)的跳转。
在这里,我简单描述一下我的编辑器的数据结构:
// 这个存储了每一个变量的信息
struct CTextSpot{
CText pCText;
int nPos;
CTextSpot(){
pCText = 0;
nPos = -1;
}
};
// 保存了HotText的基本信息
class HotText{
public:
int m_nDocOffset;//HotText应该从Doc中哪个偏移开始插入
CText m_Text;//原始文字
CText m_RetText;//替换后的文字
CTextSpot m_List[ MAX_HT ];//最多可以有几个变量
int m_nSpotPos;//指向当前Spot,这样在Tab跳转的时候我们就知道该跳到那个Spot上了
}
这些HotText均保存于Map之中,根据当前的输入进行查询,思路还是很简单的。
和编辑器的结合,基本上就是一个Replace命令,用本次输入产生的HotText替换掉上次的HotText.因为大部分的
HotText都很短,所以几乎不会浪费内存。EmEditor则是只对用户最后的输入产生Insert命令,虽然节省了一点内存
但却失去了UndoRedo的能力,到底哪个好,也不好说。MegaxEdit是采用的前者。
关于HotText的取消:
用户在输入的时候不能总处于HotText状态中吧,比如用户按下了方向键或者其他快捷键,这个时候我们要取消
HotText的功能,恢复Tab键本来的作用。那么如何取消呢?其实这东西和编辑器结合过于紧密,在MegaxEdit中
是这样来实现的:
1. 取得当前光标的偏移量 offsetA
2. 取得当前HotSpot的尾部偏移量 offsetB = m_nDocOffset + HotSpot.nPos + HotSpot.pCText.GetLength();
3. offsetA != offsetB, 即取消当前HotText;
到此,逻辑即全部结束。
虽然我们实现了一个缩水版的tab trigger,不过根据我的研究,这几乎可以满足绝绝绝绝绝绝大多数的应用,至少比
Notepad++上的QuickText要强多了。不占用 CPU,不占用内存,完美UndoRedo, 够用了。
欢迎讨论。
posted on 2010-02-02 20:39
megax 阅读(3025)
评论(12) 编辑 收藏 引用