很多人可能沒有聽過tab trigger這個功能,那么諸位可以google一把即可知道其為何物。
本來我是打算把這樣的功能放到插件里面做的,可是一想到最后能為我的編輯器開發(fā)插件的,
在初期估計也沒啥人,于是乎還是在內核上給予支持吧。
本篇文章即是分析如何去實現(xiàn)該功能的,事實上,我已經(jīng)在MegaxEdit基本上實現(xiàn)了。不過相較于
TextMate功能有所縮水。縮水的原因主要是因為我采用了正則表達式去實現(xiàn)的,而絕大多數(shù)
正則表達式庫是不支持嵌套的(本人使用了日本的鬼車正則庫),所以在我的編輯器里變量里面
不可以再定義變量。
在我的編輯器里面,這個東西不叫TextMate上的Bundle,我給起了個名字叫
HotText, hoho~~~
首先我們看一下下面的語句:
for (${1:unsigned int} ${2:i} = ${3:0}; ${4:< ${5:count}}; ${6:++}) {
$0
\}
基于上面的形式我們作出如下定義:
變量: 變量以${數(shù)字:內容}的形式開頭, 內容可以為空, 比如${1:name}, ${2:test}, ${3:}
引用: 這里面的引用是指對變量的引用, 比如 $數(shù)字, $1, $2, $3之類的
常量: 編輯器提供的常量,比如${FILE_NAME}, ${TIME}
規(guī)則: 變量不可嵌套變量, 變量必須順序定義,順序引用
經(jīng)過上面的規(guī)則,這個for語句就變成了下面這樣:
for (${1:unsigned int} ${2:i} = ${3:0}; ${4:} < ${5:count}}; ${6:++}) {
$0
\}
Ok, 我們看一下典型的效果,當用戶輸入for語句的時候,編輯器應該顯示出:
for (<unsigned int> i = 0; i < count; i++) {
}
<>部分為光標選擇區(qū)域。
聰明的讀者頭腦里面應該馬上知道如何構造了,其實也就是幾步正則替換,因為文本非常小,所以效率是非常
高效的。偽代碼如下:
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和長度信息
//正因為如此,我們在定義了上面的規(guī)則,變量要順序定義,否則就亂序了。
GetSearchPos&Length();
RegexReplace(
);
GetBackRef();//在這兒獲取一下上面的()中內引用內容,在進行一次替換
//替換對變量的引用
string strRef;
strRef.Format( "\$%d", i );
RegexReplace(
);
//到這兒對變量1替換完畢
}
上面是非常簡單的邏輯。
因為這東西要和編輯器結合,所以有一些數(shù)據(jù)結構是必要的。最重要的要屬上面循環(huán)中變量的位置和長度信息了。
有了它我們才能夠實現(xiàn)HotSpot(熱點即變量)的跳轉。
在這里,我簡單描述一下我的編輯器的數(shù)據(jù)結構:
// 這個存儲了每一個變量的信息
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之中,根據(jù)當前的輸入進行查詢,思路還是很簡單的。
和編輯器的結合,基本上就是一個Replace命令,用本次輸入產生的HotText替換掉上次的HotText.因為大部分的
HotText都很短,所以幾乎不會浪費內存。EmEditor則是只對用戶最后的輸入產生Insert命令,雖然節(jié)省了一點內存
但卻失去了UndoRedo的能力,到底哪個好,也不好說。MegaxEdit是采用的前者。
關于HotText的取消:
用戶在輸入的時候不能總處于HotText狀態(tài)中吧,比如用戶按下了方向鍵或者其他快捷鍵,這個時候我們要取消
HotText的功能,恢復Tab鍵本來的作用。那么如何取消呢?其實這東西和編輯器結合過于緊密,在MegaxEdit中
是這樣來實現(xiàn)的:
1. 取得當前光標的偏移量 offsetA
2. 取得當前HotSpot的尾部偏移量 offsetB = m_nDocOffset + HotSpot.nPos + HotSpot.pCText.GetLength();
3. offsetA != offsetB, 即取消當前HotText;
到此,邏輯即全部結束。
雖然我們實現(xiàn)了一個縮水版的tab trigger,不過根據(jù)我的研究,這幾乎可以滿足絕絕絕絕絕絕大多數(shù)的應用,至少比
Notepad++上的QuickText要強多了。不占用 CPU,不占用內存,完美UndoRedo, 夠用了。
歡迎討論。
posted on 2010-02-02 20:39
megax 閱讀(3054)
評論(12) 編輯 收藏 引用