• 关于编辑器QScintilla(Scintilla)词法分析器工作原理的分析(实现注释区分)


    入门,首先看我这两篇博客:关于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语言的词法分析器,总共有如下状态:

    1. C:\Users\86132\Downloads\QScintilla_src-2.12.1\scintilla\include\SciLexer.h
    2. #define SCE_C_DEFAULT 0
    3. #define SCE_C_COMMENT 1 这个就是C语言的注释,用的style是1
    4. #define SCE_C_COMMENTLINE 2
    5. #define SCE_C_COMMENTDOC 3
    6. #define SCE_C_NUMBER 4
    7. #define SCE_C_WORD 5
    8. #define SCE_C_STRING 6
    9. #define SCE_C_CHARACTER 7
    10. #define SCE_C_UUID 8
    11. #define SCE_C_PREPROCESSOR 9
    12. #define SCE_C_OPERATOR 10
    13. #define SCE_C_IDENTIFIER 11
    14. #define SCE_C_STRINGEOL 12
    15. #define SCE_C_VERBATIM 13
    16. #define SCE_C_REGEX 14
    17. #define SCE_C_COMMENTLINEDOC 15
    18. #define SCE_C_WORD2 16
    19. #define SCE_C_COMMENTDOCKEYWORD 17
    20. #define SCE_C_COMMENTDOCKEYWORDERROR 18
    21. #define SCE_C_GLOBALCLASS 19
    22. #define SCE_C_STRINGRAW 20
    23. #define SCE_C_TRIPLEVERBATIM 21
    24. #define SCE_C_HASHQUOTEDSTRING 22
    25. #define SCE_C_PREPROCESSORCOMMENT 23
    26. #define SCE_C_PREPROCESSORCOMMENTDOC 24
    27. #define SCE_C_USERLITERAL 25
    28. #define SCE_C_TASKMARKER 26
    29. #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来进行区分出来。

  • 相关阅读:
    【【萌新的SOC学习之自定义IP核 AXI4接口】】
    使用MySQL,请善用 JSON 这张牌
    无线Mesh自组网方案,CV5200无线模组应用,支持高清数据远距离传输
    动态住宅代理VS静态住宅代理,怎么选择?
    Leetcode1752:检查数组是否经排序和轮转得到
    PyQt5 GUI编程(QMainWindow与QWidget模块结合使用)
    【Axure视频教程】曲线图
    最速下降法
    Webrtc支持FFMPEG硬解码之解码实现(三)
    JWT 使用入门(一)配置与示例
  • 原文地址:https://blog.csdn.net/kangkanglhb88008/article/details/127902084