ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Universal Ctags 解析 reStructuredText 代码块:从 RST 文档中提取嵌入 C 代码标签的完整实战

Universal Ctags 解析 reStructuredText 代码块:从 RST 文档中提取嵌入 C 代码标签的完整实战 开发工具CLI【免费下载链接】ctagsA maintained ctags implementation项目地址https://gitcode.com/gh_mirrors/ct/ctags点击查看免费下载reStructuredTextreST是 Python 生态与 Sphinx 文档系统广泛采用的标记语言其文档中常常嵌有大量.. code-block::代码块。本篇文章围绕 Universal Ctags 的 ReStructuredText 解析器parsers/rst.c展开结合仓库Units/parser-restructuredtext.r/code-blocks.d/下的单元测试深入讲解 reST 解析器如何识别标题层级如何在开启--extrasgguest 解析后把代码块中的 C 代码当作寄宿语言进行二次解析并产出带extras:guest标记的标签以及如何用--fieldslE等选项控制输出字段帮助你在实际项目中把文档即源码的场景落地为可检索的标签索引。测试用例全景code-blocks.d 的四个输入文件仓库中的测试目录 Units/parser-restructuredtext.r/code-blocks.d/ 由一组输入文件input.rst、input-0.rst、input-1.rst、input-2.rst、选项文件args.ctags与期望输出expected.tags构成覆盖了 reST 解析器对代码块处理的四种典型场景。input.rst标准文档中的多个 C 代码块input.rst 模拟一份带标题的完整 reST 文档 C Language Example Test 1 --------------------- .. code-block:: c int test1_0(void) { return 0; } int test1_1(void) { return 0; } Some descriptions here. .. code-block:: c int test1_2(void) { return 0; } int test1_3(void) { return 0; } Test 2 --------------------- Some descriptions here. .. code-block:: c int test2_0(void) { return 0; }文档标题C Language Example使用覆盖线与下划线overline underline声明两个小节Test 1、Test 2使用-----下划线声明正文中穿插三个.. code-block:: c代码块共含 5 个 C 函数。input-0.rst空的代码块指令input-0.rst 只有两行No thing here. .. code-block:: ccode-block指令后面没有跟随任何语言名与代码行。从源码看is_markup_line_with_cstr解析出code-block::前缀后findRstTags会跳过空白并检查*markup_line是否非空——为空时不初始化代码块跟踪器因此该文件不会产生任何 guest 标签expected.tags中也确实没有input-0.rst的条目。input-1.rst连续堆叠的代码块指令input-1.rst 连续三行.. code-block:: c后面紧跟一个标题TITLE。这是对代码块指令重复出现时状态机如何处理的边界测试三个指令中前两个因缺少语言名而未初始化代码块第三个也未携带语言名随后解析器转入常规的标题解析最终只产出一个TITLE标题标签。input-2.rst列表项内的代码块本指南核心案例input-2.rst 是本次讨论的核心* an item .. code-block:: C #define DEF 1 int this_is_not_c_code (); * another itemreST 的列表项内容需要相对列表标记缩进代码块指令与代码正文同样采用缩进对齐的方式界定范围。代码块内只有一行 C 预处理宏#define DEF 1其后紧跟的int this_is_not_c_code ();因缩进回落到列表项正文层级不属于代码块因此不会被当作 C 代码解析。期望输出 expected.tags 与参数文件 args.ctagsexpected.tags 记录了所有输入文件的期望标签C Language Example input.rst /^C Language Example$/; H language:ReStructuredText Test 1 input.rst /^Test 1$/; c language:ReStructuredText title:C Language Example Test 2 input.rst /^Test 2$/; c language:ReStructuredText title:C Language Example test1_0 input.rst /^ int test1_0(void)$/; f language:C typeref:typename:int extras:guest test1_1 input.rst /^ int test1_1(void)$/; f language:C typeref:typename:int extras:guest test1_2 input.rst /^ int test1_2(void)$/; f language:C typeref:typename:int extras:guest test1_3 input.rst /^ int test1_3(void)$/; f language:C typeref:typename:int extras:guest test2_0 input.rst /^ int test2_0(void)$/; f language:C typeref:typename:int extras:guest TITLE input-1.rst /^TITLE$/; H language:ReStructuredText DEF input-2.rst /^ #define DEF /; d language:C file: extras:fileScope,guestargs.ctags 给出了产生这些结果的选项--sortno --extrasg --fieldslE其中--sortno保持输入顺序输出期望文件按文件与行号排列--extrasg启用 guest 解析后文详述--fieldslE让输出包含language:字段并展开所有 extras 标记。注意DEF所在行没有language:字段而test1_0等有原因是 C 解析器在普通字段输出下给出的 typeref/字段组合差异可对比观察-E对字段展开的影响。guest 解析机制--extrasg如何把 C 代码块变成 C 标签expected.tags中最有信息量的字段是每个 C 标签末尾的extras:guest。guest寄宿解析是 Universal Ctags 让一种语言解析器借用另一种语言解析器的能力reST 解析器发现.. code-block:: c后把该代码块的行区间提交给 C 解析器由 C 解析器在块内产出 C 函数、宏等标签。代码块跟踪状态机parsers/rst.cparsers/rst.c 中代码块的跟踪由struct codeblockTrackerparsers/rst.c完成关键成员包括blockIndent代码块的缩进列数用于界定代码块的结束languagecode-block指令后声明的语言名如c、CstartLine/endLine/endLineLength代码块在输入文件中的起止位置。处理流程findRstTagsparsers/rst.c大致如下每读一行若当前处于代码块内且run_guest为真调用does_codeblock_continueparsers/rst.c判断代码块是否延续只要该行相对块起始的缩进大于blockIndent就继续缩进回落到blockIndent以内且行为空行时也继续否则代码块结束用is_markup_line_with_cstr(line_trimmed, code-block::, 12)识别.. code-block::指令行parsers/rst.c读取语言名并通过init_codeblock记录块起点起始行号 1跳过指令行本身代码块结束或文件读完时submit_codeblockparsers/rst.c调用makePromise把语言名与行区间登记为一个 guest 解析请求主流程继续标题、目标、引用、替换定义等 reST 结构照常处理。promise 调度延迟执行的二次解析makePromise定义于 main/promise.c其作用是把(语言, 起始行, 结束行)三元组暂存为 promise。主解析完成后通用 ctags 核心会依次兑现每个 promise即以该语言重新解析指定的行区间产出的标签被打上 guest 标记markTagExtraBit(e, XTAG_GUEST)见 main/entry.c。从 parsers/rst.c 的实现可以看到startLine被同时作为 promise 的输入偏移与源行号这意味着代码块内的行号是相对于 .rst 文件本身的行号DEF输出在input-2.rst的第 5 行附近与输入文件一致若代码块语言名无法解析如拼写错误该 promise 不会产出标签但不会影响 reST 自身的标签。列表项内代码块的缩进判定input-2.rst演示了 reST 列表嵌套代码块的缩进规则。* an item后代码块指令与正文统一缩进到第 3 列blockIndent为 3因此#define DEF 1属于代码块并被 C 解析器识别为dmacro标签而int this_is_not_c_code ();缩进为 0blockOffset(0) blockIndent(3)且首字符非空does_codeblock_continue返回 false代码块在此行前结束这行文本被当作普通 reST 段落不会产生任何 C 标签。这也解释了为什么expected.tags中只有DEF而没有this_is_not_c_code。reST 标题层级识别title / chapter / section 的划分与字段guest 标签只是 reST 解析器的一半能力另一半是它本身对文档结构的识别。RstKindsparsers/rst.c定义了 9 种 kindkind 字符名称说明Htitle文档标题支持 overline underlinehsubtitle副标题cchapter章ssection节Ssubsection小节tsubsubsection小小节Ccitation引用Ttarget超链接目标dsubstdef替换定义reST 本身不区分标题级别级别由装饰线underline/overline 的字符与长度的相对出现顺序决定。get_kindparsers/rst.c维护sectionTracker记录每种装饰字符的首次出现顺序把第一个出现的装饰样式当作 title其后按出现顺序依次划分 chapter、section 等adjustSectionKindsparsers/rst.c在解析结束后校正层级若出现两个不同样式的标题级装饰线则进行 kind 平移保证文档层级语义正确。expected.tags中Test 1、Test 2被标记为cchapter并带有title:C Language Example作用域说明先出现被识别为 title-----后出现被识别为下一级chapter。解析器扩展字段sectionMarker 与 overlineRstFieldsparsers/rst.c为标题标签提供两个默认关闭的扩展字段sectionMarker--fields-rst{sectionMarker}声明标题所用的装饰字符 - ~等overline--fields-rst{overline}布尔值指示该标题是否使用 overline underline 双线装饰。makeSectionRstTagparsers/rst.c通过attachParserField填充这两个字段并基于nestingLevels为标题建立父子作用域如Test 1的title:C Language Example。如何复现与验证从仓库运行 code-blocks 测试所有输入文件与期望输出都已入库可在本地复现。ctags 仓库的单元测试框架Tmain/Units支持按目录运行验证命令大致为# 在 ctags 源码目录完成构建后运行 code-blocks 测试 ./misc/units run Units/parser-restructuredtext.r/code-blocks.d若手工验证 guest 解析效果可用与args.ctags一致的选项直接解析测试输入./ctags --sortno --extrasg --fieldslE -o - \ Units/parser-restructuredtext.r/code-blocks.d/input-2.rst预期输出包含DEF input-2.rst /^ #define DEF /; d language:C file: extras:fileScope,guest实际输出格式随构建版本与字段默认值略有差异以--list-fields与--list-extras为准。参数速查与代码块、guest 相关的核心选项在args.ctags之外以下选项直接影响 reST 代码块解析与输出可在你的项目中按需组合--extrasg/--extras-g启用/禁用 guest 解析。这是代码块内的 C 标签能否产出的开关禁用后.. code-block:: c中的内容不会被任何语言解析等价于run_guest为假时findRstTags直接跳过代码块跟踪。--fieldsl输出language:字段标明标签所属语言language:ReStructuredText或language:C。--fieldsE展开 extras 列表把extras:guest、extras:fileScope之类的标记逐项列出。--fieldsK输出 kind 字符与名称如H、c、f、d。--kinds-C-d等按语言精细裁剪 guest 产出的标签种类若只关心函数与结构体可关掉宏、枚举等 kind。--language-forceC强制把输入文件当作某种语言解析谨慎用于 guest 场景它会影响主语言判定。--list-extras查看当前构建支持的 extras 全名单含guest、fileScope等。实用场景文档驱动代码索引理解代码块 guest 解析后你可以把 reST 文档当作第二源码库来检索API 文档索引Sphinx 风格的.rst文档中嵌入的示例代码C/Python/JavaScript 等会被对应语言解析器索引ctags -x --extrasg --fieldsl file.rst即可得到带语言标注的符号列表方便在 IDE 中直接从文档示例跳转到源码定义教程示例审计通过extras:guest快速找出文档中示例代码定义的符号再与真实源码中的符号做交集/差集发现文档示例的过期或缺失定义列表嵌套场景如input-2.rst所示只有缩进正确对齐到代码块指令的正文才会被 guest 解析缩进回落的行会被当作普通段落——编写文档时可用此规则精确控制哪些行进入索引。与相关测试与文档的衔接解析器源码parsers/rst.c测试目录Units/parser-restructuredtext.r/code-blocks.d/其他 reST 测试用例见 Units/parser-restructuredtext.r/ 下各.d目录标题、目标、引用等场景构建与测试说明docs/building.rst、docs/testing-ctags.rst小结Universal Ctags 的 ReStructuredText 解析器通过codeblockTracker状态机识别.. code-block::指令的缩进范围借助 promise 调度机制把块内文本交给指定语言解析器做 guest 二次解析从而在纯文本标记文档中同时产出 reST 标题标签与嵌入式代码标签。Units/parser-restructuredtext.r/code-blocks.d/的四个输入文件分别验证了标准多代码块、空指令、指令堆叠与列表项嵌套四种场景expected.tags中的extras:guest与language:C字段是理解这套机制最直观的参照。掌握--extrasg与--fieldslE的组合用法即可把 reST 文档变成可检索的代码索引。赞分享开发工具CLI【免费下载链接】ctagsA maintained ctags implementation项目地址https://gitcode.com/gh_mirrors/ct/ctags点击查看免费下载相关推荐DBX CLI 实战指南基于 DBX Desktop 的数据库 Schema 探索与只读查询工具DBX CLI 实战指南基于 DBX Desktop 的数据库 Schema 探索与只读查询工具 DBX CLI 是 DBX 桌面数据库客户端的官方命令行工具开发工具CLITandoor 食谱管理 Telegram 购物机器人Webhook 配置、消息解析与源码原理全解析Tandoor 食谱管理 Telegram 购物机器人Webhook 配置、消息解析与源码原理全解析 本文面向希望为 Tandoor 食谱管理应用本仓库即开发工具CLIctags 解析 UTF-8 编码的 reStructuredText 文档章节标题提取与标签生成实战指南ctags 解析 UTF 8 编码的 reStructuredText 文档章节标题提取与标签生成实战指南 导读 reStructuredTextreST开发工具CLI上一篇OpenShell CLIopenshell完整使用指南沙箱生命周期、网关注册、策略与 Provider 管理实战下一篇Infer 静态分析工具安装与使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表