refactor(editor): support dynamic languages python/javascript in codemirror
- editor.ts: 新增 languageCompartment + buildLanguageExtension(lang, schema) python → lang-python, node/javascript → lang-javascript, sql/缺省 → lang-sql 新增 setLanguage(lang) 热切换; setSchema 保留当前语言不被强制切回 sql - package.json: +@codemirror/lang-python +lang-javascript - codemirror_bridge.rs: EditorInstance 新增 set_language wasm-bindgen extern (产物 public/codemirror/ 被 gitignore,本地已重建)
This commit is contained in:
parent
a77a2e3502
commit
576803302d
@ -14,6 +14,8 @@
|
|||||||
"@catppuccin/codemirror": "^1.0.3",
|
"@catppuccin/codemirror": "^1.0.3",
|
||||||
"@codemirror/autocomplete": "^6.18.0",
|
"@codemirror/autocomplete": "^6.18.0",
|
||||||
"@codemirror/commands": "^6.8.0",
|
"@codemirror/commands": "^6.8.0",
|
||||||
|
"@codemirror/lang-javascript": "^6.2.3",
|
||||||
|
"@codemirror/lang-python": "^6.2.0",
|
||||||
"@codemirror/lang-sql": "^6.10.0",
|
"@codemirror/lang-sql": "^6.10.0",
|
||||||
"@codemirror/language": "^6.11.0",
|
"@codemirror/language": "^6.11.0",
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { python } from '@codemirror/lang-python';
|
||||||
import { PostgreSQL, sql } from '@codemirror/lang-sql';
|
import { PostgreSQL, sql } from '@codemirror/lang-sql';
|
||||||
import { Compartment, EditorState } from '@codemirror/state';
|
import { Compartment, EditorState, type Extension } from '@codemirror/state';
|
||||||
import { EditorView } from '@codemirror/view';
|
import { EditorView } from '@codemirror/view';
|
||||||
import { vim } from '@replit/codemirror-vim';
|
import { vim } from '@replit/codemirror-vim';
|
||||||
import { basicSetup } from 'codemirror';
|
import { basicSetup } from 'codemirror';
|
||||||
@ -25,20 +27,48 @@ export class EditorOptions {
|
|||||||
onReady?: () => void;
|
onReady?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把语言标识映射成 CodeMirror 语言 Extension。
|
||||||
|
* - `python` → @codemirror/lang-python
|
||||||
|
* - `node` / `javascript` / `js` → @codemirror/lang-javascript
|
||||||
|
* - `sql` / 缺省 → @codemirror/lang-sql(PostgreSQL 方言 + schema 补全)
|
||||||
|
*
|
||||||
|
* SQL 分支使用 schema 补全;非 SQL 语言忽略 schema(补全仅对 SQL 有意义)。
|
||||||
|
*/
|
||||||
|
function buildLanguageExtension(lang: string, schema: SqlSchema): Extension {
|
||||||
|
const normalized = (lang ?? '').toLowerCase();
|
||||||
|
if (normalized === 'python') {
|
||||||
|
return python();
|
||||||
|
}
|
||||||
|
if (normalized === 'node' || normalized === 'javascript' || normalized === 'js') {
|
||||||
|
return javascript();
|
||||||
|
}
|
||||||
|
// sql / 缺省:保留原有 PostgreSQL + schema 补全行为
|
||||||
|
return sql({
|
||||||
|
dialect: PostgreSQL,
|
||||||
|
schema: schemaToCompletions(schema),
|
||||||
|
upperCaseKeywords: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CodeMirror 实例封装。
|
* CodeMirror 实例封装。
|
||||||
* create() 时用三个 Compartment 注入 theme/schema/vim,
|
* create() 时用四个 Compartment 注入 theme/language/schema/vim,
|
||||||
* 支持后续热切换(reconfigure)而不重建实例——保留 Vim 状态、光标、撤销栈。
|
* 支持后续热切换(reconfigure)而不重建实例——保留 Vim 状态、光标、撤销栈。
|
||||||
*/
|
*/
|
||||||
export class CodeMirrorInstance {
|
export class CodeMirrorInstance {
|
||||||
private view: EditorView;
|
private view: EditorView;
|
||||||
private themeCompartment = new Compartment();
|
private themeCompartment = new Compartment();
|
||||||
private schemaCompartment = new Compartment();
|
private languageCompartment = new Compartment();
|
||||||
private vimCompartment = new Compartment();
|
private vimCompartment = new Compartment();
|
||||||
|
private language: string;
|
||||||
|
private schema: SqlSchema;
|
||||||
|
|
||||||
constructor(container: HTMLElement, options: EditorOptions) {
|
constructor(container: HTMLElement, options: EditorOptions) {
|
||||||
const theme: ThemeName = options.theme ?? 'light';
|
const theme: ThemeName = options.theme ?? 'light';
|
||||||
const schema = options.schema ?? { tables: [] };
|
const schema = options.schema ?? { tables: [] };
|
||||||
|
this.schema = schema;
|
||||||
|
this.language = options.language ?? 'sql';
|
||||||
|
|
||||||
this.view = new EditorView({
|
this.view = new EditorView({
|
||||||
state: EditorState.create({
|
state: EditorState.create({
|
||||||
@ -48,12 +78,8 @@ export class CodeMirrorInstance {
|
|||||||
// vim 必须在 keymap 最前(@replit/codemirror-vim 仓库要求)
|
// vim 必须在 keymap 最前(@replit/codemirror-vim 仓库要求)
|
||||||
this.vimCompartment.of(options.vim ? [vim()] : []),
|
this.vimCompartment.of(options.vim ? [vim()] : []),
|
||||||
this.themeCompartment.of(themeExtension(theme)),
|
this.themeCompartment.of(themeExtension(theme)),
|
||||||
this.schemaCompartment.of(
|
this.languageCompartment.of(
|
||||||
sql({
|
buildLanguageExtension(this.language, schema),
|
||||||
dialect: PostgreSQL,
|
|
||||||
schema: schemaToCompletions(schema),
|
|
||||||
upperCaseKeywords: true,
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
EditorView.updateListener.of((v) => {
|
EditorView.updateListener.of((v) => {
|
||||||
if (v.docChanged) {
|
if (v.docChanged) {
|
||||||
@ -85,19 +111,30 @@ export class CodeMirrorInstance {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新 SQL 补全 schema(Compartment.reconfigure)。 */
|
/** 热切换语言(python/node/javascript/sql),不重建实例(Compartment.reconfigure)。 */
|
||||||
setSchema(schema: SqlSchema): void {
|
setLanguage(lang: string): void {
|
||||||
|
this.language = lang;
|
||||||
this.view.dispatch({
|
this.view.dispatch({
|
||||||
effects: this.schemaCompartment.reconfigure(
|
effects: this.languageCompartment.reconfigure(
|
||||||
sql({
|
buildLanguageExtension(lang, this.schema),
|
||||||
dialect: PostgreSQL,
|
|
||||||
schema: schemaToCompletions(schema),
|
|
||||||
upperCaseKeywords: true,
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 更新 SQL 补全 schema(Compartment.reconfigure)。
|
||||||
|
* 当前语言为 SQL 时立即生效;非 SQL 语言会缓存 schema,切回 SQL 时生效。 */
|
||||||
|
setSchema(schema: SqlSchema): void {
|
||||||
|
this.schema = schema;
|
||||||
|
// 仅当当前是 SQL 语言时才重配 extension(其它语言不消费 schema)。
|
||||||
|
if ((this.language ?? '').toLowerCase() === 'sql' || !this.language) {
|
||||||
|
this.view.dispatch({
|
||||||
|
effects: this.languageCompartment.reconfigure(
|
||||||
|
buildLanguageExtension('sql', schema),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
focus(): void {
|
focus(): void {
|
||||||
this.view.focus();
|
this.view.focus();
|
||||||
}
|
}
|
||||||
|
|||||||
48
libs/pnpm-lock.yaml
generated
48
libs/pnpm-lock.yaml
generated
@ -35,6 +35,12 @@ importers:
|
|||||||
'@codemirror/commands':
|
'@codemirror/commands':
|
||||||
specifier: ^6.8.0
|
specifier: ^6.8.0
|
||||||
version: 6.10.4
|
version: 6.10.4
|
||||||
|
'@codemirror/lang-javascript':
|
||||||
|
specifier: ^6.2.3
|
||||||
|
version: 6.2.5
|
||||||
|
'@codemirror/lang-python':
|
||||||
|
specifier: ^6.2.0
|
||||||
|
version: 6.2.1
|
||||||
'@codemirror/lang-sql':
|
'@codemirror/lang-sql':
|
||||||
specifier: ^6.10.0
|
specifier: ^6.10.0
|
||||||
version: 6.10.0
|
version: 6.10.0
|
||||||
@ -162,6 +168,12 @@ packages:
|
|||||||
'@codemirror/commands@6.10.4':
|
'@codemirror/commands@6.10.4':
|
||||||
resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==}
|
resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==}
|
||||||
|
|
||||||
|
'@codemirror/lang-javascript@6.2.5':
|
||||||
|
resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==}
|
||||||
|
|
||||||
|
'@codemirror/lang-python@6.2.1':
|
||||||
|
resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==}
|
||||||
|
|
||||||
'@codemirror/lang-sql@6.10.0':
|
'@codemirror/lang-sql@6.10.0':
|
||||||
resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==}
|
resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==}
|
||||||
|
|
||||||
@ -207,9 +219,15 @@ packages:
|
|||||||
'@lezer/highlight@1.2.3':
|
'@lezer/highlight@1.2.3':
|
||||||
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
|
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
|
||||||
|
|
||||||
|
'@lezer/javascript@1.5.4':
|
||||||
|
resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
|
||||||
|
|
||||||
'@lezer/lr@1.4.10':
|
'@lezer/lr@1.4.10':
|
||||||
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
|
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
|
||||||
|
|
||||||
|
'@lezer/python@1.1.19':
|
||||||
|
resolution: {integrity: sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==}
|
||||||
|
|
||||||
'@marijn/find-cluster-break@1.0.3':
|
'@marijn/find-cluster-break@1.0.3':
|
||||||
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
|
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
|
||||||
|
|
||||||
@ -970,6 +988,24 @@ snapshots:
|
|||||||
'@codemirror/view': 6.43.4
|
'@codemirror/view': 6.43.4
|
||||||
'@lezer/common': 1.5.2
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@codemirror/lang-javascript@6.2.5':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/lint': 6.9.7
|
||||||
|
'@codemirror/state': 6.7.0
|
||||||
|
'@codemirror/view': 6.43.4
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/javascript': 1.5.4
|
||||||
|
|
||||||
|
'@codemirror/lang-python@6.2.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.0
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/python': 1.1.19
|
||||||
|
|
||||||
'@codemirror/lang-sql@6.10.0':
|
'@codemirror/lang-sql@6.10.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@codemirror/autocomplete': 6.20.3
|
'@codemirror/autocomplete': 6.20.3
|
||||||
@ -1046,10 +1082,22 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@lezer/common': 1.5.2
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@lezer/javascript@1.5.4':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
'@lezer/lr@1.4.10':
|
'@lezer/lr@1.4.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lezer/common': 1.5.2
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@lezer/python@1.1.19':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
'@marijn/find-cluster-break@1.0.3': {}
|
'@marijn/find-cluster-break@1.0.3': {}
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
||||||
|
|||||||
@ -89,6 +89,11 @@ pub mod wasm {
|
|||||||
#[wasm_bindgen(method, js_name = setTheme)]
|
#[wasm_bindgen(method, js_name = setTheme)]
|
||||||
pub fn set_theme(this: &EditorInstance, theme: &str);
|
pub fn set_theme(this: &EditorInstance, theme: &str);
|
||||||
|
|
||||||
|
/// 热切换语言(python/node/javascript/sql,Compartment.reconfigure)。
|
||||||
|
/// 由 CodeRunner 组件在挂载时按 data-lang 调用。
|
||||||
|
#[wasm_bindgen(method, js_name = setLanguage)]
|
||||||
|
pub fn set_language(this: &EditorInstance, lang: &str);
|
||||||
|
|
||||||
/// 更新 SQL 补全 schema(Compartment.reconfigure)。
|
/// 更新 SQL 补全 schema(Compartment.reconfigure)。
|
||||||
/// 参数为 serde_wasm_bindgen::to_value 序列化后的 JsValue
|
/// 参数为 serde_wasm_bindgen::to_value 序列化后的 JsValue
|
||||||
///(SqlSchema 是 serde 类型,非 wasm-bindgen 类型,故不能直接传 &SqlSchema)。
|
///(SqlSchema 是 serde 类型,非 wasm-bindgen 类型,故不能直接传 &SqlSchema)。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user