yggdrasil/src/codemirror_bridge.rs
xfy 17d596c512 refactor: 清理死代码与冗余,修复 mhchem 模块的 clippy 阻断
mhchem 机械移植引入了一个 deny-by-default 的 useless_attribute 硬错误
(mhchem_tables.rs 顶部的 outer #[allow] 挂在 use 语句上),导致
make lint / CI check job 失败。顺带清理同模块的死代码与风格项,以及仓库
内其他确认无引用的死代码。

- mhchem_tables.rs: 删除无效 outer allow、未使用的 `use super::*`、
  pq_action 未用形参 buf→_buf;v.get(0)→v.first()
- mhchem.rs: 删除从未调用的 Field::empty;修正 &*RE→&RE、Default 字段
  重赋值;Parsed/Out 加 large_enum_variant allow(镜像上游结构,
  经 Vec 进堆,非栈热路径)
- codemirror_bridge.rs: 删除 SqlSchema/SqlTable 上过期的 dead_code
  allow——get_db_schema 早已消费这两个类型
- ssr_cache.rs: 删除生产路径从未调用的 invalidate_ssr_home(首页已由
  invalidate_ssr_all_public 覆盖),修正相关注释与测试
- auth.rs: 删除预留给未实现用户管理功能的 invalidate_user_sessions

验证:cargo clippy --all-targets --all-features -- -D warnings 通过;
mhchem/ssr_cache 共 20 项测试通过。
2026-07-23 13:32:46 +08:00

203 lines
9.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! CodeMirror 编辑器的 wasm-bindgen 绑定层。
//!
//! 封装与 `window.CodeMirrorEditor`IIFE 暴露的全局对象字面量)的全部交互,
//! 严格镜像 [`crate::tiptap_bridge`] 的结构:共享纯数据类型双目标编译,
//! wasm-bindgen extern + `EditorHandle` 仅在 WASM 前端编译server 构建无 window
//!
//! 与 tiptap 一样,`CodeMirrorEditor` 是 IIFE 挂在 window 上的**对象字面量**
//! `{ create }`),不是函数——因此用 `js_sys::Reflect::get` 做属性访问拿到,
//! 不能用 wasm-bindgen 的 extern fn那会被编成函数调用"not a function")。
use serde::{Deserialize, Serialize};
/// SQL 补全用 schema 数据,由 `get_db_schema` server function 填充。
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct SqlSchema {
pub tables: Vec<SqlTable>,
}
/// 单张表的补全数据:表名 + 列名列表。
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SqlTable {
pub name: String,
pub columns: Vec<String>,
}
// ============================================================================
// 以下全部仅在 WASM 前端编译wasm-bindgen extern + EditorHandle + 闭包。
// 放在 #[cfg] 子模块内,避免 server 构建尝试编译引用 JS 对象的 extern。
// ============================================================================
#[cfg(target_arch = "wasm32")]
pub mod wasm {
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
// —— window.CodeMirrorEditor 模块对象 ——
//
// CodeMirrorEditor 是 IIFE 产物挂在 window 上的对象字面量(含 create 方法),
// 不是函数。wasm-bindgen 对 `fn get_module() -> T` 形式的 extern 会生成
// `window.CodeMirrorEditor()`(函数调用),会因 "not a function" 失败。
// 因此用 js_sys::Reflect::get 做属性访问拿到模块对象,再 unchecked_into。
#[wasm_bindgen]
extern "C" {
/// `window.CodeMirrorEditor` 模块对象的 Rust 映射IIFE 产物挂在 window 上的对象字面量)。
/// 不是函数——通过 [`get_module`] 用 Reflect::get 取属性而非 extern fn 调用拿到。
pub type CodeMirrorEditorModule;
/// 调用 `CodeMirrorEditor.create(containerId, opts)`。
/// 找不到容器返回 null被 Option 捕获);构造失败抛异常(被 catch 捕获)。
#[wasm_bindgen(method, catch)]
pub fn create(
this: &CodeMirrorEditorModule,
container_id: &str,
opts: &EditorOptions,
) -> Result<Option<EditorInstance>, JsValue>;
}
/// 读取 `window.CodeMirrorEditor`IIFE 默认导出,顶层 var 即 window 属性)。
/// 用 Reflect::get 做属性访问——extern fn 形式会被 wasm-bindgen 编成函数调用。
///
/// 用 unchecked_into 而非 dyn_intoCodeMirrorEditor 是 JS 对象字面量,
/// 不是 wasm-bindgen 注册的构造函数实例dyn_into 的 instanceof 检查必然失败。
/// unchecked_into 只做编译期类型标注,不做运行时校验
/// Reflect.get 已保证拿到的是目标对象)。
pub fn get_module() -> CodeMirrorEditorModule {
let window = web_sys::window().expect("no window");
let val = js_sys::Reflect::get(&window, &"CodeMirrorEditor".into())
.expect("window.CodeMirrorEditor missing");
val.unchecked_into::<CodeMirrorEditorModule>()
}
// —— 编辑器实例CodeMirrorInstance——
#[wasm_bindgen]
extern "C" {
/// `CodeMirrorEditor.create` 返回的编辑器实例对象,承载 CodeMirror EditorView。
pub type EditorInstance;
/// 返回当前文档全文。
#[wasm_bindgen(method, js_name = getValue)]
pub fn get_value(this: &EditorInstance) -> String;
/// 替换整个文档内容dispatch changes触发 onChange
#[wasm_bindgen(method, js_name = setValue)]
pub fn set_value(this: &EditorInstance, s: &str);
/// 热切换主题Compartment.reconfigure不重建实例
#[wasm_bindgen(method, js_name = setTheme)]
pub fn set_theme(this: &EditorInstance, theme: &str);
/// 热切换 Vim 模式Compartment.reconfigure不重建实例
#[wasm_bindgen(method, js_name = setVim)]
pub fn set_vim(this: &EditorInstance, v: bool);
/// 热切换语言python/node/javascript/sqlCompartment.reconfigure
/// 由 CodeRunner 组件在挂载时按 data-lang 调用。
#[wasm_bindgen(method, js_name = setLanguage)]
pub fn set_language(this: &EditorInstance, lang: &str);
/// 更新 SQL 补全 schemaCompartment.reconfigure
/// 参数为 serde_wasm_bindgen::to_value 序列化后的 JsValue
///SqlSchema 是 serde 类型,非 wasm-bindgen 类型,故不能直接传 &SqlSchema
#[wasm_bindgen(method, js_name = setSchema)]
pub fn set_schema(this: &EditorInstance, schema: &wasm_bindgen::JsValue);
/// 让编辑器获取焦点。
#[wasm_bindgen(method)]
pub fn focus(this: &EditorInstance);
/// 销毁编辑器,释放 JS 侧资源。
#[wasm_bindgen(method)]
pub fn destroy(this: &EditorInstance);
}
// —— EditorOptions用 builder 模式setter构造 JS 对象 ——
#[wasm_bindgen]
extern "C" {
/// 传给 `CodeMirrorEditor.create` 的配置对象,对应 JS 侧的 EditorOptions。
/// 用 `new()` 创建空对象后通过 setter 链式设置字段。
pub type EditorOptions;
/// 构造一个空的 EditorOptions随后用各 setter 填充。
#[wasm_bindgen(constructor)]
pub fn new() -> EditorOptions;
/// 语言(默认 'sql')。
#[wasm_bindgen(method, setter, js_name = language)]
pub fn set_language(this: &EditorOptions, v: &str);
/// 主题:'light'Catppuccin Latte或 'dark'Catppuccin Mocha
#[wasm_bindgen(method, setter, js_name = theme)]
pub fn set_theme(this: &EditorOptions, v: &str);
/// 是否启用 Vim keymap。
#[wasm_bindgen(method, setter, js_name = vim)]
pub fn set_vim(this: &EditorOptions, v: bool);
/// SQL 补全 schema表/列数据。v 为 serde_wasm_bindgen::to_value 序列化结果。
#[wasm_bindgen(method, setter, js_name = schema)]
pub fn set_schema(this: &EditorOptions, v: &wasm_bindgen::JsValue);
/// 初始文档内容。
#[wasm_bindgen(method, setter, js_name = value)]
pub fn set_value(this: &EditorOptions, v: &str);
/// 文档变更回调(参数为最新全文)。
#[wasm_bindgen(method, setter, js_name = onChange)]
pub fn set_on_change(this: &EditorOptions, cb: &Closure<dyn FnMut(String)>);
/// 编辑器就绪回调(构造末尾同步触发一次)。
#[wasm_bindgen(method, setter, js_name = onReady)]
pub fn set_on_ready(this: &EditorOptions, cb: &Closure<dyn FnMut()>);
/// Ctrl/Cmd + Enter 快捷键回调SQL 控制台触发执行)。
#[wasm_bindgen(method, setter, js_name = onRunShortcut)]
pub fn set_on_run_shortcut(this: &EditorOptions, cb: &Closure<dyn FnMut()>);
}
/// 编辑器实例句柄:持有 instance + 所有 ClosureDrop 时销毁实例并释放闭包。
///
/// 闭包字段 `_` 前缀表示仅用于保持生命周期——它们被注入 JS 后JS 侧持有
/// 函数引用;只要 [`EditorHandle`] 存活闭包就不会被回收。Drop 时随结构释放。
pub struct EditorHandle {
instance: EditorInstance,
_on_change: Closure<dyn FnMut(String)>,
_on_ready: Closure<dyn FnMut()>,
_on_run_shortcut: Closure<dyn FnMut()>,
}
impl EditorHandle {
/// 调用方须先把各 closure set 进 EditorOptions再 create
/// 然后把返回的 instance + 同名 closure 一起传入 new。
/// `on_run_shortcut` 对应 Ctrl/Cmd+Enter 回调;不用该功能时传 no-op 闭包。
pub fn new(
instance: EditorInstance,
on_change: Closure<dyn FnMut(String)>,
on_ready: Closure<dyn FnMut()>,
on_run_shortcut: Closure<dyn FnMut()>,
) -> Self {
Self {
instance,
_on_change: on_change,
_on_ready: on_ready,
_on_run_shortcut: on_run_shortcut,
}
}
/// 借用底层实例,供宿主调 getValue/setTheme/setSchema 等。
pub fn instance(&self) -> &EditorInstance {
&self.instance
}
}
impl Drop for EditorHandle {
fn drop(&mut self) {
// 销毁 JS 侧编辑器;随后 _on_change/_on_ready 字段按声明顺序释放,
// 释放 wasm-bindgen 函数表槽位。
self.instance.destroy();
}
}
}
#[cfg(target_arch = "wasm32")]
pub use wasm::*;