posts - 76,  comments - 621,  trackbacks - 0
很多人可能没有听过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;

forint 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 阅读(3006) 评论(12)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理