CodeEditor 代码编辑器
通用布局能力:
RenderCodeEditor实例统一支持width、height、min/max、margin和槽位对齐;编辑器内容 padding、行高和 gutter 是内部编辑规格。详见组件通用布局属性。
RenderCodeEditor 是基于 PlainTextEditor 大文本编辑器 的代码编辑器外壳。它复用 PlainTextEditor 的文本模型、光标、选区、滚动、行号、IME 和虚拟绘制能力,并增加通用语言服务接入点。
核心原则:
CodeEditor 管输入、选择、绘制和应用 edit。
LanguageAdapter 管语言语义、诊断、语义 token、补全、hover 和格式化。
CodeEditor 不内置业务语言语义,也不硬编码字段、关键字或操作符。业务 DSL 应通过 CodeEditorLanguageAdapter 接入。
何时使用
- 需要在业务系统内编辑 DSL、脚本、表达式、配置或规则。
- 需要展示诊断波浪线、语义高亮、格式化和后续补全/hover。
- 需要和 DirectSurface UI 的主题、焦点、Overlay、DevTools、布局检查保持一致。
- 已有独立语言服务,不希望编辑器直接依赖 parser/semantic/compiler 内部实现。
何时不要使用
- 只编辑普通备注,使用 TextArea。
- 只展示日志或大文本,使用 PlainTextEditor。
- 需要富文本、表格、图片、病历数据元,使用专业业务编辑器。
API 总览
import { RenderCodeEditor, type CodeEditorLanguageAdapter, type CodeEditorDiagnostic, type CodeEditorSemanticToken, type CodeEditorTextEdit, type CodeEditorAcceptSuggestionOnEnter, type CodeEditorCompletionActivation, type CodeEditorCompletionContext, type CodeEditorCompletionItem, type CodeEditorCompletionList, type CodeEditorCompletionResult, type CodeEditorCompletionTriggerKind, type CodeEditorHover, type CodeEditorSignature, type CodeEditorSignatureHelp, type CodeEditorSignatureHelpContext, type CodeEditorSignatureHelpTriggerKind, type CodeEditorSignatureParameter, type CodeEditorCodeAction, type CodeEditorContextMenuRequest, type CodeEditorLocation, type CodeEditorReference, type CodeEditorSelectionRange,} from 'ds-ui'
| API | 类型 | 用途 |
|---|---|---|
RenderCodeEditor |
class | 通用代码编辑器 render object。 |
CodeEditorLanguageAdapter |
interface | 语言服务适配器。 |
CodeEditorDiagnostic |
type | 诊断信息。 |
CodeEditorSemanticToken |
type | 语义高亮 token。 |
CodeEditorTextEdit |
type | 文本编辑。 |
CodeEditorAcceptSuggestionOnEnter |
type | Enter 接受策略:on、off 或 smart;兼容 always、never 和 explicit 别名。 |
CodeEditorCompletionActivation |
type | 补全触发来源,manual 或 auto。 |
CodeEditorCompletionContext |
interface | 自动/手动、trigger kind、trigger character 和取消信号。 |
CodeEditorCompletionItem |
type | 补全项。 |
CodeEditorCompletionList |
interface | 结构化候选列表,支持 isIncomplete、validFor 和共享 range。 |
CodeEditorCompletionResult |
type | 旧数组或结构化 CompletionList。 |
CodeEditorCompletionTriggerKind |
type | invoked、trigger-character 或 incomplete。 |
CodeEditorHover |
type | Hover 信息。 |
CodeEditorSignatureHelp |
type | 函数签名、当前重载和当前参数。 |
CodeEditorSignatureHelpContext |
interface | Signature 触发原因、字符、retrigger 标记和取消信号。 |
CodeEditorCodeAction |
type | 快速修复或源码操作。 |
CodeEditorContextMenuRequest |
type | 右键菜单请求上下文。 |
CodeEditorLocation |
type | 跳转定义位置。 |
CodeEditorReference |
type | 引用位置。 |
CodeEditorSelectionRange |
type | 智能选区候选。 |
MaybePromise |
type | 同步或异步返回值。 |
最小示例
const editor = new RenderCodeEditor({ value: '规则 儿童禁用\n如果 患者.年龄 小于 18', languageAdapter: { getDiagnostics: (source: string) => validateRule(source), getSemanticTokens: (source: string) => tokenizeRule(source), formatDocument: (source: string) => formatRule(source), },})
LanguageAdapter
CodeEditorLanguageAdapter 的 position/range 使用 DirectSurface UI 文本模型约定:line 和 column 都是 0-based,range.end 是右开区间。
interface CodeEditorLanguageAdapter< TContext = unknown, TCompletionResult extends CodeEditorCompletionResult = CodeEditorCompletionItem[],> { completionTriggerCharacters?: readonly string[] completionQuickSuggestionCharacters?: readonly string[] completionWordPattern?: RegExp signatureTriggerCharacters?: readonly string[] signatureRetriggerCharacters?: readonly string[] getDiagnostics?(source: string, context: TContext | undefined): MaybePromise<CodeEditorDiagnostic[]> getSemanticTokens?(source: string, context: TContext | undefined): MaybePromise<CodeEditorSemanticToken[]> getCompletions?( source: string, position: TextPosition, context: TContext | undefined, completionContext?: CodeEditorCompletionContext, ): MaybePromise<TCompletionResult> getHover?(source: string, position: TextPosition, context: TContext | undefined): MaybePromise<CodeEditorHover | null> getSignatureHelp?( source: string, position: TextPosition, context: TContext | undefined, signatureContext: CodeEditorSignatureHelpContext, ): MaybePromise<CodeEditorSignatureHelp | null> formatDocument?(source: string, context: TContext | undefined): MaybePromise<string | CodeEditorTextEdit[]> getCodeActions?(source: string, diagnostic: CodeEditorDiagnostic | undefined, context: TContext | undefined): MaybePromise<CodeEditorCodeAction[]> getDefinition?(source: string, position: TextPosition, context: TContext | undefined): MaybePromise<CodeEditorLocation | null> getReferences?(source: string, position: TextPosition, context: TContext | undefined): MaybePromise<CodeEditorReference[]> getRenameEdits?(source: string, position: TextPosition, newName: string, context: TContext | undefined): MaybePromise<CodeEditorTextEdit[]> getSelectionRanges?(source: string, position: TextPosition, context: TContext | undefined): MaybePromise<CodeEditorSelectionRange[]>}
旧 Adapter 默认仍返回 CodeEditorCompletionItem[],不需要修改。需要 CompletionList 的 Adapter 显式指定第二个泛型参数。Editor options 的 trigger、quick-suggestion character 和 word-pattern 分别覆盖 Adapter capability。
当前版本已经接入:
getDiagnostics():显示错误、警告、提示装饰。getSemanticTokens():作为外部 line token provider 驱动语义高亮。getCompletions():打开补全列表、键盘选择并应用补全。getHover():显示可交互 Semantic Hover Card。getSignatureHelp():显示函数重载、当前参数和函数说明。formatDocument():可返回完整格式化文本或 edit 列表。getCodeActions():返回快速修复或源码操作,业务可通过按钮、命令或诊断列表触发。getDefinition():返回当前位置的定义位置,Ctrl/Meta点击也会触发。getReferences():返回当前位置符号的引用列表。getRenameEdits():返回重命名需要应用的 edit 列表。getSelectionRanges():返回从小到大的结构化选区候选。
构造参数
RenderCodeEditorOptions 继承 RenderPlainTextEditorOptions,额外支持:
因此代码编辑器也继承 appearance: 'framed' | 'flush',默认为 framed。独立编辑器保留默认边界;放入已绘制边框的 pane、tab 内容区或设计器内嵌区时,可设为 flush 避免双重边框。切换外观不会改变编辑器布局尺寸。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
languageAdapter |
CodeEditorLanguageAdapter |
undefined |
语言服务适配器。 |
languageContext |
unknown |
undefined |
传给语言服务的业务上下文。 |
diagnosticsDebounceMs |
number |
300 |
文本变化后的诊断防抖时间。 |
completionTriggerCharacters |
string[] |
['.', ' ', '\n'] |
自动触发补全的字符。 |
completionQuickSuggestionCharacters |
string[] |
Adapter capability 或 [] |
quickSuggestions:true 时,允许输入该字符后延迟询问 Provider;Provider 可以用空结果拒绝展示。 |
completionWordPattern |
RegExp |
/[\p{L}\p{N}_$]/u |
quick suggestions 判断标识符字符的模式。 |
signatureTriggerCharacters |
string[] |
Adapter capability 或 [] |
输入后自动请求 Signature Help 的字符。 |
signatureRetriggerCharacters |
string[] |
Adapter capability 或 [] |
已有 Signature Session 中重新请求的字符。 |
signatureHelpDelayMs |
number |
0 |
自动 Signature Help 请求延迟。 |
quickSuggestions |
boolean |
false |
是否在输入标识符时自动启动补全。 |
quickSuggestionsDelayMs |
number |
100 |
quick suggestions 的防抖时间。 |
acceptSuggestionOnEnter |
'on' | 'off' | 'smart' | 'always' | 'never' | 'explicit' |
'on' |
Enter 是否接受当前候选。always / never / explicit 分别是 on / off / smart 的兼容别名。 |
hoverDelayMs |
number |
300 |
鼠标停留后触发 hover 的延迟。 |
hoverCardCloseDelayMs |
number |
150 |
pointer 在 token 与 HoverCard 之间移动时的关闭缓冲。 |
onDiagnosticsChange |
(diagnostics) => void |
undefined |
诊断更新回调。 |
onSemanticTokensChange |
(tokens) => void |
undefined |
语义 token 更新回调。 |
onCompletionsChange |
(items) => void |
undefined |
补全列表更新回调。 |
onHoverChange |
(hover) => void |
undefined |
Hover 信息更新回调。 |
onHoverLinkClick |
(link: CodeEditorHoverLink) => void |
undefined |
点击结构化 Hover 链接时回调;CodeEditor 不验证协议、不导航。 |
onSignatureHelpChange |
(help) => void |
undefined |
Signature Help 语义结果更新回调;pending/suppressed 发布 null。 |
onContextMenuRequest |
(request) => void |
undefined |
编辑器右键菜单请求回调,业务可接入 AppOverlay 或 Shell 菜单。 |
诊断
诊断使用 0-based range:
interface CodeEditorDiagnostic { range: TextRange message: string severity?: 'error' | 'warning' | 'info' source?: string code?: string}
CodeEditor 会把诊断转换为 PlainTextEditor decoration:
error:错误下划线。warning:警告下划线。info:提示下划线。
业务可通过 editor.diagnostics 获取当前诊断副本,也可以调用:
await editor.validateNow()editor.setDiagnostics(diagnostics)
异步诊断会绑定内部版本号。旧请求返回时,如果文本已更新,结果会被忽略。
语义高亮
语义 token 使用同样的 0-based range:
interface CodeEditorSemanticToken { range: TextRange type: string modifiers?: string[]}
常用 type:
keywordvariablefieldtypeoperatoractiondatasetrulestringnumberquantitypunctuation
CodeEditor 会将这些 token 转换为 PlainTextEditor 的行级 token provider。没有语义 token 的行会退回到 PlainTextEditor 自身 tokenizer。
补全
补全项由 getCompletions() 返回:
interface CodeEditorCompletionItem { label: string filterText?: string kind?: CodeEditorCompletionKind insertText?: string detail?: string documentation?: string range?: TextRange sortText?: string commitCharacters?: string[]}
结构化结果:
interface CodeEditorCompletionList { items: CodeEditorCompletionItem[] isIncomplete?: boolean validFor?: RegExp range?: TextRange}
Provider 可以通过补全上下文区分普通输入与上下文边界:
interface CodeEditorCompletionContext { activation: 'manual' | 'auto' triggerKind: 'invoked' | 'trigger-character' | 'incomplete' triggerCharacter?: string invocationReason?: 'typing' | 'context-boundary' signal: AbortSignal}
invocationReason 是可选的向后兼容字段。context-boundary 表示编辑器只在该位置询问 Provider,语言适配器仍需根据语法与语义决定返回候选或空列表。
validFor匹配时,继续输入会本地过滤候选。isIncomplete:true时,继续输入会取消旧请求并重新请求。- 新请求会通过
AbortSignal主动取消旧请求;文档与上下文版本隔离仍然保留。 - 候选列表超过紧急保护数量时会被裁剪并视为 incomplete。
触发方式:
- 调用
editor.triggerCompletion()手动触发。 - 输入
completionTriggerCharacters中的字符后自动触发。 - 默认触发字符是
.、空格和换行。 quickSuggestions:true时,输入符合completionWordPattern的标识符字符会在 delay 后自动请求。quickSuggestions:true且输入completionQuickSuggestionCharacters中的字符时,会共用同一 delay 询问 Provider;该列表默认为空。Ctrl+Space/Meta+Space触发补全。
键盘交互:
ArrowDown/ArrowUp切换选中项。PageUp/PageDown、Home/End按页或边界移动。- 鼠标滚轮滚动大候选列表;弹窗默认绘制 8 行可见窗口。
Tab在存在当前选中项时应用它。acceptSuggestionOnEnter: 'on'是默认策略:候选列表打开时,Enter应用当前高亮项。acceptSuggestionOnEnter: 'smart'仅在手动触发或用户导航过自动候选时接受;未导航的自动候选会在Enter时关闭并正常换行。'off'始终关闭补全并正常换行。Escape关闭补全。- 输入当前项
commitCharacters中的字符,会应用补全并同时插入该字符。 - 上下文边界返回空前缀列表时会“软打开”:列表可见但不预选首项,Enter/Tab 不会被补全会话消费。方向键或继续输入前缀后才建立选中。
补全弹层最多显示 8 项。项目更多时,方向键会自动滚动可视窗口,保证当前选中项始终可见;弹层宽高会限制在编辑器可交互边界内,窄小编辑器也不会产生组件外的不可点击项目。
应用规则:
- 如果 completion item 提供
range,替换该 range。 - 如果没有
range,在当前光标处插入insertText || label。 - 补全结果绑定当前 source version,旧异步结果不会覆盖新文本。
常用 API:
await editor.triggerCompletion()await editor.triggerCompletion(undefined, { activation: 'auto', triggerKind: 'trigger-character', triggerCharacter: '.',})editor.moveCompletionSelection(1)editor.acceptSelectedCompletion()editor.closeCompletion()
Signature Help
Signature Help 由 Adapter 返回通用 DTO,CodeEditor 不解析函数名或签名 label:
interface CodeEditorSignatureParameter { label: string documentation?: string} interface CodeEditorSignature { label: string documentation?: string parameters: readonly CodeEditorSignatureParameter[]} interface CodeEditorSignatureHelp { signatures: readonly CodeEditorSignature[] activeSignature: number activeParameter: number range?: TextRange}
触发和交互:
- 输入
signatureTriggerCharacters自动请求;已有 Session 中输入signatureRetriggerCharacters重新请求。 Ctrl+Shift+Space/Meta+Shift+Space手动触发,优先于普通 Completion 快捷键。- 多重载时
ArrowUp/ArrowDown切换当前重载;Enter/Tab不由 Signature Help 消费。 Escape关闭 pending、active 或 suppressed Session,并取消在途请求。- Completion 打开时 Signature 进入 suppressed;Completion 关闭后只基于当前 source/caret 重新请求,不恢复旧结果。
- 自动 Hover 不会覆盖 pending/active Signature;显式
triggerHover()会先关闭 Signature。 - IME composition 开始、失焦、wheel、实际滚动、
revealPosition()、hidden、detach 和 dispose 都会关闭 Session。
每次请求都带独立 AbortSignal,并同时校验 request sequence、document version、language-context version 和 cursor/selection generation。Provider 结果会深复制、移除空 label,并把 activeSignature 从原始数组坐标重映射到过滤后数组。activeParameter 只归一为非负整数,不把固定签名的超量参数错误绑到末参。
面板最大宽度 460px、最多 8 行和 3 个重载,当前重载始终位于可见窗口中。参数使用独立行,说明最多 3 行;面板优先放在 caret 上方,空间不足时改到下方。
Signature 的语义 Session、取消和快捷键仍由 CodeEditor 管理;可见内容通过 CodeEditor 自有的 anchored TooltipManager 绘制到 tooltip compositor layer。它使用 caret PopupAnchor、编辑器 boundary、显式 stable render root 和 editor owner,不会被全局 pointer TooltipService.hide() 误关闭。Completion suppression、外部 modal/root dismiss 和 editor dispose 会同步清理 Session 与 Rich Tooltip content。
await editor.triggerSignatureHelp()editor.moveSignatureSelection(1)editor.closeSignatureHelp()editor.signatureHelpInfo
Hover
Hover 信息由 getHover() 返回:
interface CodeEditorHover { range: TextRange label: string detail?: string type?: string documentation?: string sections?: readonly CodeEditorHoverSection[] links?: readonly CodeEditorHoverLink[]} interface CodeEditorHoverLink { label: string href: string title?: string} type CodeEditorHoverSectionKind = | 'text' | 'code' | 'list' | 'warning' | 'reference' type CodeEditorHoverSectionPriority = 'required' | 'normal' | 'optional' interface CodeEditorHoverSection { kind: CodeEditorHoverSectionKind lines: readonly string[] priority?: CodeEditorHoverSectionPriority language?: string text?: string copyText?: string}
documentation 保持向后兼容,并在内部转为一个 normal/text section。结构化 sections 适合展示代码、列表、警告和引用文本;reference 仍是非交互 accent 文本,不会自动解析其中的 URL。只有 links 创建真实 hit region。
Section 会在保存时深复制和归一化:普通文本会 trim 并移除空行,code 的 text/copyText 按原字符串保留;未传 text 时使用 lines.join('\n'),未传 copyText 时复制最终 text。language 用于代码语法着色。
触发方式:
- 调用
editor.triggerHover(position)手动触发。 - 鼠标停留在文本位置超过
hoverDelayMs后自动触发。 - 文本变化、点击编辑器、Completion/Signature 打开时会立即关闭 Hover。
- 鼠标仍在当前
hover.range内时复用当前 Hover,不重启计时器或重复请求;range end 使用排他语义。 - 鼠标拖选文本或拖动编辑器滚动条时不调度 Hover;释放后也不补触发,等待下一次无按键 pointer move 重新开始延迟。
- 鼠标可从当前 range 穿过间隙进入 Card;只有同时离开 range、Card、Card focus 和 pointer capture 后,才按关闭缓冲结束。
- Card 内链接、代码 Copy、文本拖选、
Ctrl/Cmd+C和内部滚动不会关闭 Hover。 - editor 自身 wheel、滚动或
revealPosition()改变视口时立即关闭 Hover;Card 有纵向 overflow 时,即使滚到边界也消费 wheel,避免穿透到底层 editor。 - 编辑器被隐藏或从渲染树 detach 时,同样取消延迟请求并关闭 Hover,重新显示或挂载不会恢复旧面板。
- 补全列表打开时,HoverCard 会自动关闭,避免两个浮层互相遮挡。
- Card 以当前
hover.range的完整可见 token 矩形为锚点,优先显示在 token 末尾右侧;空间不足时按左侧、下方、上方回退,不覆盖锚定 token。 - Semantic Hover 的面板边界是编辑器所属 stable
anchorRoot的 Popup viewport,不是 Editor 自身矩形。Card 可覆盖同一窗口内的结果、消息或其他 Dock 区域,但不越出应用/浮动窗口 viewport。 - 锚定 token 是否可见仍由 CodeEditor 判断;紧凑 viewport 中 Card 可缩小主轴尺寸并在内部滚动,不通过 clamp 回压到 token 上。
面板使用 HoverCard 和 embedded RenderMarkdownViewer:默认宽度 220~560px、最大高度 360px;短内容按自然宽高收缩,长文档、DDL、代码和表格在 Card 内滚动。
Hover 与 Signature 仍互斥,但使用不同 presentation:Semantic Hover 是 CodeEditor-owned nonmodal HoverCard,Signature Help 是 CodeEditor-owned passive Tooltip。通用 CodeEditor 不包含 SQL、URL protocol 或 window.open() 规则。
debugState().hoverRect/signatureHelpRect 只在真实 tooltip-layer paint 完成且当前 anchor、root viewport、theme 和 presentation 仍匹配时返回矩形;Signature Help 还会校验自己的 editor boundary。语义结果已 active 但尚未绘制或空间不足时,visible 仍反映语义状态,而 lines=[]、rect=null。
常用 API:
await editor.triggerHover({ line: 0, column: 4 })editor.closeHover()editor.hoverInfo
Hover 结果同样绑定当前 source version,旧异步结果不会覆盖新文本。
格式化和 Edit
格式化可以返回完整文本:
formatDocument: (source: string) => formatDsl(source)
也可以返回 edit 列表:
formatDocument: (source: string) => [ { range: { start: { line: 0, column: 0 }, end: { line: 0, column: 2 } }, newText: '规则', },]
CodeEditor 应用多个 edit 时会从后往前执行,避免前一个 edit 改变后续 range。
await editor.formatDocument()editor.applyTextEdits(edits)
快速修复、导航和重构
快速修复由 getCodeActions() 返回:
interface CodeEditorCodeAction { title: string kind: 'quickfix' | 'source.format' | string edits: CodeEditorTextEdit[] diagnostics?: CodeEditorDiagnostic[]}
常用 API:
const actions = await editor.triggerCodeActions(editor.diagnostics[0])editor.applyCodeAction(actions[0])
定义和引用:
interface CodeEditorLocation { name: string kind?: string range: TextRange selectionRange?: TextRange} interface CodeEditorReference { name: string kind?: string role?: 'declaration' | 'reference' range: TextRange}
常用 API:
await editor.goToDefinition()const references = await editor.findReferences()
goToDefinition() 会自动跳转到返回的 selectionRange ?? range。findReferences() 只返回位置列表,不会改变编辑器选区,业务页面可以把结果放入侧栏、底部面板或命令面板。
Definition 和 References 各自维护请求序号,并共享 cursor/selection generation。新 Definition 只取消旧 Definition,新 References 只取消旧 References;请求开始后如果光标或选区变化,旧结果会被忽略。显式传入的 position 不需要与当前 caret 相同,因此 Ctrl / Meta 点击仍可安全导航。
重命名和智能选区:
await editor.renameSymbol('新名称')await editor.expandSelection()
renameSymbol() 会应用 language adapter 返回的 edits。expandSelection() 会从 getSelectionRanges() 返回值中选择比当前选区更大的下一个 range,并把它设置为当前选区。selectSelectionRangeAt() 用于双击场景,会选择包含当前位置的最小 selection range。
快捷键和鼠标交互:
Shift+Alt+F格式化。Shift+Alt+ArrowRight扩大选区。Alt+Enter触发 code actions。Ctrl/Meta点击触发跳转定义。- 双击优先使用
getSelectionRanges()选择语义 token,没有语言服务结果时保留 PlainTextEditor 的默认 token 选择。 - 右键会触发
onContextMenuRequest,回调里包含右键位置、文本位置、命中的诊断和已加载的 code actions;编辑器不直接持有业务 overlay。
和 PlainTextEditor 的关系
RenderCodeEditor 继承 RenderPlainTextEditor。PlainTextEditor 新增了两个通用扩展点:
editor.setLineTokenProvider(provider)editor.setDecorations(decorations)
这些扩展点不依赖 CodeEditor,也可以被日志查看器、配置编辑器或自定义脚本编辑器复用。
业务 DSL 接入建议
业务 DSL 应提供薄适配层,把自身 Language Service 的位置模型转换为 DirectSurface UI 的 0-based 模型:
const adapter: CodeEditorLanguageAdapter<DslContext> = { getDiagnostics(source, context) { return getDslDiagnostics(source, { context }).map(fromDslDiagnostic) }, getSemanticTokens(source, context) { return getDslSemanticTokens(source, { context }).map(fromDslSemanticToken) }, formatDocument(source) { return formatDsl(source) },}
编辑器层不应直接引用 DSL 的 lexer、parser、checker、compiler 内部源码。
CIS Formula Authoring Provider
完整组件、catalog语言服务和只读preview用法见Formula Authoring。
ds-ui提供领域中立的Formula Authoring适配层,把provider返回的UTF-16 offset DTO转换为CodeEditor语言服务结果:
import { RenderCodeEditor, createFormulaAuthoringLanguageAdapter, type FormulaAuthoringLanguageContext, type FormulaAuthoringProvider,} from 'ds-ui' interface ExampleFormulaAnchor { readonly owner: string} const formulaService = { analyze: (_request: unknown) => ({ catalogSnapshotKey: 'catalog-v42', value: { diagnostics: [], semanticTokens: [] }, }), getCompletions: (_request: unknown) => ({ catalogSnapshotKey: 'catalog-v42', value: [], }),}const formulaAnalysisPanel = { show: (_summary: unknown): void => {} }const currentFormulaAnchor: ExampleFormulaAnchor = { owner: 'example-owner' } const formulaProvider: FormulaAuthoringProvider<ExampleFormulaAnchor> = { analyze(request) { return formulaService.analyze(request) }, getCompletions(request) { return formulaService.getCompletions(request) },} const formulaContext: FormulaAuthoringLanguageContext<ExampleFormulaAnchor> = { catalogSnapshotKey: 'catalog-v42', anchor: currentFormulaAnchor,} const formulaCodeEditor = new RenderCodeEditor({ value: '=SUM(DOC.amount)', language: 'cis-formula', languageAdapter: createFormulaAuthoringLanguageAdapter(formulaProvider, { onAnalysisChange: analysis => { formulaAnalysisPanel.show(analysis?.summary ?? null) }, }), languageContext: formulaContext,})
Provider负责使用自己的Formula Engine/Binding catalog产生diagnostic、semantic token、completion、Hover、Signature和format结果;CodeEditor不理解Medical、Report或Formula语义。
Formula DTO的span是0-based、UTF-16 code-unit、end-exclusive offset。适配层通过formulaAuthoringSpanToRange()转换为CodeEditor TextRange,并覆盖LF、CRLF、Unicode surrogate pair和EOF。catalogSnapshotKey不匹配时结果会fail closed,旧catalog结果不会展示。
createFormulaEngineAuthoringProvider()可以把公开Engine API注入为真实compile/analyze provider。Formula Engine必须由调用方从独立入口导入:
import {
compileFormula,
computeSourceHash,
getCheckedFormulaEnvelope,
getFormulaAnalysis,
normalizeEnvironment,
standardLibraryCatalog,
structuredHash,
} from '@ds-ui/formula-engine'
import { createFormulaEngineAuthoringProvider } from 'ds-ui'
const provider = createFormulaEngineAuthoringProvider({
engine: {
compileFormula,
computeSourceHash,
structuredHash,
normalizeEnvironment,
getCheckedFormulaEnvelope,
getFormulaAnalysis,
},
createCompileRequest: request => formulaBinding.createCompileRequest(
request.source,
request.anchor,
),
getEnvironment: request => request.environment,
functionCatalog: standardLibraryCatalog.functions,
standardLibraryProfile: 'common-v1',
})
该provider保留Engine diagnostic的code/args/span,提供默认message并允许宿主注入message resolver;Checked analysis映射为result type/shape、ordered dependencies、selected functions/overloads和cost upper bound。compile缓存同时绑定catalog snapshot、source hash、normalized environment hash和完整request hash。
Formula Authoring bridge从ds-ui根入口导出,但不会重新导出或静态依赖@ds-ui/formula-engine。只有实际创建provider的调用方bundle才包含Engine runtime。
状态和调试
debugState() 在 PlainTextEditor 的基础上增加:
| 字段 | 说明 |
|---|---|
languageFeatureVersion |
当前语言服务调度版本。 |
diagnosticsCount |
当前诊断数量。 |
semanticTokenCount |
当前语义 token 数量。 |
completionCount |
当前补全项数量。 |
completionVisible |
补全列表是否打开。 |
selectedCompletionLabel |
当前选中的补全项 label。 |
hoverVisible |
Hover tooltip 是否打开。 |
hoverLabel |
当前 hover 的标题。 |
hoverLines |
当前实际绘制的 Hover 行,返回脱离副本;首次 paint 前为空。 |
hoverRect |
当前实际 Hover 全局矩形;首次 paint 或缓存失效时为 null。 |
hoverTruncated |
当前实际布局是否发生裁剪。 |
diagnosticsDebounceMs |
诊断防抖时间。 |
hoverDelayMs |
Hover 延迟时间。 |
hasLanguageAdapter |
是否设置语言适配器。 |