免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Zulip 的 Markdown 标签页扩展:`{start_tabs}` 语法如何把 API 文档渲染成分 Tab 的操作指南

Zulip 的 Markdown 标签页扩展:`{start_tabs}` 语法如何把 API 文档渲染成分 Tab 的操作指南 Zulip 的 Markdown 标签页扩展{start_tabs}语法如何把 API 文档渲染成分 Tab 的操作指南【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip本篇指南解析 Zulip 服务端自带的 Tabbed Sections Markdown 扩展从{start_tabs}/{tab|key}/{end_tabs}的书写语法讲到zerver/lib/markdown/tabbed_sections.py中预处理器如何逐行解析、校验标签并拼装 HTML再到 Django 模板过滤器render_markdown_path如何将其挂载进文档渲染管线。读完你可以掌握如何为自己的文档撰写多平台/多语言分 Tab 内容、合法tab_key的完整清单、未知标签触发的报错机制以及扩展在预处理器优先级体系中的执行位置。语法速览一个真实的测试文档仓库中有一份专门验证该扩展语法的示例文件 test_tabbed_sections.md它恰好覆盖了三种典型写法。以下按其原文结构讲解# Heading {start_tabs} {tab|ios} iOS instructions {tab|desktop-web} Desktop/browser instructions {end_tabs} ## Heading 2 {start_tabs} {tab|desktop-web} Desktop/browser instructions {tab|android} Android instructions {end_tabs} ## Heading 3 {start_tabs} Instructions for all platforms {end_tabs}三个片段分别演示了两个标签ios与desktop-web每个{tab|key}声明一个 Tab其后的内容属于该 Tab直到下一个{tab|key}或{end_tabs}无空行的紧凑写法{tab|desktop-web}与{tab|android}之间可以没有空行解析器按行匹配标记不依赖空行分隔无 Tab 的伪 Tab段落{start_tabs}与{end_tabs}之间只写内容、不写任何{tab|...}行。此时扩展会把整段内容当作单一 Tab 处理Tab 键固定为instructions-for-all-platforms。需要牢记的三条硬性规则标记必须独占一行且严格匹配行首行尾都不能有多余字符Tab 的键{tab|...}中竖线后面的部分必须是 zerver/lib/markdown/tabbed_sections.py 中TAB_SECTION_LABELS字典里已注册的 key一段{start_tabs}...{end_tabs}只能出现在文档中的一层同一文档内可以有多段如上述示例的三个 H 段各有一段。实现剖析预处理阶段的逐行解析该扩展位于 zerver/lib/markdown/tabbed_sections.py基于 Pythonmarkdown库的Extension机制实现核心是一个Preprocessor预处理器——即在任何 HTML 生成之前先对纯文本行做改写。三个锚定正则START_TABBED_SECTION_REGEX re.compile(r^\{start_tabs\}$) END_TABBED_SECTION_REGEX re.compile(r^\{end_tabs\}$) TAB_CONTENT_REGEX re.compile(r^\{tab\|([^}])\}$)tabbed_sections.py#L12-L14^...$的双端锚定解释了第一条硬性规则{ start_tabs }、{tab|ios} 注释这类带额外字符的行都不会被识别为标记会被当作普通文本原样输出。TAB_CONTENT_REGEX用捕获组([^}])提取 Tab 键允许任意非}字符所以 key 可以含连字符。parse_tabs从行列表中定位一个 Tab 段parse_tabs(lines)tabbed_sections.py#L71-L88从行列表顶部开始扫描记录第一个{start_tabs}的行号 →start_tabs_index每个{tab|key}的行号与键名 → 依次追加到tabs列表遇到{end_tabs}时记录end_tabs_index并break返回该段的字典结构。预处理器TabbedSectionsPreprocessor.run()tabbed_sections.py#L126-L150随后进入一个while循环找到一段就渲染掉把lines[start:end1]整段替换为生成的 HTML 字符串并打上markdown1属性以便内部内容继续参与 Markdown 渲染再对剩余行重新调用parse_tabs直到文档中不再存在{start_tabs}...{end_tabs}段为止。这就是为什么同一篇文档可以包含任意多个 Tab 段且它们互不干扰。has-tabs 与 no-tabs 两种形态if tabs in tab_section: tab_class has-tabs else: tab_class no-tabs tab_section[tabs] [{tab_key: instructions-for-all-platforms, ...}]tabbed_sections.py#L129-L139对照示例文档的三个段落前两段各有{tab|...}行生成classtabbed-section has-tabs第三段没有任何 Tab 声明被自动补成一个键为instructions-for-all-platforms的隐式 Tab生成classtabbed-section no-tabs。no-tabs形态下导航栏仍会渲染出一个名为 Instructions for all platforms 的项见测试期望 HTML 中的data-tab-keyinstructions-for-all-platforms前端样式据此决定是否显示可点击的 Tab 头。TAB_SECTION_LABELS合法 Tab 键的权威清单TAB_SECTION_LABELStabbed_sections.py#L43-L58同时承担两个职责key 是文档中允许使用的 Tab 标识符value 是渲染到页面上的显示标签。当前清单tab_key显示标签desktop-webDesktop/WebiosiOSandroidAndroidpythonPythonjsJavaScriptcurlcurlzulip-sendzulip-sendinstructions-for-all-platformsInstructions for all platformsfor-a-botFor a botfor-yourselfFor yourselfgrafana-latestGrafana 8.3grafana-older-versionGrafana 8.2 and belowsend-channel-messageSend a channel messagesend-dmSend a DM从这份 key 的命名可以推断出该扩展的主要服务对象Zulip 的 API 文档api_docs/ 下大量文件使用{start_tabs}按 Desktop/Web、iOS、Android 客户端或 curl/Python/zulip-send 等调用方式分 Tab以及部分 Webhook/集成文档zerver/webhooks/ 各服务的doc.md亦广泛使用grafana-*两个 key 正是为 Grafana Webhook 文档的版本差异而设。源码中有一条值得注意的注释新增 key 时也请检查是否需要同步更新tabbed-instructions.jstabbed_sections.py#L41-L42。从当前仓库结构看前端对.tabbed-section样式的定义位于 web/styles/portico/markdown.css即该机制主要服务于服务端渲染的文档页面Portico 文档站。错误路径未知 Tab 键触发 ValueError仓库配有第二份测试文档 test_tabbed_sections_missing_tabs.md其中声明了{tab|minix}——一个未在TAB_SECTION_LABELS中注册的键{start_tabs} {tab|ios} iOS instructions {tab|minix} Minix instructions. We expect an exception because the minix tab doesnt have a declared label. {end_tabs}对应的测试test_markdown_tabbed_sections_missing_tabszerver/tests/test_templates.py#L93-L100断言渲染过程抛出ValueError且消息精确匹配Tab minix is not present in TAB_SECTION_LABELS in zerver/lib/markdown/tabbed_sections.py抛出点位于generate_nav_bar()tabbed_sections.py#L152-L166生成导航栏时需要把每个 key 映射为显示标签查不到即报错。这种快速失败设计保证了文档作者在发布前就能发现拼写错误比如把desktop-web写成desktop_web而不是得到一个 Tab 名显示为 key 原文的页面。渲染产物结构由模板字符串拼出的 HTML预处理器用三组模板字符串拼装最终 HTMLtabbed_sections.py#L16-L39TABBED_SECTION_TEMPLATE外层div classtabbed-section has-tabs|no-tabs markdown1 导航栏 div classblocks包裹的内容块NAV_BAR_TEMPLATEul classnav内逐 Tab 生成liDIV_TAB_CONTENT_TEMPLATE每个 Tab 一个div classtab-content [active]>div classtabbed-section has-tabs markdown1 ul classnav li classactive>zerver.lib.markdown.nested_code_blocks.makeExtension(), zerver.lib.markdown.tabbed_sections.makeExtension(), zerver.lib.markdown.help_settings_links.makeExtension(), ...测试正是经由 Django 模板tests/test_markdown.html触发该过滤器上下文变量markdown_test_file指向zerver/tests/markdown/test_tabbed_sections.mdtemplate.render(context)即完成Markdown 文件 → HTML的全过程test_templates.py#L21-L27。注意render_markdown_path的 docstring 强调渲染出的 HTML 被视为可信内容该路径面向文档而非用户输入——{tab|minix}抛出的ValueError也因此发生在服务端渲染阶段而非任何用户可见界面。执行时机由优先级决定扩展注册时使用的优先级取自 zerver/lib/markdown/priorities.pyPREPROCESSOR_PRIORITIES { ... fenced_code_block: 25, # html_block: 20, tabbed_sections: -500, nested_code_blocks: -500, ... }注册代码见 tabbed_sections.py#L62-L68TabbedSectionsGenerator.extendMarkdown把预处理器以名称tabbed_sections、优先级-500挂入md.preprocessors。-500是刻意取的低值注释标明 registry 中数值越大越先执行Tab 段内部可能含有围栏代码块必须先由fenced_code_block优先级 25等上游预处理器处理Tabbed Sections 在接近最后阶段对整个段做整体替换避免干扰其他扩展对段内行的解析。小结Zulip 的 Tabbed Sections 扩展用不到两百行代码实现了一套完整的多平台文档分栏机制三个行级正则锚定语法、parse_tabs定位段落、TAB_SECTION_LABELS提供白名单式校验与显示文案、四组 HTML 模板负责拼装、markdown1属性让 Tab 内部 Markdown 得以二次渲染而-500的预处理器优先级则保证了它与其他文档扩展围栏代码、嵌套代码块等的安全协作。如果你需要在 Zulip 的 API/集成/帮助文档中新增分 Tab 说明只需遵循{start_tabs}→ 若干{tab|合法key}→{end_tabs}的独占行写法把test_tabbed_sections.md与 test_templates.py 中的期望 HTML 当作可运行的验收标准即可新增 Tab 键时则必须同步在TAB_SECTION_LABELS中登记否则渲染将以ValueError快速失败。【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表