feat(editor): NodeView 适配 onRunCode 协议(extractLang + overridesJson 提取)
runCode 改传纯语言名(extractLang) + overrides JSON 字符串(extractOverridesJson),
对齐 Task 4 Rust 桥接 RunCodeOptsJs(language/source/overrides_json):
- highlight.ts: 新增 extractOverridesJson,对齐后端 parse_fence_info 取以 { 开头的 token
- code-block-view.ts: RunCodeOpts 加 overridesJson 字段,runCode 改传 extractLang/extractOverridesJson 提取结果
- index.ts: EditorOptions.onRunCode 类型同步加 overridesJson
- 测试: extractOverridesJson 6 例 + code-block-view 点击断言更新(纯语言名 + overridesJson)
This commit is contained in:
parent
0c1e8b7640
commit
763edc300d
@ -97,13 +97,17 @@ describe('CodeBlockNodeView', () => {
|
||||
it('点击运行按钮调用 onRunCode(storage 回调)', () => {
|
||||
const onRunCode = vi.fn(() => Promise.resolve('结果'));
|
||||
const view = new CodeBlockNodeView({
|
||||
node: mockNode('python runnable', 'print(1)'),
|
||||
node: mockNode('python runnable {"timeout_secs":10}', 'print(1)'),
|
||||
editor: mockEditor(onRunCode),
|
||||
} as any);
|
||||
view.dom.querySelector<HTMLButtonElement>('.tiptap-codeblock-run')!.click();
|
||||
expect(onRunCode).toHaveBeenCalledTimes(1);
|
||||
expect(onRunCode).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ language: 'python runnable', source: 'print(1)' }),
|
||||
expect.objectContaining({
|
||||
language: 'python',
|
||||
source: 'print(1)',
|
||||
overridesJson: '{"timeout_secs":10}',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { extractLang, lowlight } from '../highlight';
|
||||
import { extractLang, extractOverridesJson, lowlight } from '../highlight';
|
||||
|
||||
/**
|
||||
* extractLang 测试:从完整 fence info string 提取语言名(首个 token)。
|
||||
@ -21,6 +21,23 @@ describe('extractLang', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractOverridesJson', () => {
|
||||
it.each([
|
||||
['python runnable {"timeout_secs":10}', '{"timeout_secs":10}', 'runnable + overrides'],
|
||||
[
|
||||
'python runnable {"timeout_secs":10,"memory_mb":256}',
|
||||
'{"timeout_secs":10,"memory_mb":256}',
|
||||
'多字段 overrides',
|
||||
],
|
||||
['python runnable', '', '无 overrides'],
|
||||
['python', '', '纯语言名'],
|
||||
['', '', '空字符串'],
|
||||
['python runnable {"allow_network":true}', '{"allow_network":true}', 'allow_network'],
|
||||
])('%s → %s (%s)', (input, expected) => {
|
||||
expect(extractOverridesJson(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* lowlight wrapper 测试:highlight 经过 extractLang 处理,
|
||||
* runnable info string 也能按正确语言高亮。
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import type { Editor } from '@tiptap/core';
|
||||
import type { Node as PMNode } from '@tiptap/pm/model';
|
||||
import { extractLang } from './highlight';
|
||||
import { extractLang, extractOverridesJson } from './highlight';
|
||||
|
||||
/** editor.storage 的 key,宿主(index.ts)在此注入 onRunCode 回调。 */
|
||||
export const ON_RUN_CODE_STORAGE_KEY = '__onRunCode';
|
||||
|
||||
/** 运行请求参数(传给 onRunCode 回调)。 */
|
||||
export interface RunCodeOpts {
|
||||
/** 完整 info string(如 `python runnable {"timeout_secs":10}`)。 */
|
||||
/** 纯语言名(extractLang 提取,如 "python")。 */
|
||||
language: string;
|
||||
/** 代码块文本内容。 */
|
||||
source: string;
|
||||
/** overrides 的 JSON 字符串(extractOverridesJson 提取,空串表示无 overrides)。 */
|
||||
overridesJson: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,9 +155,11 @@ export class CodeBlockNodeView {
|
||||
this.ensureResultArea('运行中…');
|
||||
|
||||
try {
|
||||
const info = (this.node.attrs.language as string) ?? '';
|
||||
const result = await onRunCode({
|
||||
language: (this.node.attrs.language as string) ?? '',
|
||||
language: extractLang(info),
|
||||
source: this.node.textContent,
|
||||
overridesJson: extractOverridesJson(info),
|
||||
});
|
||||
this.renderResult(result);
|
||||
} catch (e) {
|
||||
|
||||
@ -14,6 +14,21 @@ export function extractLang(info: string): string {
|
||||
return first ? first.toLowerCase() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从完整 fence info string 提取 overrides JSON 字符串。
|
||||
*
|
||||
* 与后端 parse_fence_info(src/api/code_runner/languages.rs:93-97)对齐:
|
||||
* info string 形如 `python runnable {"timeout_secs":10}`,提取以 `{` 开头的 token。
|
||||
* 无 overrides 时返回空串(Rust 侧空串视为 None)。
|
||||
*/
|
||||
export function extractOverridesJson(info: string): string {
|
||||
const token = info
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.find((t) => t.startsWith('{'));
|
||||
return token ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* lowlight 实例,包装 highlight 方法以处理完整 info string。
|
||||
*
|
||||
|
||||
@ -40,8 +40,12 @@ class EditorOptions {
|
||||
// 上传状态事件(error/success/removed + counts),替代 window.__tiptap_uploads 轮询
|
||||
onUploadEvent?: (event: UploadEvent) => void;
|
||||
// 编辑器内运行代码回调:Rust 注入,NodeView 点击「运行」时经 editor.storage 转发。
|
||||
// opts: { language, source };返回格式化结果字符串(Rust 拼好 stdout/stderr/状态)。
|
||||
onRunCode?: (opts: { language: string; source: string }) => Promise<string>;
|
||||
// opts: { language(纯语言名), source, overridesJson };返回格式化结果字符串(Rust 拼好 stdout/stderr/状态)。
|
||||
onRunCode?: (opts: {
|
||||
language: string;
|
||||
source: string;
|
||||
overridesJson: string;
|
||||
}) => Promise<string>;
|
||||
}
|
||||
// wasm-bindgen 生成的 glue 用裸标识符 `new EditorOptions()` 从全局解析,
|
||||
// IIFE 的 name 只能挂一个全局(TiptapEditor),这里手动把 EditorOptions 也挂到 window 上。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user