入门,首先看我这两篇博客:关于QScintilla库的入门大全https://biao2488890051.blog.csdn.net/article/details/126798996?spm=1001.2014.3001.5502
正式开始,先来看看词法分析器和编辑器的关系:
(注意:如果自己重写一个词法分析器,那么用的是继承另一个类 QsciLexerCustom QScintilla
这样高亮颜色配置就更加灵活了)
我们可以看到,Scintilla自己是有一套词法分析器的,而QScintilla也自己提供了类似的词法分析器类,但是这些类和Scintilla那边的词法分析器是没有什么关系的,调用不到Scintilla那边的词法分析里面的内容,只能是告诉QScintilla,现在我安装了一个xx词法分析器,此时QScintilla会告诉底层Scintilla那边,给对应安装上相应的词法分析器。
因此,QScintilla这边的词法分析器,功能是比较弱的,因为它不能直接操作Scintilla那边的词法分析器。其实阅读一下Scintilla那边的词法分析器的源码(QScintilla_src-2.12.1\scintilla\lexers\LexCPP.cpp,主要是里面的Lex函数)不能发现,功能也不多,其实也比较好理解的。
就是一个解析C语言词法的状态机,输入字符的开始位置,长度,然后就一个循环进行逐个字符分析了,用大量的switch case来进行状态的转换。而这个函数会让输入一个initSyle,其实就是用来标注当前状态机所处的初始状态的,比如从文本的中间字符串段开始分析,那么输入这个初始状态,就能正确从中间做分析啦。比如说用户现在从中间的注释里键入字符,那么直接就能知道现在应该仍然处在注释中,否则需要从整个文本内容的第一个字符开发分析才能知道的。
这里我举个例,比如识别一个注释,那么如果遇到 /* 就记录当前状态为注释开始状态,当遇到 */ 时候,就切换当前状态为其它状态了,识别其它标识符,大括号啥的,也是这样的过程。
这里每个状态,会用函数 sc.SetState(SCE_C_DEFAULT|activitySet); 来设置为当前状态。C语言的词法分析器,总共有如下状态:
C:\Users\86132\Downloads\QScintilla_src-2.12.1\scintilla\include\SciLexer.h
#define SCE_C_DEFAULT 0
#define SCE_C_COMMENT 1 这个就是C语言的注释,用的style是1
#define SCE_C_COMMENTLINE 2
#define SCE_C_COMMENTDOC 3
#define SCE_C_NUMBER 4
#define SCE_C_WORD 5
#define SCE_C_STRING 6
#define SCE_C_CHARACTER 7
#define SCE_C_UUID 8
#define SCE_C_PREPROCESSOR 9
#define SCE_C_OPERATOR 10
#define SCE_C_IDENTIFIER 11
#define SCE_C_STRINGEOL 12
#define SCE_C_VERBATIM 13
#define SCE_C_REGEX 14
#define SCE_C_COMMENTLINEDOC 15
#define SCE_C_WORD2 16
#define SCE_C_COMMENTDOCKEYWORD 17
#define SCE_C_COMMENTDOCKEYWORDERROR 18
#define SCE_C_GLOBALCLASS 19
#define SCE_C_STRINGRAW 20
#define SCE_C_TRIPLEVERBATIM 21
#define SCE_C_HASHQUOTEDSTRING 22
#define SCE_C_PREPROCESSORCOMMENT 23
#define SCE_C_PREPROCESSORCOMMENTDOC 24
#define SCE_C_USERLITERAL 25
#define SCE_C_TASKMARKER 26
#define SCE_C_ESCAPESEQUENCE 27
每个状态,编辑器Scintilla会给它赋予一个style,这个style是对应一个前景色,背景色,字体等信息的,所以C语言的每一种词法都有了不同的颜色,也叫词法高亮。这里的style是一个唯一的ID标识的。
因此,我们只要为每一个style指定好颜色,那么我们就能自己定制各种词法单词的颜色啦。为了便于使用给,QScintilla给这个style搞了个类,叫做QsciStyle,我们赋值后apply就行了。看我这个博客:https://biao2488890051.blog.csdn.net/article/details/127323327?spm=1001.2014.3001.5502
上面说的 编辑器Scintilla会给它赋予一个style,那么这些style存放在哪呢,追踪源码,可以发现在 LexInterface 类的 Document *pdoc 成员中; 这个Document 就是编辑器的文本内容类。因此我们想知道这些style的情况,那么就访问这个pdoc指向的doc实体即可。我们看LexCPP.cpp源码可以看到一个styleAt(position)函数,也就是根据字符坐标,返回该坐标被赋予的style的ID,但是我们使用QScintilla是无法直接访问到这个函数的(从最上面的架构图可以看到),那怎么办呢,其实是这样,因为QScintilla是对Scintilla的qt版的封装,而QScintilla支持Scintilla的SendScintilla(xx)发送宏的方式进行访问底层api的,因此,全局搜索这个styleAt函数,可以发现在这里暴漏出来的:
而这个是在 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) 函数里,理论上我们只需要 WndProc(SCI_GETSTYLEAT, position)就行了,但是我们还是无法直接访问到Scintilla的Editor的,需要用QScintilla封装的 SendScintilla(SCI_GETSTYLEAT, position) 就可以了,能拿到在position处,用的style了。
我们全局搜索 SCI_GETSTYLEAT 可以发现,在宏列表中,它就是字如其名,得到style的。所以,其实很多很多功能,都是在这些宏里,就是暴漏出接口的(QScintilla并没有对所有的这些宏进行封装成一个个函数),所以自己多看看这些宏,可能就知道功能了。
因此,要想知道,此时光标所在位置,是不是注释,只需要首先拿到当前光标的position,然后调用上面的函数,得到style,判断这个style是不是注释(C_COMMENT)的style的ID即可,那么此时我们就能区分出来了。这个很有用,比如调试时候(或者代码定义/声明跳转),鼠标悬浮显示变量的值,而注释里面的符号,我们是不需要显示的,所以就需要本博客说的这个style来进行区分出来。