REAL SHOWCASE SOURCEcode-editor-clinical-dsl
1 个文件 · 81 行 · TypeScript
// ===== showcase/src/examples/components/code_editor.ts ===== import { RenderButton, RenderCodeEditor, RenderPageHeader, RenderStackPanel, RenderText, RenderWrapPanel, type CodeEditorLanguageAdapter, type CodeEditorSemanticToken,} from 'ds-ui' const source = ['规则 儿童禁用', '如果 患者.年龄 小于 18', '则 提醒 禁用该药品'].join('\n') function semanticTokens(value: string): CodeEditorSemanticToken[] { const tokens: CodeEditorSemanticToken[] = [] value.split(/\r?\n/).forEach((line, lineIndex) => { for (const [text, type] of [['规则', 'keyword'], ['如果', 'keyword'], ['则', 'keyword'], ['提醒', 'action'], ['患者', 'variable'], ['年龄', 'field'], ['小于', 'operator']] as const) { const column = line.indexOf(text) if (column >= 0) tokens.push({ type, range: { start: { line: lineIndex, column }, end: { line: lineIndex, column: column + text.length } } }) } }) return tokens} function languageAdapter(): CodeEditorLanguageAdapter { return { getDiagnostics(value) { if (value.includes('提醒')) return [] return [{ range: { start: { line: 2, column: 0 }, end: { line: 2, column: 2 } }, severity: 'warning', message: '规则动作建议包含提醒', source: 'clinical-dsl' }] }, getSemanticTokens: value => semanticTokens(value), getCompletions: () => [ { label: '患者.年龄', kind: 'field', detail: 'number' }, { label: '患者.诊断', kind: 'field', detail: 'string[]' }, { label: '医嘱.药品名称', kind: 'field', detail: 'string' }, { label: '提醒', kind: 'action', detail: 'action' }, ], getHover(_value, position) { return position.line === 1 ? { label: '患者.年龄', type: 'number', detail: '患者字段', documentation: '当前患者年龄,单位为岁。', range: { start: { line: position.line, column: 3 }, end: { line: position.line, column: 8 } }, } : null }, formatDocument: value => value.split(/\r?\n/).map(line => line.trim()).filter(Boolean).join('\n'), }} export function createCodeEditorExample(): RenderStackPanel { const status = new RenderText('语言服务已接入:诊断、语义高亮、补全、Hover 和格式化。', { role: 'secondary' }) const editor = new RenderCodeEditor({ value: source, language: 'clinical-dsl', fontSize: 14, lineHeight: 22, folding: false, diagnosticsDebounceMs: 0, languageAdapter: languageAdapter(), onDiagnosticsChange: diagnostics => { status.text = `诊断 ${diagnostics.length} 项` }, }) editor.height = 440 const actions = new RenderWrapPanel({ spacing: 8, runSpacing: 8 }) actions.addChild(new RenderButton({ label: '立即校验', onClick: () => { void editor.validateNow().then(items => { status.text = `校验完成:${items.length} 项诊断` }) } })) actions.addChild(new RenderButton({ label: '触发补全', variant: 'primary', onClick: () => { void editor.triggerCompletion().then(items => { status.text = `语言服务返回 ${items.length} 个补全项` }) } })) actions.addChild(new RenderButton({ label: '查看字段 Hover', onClick: () => { void editor.triggerHover({ line: 1, column: 6 }).then(hover => { status.text = hover?.documentation ?? '当前位置没有 Hover 信息' }) } })) actions.addChild(new RenderButton({ label: '格式化', onClick: () => { void editor.formatDocument().then(changed => { status.text = changed ? '格式化结果已应用,可使用撤销恢复。' : '文档已经是格式化状态。' }) } })) const panel = new RenderStackPanel({ orientation: 'vertical', spacing: 10, crossAxisAlignment: 'stretch' }) panel.addChild(new RenderPageHeader({ title: 'CodeEditor 临床规则 DSL', description: '编辑器负责输入和绘制,LanguageAdapter 提供诊断、补全、Hover、格式化与业务语义。' })) panel.addChild(actions) panel.addChild(status) panel.addChild(editor, 1) panel.padding = 14 return panel}