新增根 biome.json(v2.5,v2 起原生支持 monorepo,子包自动向上查找):
- formatter: 2 空格缩进、单引号、行宽 100、LF、尾逗号、箭头函数括号
(匹配现有代码风格)
- linter: recommended preset + 项目化裁剪
- useTemplate/useTemplate off(geometry.ts 故意保留字符串拼接,
且测试用精确浮点断言锁定行为)
- noNonNullAssertion off(测试与 DOM 访问里的 ! 是惯用写法)
- noConsole off(4 个 lib 用 console.error/warn 做错误上报)
- CSS 关闭 noDescendingSpecificity/noDuplicateProperties
(手写 CSS 的 fallback 模式)
- useImportType/useNodejsImportProtocol/noUnusedVariables warn
- vcs.useIgnoreFile 自动读 .gitignore;排除 public/target/node_modules/
dist/.dioxus/static + Tailwind 入口 input.css(含 @theme 等 Biome 无法
解析的 at-rules)
首次格式化覆盖 29 个源文件:import 排序、node: 协议、引号/缩进统一,
并修复 index.ts 两处 forEach 回调隐式返回值(useIterableCallbackReturn)。
90 个 vitest 用例全绿,确认无语义改动。
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
import { PostgreSQL, sql } from '@codemirror/lang-sql';
|
||
import { Compartment, EditorState } from '@codemirror/state';
|
||
import { EditorView } from '@codemirror/view';
|
||
import { vim } from '@replit/codemirror-vim';
|
||
import { basicSetup } from 'codemirror';
|
||
import { type ThemeName, themeExtension } from './themes';
|
||
|
||
/** SQL 补全用 schema 数据(由 Rust 侧从实时库拉取注入)。 */
|
||
export interface SqlSchema {
|
||
tables: { name: string; columns: string[] }[];
|
||
}
|
||
|
||
/**
|
||
* 传给 CodeMirrorEditor.create 的配置。
|
||
* 必须是 class(非 interface),以便 TS 擦除后存活,
|
||
* wasm 侧能用 `new EditorOptions()` 构造,并通过 setter 填充字段。
|
||
*/
|
||
export class EditorOptions {
|
||
language?: string;
|
||
theme?: ThemeName;
|
||
vim?: boolean;
|
||
schema?: SqlSchema;
|
||
value?: string;
|
||
onChange?: (value: string) => void;
|
||
onReady?: () => void;
|
||
}
|
||
|
||
/**
|
||
* CodeMirror 实例封装。
|
||
* create() 时用三个 Compartment 注入 theme/schema/vim,
|
||
* 支持后续热切换(reconfigure)而不重建实例——保留 Vim 状态、光标、撤销栈。
|
||
*/
|
||
export class CodeMirrorInstance {
|
||
private view: EditorView;
|
||
private themeCompartment = new Compartment();
|
||
private schemaCompartment = new Compartment();
|
||
private vimCompartment = new Compartment();
|
||
|
||
constructor(container: HTMLElement, options: EditorOptions) {
|
||
const theme: ThemeName = options.theme ?? 'light';
|
||
const schema = options.schema ?? { tables: [] };
|
||
|
||
this.view = new EditorView({
|
||
state: EditorState.create({
|
||
doc: options.value ?? '',
|
||
extensions: [
|
||
basicSetup,
|
||
// vim 必须在 keymap 最前(@replit/codemirror-vim 仓库要求)
|
||
this.vimCompartment.of(options.vim ? [vim()] : []),
|
||
this.themeCompartment.of(themeExtension(theme)),
|
||
this.schemaCompartment.of(
|
||
sql({
|
||
dialect: PostgreSQL,
|
||
schema: schemaToCompletions(schema),
|
||
upperCaseKeywords: true,
|
||
}),
|
||
),
|
||
EditorView.updateListener.of((v) => {
|
||
if (v.docChanged) {
|
||
options.onChange?.(this.view.state.doc.toString());
|
||
}
|
||
}),
|
||
],
|
||
}),
|
||
parent: container,
|
||
});
|
||
|
||
options.onReady?.();
|
||
}
|
||
|
||
getValue(): string {
|
||
return this.view.state.doc.toString();
|
||
}
|
||
|
||
setValue(s: string): void {
|
||
this.view.dispatch({
|
||
changes: { from: 0, to: this.view.state.doc.length, insert: s },
|
||
});
|
||
}
|
||
|
||
/** 热切换主题,不重建实例(Compartment.reconfigure)。 */
|
||
setTheme(theme: ThemeName): void {
|
||
this.view.dispatch({
|
||
effects: this.themeCompartment.reconfigure(themeExtension(theme)),
|
||
});
|
||
}
|
||
|
||
/** 更新 SQL 补全 schema(Compartment.reconfigure)。 */
|
||
setSchema(schema: SqlSchema): void {
|
||
this.view.dispatch({
|
||
effects: this.schemaCompartment.reconfigure(
|
||
sql({
|
||
dialect: PostgreSQL,
|
||
schema: schemaToCompletions(schema),
|
||
upperCaseKeywords: true,
|
||
}),
|
||
),
|
||
});
|
||
}
|
||
|
||
focus(): void {
|
||
this.view.focus();
|
||
}
|
||
|
||
destroy(): void {
|
||
this.view.destroy();
|
||
}
|
||
}
|
||
|
||
/** 把 SqlSchema 转成 @codemirror/lang-sql 期望的补全结构。 */
|
||
function schemaToCompletions(schema: SqlSchema) {
|
||
return schema.tables.map((t) => ({
|
||
label: t.name,
|
||
type: 'table',
|
||
detail: 'table',
|
||
columns: t.columns.map((c) => ({ label: c, type: 'column' })),
|
||
}));
|
||
}
|
||
|
||
// 暴露 EditorOptions 到 window,供 wasm-bindgen 用 new EditorOptions()。
|
||
// IIFE 的 name 只能挂一个全局(CodeMirrorEditor),故手动 hoist EditorOptions。
|
||
declare global {
|
||
interface Window {
|
||
EditorOptions: typeof EditorOptions;
|
||
}
|
||
}
|
||
window.EditorOptions = EditorOptions;
|