Skip to main content

monaco/api/
editor.rs

1use super::TextModel;
2use crate::sys::{
3    editor::{
4        self,
5        BuiltinTheme,
6        ConfigurationChangedEvent,
7        EditorLayoutInfo,
8        IContentSizeChangedEvent,
9        ICursorPositionChangedEvent,
10        ICursorSelectionChangedEvent,
11        IDimension,
12        IEditorMouseEvent,
13        IModelChangedEvent,
14        IModelContentChangedEvent,
15        IModelLanguageChangedEvent,
16        IModelOptionsChangedEvent,
17        IPasteEvent,
18        IStandaloneCodeEditor,
19        IStandaloneEditorConstructionOptions,
20    },
21    IKeyboardEvent,
22    IScrollEvent,
23};
24use std::borrow::Borrow;
25use wasm_bindgen::JsValue;
26use web_sys::HtmlElement;
27
28/// Switches to a theme.
29pub fn set_global_theme(theme: &str) {
30    editor::set_theme(theme);
31}
32
33/// Switches to a built-in theme.
34pub fn set_global_builtin_theme(theme: BuiltinTheme) {
35    set_global_theme(theme.to_value())
36}
37
38macro_rules! simple_setters {
39    ($target:ident => ) => {};
40    ($target:ident => ref $key:ident, $($tail:tt)*) => {
41        ::paste::paste! {
42            $target.[<set_ $key>]($key.as_ref().map(|v| v.as_ref()));
43        }
44        simple_setters!($target => $($tail)*);
45    };
46    ($target:ident => $key:ident, $($tail:tt)*) => {
47        ::paste::paste! {
48            $target.[<set_ $key>](*$key);
49        }
50        simple_setters!($target => $($tail)*);
51    };
52}
53
54/// Options for creating a new editor. This represents a simplified version of
55/// [`IStandaloneEditorConstructionOptions`].
56///
57/// If you need an option that isn't present you can use
58/// [`to_sys_options`](Self::to_sys_options) to
59/// build the [`IStandaloneEditorConstructionOptions`] object and expand it.
60#[derive(Clone, Debug, Default, Eq, PartialEq)]
61pub struct CodeEditorOptions {
62    pub dimension: Option<IDimension>,
63    pub theme: Option<String>,
64    pub model: Option<TextModel>,
65    pub language: Option<String>,
66    pub value: Option<String>,
67    pub scroll_beyond_last_line: Option<bool>,
68    pub automatic_layout: Option<bool>,
69}
70impl CodeEditorOptions {
71    builder_methods! {
72        pub with dimension(IDimension);
73        pub with theme(String);
74        pub with model(TextModel);
75        pub with language(String);
76        pub with value(String);
77        pub with scroll_beyond_last_line(bool);
78        pub with automatic_layout(bool);
79    }
80
81    pub fn with_builtin_theme(self, theme: BuiltinTheme) -> Self {
82        self.with_theme(theme.to_value().to_owned())
83    }
84
85    pub fn with_new_dimension(self, width: impl Into<f64>, height: impl Into<f64>) -> Self {
86        self.with_dimension(IDimension::new(width, height))
87    }
88
89    /// Convert into [`IStandaloneEditorConstructionOptions`].
90    pub fn to_sys_options(&self) -> IStandaloneEditorConstructionOptions {
91        let options = IStandaloneEditorConstructionOptions::default();
92
93        // this helps ensure we don't miss any members
94        let CodeEditorOptions {
95            dimension,
96            theme,
97            model,
98            language,
99            value,
100            scroll_beyond_last_line,
101            automatic_layout,
102        } = self;
103
104        simple_setters! {
105            options =>
106                ref language,
107                ref dimension,
108                ref theme,
109                ref model,
110                ref value,
111                scroll_beyond_last_line,
112                automatic_layout,
113        }
114
115        options
116    }
117}
118
119impl From<CodeEditorOptions> for IStandaloneEditorConstructionOptions {
120    fn from(options: CodeEditorOptions) -> Self {
121        options.to_sys_options()
122    }
123}
124
125/// Monaco code editor.
126///
127/// This struct should be the sole owner of the underlying
128/// [`IStandaloneCodeEditor`] because it will call
129/// [`dispose`](IStandaloneCodeEditor::dispose) when dropped.
130/// This is only an issue when using the [`From`] trait.
131#[must_use = "editor is disposed when dropped"]
132#[derive(Debug)]
133pub struct CodeEditor {
134    js_editor: IStandaloneCodeEditor,
135}
136impl CodeEditor {
137    event_methods! {
138        /// An event emitted on a "contextmenu".
139        pub on_context_menu(FnMut(IEditorMouseEvent));
140        /// An event emitted when the text inside this editor lost focus (i.e. cursor stops blinking).
141        pub on_did_blur_editor_text(FnMut());
142        /// An event emitted when the text inside this editor or an editor widget lost focus.
143        pub on_did_blur_editor_widget(FnMut());
144        /// An event emitted when the configuration of the editor has changed. (e.g. editor.updateOptions())
145        pub on_did_change_configuration(FnMut(ConfigurationChangedEvent));
146        /// An event emitted when the cursor position has changed.
147        pub on_did_change_cursor_position(FnMut(ICursorPositionChangedEvent));
148        /// An event emitted when the cursor selection has changed.
149        pub on_did_change_cursor_selection(FnMut(ICursorSelectionChangedEvent));
150        /// An event emitted when the model of this editor has changed (e.g. editor.setModel()).
151        pub on_did_change_model(FnMut(IModelChangedEvent));
152        /// An event emitted when the content of the current model has changed.
153        pub on_did_change_model_content(FnMut(IModelContentChangedEvent));
154        /// An event emitted when the decorations of the current model have changed.
155        pub on_did_change_model_decorations(FnMut(JsValue));
156        /// An event emitted when the language of the current model has changed.
157        pub on_did_change_model_language(FnMut(IModelLanguageChangedEvent));
158        /// An event emitted when the language configuration of the current model has changed.
159        pub on_did_change_model_language_configuration(FnMut(JsValue));
160        /// An event emitted when the options of the current model has changed.
161        pub on_did_change_model_options(FnMut(IModelOptionsChangedEvent));
162        /// An event emitted when the content width or content height in the editor has changed.
163        pub on_did_content_size_change(FnMut(IContentSizeChangedEvent));
164        /// An event emitted when the editor has been disposed.
165        pub on_did_dispose(FnMut());
166        /// An event emitted when the text inside this editor gained focus (i.e. cursor starts blinking).
167        pub on_did_focus_editor_text(FnMut());
168        /// An event emitted when the text inside this editor or an editor widget gained focus.
169        pub on_did_focus_editor_widget(FnMut());
170        /// An event emitted when the layout of the editor has changed.
171        pub on_did_layout_change(FnMut(EditorLayoutInfo));
172        /// An event emitted when users paste text in the editor.
173        pub on_did_paste(FnMut(IPasteEvent));
174        /// An event emitted when the scroll in the editor has changed.
175        pub on_did_scroll_change(FnMut(IScrollEvent));
176        /// An event emitted on a "keydown".
177        pub on_key_down(FnMut(IKeyboardEvent));
178        /// An event emitted on a "keyup".
179        pub on_key_up(FnMut(IKeyboardEvent));
180        /// An event emitted on a "mousedown".
181        pub on_mouse_down(FnMut(IEditorMouseEvent));
182        /// An event emitted on a "mouseleave".
183        pub on_mouse_leave(FnMut(IEditorMouseEvent));
184        /// An event emitted on a "mousemove".
185        pub on_mouse_move(FnMut(IEditorMouseEvent));
186        /// An event emitted on a "mouseup".
187        pub on_mouse_up(FnMut(IEditorMouseEvent));
188    }
189
190    /// Create a new editor under `element`.
191    /// `element` should be empty (not contain other dom nodes).
192    /// The editor will read the size of `element`.
193    pub fn create<OPT>(element: &HtmlElement, options: Option<OPT>) -> Self
194    where
195        OPT: Into<IStandaloneEditorConstructionOptions>,
196    {
197        #[cfg(feature = "workers")]
198        crate::workers::ensure_environment_set();
199
200        let ioptions = options.map(|x| x.into());
201        let options = ioptions.as_ref().map(Borrow::borrow);
202        let js_editor = editor::create(element, options, None);
203        Self::from(js_editor)
204    }
205
206    /// Create a new editor under `element`.
207    /// `element` should be empty (not contain other dom nodes).
208    /// The editor will read the size of `element`.
209    #[deprecated(since = "0.3.0", note = "Use `create` instead")]
210    pub fn create_with_sys_options(
211        element: &HtmlElement,
212        options: Option<IStandaloneEditorConstructionOptions>,
213    ) -> Self {
214        Self::create(element, options)
215    }
216
217    /// Gets the current model attached to this editor.
218    pub fn get_model(&self) -> Option<TextModel> {
219        self.js_editor.get_model().map(TextModel::from)
220    }
221
222    /// Sets the current model attached to this editor.
223    /// If the previous model was created by the editor via the `value` key in
224    /// the options, it will be destroyed. Otherwise, if the previous model was
225    /// set via this method, or the `model` key in the options, the
226    /// previous model will not be destroyed.
227    pub fn set_model(&self, model: &TextModel) {
228        self.js_editor.set_model(Some(model.as_ref()))
229    }
230
231    /// Detaches the current model from the editor and returns it.
232    /// The handling of the model is the same as described in
233    /// [`set_model`](Self::set_model). This operation acts like
234    /// `setModel(null)` in the Javascript API.
235    pub fn detach_model(&self) -> Option<TextModel> {
236        let model = self.get_model();
237        self.js_editor.set_model(None);
238        model
239    }
240}
241impl Drop for CodeEditor {
242    fn drop(&mut self) {
243        self.js_editor.dispose();
244    }
245}
246
247impl AsRef<IStandaloneCodeEditor> for CodeEditor {
248    fn as_ref(&self) -> &IStandaloneCodeEditor {
249        &self.js_editor
250    }
251}
252impl From<IStandaloneCodeEditor> for CodeEditor {
253    fn from(js_editor: IStandaloneCodeEditor) -> Self {
254        Self { js_editor }
255    }
256}