zh CN DslContext - chiba233/yumeDSL GitHub Wiki

DslContext

导出一览 | 源码位置追踪

DslContext 是一个很小的上下文对象,把"当前用什么语法"和"怎么生成 token ID"打包在一起。 解析器在调用你的 handler 时会把它作为最后一个参数传进来——你只需要原样转发给工具函数就行。


它在哪里出现

createParser / parseRichText
        │
        │  解析时内部创建 DslContext
        │  { syntax, createId }
        │
        ▼
  你的 handler(tokens, ctx)        ← ctx 就是 DslContext
        │
        │  转发 ctx
        ▼
  parsePipeArgs(tokens, ctx)       ← 用 ctx.syntax 知道管道符是什么
  materializeTextTokens(tokens, ctx) ← 用 ctx.syntax 知道怎么反转义
  createTextToken(value, ctx)      ← 用 ctx.createId 生成 ID

接口

interface DslContext {
    syntax: SyntaxConfig;   // 当前生效的语法配置
    createId?: CreateId;    // token ID 生成器(可选)
}

就两个字段,很轻。打印出来长这样:

// 在 handler 里 console.log(ctx) 看到的东西:
{
    syntax: {
        tagPrefix: "$$",
        tagOpen: "(",
        tagClose: ")",
        tagDivider: "|",
        endTag: ")$$",
        rawOpen: ")%",
        blockOpen: ")*",
        rawClose: "%end$$",
        blockClose: "*end$$",
        escapeChar: "\\",
        escapableTokens: ["%end$$", "*end$$", ")$$", ")%", ")*", "(", ")", "|", "\\"],
    },
    createId: [Function],   // 生成 token ID 的函数
}

就是告诉你的回调"当前用什么语法符号"和"怎么生成 ID"。


什么时候需要关心它

场景 要做什么
写 handler(最常见) ctx 原样转发给 parsePipeArgs 等工具函数就行
在解析之外调工具函数 自己构造一个:{ syntax: createSyntax(), createId: ... }
用 createParser 且没有自定义 handler 完全不用管
旧 handler 没接 ctx 参数 能跑(JS 会忽略多余参数),但管道分割和转义会用模块默认值。换了自定义语法就会出问题——加一行 ctx 参数就好

ctx 到底要不要写

永远写。 不管你的回调里用不用得到 ctx,都声明它:

// 底层 TagHandler
inline: (tokens, ctx) => ...
raw: (arg, content, ctx) => ...

// createPipeHandlers 回调
inline: (args, ctx) => ...
raw: (args, content, ctx) => ...

原因:

  • ctx 由框架保证提供,声明它没有任何额外开销
  • 未来大版本会把 ctx 改为 required,现在写了就不用改
  • 并发环境(如 SSR)下,显式传 ctx 可以避免依赖模块级环境状态,确保每个请求独立

写 handler 时的标准写法

link: {
    inline: (tokens, ctx) => {
        const args = parsePipeArgs(tokens, ctx);  // ← 转发 ctx
        return {type: "link", url: args.text(0), value: args.materializedTailTokens(1)};
    },
}

在解析之外使用

const ctx: DslContext = {
    syntax: createSyntax(),
    createId: (draft) => `demo-${draft.type}`,
};
const args = parsePipeTextArgs("ts | Demo", ctx);

省略 ctx 时的兜底链

工具函数在没收到 ctx 时按以下优先级兜底:

  1. 语法相关函数(分割、转义等)→ getSyntax()(模块默认语法)
  2. createToken → 模块级计数器 rt-0, rt-1, ...

只要你没改默认语法,不传 ctx 也不会出错。但未来的大版本会把 ctx 变成必传参数,建议现在就养成转发的习惯。