用Scintilla让程序支持语法高亮
Scintilla是一个免费、跨平台、支持语法高亮的编辑控件。它完整支持源代码的编辑和调试,包括语法高亮、错误指示、代码完成 (code completion)和调用提示(call tips)。能包含标记(marker)的页边(margin)可用于标记断点、折叠和高亮当前行。
可以从这里下载Scintilla库:https://www.scintilla.org/ScintillaDownload.html
另外,Scintilla的作者为了演示,编写了一个叫SciTE的演示程序。不过这个程序的功能已经强大到足以作为我们的常用代码编辑器,很值得下载下来学习学习
编译
偶只在Windows下编译过,所以只好说说Windows环境下的编译方法。对于Linux,没试过(丢人-_-)
下载、解压略过不提
- 首先进入scintilla的win32目录:
cd scintilla\win32
- mingw,输入:
mingw32-make
- VS输入:
nmake -f scintilla.mak
- 对于C++Builder,输入:
make -fscintilla.mak
编译完成后,在bin目录里会得到Scintilla.dll和SciLexer.dll文件,SciLexer.dll是包含了语法解析器 (Lexer)的Scintilla控件,一般来说我们只要用它就可以了。
需要说明的是,不管是用什么编译器生成的DLL文件,都可以供给其它编译器使用(就象系统DLL一样,任何编译器都能使用),所以不用为各种编译器都编译一份。
如果觉得生成的SciLexer.dll太大的话,可以考虑去除自带的部分语法解析器。比如你打算只用它来高亮C++代码的话,可以:
- 进到src目录里,移除除LexCPP.cxx以外的所有Lex*.cxx文件
- 执行LexGen.py重建make文件和KeyWords.cxx文件(需要安装Python)。
- 重新按前面的方法编译,这样生成的SciLexer.dll就只带有C++语法解析器了,体积也大小减小了(我VC编译的结果是从1.4M减小到 206K)。
启用Scintilla作为编辑控件
要启用Scintilla,首先当然是要加载之前编译的DLL文件啦~~
1 | ::LoadLibrary(_T("SciLexer.dll")); |
SciLexer.dll加载后会自动以Scintilla
作为类名注册一个窗体类,我们只要直接用这个类名建立窗体就可以了:
1 | ::CreateWindow(_T("Scintilla"),...); |
演示(在C++Builder下编写)
由于Scintilla主要是窗体操作,为了减少不必要的窗体代码(主要是偷懒外加推广一下C++Builder,呵呵),这里使用C++Builder 来写演示程序。对于一些C++Builder的VCL库特有的东东,后面会有解释的。
首先新建一个窗体应用程序,
然后在WinMain里载入SciLexer.dll:
1
2
3
4WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
LoadLibrary(_T("SciLexer.dll"));
//...最后,在TForm1的构造里建立Scintilla窗体:
1
2
3
4
5
6
7
8
9
10
11
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
::CreateWindow(_T("Scintilla"),
NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE,
0,0,ClientWidth,ClientHeight,
Handle,
(HMENU)SCINT_ID, HInstance, NULL);
}很简单,是吧?对于Scintilla来说,没什么好解释的了。
这里主要给不了解C++Builder的童鞋介绍一下VCL的东东,以便于接下来的讲解和代码阅读(以及移植到其它编译器中)。
TForm1
是一个C++Builder自动生成的窗体类,它继承自TForm,可以把它看成是WS_OVERLAPPEDWINDOW
风格的HWND
的封装。ClientWidth
和ClientHeight
是TForm的属性,看名字就知道它是客户区(ClientRect
)的宽和高Handle
也是TForm
的属性,就是该窗体的HWND
HInstance
不用解释了吧,这是VCL的一个全局变量。
现在,我们的成果是这样的:

现在,看上去还比较土,接下来我们开始配置它,为使它成为可与VS媲美的代码编程器而战!
配置Scintilla的两种方法
配置Scintilla控件是通过向该控件发送配置命令实现的,各种配置命令可以在doc目录下找到(或者是这里https://www.scintilla.org/ScintillaDoc.html),后面的大部分事情都是在介绍这些配置命令。
有两种方法来发送配置命令,一种是直接使用SendMessage
API。另一种是取得直接控制函数,通过函数来执行配置命令。
在Windows下,第二种方法要比第一种快得多。
直接控制函数的定义为:
1 | typedef sptr_t (*SciFnDirect)( |
后三个参数和SendMessage
的后三个参数一样。SciFnDirect
的第一个参数用于指定具体的Scintilla窗体,它类似于窗体的HWND又不完全相同,姑且也称之为句柄吧。它是用一个配置命令取得的,下面马上就要讲到。
取得直接控制函数和句柄的方法是:
1 | SciFnDirect fnDirect = (SciFnDirect)SendMessage(hwndEditor,SCI_GETDIRECTFUNCTION,0,0); |
取得这两样东西以后,就可以直接执行配置命令了,如:
1 | fnDirect(ptrDirect, SCI_CLEARALL, 0, 0); |
演示代码:编写成员函数SendEditor,用于配置之前建立的Scintilla控件。
1 |
|
让Scintilla支持语法高亮
有了前面的SendEditor
控制函数,我们就可以配置语法高亮了,下面这段代码可以使我们的Scintilla控件显示C++语法高亮代码:
1 | const char* g_szKeywords= |
要支持语法高亮,要做三件事:
选定语法解析器
语法解析器用于把一大段代码分解成一个个的单词(token),另外还用于代码折叠的控制(后面会说到)。选定语法解析器的命令是
SCI_SETLEXER
,如:1
SendEditor(SCI_SETLEXER, SCLEX_CPP);
除了
SCLEX_CPP
以外,还有SCLEX_HTML
、SCLEX_PERL
、SCLEX_SQL
、SCLEX_VB
等,定义在SciLexer.h
里。现代的IDE应该可以定位SCLEX_CPP
定义,它周围的SCLEX_XXX
就是其它的语法解析器。另外,也可以用
SCI_SETLEXERLANGUAGE
命令,如:1
SendEditor(SCI_SETLEXERLANGUAGE, 0, (sptr_t)"cpp");
SCI_SETLEXERLANGUAGE
接受的是一个字符串参数,这个字符串定义于代码解析器源代码(src\lex*.cxx
) 最后面LexerModule
开头的那行代码,那里的第三个参数就是。设置关键字
语法解析只负责把代码拆分开,至于哪些是关键字,还得我们来指定。这种方式带来了些许的灵活性,比如我们要高亮一种自定义的语言,这种语言的风格与
C++
类似(如Java
、C#
、php
等),我们也可以选定SCLEX_CPP
作为语法解析器,然后定义自己的关键字。(所以不需要把各种解析器都编译进DLL文件里)设置关键字的命令是
SCI_SETKEYWORDS
。它的wParam
用于指定关键字种类,可以是0~8即9种类型,这样我们可以做 更细致的区分,如把关键字for
if
和int
bool
区分显示。lParam
指定关键字,以空格分隔。设置文本元素对应的字体风格
即字体、前景色、背景色、斜体粗体等设置字体风格的命令以
SCI_STYLE
作为前缀,这组命令比较多,为了不浪费篇幅,偶这里只列举几个,其它的可以参考这里 https://www.scintilla.org/ScintillaDoc.html#StyleDefinition 。1
2
3
4
5SCI_STYLESETBACK(int styleNumber, int colour) //设置背景色
SCI_STYLESETFORE(int styleNumber, int colour) //设置前景色
SCI_STYLESETFONT(int styleNumber, char *fontName) //设置字体
SCI_STYLESETSIZE(int styleNumber, int sizeInPoints)//设置字号
SCI_STYLESETBOLD(int styleNumber, bool bold) //设置粗体这里的
styleNumber
是指文本元素,如关键字、行号、控制字串等。前面代码中的SCE_C_XXXX
是C++解析器分解出的 语法相关的元素。另外还有STYLE_DEFAULT
(默认)、STYLE_LINENUMBER
(行号)、STYLE_BRACELIGHT
(括号匹 配)、STYLE_BRACEBAD
(括号失配)、STYLE_CONTROLCHAR
(控制字符)、STYLE_INDENTGUIDE
(缩进线)、STYLE_CALLTIP
(调用提示)。1
SCI_STYLECLEARALL //把所有文本元素设置成与STYLE_DEFAULT相同的风格
Scintilla文档建议的顺序是先向
STYLE_DEFAULT
设置一些通用风格,然后再用SCI_STYLECLEARALL
把所有元素风格重置成与STYLE_DEFAULT
一致,最后单独设置其它元素。
演示,我们的编辑器支持C++高亮啦!
1 |
|
看上去不错,如果你愿意,还可以加上当前行高亮功能:
1 | SendEditor(SCI_SETCARETLINEVISIBLE, TRUE); |
最后,建议把TAB宽度由默认的8改为4(依个人习惯~~)
1 | SendEditor(SCI_SETTABWIDTH, 4); |
现在,我们的成果是这样的:
