@nx-lang/language-protocol

中文文档
0.10.0发布于 2026/11/02MITTypeScriptES Module零依赖构建来源已验证

NX语言查询的独立于传输的请求和回答形状,以及每个客户端实现的NxLanguageService接口。

中文使用文档
文档目录12 节
  1. 文档集模型
  2. 位置是UTF-16代码单位
  3. 查询
  4. hover
  5. completions
  6. diagnostics
  7. documentSymbols
  8. sourceTree (不稳定)
  9. sourceEdit (不稳定)
  10. 保留的名字
  11. 服务接口
  12. 错误

@nx-lang / language-protocol

编辑器和NX语言服务之间的契约:语言查询的形状及其 答案,编码双方使用的位置,和 NxLanguageService 连接每个客户端 实现满足。类型,一些常量和保护,以及错误类;没有运行时 依赖。

该接口的实现存在于其他地方:

  • @nx-lang/language-client -浏览器的HTTP实现,与
  • @nx-lang/language-http -一个可挂载的节点处理程序,通过 @nx-lang/sdk-node.

编辑器集成,例如 @nx-lang/monaco 只依赖于这个包,所以一个http支持的, 工人支持的或进程内服务是可互换的。

文档集模型

每个查询都包含将从中得到回答的完整文档集,以及对象的URI 查询的文档是关于:

interface LanguageQuery {
  documents: { uri: string; source: string; version?: number; identity?: string }[];
  uri: string; // one of documents[].uri
}

该服务在查询之间不保留任何内容。两个相同的查询产生相同的答案 在中间被询问,这是让几个服务器实例在没有会话关联的情况下回答的原因。 单文档主机发送它的一个文档;一个主机编辑一组文件中的一个,每个文件都导入 Other发送整个集合,其中编辑的文档包含编辑器未保存的文本,以及 完成包括编辑的文档从同级文档导入的内容。

version 是编辑想要的;它在应答中被回显,因此客户端可以丢弃 在发送新版本后到达的应答。 identity 是NX解决的工作空间身份 按(tenant/form.nx);当省略时,它从URI派生。

位置是UTF-16代码单位

一、 TextPosition 是零基的 line 和一个零基的 character 以UTF-16编码单位计算 从一线出发,一路前行 String.prototype.length 计数和摩纳哥的方式,codemmirror和 语言服务器协议默认计数。之前的表情符号是两个单位。靶场携带 它的起始和结束位置,在它们旁边, startByte 和 endByte 抵消到 文档的UTF-8文本,这是语言服务测量的。

// let s = "😀" + name
//                ^ name starts at character 15 (the emoji is 2 units) and byte 18 (it is 4 bytes)
{ start: { line: 0, character: 15 }, end: { line: 0, character: 19 }, startByte: 18, endByte: 22 }

查询

定义了五个查询;每个都有一个请求和一个通过JSON往返的回答类型。这个 下面的例子编译在 test/examples.test.ts,因此它们不会偏离类型。

hover

const request: HoverRequest = {
  documents,
  uri: "nx://tenant/form.nx",
  position: { line: 1, character: 3 },
};

const answer: Hover | null = {
  uri: "nx://tenant/form.nx",
  identity: "tenant/form.nx",
  version: 7,
  range: { start: { line: 1, character: 1 }, end: { line: 1, character: 7 }, startByte: 34, endByte: 40 },
  contents: "```nx\n<Button label:string />\n```",
};

contents 有NX碎片的markdown吗 ```nx 栅栏。当服务无事可做时 假设答案是 null,不是错误。

completions

const request: CompletionsRequest = {
  documents,
  uri: "nx://tenant/form.nx",
  position: { line: 1, character: 23 },
};

const answer: CompletionList = {
  uri: "nx://tenant/form.nx",
  identity: "tenant/form.nx",
  version: 7,
  items: [{ label: "label", kind: "Property", detail: "label:string", documentation: "The caption." }],
};

kind 是其中之一 Keyword, Type, Declaration, Component, Property, Member. documentation 是 /// 项目名称的文档,如markdown,当 它没有。

diagnostics

const request: DiagnosticsRequest = { documents, uri: "nx://tenant/form.nx" };

const answer: DiagnosticReport = {
  documents: [
    { uri: "nx://tenant/ui.nx", identity: "tenant/ui.nx", version: 3, diagnostics: [] },
    {
      uri: "nx://tenant/form.nx",
      identity: "tenant/form.nx",
      version: 7,
      diagnostics: [
        {
          range: { start: { line: 1, character: 8 }, end: { line: 1, character: 13 }, startByte: 41, endByte: 46 },
          severity: "Error",
          code: "type-mismatch",
          message: "Expected string, found int",
          related: [],
        },
      ],
    },
  ],
  workspace: [],
};

报告涵盖集合中的每个文档,每个文档一个条目,因此可以设置多文档主机 所有的答案都是一个标记。 workspace 保存不属于任何文档的诊断信息, 例如导入既不在集合中也不在库中的模块。 severity 是其中之一 Error, Warning, Info, Hint.

documentSymbols

const request: DocumentSymbolsRequest = { documents, uri: "nx://tenant/ui.nx" };

const answer: DocumentSymbol[] = [
  {
    name: "Button",
    kind: "Component",
    range: { start: { line: 0, character: 0 }, end: { line: 0, character: 47 }, startByte: 0, endByte: 47 },
    selectionRange: { start: { line: 0, character: 12 }, end: { line: 0, character: 18 }, startByte: 12, endByte: 18 },
  },
];

kind 是其中之一 Function, Value, TypeAlias, Record, Action, Union, Component, Element.

sourceTree (不稳定)

不稳定: 答案的形状可能会在任何版本中改变,直到下一个版本提交它。它是 为NX查看器发布,并在此基础上构建结构更改视图。

const request: SourceTreeRequest = { documents, uri: "nx://tenant/ui.nx" };

const answer: SourceTree = {
  uri: "nx://tenant/ui.nx",
  identity: "tenant/ui.nx",
  version: 3,
  nodes: [
    { role: "declaration", range: range(0, 47), key: "Button", name: "Button", declaration: 0, flags: ["export"] },
    { role: "parameter", range: range(19, 31), parent: 0, key: "Button.label", name: "label" },
    { role: "typeReference", range: range(25, 31), parent: 1, key: "Button.label.type", value: "string" },
    { role: "element", range: range(37, 47), parent: 0, key: "Button.body", name: "button", type: "button" },
  ],
  declarations: [
    { module: "tenant/ui.nx", name: "Button", kind: "function", range: range(0, 47), properties: [{ name: "label", type: "string" }] },
  ],
};

(range(start, end) 代表 EditorRange 这些字节在第0行。)

文档的每个部分都是一个节点,包含未解析的注释和区域,以及每个节点 令牌只属于一个节点:其范围内包含它的最小节点。节点按顺序列出 以父节点的索引开始偏移,每个父节点在其子节点之前。一个节点有一个 role (element, attribute, literal, case, operator, condition, comment, unparsed 和 剩下的 SourceRole),并载有书面内容 name, value 和 textType,它的 用NX拼写 type 当类型检查器给它一个时,输入 declaration 的指数 它所指的宣言。节点的类型和声明是hover在其中报告的内容。

key 节点在文档中的路径是否为 roleQuestion.value.choices[2].label: 顶层声明的名称,然后是属性、成员、武器和插槽,以及位置 为项目。对另一个声明或另一个属性的编辑不会改变它,所以有两个版本 的值可以通过键进行比较。键是用于比较是否相等的标识符,而不是指向它的路径 拆分:手臂以其图案或测试来命名(is Mode.dark, when x > 1)和 通过路径导入,这些路径可以保存点和空格。读取树的结构 parent.

declarations 列出每个声明一次,一个节点从本文档引用另一个文档 集合或库的:它的模块标识,名称和类型,它在本文档声明时的范围 它、它的文档注释、它的属性按声明顺序排列,首先是继承的属性(每个属性都有它的 类型,它的默认写入,以及它是可选的(内容的还是继承的),它的基,a 组件的状态、联合的情况、别名的类型、值的类型和值的范围。 不存在节点或声明的可选属性,而不是 null 当他们不适用时。

请求 visualizers: {} (或 visualizers: { modules: [...] },命名进一步可视化 模块(宿主提供的)也解析可视化器声明:每个声明都有一个 带有 visualizer,带有标题、id和图标,可选 preview: { renderer: string },它的属性按顺序显示, 对于每个属性或别名,a format 和一个 editor. editor 的编辑器提示 类型: kind (field, dropdown 或者 segmented), min, max, step, multiline, placeholder 和 choices。每个选择的 value 类型为 sourceEdit setValue 值,所以a 客户端传递它,因为它是写选择:

const sizeProperty: SourcePropertyVisualizer = {
  name: "size",
  editor: {
    max: 96,
    choices: [
      { value: { kind: "number", text: "12" }, label: "sm" },
      { value: { kind: "number", text: "16" }, label: "base" },
    ],
  },
};

sourceEdit (不稳定)

不稳定: 请求和答案的形状可能会在任何版本中改变,直到下一个版本 向他们承诺。它是为NX查看器的可视化编辑而发布的。

const request: SourceEditRequest = {
  documents,
  uri: "nx://tenant/ui.nx",
  key: "Button.label.type",
  edit: { kind: "setValue", value: { kind: "string", value: "Save" } },
};

const answer: SourceEditAnswer = { edits: [{ range: range(25, 31), newText: '"Save"' }] };
const refused: SourceEditAnswer = { edits: [], refused: "notEditable" };

请求将文档源树的节点命名为 key 还有一个变化: setValue 使用类型化值(string, number 作为输入, boolean,或者 case (通过名称)表示文字或 这是一个完整的值, setText 一行文字代表一组文字内容, remove 对于作为列表项的属性或元素, addProperty 与一个 属性的名称和元素的类型化值,该值将该属性写入元素的 最后一个,在它的布局中,或者 move 有一个位置 to 对于作为列表项的元素。一个 列表的Item是带括号序列中的元素或元素内容中的元素,而不是 if 分支 或者一个 for Body,在不包含文本的列表中; to 一旦移动,它所占据的位置是否被算作 列表的键数它的项。在将一项写到一行的列表中,一项随其移动或移动 整行,其上方的注释,以及将其与下一项分开的空行 (前一个是最后一个);在一行的列表中,旁边有空格。其他布局有 拒绝 notEditable,和 to 在列表之外 invalidValue。答案是 edits 使 更改:每个范围都在发送时的文档中,没有两个重叠,因此主机将它们一起应用;该服务编写拼写(引号、转义、大括号) 属性的整行)。一个改变,它不能回答没有编辑和 refused: unknownNode, notEditable 或者 invalidValue。将文本保留原样的更改回答不编辑和不 拒绝。

保留的名字

definition, references, rename, signatureHelp, inlayHints 和 semanticTokens 是 保留。的实现 unsupported-query 错误,而 而不是传输失败或错误的回答,这样客户端就可以区分“还没有”和“坏了”。 isLanguageQueryName 和 isReservedLanguageQueryName 对名称进行分类。

服务接口

interface NxLanguageService {
  hover(request: HoverRequest, signal?: AbortSignal): Promise<Hover | null>;
  completions(request: CompletionsRequest, signal?: AbortSignal): Promise<CompletionList>;
  diagnostics(request: DiagnosticsRequest, signal?: AbortSignal): Promise<DiagnosticReport>;
  documentSymbols(request: DocumentSymbolsRequest, signal?: AbortSignal): Promise<DocumentSymbol[]>;
  sourceTree(request: SourceTreeRequest, signal?: AbortSignal): Promise<SourceTree>; // unstable
  sourceEdit(request: SourceEditRequest, signal?: AbortSignal): Promise<SourceEditAnswer>; // unstable
}

取消 signal 在应答到达之前,使用一个中止错误拒绝承诺 isAbortError 认错了,承诺就永远不会兑现。编辑器集成如下 针对该接口的进程内假例进行了测试,并对HTTP客户端进行了相同的行为 相同的答案。

错误

类当
UnsupportedQueryError服务识别了查询名称,但不支持查询名称。携带 query.
LanguageServiceHttpErrorHTTP实现收到非成功状态。携带 status, url,一个 bodyExcerpt和身体的 code 当它是一个错误体时。
LanguageServiceTimeoutError请求在客户端超时时间内没有得到响应;请求被中止。携带 timeoutMs.

HTTP实现用JSON主体回答失败的查询

{ error: { code: LanguageServiceErrorCode; message: string; query?: string } }

哪里 code 是其中之一 unsupported-query, unknown-query, method-not-allowed, invalid-request, payload-too-large, internal-error. isLanguageServiceErrorBody 认识到 的形状。

源树调用者可以设置 visualizers: { includeUnreferencedDeclarations: true } 包括 加载的可视化器目标和继承的表示元数据,即使入口源没有 直接引用它们。默认情况下将声明表绑定到源树。 未知的预览渲染器id仍然是元数据;主机只激活它们支持的实现。

版本与发布记录

@nx-lang/language-protocol 共发布 10 个版本,首次发布于 2026/09/15;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.10.0latest2026/11/021 天前
0.9.02026/10/268 天前
0.8.02026/10/1816 天前
0.7.02026/10/1024 天前
0.6.02026/10/0628 天前
0.5.02026/09/291 个月前

关于 @nx-lang/language-protocol 的常见问题

@nx-lang/language-protocol 是什么?

NX语言查询的独立于传输的请求和回答形状,以及每个客户端实现的NxLanguageService接口。

如何安装 @nx-lang/language-protocol?

在项目目录运行 npm install @nx-lang/language-protocol,或使用 pnpm add / yarn add / bun add。

@nx-lang/language-protocol 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@nx-lang/language-protocol 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@nx-lang/language-protocol 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@nx-lang/language-protocol 可以商用吗?

@nx-lang/language-protocol 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@nx-lang/language-protocol 还在维护吗?

最新版本 0.10.0 发布于 2026/11/02(1 天前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/09/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。