cm.rs
⎇
Raw
1//! Thin, swappable bindings to the vendored CodeMirror 6 bundle.
2//!
3//! The bundle (`web/cm6.js`, built from `web/cm/wrapper.js`) installs a
4//! `window.__dovenest_cm` global. This module is the *only* place that knows how
5//! to talk to it, so swapping the editor later means re-implementing just
6//! these functions.
7//!
8//! The bundle is a separate `<script>`, so it may be missing (a failed load,
9//! a stale cache). Every binding is therefore `catch`: a missing global
10//! surfaces as an `Err` instead of a wasm trap.
11
12use wasm_bindgen::JsCast;
13use wasm_bindgen::prelude::*;
14
15#[wasm_bindgen]
16extern "C" {
17 #[wasm_bindgen(js_namespace = __dovenest_cm, js_name = create, catch)]
18 fn js_create(
19 container: &web_sys::Element,
20 value: &str,
21 filename: &str,
22 editable: bool,
23 on_update: Option<&js_sys::Function>,
24 ) -> Result<JsValue, JsValue>;
25
26 #[wasm_bindgen(js_namespace = __dovenest_cm, catch)]
27 pub fn destroy(view: &JsValue) -> Result<(), JsValue>;
28
29 /// Replace the whole document (used when (re)loading a file).
30 #[wasm_bindgen(js_namespace = __dovenest_cm, js_name = setValue, catch)]
31 pub fn set_value(view: &JsValue, text: &str) -> Result<(), JsValue>;
32
33 #[wasm_bindgen(js_namespace = __dovenest_cm, js_name = getValue, catch)]
34 fn js_get_value(view: &JsValue) -> Result<JsValue, JsValue>;
35
36 #[wasm_bindgen(js_namespace = __dovenest_cm, catch)]
37 pub fn focus(view: &JsValue) -> Result<(), JsValue>;
38}
39
40/// Extract a human-readable message from a JS error value.
41fn err_str(e: JsValue) -> String {
42 e.dyn_into::<js_sys::Error>()
43 .ok()
44 .and_then(|e| e.message().as_string())
45 .unwrap_or_else(|| "editor error".to_string())
46}
47
48/// Create an editor inside `container` and return its view handle. Destroy
49/// it with [`destroy`] when the host goes away.
50///
51/// `on_update` is called whenever the document changes (used to track the
52/// editor's dirty state). The caller must keep the backing `Closure` alive
53/// for as long as the editor exists.
54pub fn create(
55 container: &web_sys::Element,
56 value: &str,
57 filename: &str,
58 editable: bool,
59 on_update: Option<&js_sys::Function>,
60) -> Result<JsValue, String> {
61 js_create(container, value, filename, editable, on_update).map_err(err_str)
62}
63
64/// Read the current document text (used by the editor's save path).
65pub fn get_value(view: &JsValue) -> Option<String> {
66 js_get_value(view).ok()?.as_string()
67}
68