NotepadNext 词法测试剖析:以 HeaderEOLFill_1.md 验证 lexer.markdown.header.eolfill 属性的实现与预期输出

发布时间:2026/9/14 7:47:08
NotepadNext 词法测试剖析:以 HeaderEOLFill_1.md 验证 lexer.markdown.header.eolfill 属性的实现与预期输出 NotepadNext 词法测试剖析以 HeaderEOLFill_1.md 验证 lexer.markdown.header.eolfill 属性的实现与预期输出【免费下载链接】NotepadNextA cross-platform, reimplementation of Notepad项目地址: https://gitcode.com/GitHub_Trending/no/NotepadNext本文以 Lexilla 测试套件中的 Markdown 测试样例 HeaderEOLFill_1.md 为主体完整讲解它如何通过 Lexilla TestLexers 框架验证 Markdown 词法器的lexer.markdown.header.eolfill属性读完后你能掌握该属性的行为语义、.styled预期输出文件的读法以及 LexMarkdown.cxx 中对应的源码级实现路径从而理解 NotepadNext 中 Scintilla/Lexilla 词法引擎的测试组织方式。一、HeaderEOLFill_1.md一个词法器属性回归测试的输入文件HeaderEOLFill_1.md 是 Lexilla 测试目录thirdparty/lexilla/test/examples/markdown/下的一个 Markdown 样例文件它不是给人阅读的文档而是 TestLexers 词法测试程序的输入。根据 test/README 的说明examples目录下每个子目录包含样例文件、一个控制文件SciTE.properties以及带.styled预期着色结果和.folded预期折叠结果后缀的期望文件程序对样例执行词法分析后与.styled文件逐字符比对不一致时会生成.styled.new文件供人工检查。该样例专门服务于一个主题Markdown 词法器的lexer.markdown.header.eolfillheader EOL fill标题行行尾填充属性对应 Lexilla 上游 issue #60Markdown: Optionally style all of Markdown header lines见 LexillaHistory.html 中的版本记录。目录下存在成对的两个测试用例HeaderEOLFill_0.md在lexer.markdown.header.eolfill0默认值下运行HeaderEOLFill_1.md在lexer.markdown.header.eolfill1下运行。两者的文件内容完全相同差异只在于SciTE.properties按文件名条件下发的属性值从而把同一个输入在两种属性取值下的着色结果分别固化为两个.styled期望文件形成对属性行为的双重回归验证。二、样例全文两种 Markdown 标题语法的穷举组合HeaderEOLFill_1.md 全文共 20 行其内容刻意覆盖了两类 Markdown 标题语法在有/无空行分隔下的全部组合如下所示原样引用H1 H2 -- # H1 ## H2 H1 H2 -- # H1 ## H2 ### H3 #### H4 ##### H5 ###### H6可以将其拆成两个测试区块带空行的规范写法第 1–9 行Setext 风格的一级标题H1、二级标题H2--以及 ATX 风格的# H1、## H2。其中 Setext 标题要求上一行是正文内容/--仅是下划线行这正好考验词法器回溯上一行是否有内容的判断逻辑。无空行连写的紧凑写法第 11–20 行H1/、H2/--紧贴随后# H1到###### H6六级 ATX 标题连续排列用于验证在标题状态尚未回到LINE_BEGIN的情况下后续行能否被正确重新识别为新的标题。三、SciTE.properties按文件名条件化下发属性该目录下的 SciTE.properties 全文如下它同时展示了测试框架的条件属性语法此条件语法正是为 issue #62 在 TestLexers 中引入的if $(与match表达式见 LexillaHistory.htmlcode.page65001 lexer.*.mdmarkdown fold1 # Tests for the lexer.markdown.header.eolfill property, issue #62 if $( $(FileNameExt);HeaderEOLFill_0.md) lexer.markdown.header.eolfill0 if $( $(FileNameExt);HeaderEOLFill_1.md) lexer.markdown.header.eolfill1逐项解读配置项含义code.page65001以 UTF-8 编码读取样例文件lexer.*.mdmarkdown对所有.md样例启用markdown词法器Lexilla 中由lmMarkdown模块注册见 LexMarkdown.cxx 末尾的LexerModule lmMarkdown(SCLEX_MARKDOWN, ColorizeMarkdownDoc, markdown)fold1启用折叠测试与.folded期望文件配套if $( $(FileNameExt);...)TestLexers 的条件表达式当文件名含扩展名等于右侧字符串时对当前文件追加设置其下方的属性行按 test/README 的说明if $(与matchglob 匹配如match Header*1.md是等价的两种按文件下发属性的写法此外 README 还指出其他既非 lexer/keywords 前缀的语句会被原样转发为词法器属性这正是lexer.markdown.header.eolfill能够直达LexMarkdown.cxx的通道。四、预期着色输出.styled 文件逐行解读.styled文件的格式是原文 花括号中的样式号切换标记如{5}function{0}说明见 test/README。本测试用到的样式号来自 SciLexer.h 中的 Markdown 样式定义样式号宏含义0SCE_MARKDOWN_DEFAULT默认正文1SCE_MARKDOWN_LINE_BEGIN行起始逻辑状态6SCE_MARKDOWN_HEADER1一级标题7SCE_MARKDOWN_HEADER2二级标题8–11SCE_MARKDOWN_HEADER3–HEADER6三至六级标题eolfill0 的期望结果HeaderEOLFill_0.md.styled 关键行节选{n}表示从此处起样式切换为 n{0}H1{1} {6}{1} {0}H2{1} {7}--{1} {6}#{0} H1{1} {7}##{0} H2{1} ... {6}#{0} H1{1} {7}##{0} H2{1} {8}###{0} H3{1}默认行为是只着色语法标记本身ATX 标题中#、##等井号取HEADER1/HEADER26/7而标题文字H1回落到SCE_MARKDOWN_DEFAULT0Setext 的下划线行/--整行为 6/7。注意行末尾出现的{1}它是紧接其后的LINE_BEGIN状态的标记按.styled的紧凑写法附在前一行行尾。eolfill1 的期望结果HeaderEOLFill_1.md.styled 对同一输入的输出关键行节选{0}H1{1} {6} {1} {0}H2{1} {7}-- {1} {6}# H1 {1} {7}## H2 {1} {0}H1{1} {6} {0}H2{1} {7}-- {6}# H1 {7}## H2 {8}### H3 {9}#### H4 {10}##### H5 {11}###### H6与eolfill0相比差异正是该属性的全部语义ATX 标题整行井号 空格 标题文字都取标题样式{6}# H1、{7}## H2一直到{11}###### H6而不是只有井号着色Setext 下划线行/--在两种模式下都整行着色但eolfill1时LINE_BEGIN{1}被推迟到下一行的行首才出现如行末不再有{1}而是下一空行以{1}开头因为词法器在整个标题行内保持HEADER1/HEADER2状态直到换行。这正是 LexillaHistory.html 中 Optionally style all of Markdown header lines. Enabled withlexer.markdown.header.eolfill1 的可执行定义。五、源码级实现LexMarkdown.cxx 中属性如何改变状态机属性在词法器中的解析只有一处位于 LexMarkdown.cxx// property lexer.markdown.header.eolfill // Set to 1 to highlight all ATX header text. const bool headerEOLFill styler.GetPropertyInt(lexer.markdown.header.eolfill, 0) 1;从源码结构看ColorizeMarkdownDoc主循环中该布尔值在三类分支上生效ATX 标题识别#…######见 LexMarkdown.cxx。eolfill1时直接sc.SetState(SCE_MARKDOWN_HEADERn)让标题状态覆盖整行eolfill0时改走SetStateAndZoom(...)只对井号段赋标题样式、其余回落默认样式——对应.styled中{6}#{0} H1与{6}# H1的差别。Setext 下划线行/-见 LexMarkdown.cxx。两个分支都先用HasPrevLineContent(sc)确认上一行有正文、再用FollowToLineEnd(...)把下划线整行设为HEADER1/HEADER2差异仅在后处理eolfill0时立刻sc.SetState(SCE_MARKDOWN_LINE_BEGIN)对应行尾的{1}eolfill1时保留标题状态跨到换行。标题状态的行尾收束见 LexMarkdown.cxx。eolfill1时词法器在标题状态内每遇到行首sc.atLineStart才置回SCE_MARKDOWN_LINE_BEGIN并通过freezeCursor让当前循环不前进重新走一遍行首识别eolfill0时则遇到换行符即收束。这段逻辑解释了紧凑区块第 11–20 行为何在eolfill1下每一行仍能独立成为新标题。另有一个细节值得注意#分支里优先处理#.这种井号点有序列表的特殊情形LexMarkdown.cxx避免把列表项误判为一级标题而-分支则要先排除单横线空格的无序列表SCE_MARKDOWN_PRECHAR路径。这些判断保证了HeaderEOLFill样例中--与H2的组合不会触发列表语义。六、测试如何运行与校验整篇与逐行双通道按 test/README 的描述TestLexers 对每个样例执行两轮校验先对整文件做词法/折叠再逐行line-by-line重做两者结果不一致时输出per-line is different提示这通常暴露词法器里未正确初始化的局部状态。对 Markdown 词法器而言eolfill1模式恰好依赖标题状态跨行保持 行首重新进入识别的跨行状态机因此HeaderEOLFill_1.md这个用例实际上同时承担了整篇一致性与逐行一致性的双重压力测试。构建与运行方式以当前仓库自带文件为准先构建 Lexilla 共享库再进入lexilla/test目录Linux/macOS 下用make testClang 可加CLANG1Windows 下用nmake -f testlexers.mak test或加载TestLexers.vcxproj要求 C20 编译器README 列出 MSVC 2019.4、GCC 9.0、Clang 9.0、Apple Clang 11.0 为已验证版本。若结果变化程序会写出.styled.new/.folded.new人工核对无误后可将其提升promote为正式的期望文件提交。七、小结HeaderEOLFill_1.md 这 20 行看似简陋的样例完整编码了lexer.markdown.header.eolfill属性的验收标准Setext 与 ATX 两种标题语法 × 规范/紧凑两种排版 × 属性开/关两个取值。它与 HeaderEOLFill_0.md、条件化的 SciTE.properties 及两份.styled期望文件共同构成 Lexilla 测试框架内一个自洽的回归测试单元而其行为最终锚定在 LexMarkdown.cxx 对 ATX 识别、Setext 下划线行与标题状态行尾收束三处分支的实现之上。在 NotepadNext 这类基于 Scintilla 的编辑器中理解这条样例 → SciTE.properties → 词法器属性 → 样式号的链路是定制 Markdown 高亮行为例如希望标题整行高亮与阅读其测试体系的共同基础。【免费下载链接】NotepadNextA cross-platform, reimplementation of Notepad项目地址: https://gitcode.com/GitHub_Trending/no/NotepadNext创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考