先区分三类插件
| 能力 | MSFE 入口 | 是否改源码 |
|---|---|---|
| 新语法识别 | addMarkdownExtensions() 的 Lezer Markdown 扩展 | 否 |
| 已有语法的组件和视觉效果 | addSource()、addNodeView()、addProjectionLayers() | 渲染本身不改 |
| 输入和粘贴时替换文本 | InputRule、PasteRule | 是,参与事务和历史 |
markdown-it 插件接受 token 流、规则链和 HTML renderer;MSFE 使用 Lezer 的增量语法树。这两种协议不同,不能把 md.use(plugin) 的插件对象直接放进 extensions。可以在隔离的非编辑预览中使用第三方渲染库,但解析、源码范围和写回仍需自己接入。
一个真实的解析插件
可选文档属性
Frontmatter 只识别源码。只有显式安装 DocumentProperties 才会将文首 YAML 显示成属性表;
MD Editor 默认不安装它。Vue 宿主可以复用公共编辑面板:
<script setup lang="ts">
import { MdEditor, MdDocumentProperties } from '@quirekit/md-editor/vue'
import { DocumentProperties } from '@quirekit/msfe-markdown-extras'
import { VueComponentViewRenderer } from '@quirekit/msfe-vue'
const extensions = [DocumentProperties.configure({
render: VueComponentViewRenderer(MdDocumentProperties), locale: 'zh-CN',
defaultCollapsed: true,
})]
const markdown = '---\ntitle: 示例\ntags: [写作, 编辑]\n---\n\n正文'
</script>
<template><MdEditor :markdown="markdown" :extensions="extensions" /></template>
MD Editor 已包含 Frontmatter;直接使用 MSFE Core 时须在 extensions 中同时加入
Frontmatter。不传 render 时提供原生属性展示和查看源码入口。
setDocumentPropertiesEnabled(editor, false) 可恢复原始 Frontmatter,true 再次启用,
不重建编辑器、不清空撤销、不改正文和选区。用 documentPropertiesEnabled(editor.state)
读取实际状态。示例页的“文档属性”开关使用这个公共接口。
defaultCollapsed: true 让属性区初始只显示标题和数量,适合优先展示正文的产品;
省略或设为 false 时默认展开。此配置同时适用于 Vue 面板与默认原生渲染器。
用户手动切换后,同一文档内的编辑、模式切换、投影开关和视口重挂载都会保留选择;
replaceDocument 加载另一篇文档时重新使用默认值。enabled: false 会显示原 YAML,
与折叠属性区不同。跨会话记忆可由宿主保存,并作为下一次安装的默认值。
运行时可通过 documentPropertiesCollapsed(editor.state) 读取、
setDocumentPropertiesCollapsed(editor, true) 收起,false 展开;自定义渲染器使用
view.collapsed 与 view.setCollapsed()。只读模式也能折叠,不改变编辑权限。
MdDocumentProperties 默认预览六个字段,长文本与标签列表可单独展开。
点击字段值直接编辑,确认与取消保持同行;单行 Enter 保存,Escape 取消,多行可用
⌘/Ctrl + Enter 保存。收起面板保留未保存草稿,展开状态不会写入 Markdown。
字符串、数字、布尔和字符串列表可编辑,操作参与源码撤销。其他值展示原文;无效 YAML、 重复键、别名、锚点、显式标签、行内或缩进根映射、显式或跨行键、TOML、未闭合或超过 20,000 字符的头部回退源码。新增属性名始终编码为字符串键。 写回只替换目标范围,保留其他字段、注释和空行;文档或头部已变化时拒绝过期表单。 源码模式显示完整原文,编辑器只读时不提供修改控件。
自定义徽标
本例支持 :badge[label]:,保留可见分隔符并用主题色标记。未闭合、超长或含嵌套方括号的内容保持普通源码;代码 span 内的同样字符串不被当作徽标。
1. 贡献语法
依赖 @quirekit/lezer-markdown,使用 MarkdownExtension。plugins/syntax.ts 的 defineNodes 注册 ExampleBadge,parseInline 在看到 : 时先检查精确前缀,再在最多 82 个后续单元内寻找合法结束。
context.elt() 创建与规范源码同坐标的语法元素,addElement() 返回消费到的位置;不匹配返回 -1。before / after 是解析规则顺序,不是视觉优先级。这个例子有意不支持嵌套;更复杂语法必须定义明确的块/行内规则和回退。
简单的、不嵌套的单行字面分隔符可以直接使用 defineSource({ syntax }),参考 状态节点。不要为了简单 token 额外维护一个全文件解析器。
2. 接入语义查询
addSource() 将已确认的 ExampleBadge 解释为 { label }。read() 只读匹配范围;需要可编辑字段时再增加 addAttributes() 的验证与 patch()。语义层不需要渲染成 HTML。
const query = editor.querySource('exampleBadge')
if (query.status === 'ready') {
for (const match of query.matches) console.log(match.from, match.to, match.attrs)
}
查询默认针对当前选区,可传显式有界范围。pending 表示相关解析还未确认,不能误判为文档里不存在语法;unsupported 表示该语义入口不可用。需要解析推进时使用 requestMarkdownParse(editor.state),再通过接受状态的通知重试,不在输入路径同步解析全文。
3. 选择视觉与写回
示例 inline 层只返回 mark,并用 EditorView.theme() 设置颜色,不隐藏分隔符。需要点击控件时使用 NodeView;需要跨行结构替换时使用 block 层。只做显示的插件不要附带输入转换,避免加载插件时悄悄改文档。
迁移一个 markdown-it 插件
- 列出它的语法、输入输出、是否有块嵌套、转义和引用规则。
- 用 Lezer 语法保证不完整输入、代码区域和嵌套上下文的行为。
- 用源码 codec 描述字段;没有可逆编码的字段先保持只读。
- 通过投影或组件提供视觉结果;HTML 输出另由导出管线实现。
- 为每项能力建立源码 fixture:原样读取、局部写回、撤销、加载/错误、只读及解析未完成。