NotepadNext 内嵌 Lexilla 的 Markdown 标题着色测试:HeaderEOLFill 样例与 lexer.markdown.header.eolfill 属性

NotepadNext 内嵌 Lexilla 的 Markdown 标题着色测试:HeaderEOLFill 样例与 lexer.markdown.header.eolfill 属性 NotepadNext 内嵌 Lexilla 的 Markdown 标题着色测试HeaderEOLFill 样例与 lexer.markdown.header.eolfill 属性【免费下载链接】NotepadNextA cross-platform, reimplementation of Notepad项目地址: https://gitcode.com/GitHub_Trending/no/NotepadNext本文以 NotepadNext 仓库中 HeaderEOLFill_0.md 测试样例为主体完整解析 Lexilla Markdown 词法分析器如何识别 Setext 与 ATX 两种标题语法、lexer.markdown.header.eolfill属性如何改变标题样式的作用范围以及如何通过配套的.styled/.folded期望文件对该行为做 token 级验证。读完后你可以独立读懂 Lexilla 测试框架中 Markdown 标题用法的每一处预期输出并能在 LexMarkdown.cxx 中定位到对应的状态机分支。一、测试样例文件对两种 Markdown 标题语法的完整覆盖HeaderEOLFill_0.md 位于 Lexilla 的测试用例目录 thirdparty/lexilla/test/examples/markdown/全文共 20 行完整内容如下H1 H2 -- # H1 ## H2 H1 H2 -- # H1 ## H2 ### H3 #### H4 ##### H5 ###### H6虽然篇幅很短但它是一份结构精心设计的“标题识别”探针覆盖了 Markdown 规范中全部两类标题写法Setext 式标题第 1–5 行H1行紧跟一行全的下划线构成一级标题H2行紧跟全-的下划线构成二级标题。两者之间用空行分隔属于“标准”排版。ATX 式标题第 7–9 行以 12 个#号开头的# H1、## H2同样以空行分隔。连续标题边界情况第 11–20 行从H1开始连续 10 行密集排列 Setext 与 ATX 标题行与行之间没有任何空行且 ATX 部分一次性覆盖 H1 到 H6 全部六个级别#到######。这一段用来验证当上一行本身是标题或标题下划线时词法分析器能否正确地把新的一行重新判定为标题而不是沿用上一行的状态。同目录下还有一份内容完全相同的 HeaderEOLFill_1.md两者构成一组对照实验的输入区别仅在于运行测试时各自被赋予不同的词法器属性取值见下节。二、测试如何被驱动SciTE.properties 中的条件化属性配置同目录下的 SciTE.properties 定义了这批样例的运行参数全文如下code.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后缀文件交给 Lexilla 的 Markdown 词法器LexMarkdownfold1开启折叠计算因此需要生成.folded期望文件lexer.markdown.header.eolfill0/1测试目标属性按文件名条件分支HeaderEOLFill_0.md使用0HeaderEOLFill_1.md使用1注释中的 “issue #62” 表明这组用例是为 Lexilla 上游的一个编号问题专门建立的回归测试LexillaHistory.html 中也记录了lexer.markdown.header.eolfill1启用标题整行高亮的条目。Lexilla 的测试框架说明见 thirdparty/lexilla/test/README会分别用两个属性值对样例文件做词法分析与折叠计算把实际结果与同名的.styled、.folded期望文件逐字符比对任何 token 或折叠级别的偏差都会导致测试失败。对本文这个用例而言HeaderEOLFill_0.md.styled和HeaderEOLFill_0.md.folded就是eolfill0时的标准答案。三、期望输出解读.styled 与 .folded 的 token 级验证3.1 样式编号与 .styled 内容HeaderEOLFill_0.md.styled 用{样式号}文本的记号标注每个字符片段所属的样式完整内容如下{0}H1{1} {6}{1} {0}H2{1} {7}--{1} {6}#{0} H1{1} {7}##{0} H2{1} {0}H1{1} {6}{1} {0}H2{1} {7}--{1} {6}#{0} H1{1} {7}##{0} H2{1} {8}###{0} H3{1} {9}####{0} H4{1} {10}#####{0} H5{1} {11}######{0} H6{1}从样例中可以直接确认样式号与标题级别的对应关系{1}表示行结束符归入默认样式样式号对应含义在样例中的位置0默认文本标题文字部分、正文6H1 标题下划线行、单个#7H2 标题--下划线行、##8H3 标题###9H4 标题####10H5 标题#####11H6 标题######这份期望输出固化了eolfill0默认值下的两条着色规则Setext 标题只给“下划线行”着色如{0}H1与{6}标题文字本身仍是默认样式 0而/--整行分别取 HEADER1 / HEADER2 样式。ATX 标题只给#号序列着色如{6}#{0} H1仅行首的#连串是标题样式后面的标题文字退回默认样式。另外可以看到第 11–20 行的连续标题段中每一行都被正确识别为相应级别的标题没有因为缺少空行而错判成普通文本或列表——这正是该样例的核心验证点。3.2 .folded标题不产生折叠区域HeaderEOLFill_0.md.folded 的格式为起始行 行宽 折叠级别 行文本0 400 0 H1 0 400 0 0 400 0 0 400 0 H2 0 400 0 -- 0 400 0 0 400 0 # H1 0 400 0 0 400 0 ## H2 0 400 0 0 400 0 H1 0 400 0 0 400 0 H2 0 400 0 -- 0 400 0 # H1 0 400 0 ## H2 0 400 0 ### H3 0 400 0 #### H4 0 400 0 ##### H5 0 400 0 ###### H6 0 400 0每一行的折叠级别列都是0即这份样例中的标题行不会形成任何可折叠区域。对 Markdown 词法器而言这是一个有验证价值的预期标题只做样式判定不参与折叠结构避免把“下划线行”之类的行误当作某种块结构的边界。四、属性实现剖析LexMarkdown.cxx 中的状态机上述所有期望输出都由 LexMarkdown.cxx 中的ColorizeMarkdownDoc生成。逐段对照源码可以看到样例的每一条规则如何落地。4.1 属性读取// 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;见 LexMarkdown.cxx。属性只接受整数值非1一律按关闭处理因此与 SciTE.properties 中0/1两种取值一一对应。4.2 ATX 标题最长匹配优先eolfill 决定样式停留范围行首状态SCE_MARKDOWN_LINE_BEGIN下ATX 标题按“先长后短”的顺序尝试匹配######直到#L266-L309if (sc.Match(######)) { if (headerEOLFill) sc.SetState(SCE_MARKDOWN_HEADER6); else SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, #, sc); } else if (sc.Match(#####)) { ... }两条路径的语义差异正是eolfill属性的核心关闭时eolfill0调用SetStateAndZoom(SCE_MARKDOWN_HEADERn, n, #, sc)即只把 n 个#字符标记为标题样式并立即跳过其后文字落回默认状态——对应.styled中{6}#{0} H1的形态。开启时eolfill1直接把状态置为SCE_MARKDOWN_HEADEn并停留。配合标题状态的驻留逻辑L344-L351else if (sc.state SCE_MARKDOWN_HEADER1 || ... || sc.state SCE_MARKDOWN_HEADER6) { if (headerEOLFill) { if (sc.atLineStart) { sc.SetState(SCE_MARKDOWN_LINE_BEGIN); freezeCursor true; } } else if (IsNewline(sc.ch)) sc.SetState(SCE_MARKDOWN_LINE_BEGIN); }开启后标题状态会一直保持到行尾到下一行行首才交回LINE_BEGIN并用freezeCursor原地重试以正确处理行首语义freezeCursor的含义见 L170-L173 的注释关闭时则遇到换行符即退出标题状态。因此从源码逻辑看eolfill1时# H1一整行都会带上 HEADER1 样式这正是属性注释 “highlight all ATX header text” 所指的行为也是HeaderEOLFill_0与HeaderEOLFill_1两份期望输出产生差异的根源。另外注意一个易混淆点单个#若后跟.和空白如#.会被识别为有序列表而非标题先行分支到SCE_MARKDOWN_PRECHARL299-L303。本样例不含该行但--下划线与列表项-的歧义则由下一段处理。4.3 Setext 标题/-下划线的上下文判定行首遇到或-时进入 Setext 判定L317-L334else if (sc.ch ) { if (HasPrevLineContent(sc) FollowToLineEnd(, SCE_MARKDOWN_HEADER1, endPos, sc)) { if (!headerEOLFill) sc.SetState(SCE_MARKDOWN_LINE_BEGIN); } else sc.SetState(SCE_MARKDOWN_DEFAULT); } else if (sc.ch -) { if (HasPrevLineContent(sc) FollowToLineEnd(-, SCE_MARKDOWN_HEADER2, endPos, sc)) { if (!headerEOLFill) sc.SetState(SCE_MARKDOWN_LINE_BEGIN); } else { ... // 否则进入 PRECHAR可能判为无序列表或 HRULE } }三个判定点与样例逐行对应HasPrevLineContent(sc)要求上一行有实际内容。样例第 11–14 行的H1 / / H2 / --连续排布时每一下划线的上一行都是标题文字条件成立从而连续标题段全部命中——这就是该样例“无空行”设计的验证目标。FollowToLineEnd(, ...)整行必须全是或-才构成下划线于是、--整行取 HEADER1 / HEADER2 样式对应.styled中的{6}、{7}--。-的三路消歧不满足 Setext 条件的-行会退回PRECHAR状态在那里再区分无序列表-与水平线---等且水平线要求上一行无内容见IsValidHrule的!HasPrevLineContent(sc)L141-L163。eolfill开启时下划线行判定成功后不再重置为LINE_BEGIN行内其余部分继续按标题状态处理。4.4 行为汇总场景eolfill0样例验证eolfill1源码行为ATX# H1仅#为标题样式文字默认整行#与文字均为标题样式Setext 下划线行下划线整行为标题样式文字行默认下划线行仍为标题样式且行尾不再提前退出标题状态标题状态退出时机遇换行符即退出保持到下一行行首freezeCursor原地重试五、该用例在 NotepadNext 仓库中的定位从仓库结构看thirdparty/lexilla与thirdparty/scintilla是随 NotepadNext 一起构建的供应商目录NotepadNext 的语法高亮能力建立在 Scintilla Lexilla 之上src/languages/下的 markdown.lua 等脚本负责为各样式号即本文.styled中出现的 0、6–11 等编号配置实际颜色与字体。因此HeaderEOLFill这一组测试不仅守护 Lexilla 上游的行为也间接保证了 NotepadNext 中 Markdown 文件标题着色、折叠与eolfill属性相关外观的一致性。对需要扩展用例的开发者可复制本目录的三件套模式新增Xxx.md输入、在 SciTE.properties 中按文件名追加属性分支、再生成.styled/.folded期望文件即可为任意 Markdown 词法特性建立一份可回归的验证。小结HeaderEOLFill_0.md是一个 20 行的极简样例却完整覆盖了 Setext/ATX 两类标题与连续标题边界场景配合 SciTE.properties 的条件属性、.styled/.folded双期望文件和 LexMarkdown.cxx 中的状态机实现它演示了 Lexilla “输入—属性—期望输出”三位一体的词法器测试方法论也为理解 NotepadNext 中 Markdown 标题着色与lexer.markdown.header.eolfill属性的全部行为提供了可逐行核对的依据。【免费下载链接】NotepadNextA cross-platform, reimplementation of Notepad项目地址: https://gitcode.com/GitHub_Trending/no/NotepadNext创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考