代码编辑器之关键字染色, 优化了的方案
代码编辑器之 关键字染色
电子科技大学软件学院03级2班 周银辉
在制作代码编辑器,对关键字染色虽然都很容易地想到使用RichTextBox的SelectionColor属性来实现,但将遇到很多细节问题,导致染色的效果很差,比如染色时文本闪烁得很厉害.甚至不能正确地染色,比如将"inty"染成黑色,但用户突然在字母't'后插入换行时却不能将'int'染成关键字的蓝色.这里是我今天用RichTextBox实现的染色方案,效果挺不错的.
一,关于染色时的闪烁:
染色时闪烁得很厉害,原因是调用Select(int,int)函数次数(或是设置SelectionStart属性次数)过多。
如果你采用如下方式染色:当文本改变时,对改变的文本行进行分析,如果含有关键字,就将这些关键字进行染色。那么你的染色方式将导致严重的闪烁。因为如果文本的当前行含有较多关键字时,每键入一个字符都会对这些关键字依次染色一次,每次染色时都会调用调用Select(int,int)函数(或是设置SelectionStart属性)。当然这也会导致不真确的染色,因为一个键入可能影响两行文本(比如在一行文本中间插入换行符时)。
我的方式是:减少染色次数。当在文本筐中键入字符是,取得受本次键入影响的单词,如果该单词是关键字,那么将其染成关键字颜色(蓝色),否则染成普通文本颜色(黑色)。由于一次键入顶多影响两个单词,所以每次键入顶多染色两次(并且没有重复染色);
二,关于受键入影响的单词:
如果键入的是字母数字或者下划线,那么光标处的单词便是受影响的单词,只需要判断该单词是否为关键字,如果该单词是关键字,那么将其染成关键字颜色(蓝色),否则染成普通文本颜色(黑色)。如果键入的是除字母数字或者下划线外的字符,那么受影响的是以该字符为界的两个单词,比如在“intint”中间插入空格是其被分解成以空格为界的两个“int”,那么就对这两个单词进行判断就可以了。
以下代码描述了上述思想(其中GetCurrentWord()用于取得指定位置处的单词,Dye()用于将指定文本染色)
三, 关于不正确的染色:
染色之所以有时会出现错误,是因为算法不能应付一些特殊的情况。比如"intif"被染成黑色后,当在‘i’前面插入换行符时,其被分成两行,此时"int"与"if"都应该被染成蓝色,而往往"int”还会保持黑色。按照“二,关于受键入影响的单词”中描述的方法进行染色将解决这方面的问题。
---------------------------------
Demo下载
电子科技大学软件学院03级2班 周银辉
在制作代码编辑器,对关键字染色虽然都很容易地想到使用RichTextBox的SelectionColor属性来实现,但将遇到很多细节问题,导致染色的效果很差,比如染色时文本闪烁得很厉害.甚至不能正确地染色,比如将"inty"染成黑色,但用户突然在字母't'后插入换行时却不能将'int'染成关键字的蓝色.这里是我今天用RichTextBox实现的染色方案,效果挺不错的.
一,关于染色时的闪烁:
染色时闪烁得很厉害,原因是调用Select(int,int)函数次数(或是设置SelectionStart属性次数)过多。
如果你采用如下方式染色:当文本改变时,对改变的文本行进行分析,如果含有关键字,就将这些关键字进行染色。那么你的染色方式将导致严重的闪烁。因为如果文本的当前行含有较多关键字时,每键入一个字符都会对这些关键字依次染色一次,每次染色时都会调用调用Select(int,int)函数(或是设置SelectionStart属性)。当然这也会导致不真确的染色,因为一个键入可能影响两行文本(比如在一行文本中间插入换行符时)。
我的方式是:减少染色次数。当在文本筐中键入字符是,取得受本次键入影响的单词,如果该单词是关键字,那么将其染成关键字颜色(蓝色),否则染成普通文本颜色(黑色)。由于一次键入顶多影响两个单词,所以每次键入顶多染色两次(并且没有重复染色);
二,关于受键入影响的单词:
如果键入的是字母数字或者下划线,那么光标处的单词便是受影响的单词,只需要判断该单词是否为关键字,如果该单词是关键字,那么将其染成关键字颜色(蓝色),否则染成普通文本颜色(黑色)。如果键入的是除字母数字或者下划线外的字符,那么受影响的是以该字符为界的两个单词,比如在“intint”中间插入空格是其被分解成以空格为界的两个“int”,那么就对这两个单词进行判断就可以了。
以下代码描述了上述思想(其中GetCurrentWord()用于取得指定位置处的单词,Dye()用于将指定文本染色)
1 private void richTextBox_CodeArea_TextChanged(object sender, EventArgs e)
2 {
3 RichTextBox rbx = (RichTextBox)sender;
4 int start, length;
5 int ins = this.richTextBox_CodeArea.SelectionStart;
6
7 char c = ' ';
8 string word = "";
9 if (ins > 0)
10 {
11 c = this.richTextBox_CodeArea.Text[ins - 1];
12 }
13 else
14 {
15 if (this.richTextBox_CodeArea.Text.Length > 0)
16 {
17 c = this.richTextBox_CodeArea.Text[0];
18 }
19 }
20
21 //取得插入点两边的两个单词,并判断这两个单词是否是关键字.
22 if (!char.IsLetter(c) && !Char.IsDigit(c) && c != '_')
23 {
24 //将字符c染色
25 this.Dye(ins - 1, 1, this.textColor, this.textColor);
26
27 word = this.GetCurrentWord(ins - 1, out start, out length);
28
29 Color cl = IsKey(word) ? this.keyColor : this.textColor;
30 this.Dye(start, length, cl, this.textColor);
31
32 word = this.GetCurrentWord(ins + 1, out start, out length);
33 cl = IsKey(word) ? this.keyColor : this.textColor;
34 this.Dye(start, length, cl, this.textColor);
35
36 }
37 else//取得插入点处的单词并判断其是否是关键字
38 {
39 word = this.GetCurrentWord(ins, out start, out length);
40 Color cl = IsKey(word) ? this.keyColor : this.textColor;
41 this.Dye(start, length, cl, this.textColor);
42 }
43
44 }
45
2 {
3 RichTextBox rbx = (RichTextBox)sender;
4 int start, length;
5 int ins = this.richTextBox_CodeArea.SelectionStart;
6
7 char c = ' ';
8 string word = "";
9 if (ins > 0)
10 {
11 c = this.richTextBox_CodeArea.Text[ins - 1];
12 }
13 else
14 {
15 if (this.richTextBox_CodeArea.Text.Length > 0)
16 {
17 c = this.richTextBox_CodeArea.Text[0];
18 }
19 }
20
21 //取得插入点两边的两个单词,并判断这两个单词是否是关键字.
22 if (!char.IsLetter(c) && !Char.IsDigit(c) && c != '_')
23 {
24 //将字符c染色
25 this.Dye(ins - 1, 1, this.textColor, this.textColor);
26
27 word = this.GetCurrentWord(ins - 1, out start, out length);
28
29 Color cl = IsKey(word) ? this.keyColor : this.textColor;
30 this.Dye(start, length, cl, this.textColor);
31
32 word = this.GetCurrentWord(ins + 1, out start, out length);
33 cl = IsKey(word) ? this.keyColor : this.textColor;
34 this.Dye(start, length, cl, this.textColor);
35
36 }
37 else//取得插入点处的单词并判断其是否是关键字
38 {
39 word = this.GetCurrentWord(ins, out start, out length);
40 Color cl = IsKey(word) ? this.keyColor : this.textColor;
41 this.Dye(start, length, cl, this.textColor);
42 }
43
44 }
45
三, 关于不正确的染色:
染色之所以有时会出现错误,是因为算法不能应付一些特殊的情况。比如"intif"被染成黑色后,当在‘i’前面插入换行符时,其被分成两行,此时"int"与"if"都应该被染成蓝色,而往往"int”还会保持黑色。按照“二,关于受键入影响的单词”中描述的方法进行染色将解决这方面的问题。
---------------------------------
Demo下载
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述