自己動手設計代碼編輯器——(四)代碼智能提示(自動完成功能)

本來我的編輯器沒有自動完成功能的,而且本來應該繼續(xù)講代碼的載入與分析的。
但剛好做了上一節(jié)的功能后,我臨時做出來了,趁熱寫出來。

代碼自動完成,按自己的理解做的,不知道別人是怎么實現的,先講自己的思路吧。
首先,代碼自動完成,得記錄能自動完成的字符串(比如關鍵字、變量、類名等等)
這又要分:靜態(tài)載入和動態(tài)載入
所謂靜態(tài)載入,就是在程序啟動的時候載入的一些確定的字符串
比如:編輯器當前的配置是C#編譯器,那C#的關鍵字是確定的。這些是可以寫到配置文件里的,在程序啟動的時候就載入。
所謂動態(tài)載入,就是在程序運行的時候,分析代碼,在后才載入的字符串
比如:當前代碼的函數名、變量等,這些是不能再之前確定的,只有當代碼分析完成后,才能確定。這部分字符串,在分析器里獲得。


好了,有了提示的字符串數組后,就可以實現功能了。
為了簡單,我這里只是在程序里直接定義了一些字符串數組
首先實現智能提示的核心類

public class IntelligentManager  
{  
        private CodeManager codeManager;  
          
        private string[] ss = new string[]  
        {  
            "int", "Integer",  
            "public", "private", "protected"  
        };  
          
        private List<string> result;  
          
        public List<string> Result  
        {  
            get  
            {  
                return result;  
            }  
        }  
          
        public IntelligentManager(CodeManager setCodeManager)  
        {  
            this.codeManager = setCodeManager;  
              
            this.result = new List<string>();  
              
            this.codeManager.TextChanged += TextChangedEvent;  
        }  
          
        public void MatchingString()  
        {  
            string inputString = codeManager.GetLastCut();  
              
            this.result.Clear();  
              
            if ( inputString.Length == 0 )  
            {  
                return;  
            }  
              
            inputString = inputString.ToLower(); // 代碼提示時,要不要區(qū)分大小寫就在這里搞定  
              
            foreach ( string s in ss )  
            {  
                if ( s.ToLower().StartsWith(inputString) && inputString.Length < s.Length )  
                {  
                    result.Add(s);  
                }  
            }  
        }  
          
        public void SelectMatching(int index)  
        {  
            if ( index < 0 || index >= result.Count )  
            {  
                return;  
            }  
              
            string str = result[index];  
            string lastString = codeManager.GetLastCut();  
              
            ReplaceStringCommand cmd = new ReplaceStringCommand(  
                codeManager,  
                codeManager.Text.Length - lastString.Length,  
                lastString.Length,  
                str);  
              
            codeManager.Execute(cmd);  
              
            result.Clear();  
        }  
          
        private void TextChangedEvent()  
        {  
            MatchingString();  
        }  
}

代碼不多,也比較好理解,稍微解釋下。
MatchingString就是匹配CodeManager中,最后輸入的一個字符串塊
這里提一下,所謂字符串塊,就是被一些符號和空白符分開的東西,比如“abc,de+fg”這里就有三個塊,"abc","de","fg"。他們被“," "+”這些符號分開了
因為我們不會為包含符號的字符串做匹配處理(比如str+abc,只用abc匹配,而不是str+abc)

selectMatching函數是表示選擇了匹配列表里的值
函數里面的ReplaceStringCommand是一個替換指定字符串的命令,繼承IUndoCommand接口(不了解的,可以看上一節(jié))
TextChangedEvent是當CodeManager的文字發(fā)生改變的時候執(zhí)行了

特別說明,我這里是用 codeManager.GetLastCut()來獲取最后輸入的一個塊。

在實際的項目中,應該是取光標所在的塊,至于怎么取,就看自己的意思了,比較簡單。

CodeManager也要做一下對應的改變
加入事件
public event CodeManagerEventHandler TextChanged;
另外在所有改變了文字內容的地方,激活事件。通知所有注冊了事件的其它類(這里只有IntelligentManager)

public void InsertCharacter(int index, char ch)  
{  
            text.Insert(index, ch);  
            TextChangedEvent();  
}  
  
private void TextChangedEvent()  
{  
            if ( TextChanged != null )  
            {  
                TextChanged.Invoke();  
            }  
} 

最后在Coder里定義即可
public IntelligentManager intelligentManager;
哦,忘了說。為了顯示代碼提示的列表,在上一節(jié)的基礎上,在窗體再拖一個label控件
在KeyDown事件的最后加入代碼

string[] ss = coder.intelligentManager.Result.ToArray();  
label2.Text = string.Empty;  
foreach ( string s in ss )  
{
  label2.Text += s;  
  label2.Text += '\n';  
}  

這樣就能顯示了。至于怎么選擇,看你了,可以按回車選中第一個,也可以按數字鍵,只需要調用函數SelectMatching,傳入序號就可以了
當然,在真正的項目中,還是得由IntelligentManager插件調用Renderer插件來自己繪制
好,到這里代碼提示也完成了
當然,考慮效率問題,還有待優(yōu)化

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容