开源免费代码编辑器Scintilla使用方法集锦
我是在做毕业课题的时候发现的这个编辑控件,使用后发现它实在是强大和稳定,作为源代码编辑控件,我们能在普通的的文本编辑控件中能看到的功能,Scintilla都完全能够实现,而且,它还能够提供很多编辑和调试源代码时有用的特殊功能。包括语法高亮显示,错误指示,代码自动完成以及代码提示等。而且在左边的空白处(margin),可以显示调试代码中非常有用的显示断点以及显示当前运行行等功能。而且,自定义风格的功能比其他大部分编辑器控件都开放,它允许用户自定义不同类型下的字体,是否粗体,是否斜体,前景色和背景色,支持大量的字体。举个例子,在设置C/C++编辑模式后,你可以定义注释语句类型的颜色,字体,大小等等,同样,可以自定义关键字类型的颜色,字体,大小……诸如此类,这样给用户极大的配置的自由。
据 Scintilla的网站上发布的消息,Scitilla将在以后的开发中,更加灵活,健壮,更好地表现在.Net和java虚拟机中运行,就这一点,可以看出Scitilla项目就是一个非常有活力的项目,其开发团队对它非常有激情,这也使我们——这些用户从中受益。
据作者介绍,开发 Scintilla的动机是来自他对Richedit的失望,在Richedit中,样式的改变被看作是文档的一种改变,从而会被记录到undo的堆栈中,并且设置的文档对象的修改标记。这对源代码编辑是非常不合适的,改变关键字类型的颜色或是运算符的颜色,这不应该看作是文档的修改,也不应该能够通过 undo/redo来返回/前进。我对此深有感触,如果是Richedit来做源代码编辑的控件,那将有太多的东西需要自己编写代码实现了,选择 Scintilla,将是一个明智的选择,省时,省力,安全,可靠。
Scintilla目前提供了Win32版本和Linux版本。在 Linux中使用的是GTK+,已经在Windows95,nt4.0, Windows2000, windows XP以及RedHat Linux8和9中的GTK+1.2和2.0中测试运行正常。这是一个跨平台的控件,也是我对其非常欣赏的原因之一。下面我的主要论述的还是在 windows + VC下如何使用这个控件,但是其他平台/编译工具下也大同小异,触类旁通吧。
简介:
Scintilla的 windows版本就是一个窗体控件。它的主要编程接口是通过窗体消息来传送的。你要实现什么功能,向控件发送一个消息就可以了,当需要从控件得到什么信息,接受WM_NOTIFY消息即可,可以从它带的参数中获得大量当前控件的信息,比如是否已经修改文字了,是否正在点击左边的边框等等。但是,实际上在 MFC编程中,通过消息的方式来控制控件还是非常麻烦的,因为这些消息的大部分都是自定义消息,不便于记忆。所以一般的做法就是将这些消息的调用封装成一个窗体类,这样调用方便多了。除了能实现一般编辑控件能实现的功能外,scintilla还能实现语法高亮,代码折叠,书签,自动完成,语句提示等等功能。你完全可以不用学习标准编辑控件CEDIT或者是RichEdit,scintilla提供了协调一致的API口,这些都是它的优点之处。
事实上,scintilla开发组同时也用这个控件开发了一个编辑软件SciTE,它百分百的实现了scintilla的全部功能,如果你对 scintilla非常感兴趣,但又不知它到底能做到那些功能的活,当一个SciTE,使用一下就完全明白了。同时,SciTE也是开源软件,学习它的代码,就是学习scintilla的使用方法。在scintilla的文档中,就说明了,这个文档只是独立地讲解各个消息的使用方法,功能和参数,并不能讲解如何把它们连接起来组成一个实用的编辑器,要想知道如何实现某些特定功能,还是看看SciTE是如何实现的。我也看过SciTE的代码,可读性还是不错的,可惜我是没有耐心的人,也就读了几个函数,大体了解了一下,如果你想用好这个控件,多读读SciTE的源码还是不错的。
scintilla的消息就用SendMessage函数发送,它提供两个头文件:Scintilla.h和SciLexer.h,消息号的宏定义都在这里,而且还有很多要用到的结构体,类型等等的定义。消息带的两个参数wParam和lParam是要经常用到的,也许会用到一个,两个,或者无需参数。但建议你最好在使用的时候把不用的参数都赋予0值,因为如果以后扩充消息功能,用到了某个参数,赋值能防止程序的崩溃。
通常参数的类型如下:
bool int const char* char* colour <unused>
其他的类型不用多说,至于color类型,是用于描述颜色的,它实际上就是一个整数,计算方法是:red|(green << 8)|(blue << 16), red,green,blue是8位2进制数,也就是2位16进制数。很容易就能实现256色的调配。至于<unused>,就是说这个参数在这个消息中不用,可以赋为0值。
下面的讲解的重点是放在VC+MFC的环境中,因为我一直在这个环境下开发东西,要是用SDK编程也一样的,只是用到了更多的API,至于编译器,VC也好,GCC也好,我觉得没什么大问题,我就曾经用GDB调试SciTE的源码,来了解scintilla 如何使用的,Linux平台下的GTK+编程我没尝试过,从文档上看,也是用消息的方式来控制控件,用接受消息的方式来获得控件的信息。
下面我就如何封装scintilla成为一个窗体类,如何在程序中使用它的功能一一讲解,水平有限,而且现在还在上学中,可能写得比较慢,呵呵,谅解。
使用概述
如何使用scinitlla控件呢?scintilla控件需要两个头文件,Scintilla.h和SciLexer.h,这里定义了消息宏,所用到的数据结构类型等等。在所要用到的cpp文件中包括这两个头文件即可。
scinitlla控件与主程序交互通信是通过消息传递的。在窗体类中,创建一个scinitlla控件,然后发送消息给控件,以达到不同的目的。有的时候,我们也需要控件的反馈,比如当用户点击编辑器左边栏时,我们希望控件能通知父窗体,这时,父窗体接收到一个WM_NOTIFY消息,它的参数 lParam可以转化为结构体SCNotification的指针类型,结构体SCNotification是由控件头文件定义的,该结构体定义如下:
struct SCNotification {
struct NotifyHeader nmhdr;
int position;
// SCN_STYLENEEDED, SCN_MODIFIED, SCN_DWELLSTART,
// SCN_DWELLEND, SCN_CALLTIPCLICK,
// SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK
int ch; // SCN_CHARADDED, SCN_KEY
int modifiers; // SCN_KEY, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK
int modificationType; // SCN_MODIFIED
const char *text; // SCN_MODIFIED
int length; // SCN_MODIFIED
int linesAdded; // SCN_MODIFIED
int message; // SCN_MACRORECORD
uptr_t wParam; // SCN_MACRORECORD
sptr_t lParam; // SCN_MACRORECORD
int line; // SCN_MODIFIED
int foldLevelNow; // SCN_MODIFIED
int foldLevelPrev; // SCN_MODIFIED
int margin; // SCN_MARGINCLICK
int listType; // SCN_USERLISTSELECTION
int x; // SCN_DWELLSTART, SCN_DWELLEND
int y; // SCN_DWELLSTART, SCN_DWELLEND
};
上面的SCN_MODIFIED, SCN_MARGINCLICK,SCN_STYLENEEDED等都是控件定义不同的消息宏,通过该结构体,可以知道控件通知父窗体的内容,
具体的代码实现会在下面的内容叙述的。
在创建控件之前,必不可少的一步就是加载动态库scilexer.dll,在WinApp类的InitInstance()方法中,加入
m_hDll = LoadLibrary(_T("SciLexer.dll"));
其中m_hDll是定义的App类的成员变量,同时,在ExitInstance() 方法中,加入
// unload scintilla dll
if (m_hDll != NULL)
FreeLibrary(m_hDll);
这样才能使用功能强大的scintilla控件,否则是会出现无法创建控件的错误的。
那么如何创建scintilla控件?在MFC中,我们一般的做法是建立一个CWND窗体类,封装与scintilla控件通信的一系列实现,然后在视图类中加一个该窗体类的成员变量,通过窗体类来实现控件的各种操作,如创建、初始化等。这时起封装作用的窗体类看作是控件,这就是在设计模式中称为 PROXY的模式。
我们建立一个CScintillaWnd类,如下:
class CScintillaWnd : public CWnd
{
public:
CScintillaWnd();
virtual ~CScintillaWnd();
BOOL Create (LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
}
#define STR_SCINTILLAWND _T("Scintilla")
BOOL CScintillaWnd::Create (LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
return CWnd::CreateEx(WS_EX_CLIENTEDGE, STR_SCINTILLAWND, lpszWindowName, dwStyle, rect, pParentWnd,(UINT)nID))
}
这样就创建了控件,在视图类中,加入CScintillaWnd类型的成员变量,例如:
private:
CScintillaWnd m_wndScintilla;
在视图类的OnCreate()函数中,加入:
if (!m_wndScintilla.Create(_T("Title"), WS_CHILD | WS_VISIBLE, CRect(0,0,0,0), this, 10000))
{
AfxMessageBox("can't create scintilla!");
return -1;
}
return 0;
同时,在OnSize()函数中,加入:
if (m_wndScintilla.GetSafeHwnd())
m_wndScintilla.MoveWindow(0, 0, cx, cy);
这样保证控件与视图对齐,否则会发生找不到控件的现象。
执行了上述几步之后,在视图类中,应该可以看到scintilla控件的编辑窗口,现在就可以在该窗口上输入文本了。
---------------------------------------
另一篇:
Scintilla是一个免费、跨平台、支持语法高亮的编辑控件。它完整支持源代码的编辑和调试,包括语法高亮、错误指示、代码完成 (code completion)和调用提示(call tips)。能包含标记(marker)的页边(margin)可用于标记断点、折叠和高亮当前行。
可以从这里下载Scintilla库:http://scintilla.sourceforge.net/ScintillaDownload.html
这里有Scinilla相关的库下载,比如wxScintilla就是Scintilla的wxWidgets移植版。http://www.scintilla.org/ScintillaRelated.html
另外,Scintilla的作者为了演示这个东东的功能,编写了一个叫SciTE的演示程序。不过这个程序的功能已经强大到足以作为我 们的常用代码编辑器,很值得下载下来学习学习。
老规矩,还是从编译说起
偶只在Windows下编译过,所以只好说说Windows环境下的编译方法。对于Linux,没试过(丢人-_-)
下载、解压略过不提
首先进入scintilla的win32目录:
cd scintilla\win32
* 对于mingw,输入:
mingw32-make
* 对于VC6以上版本,输入:
nmake -f scintilla.mak
* 对于VC6(没试过,从Readme里看来的),输入:
nmake -f scintilla_vc6.mak
* 对于C++Builder,输入:
make -fscintilla.mak
编译完成后,在bin目录里会得到Scintilla.dll和SciLexer.dll文件,SciLexer.dll是包含了语法解析器 (Lexer)的Scintilla控件,一般来说我们只要用它就可以了。
需要说明的是,不管是用什么编译器生成的DLL文件,都可以供给其它编译器使用(就象系统DLL一样,任何编译器都能使用),所以不用为各种编译器都编译 一份。
如果觉得生成的SciLexer.dll太大的话,可以考虑去除自带的部分语法解析器。比如你打算只用它来高亮C++代码的话,可以:
1. 进到src目录里,移除除LexCPP.cxx以外的所有Lex*.cxx文件
2. 执行LexGen.py重建make文件和KeyWords.cxx文件(需要安装Python)。
3. 重新按前面的方法编译,这样生成的SciLexer.dll就只带有C++语法解析器了,体积也大小减小了(我VC编译的结果是从1.4M减小到 206K)。
启用Scintilla作为编辑控件
要启用Scintilla,首先当然是要加载之前编译的DLL文件啦~~
::LoadLibrary(_T("SciLexer.dll"));
SciLexer.dll加载后会自动以"Scintilla"作为类名注册一个窗体类,我们只要直接用这个类名建立窗体就可以了:
::CreateWindow(_T("Scintilla"),...);
演示(在C++Builder下编写)
由于Scintilla主要是窗体操作,为了减少不必要的窗体代码(主要是偷懒外加推广一下C++Builder,呵呵),这里使用C++Builder 来写演示程序。对于一些C++Builder的VCL库特有的东东,后面会有解释的。
首先新建一个窗体应用程序,
然后在WinMain里载入SciLexer.dll:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
LoadLibrary(_T("SciLexer.dll"));
...
最后,在TForm1的构造里建立Scintilla窗体:
#define SCINT_ID 1010
__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控件是通过向该控件发送配置命令实现的,各种配置命令可以在doc目录下找到(或者是这里http://scintilla.sourceforge.net/ScintillaDoc.html),后面的大部分事情都是在介绍这些配置命令。
有两种方法来发送配置命令,一种是直接使用SendMessage API。另一种是取得直接控制函数,通过函数来执行配置命令。
在Windows下,第二种方法要比第一种快得多。
直接控制函数的定义为:
typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam);
后三个参数和SendMessage的后三个参数一样。
SciFnDirect的第一个参数用于指定具体的Scintilla窗体,它类似于窗体的HWND又不完全相同,姑且也称之为句柄吧。它是用一个配置命 令取得的,下面马上就要讲到。
取得直接控制函数和句柄的方法是:
SciFnDirect fnDirect = (SciFnDirect)SendMessage(hwndEditor,SCI_GETDIRECTFUNCTION,0,0);
sptr_t ptrDirect = (sptr_t)SendMessage(hwndEditor,SCI_GETDIRECTPOINTER,0,0);
取得这两样东西以后,就可以直接执行配置命令了,如:
m_fnDirect(fnDirect, SCI_CLEARALL, 0, 0);
演示代码:编写成员函数SendEditor,用于配置之前建立的Scintilla控件。
1. #include <Scintilla.h>
2. #include <SciLexer.h>
3.
4. class TForm1 : public TForm
5. {
6. __published: // IDE-managed Components
7. private: // User declarations
8. SciFnDirect m_fnDirect;
9. sptr_t m_ptrDirect;
10. public: // User declarations
11. __fastcall TForm1(TComponent* Owner);
12. sptr_t SendEditor(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0)
13. {
14. return m_fnDirect(m_ptrDirect, iMessage, wParam, lParam);
15. }
16. };
17.
18. #define SCINT_ID 1010
19. __fastcall TForm1::TForm1(TComponent* Owner)
20. : TForm(Owner)
21. {
22. HWND hwndEditor = ::CreateWindow(_T("Scintilla"),
23. NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE,
24. 0,0,ClientWidth,ClientHeight,
25. Handle,
26. (HMENU)SCINT_ID, HInstance, NULL);
27. m_fnDirect = (SciFnDirect)SendMessage(hwndEditor,SCI_GETDIRECTFUNCTION,0,0);
28. m_ptrDirect = (sptr_t)SendMessage(hwndEditor,SCI_GETDIRECTPOINTER,0,0);
29. }
有了前面的SendEditor控制函数,我们就可以配置语法高亮了,下面这段代码可以使我们的Scintilla控件显示C++语法高亮代码:
const char* g_szKeywords=
"asm auto bool break case catch char class const "
"const_cast continue default delete do double "
"dynamic_cast else enum explicit extern false finally "
"float for friend goto if inline int long mutable "
"namespace new operator private protected public "
"register reinterpret_cast register return short signed "
"sizeof static static_cast struct switch template "
"this throw true try typedef typeid typename "
"union unsigned using virtual void volatile "
"wchar_t while";
...
SendEditor(SCI_SETLEXER, SCLEX_CPP); //C++语法解析
SendEditor(SCI_SETKEYWORDS, 0, (sptr_t)g_szKeywords);//设置关键字
// 下面设置各种语法元素前景色
SendEditor(SCI_STYLESETFORE, SCE_C_WORD, 0x00FF0000); //关键字
SendEditor(SCI_STYLESETFORE, SCE_C_STRING, 0x001515A3); //字符串
SendEditor(SCI_STYLESETFORE, SCE_C_CHARACTER, 0x001515A3); //字符
SendEditor(SCI_STYLESETFORE, SCE_C_PREPROCESSOR, 0x00808080);//预编译开关
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENT, 0x00008000);//块注释
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTLINE, 0x00008000);//行注释
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTDOC, 0x00008000);//文档注释(/**开头)
要支持语法高亮,要做三件事:
一、选定语法解析器
语法解析器用于把一大段代码分解成一个个的单词(token),另外还用于代码折叠的控制(后面会说到)。
选定语法解析器的命令是SCI_SETLEXER,如:
SendEditor(SCI_SETLEXER, SCLEX_CPP);
除了SCLEX_CPP以外,还有SCLEX_HTML、SCLEX_PERL、SCLEX_SQL、SCLEX_VB等一大堆,定 义在SciLexer.h里。现代的IDE应该可以定位SCLEX_CPP定义,它周围的SCLEX_XXX就是其它的语法解析器。
另外,也可以用SCI_SETLEXERLANGUAGE命令,如:
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作为前缀,这组命令比较多,为了不浪费篇幅,偶这里只列举几个,其它的可以参考这里 (http://scintilla.sourceforge.net/ScintillaDoc.html#StyleDefinition)。
SCI_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(调用提示)。
SCI_STYLECLEARALL //把所有文本元素设置成与STYLE_DEFAULT相同的风格
Scintilla文档建议的顺序是先向STYLE_DEFAULT设置一些通用风格,然后再用SCI_STYLECLEARALL 把所有元素风格重置成与STYLE_DEFAULT一致,最后单独设置其它元素。
演示,我们的编辑器支持C++高亮啦!
1. #include <Scintilla.h>
2. #include <SciLexer.h>
3. ...
4. void TForm1::setCppStyle()
5. {
6. const char* szKeywords1=
7. "asm auto break case catch class const "
8. "const_cast continue default delete do double "
9. "dynamic_cast else enum explicit extern false "
10. "for friend goto if inline mutable "
11. "namespace new operator private protected public "
12. "register reinterpret_cast return signed "
13. "sizeof static static_cast struct switch template "
14. "this throw true try typedef typeid typename "
15. "union unsigned using virtual volatile while";
16. const char* szKeywords2=
17. "bool char float int long short void wchar_t";
18. // 设置全局风格
19. SendEditor(SCI_STYLESETFONT, STYLE_DEFAULT,(sptr_t)"Courier New");
20. SendEditor(SCI_STYLESETSIZE, STYLE_DEFAULT,10);
21. SendEditor(SCI_STYLECLEARALL);
22. //C++语法解析
23. SendEditor(SCI_SETLEXER, SCLEX_CPP);
24. SendEditor(SCI_SETKEYWORDS, 0, (sptr_t)szKeywords1);//设置关键字
25. SendEditor(SCI_SETKEYWORDS, 1, (sptr_t)szKeywords2);//设置关键字
26. // 下面设置各种语法元素风格
27. SendEditor(SCI_STYLESETFORE, SCE_C_WORD, 0x00FF0000); //关键字
28. SendEditor(SCI_STYLESETFORE, SCE_C_WORD2, 0x00800080); //关键字
29. SendEditor(SCI_STYLESETBOLD, SCE_C_WORD2, TRUE); //关键字
30. SendEditor(SCI_STYLESETFORE, SCE_C_STRING, 0x001515A3); //字符串
31. SendEditor(SCI_STYLESETFORE, SCE_C_CHARACTER, 0x001515A3); //字符
32. SendEditor(SCI_STYLESETFORE, SCE_C_PREPROCESSOR, 0x00808080);//预编译开关
33. SendEditor(SCI_STYLESETFORE, SCE_C_COMMENT, 0x00008000);//块注释
34. SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTLINE, 0x00008000);//行注释
35. SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTDOC, 0x00008000);//文档注释(/**开头)
36.
37. SendEditor(SCI_SETCARETLINEVISIBLE, TRUE);
38. SendEditor(SCI_SETCARETLINEBACK, 0xb0ffff);
39. }
40.
41. __fastcall TForm1::TForm1(TComponent* Owner)
42. : TForm(Owner)
43. {
44. ...
45. setCppStyle();
46. }
看上去不错,如果你愿意,还可以加上当前行高亮功能:
SendEditor(SCI_SETCARETLINEVISIBLE, TRUE);
SendEditor(SCI_SETCARETLINEBACK, 0xb0ffff);
最后,建议把TAB宽度由默认的8改为4(依个人习惯~~)
SendEditor(SCI_SETTABWIDTH, 4);
现在,我们的成果是这样的:
本站所有文章欢迎任何形式的转载,但请注明作者及出处,尊重他人劳动成果!
文章转载自:中国研发网 [http://www.yanfaw.com]
本文标题:开源免费代码编辑器Scintilla使用方法集锦
文章转载自:中国研发网 [http://www.yanfaw.com]
本文标题:开源免费代码编辑器Scintilla使用方法集锦










