diff --git a/scripts/init.rhai b/scripts/init.rhai index c5715c2..3541df9 100644 --- a/scripts/init.rhai +++ b/scripts/init.rhai @@ -1,5 +1,23 @@ // TODO split into user_init.rhai and system_init.rhai +fn make_candidate_keymap() { + let keymap = new_keymap(); + keymap.bind_key("esc", "Exit menu", || s::escape()); + keymap.bind_key("up", "Up", || s::menu_selection_up(), false); + keymap.bind_key("down", "Down", || s::menu_selection_down(), false); + keymap.bind_key("bksp", "Backspace", || s::menu_selection_backspace(), false); + keymap +} + +fn make_node_selection_keymap(language_name) { + let keymap = make_candidate_keymap(); + for construct in s::language_constructs(s::get_language(language_name)) { + keymap.add_regular_candidate(s::construct_name(construct), construct); + } + keymap.bind_key_for_regular_candidates("enter", "Select", |construct| construct); + keymap +} + fn open_file_menu(dir) { let contents = s::list_files_and_dirs(dir); let keymap = new_keymap(); @@ -59,6 +77,12 @@ tree_keymap.bind_key("r", "Redo", || s::redo()); tree_keymap.bind_key("m", "Save bookmark", || s::save_bookmark('a')); tree_keymap.bind_key("'", "Go to bookmark", || s::goto_bookmark('a')); +tree_keymap.bind_key("i", "Insert", || { + s::open_menu("node_selection"); + let construct = s::block(); + s::insert_node(construct); +}); + // ~~~ Text Keymap ~~~ let text_keymap = new_keymap(); @@ -70,16 +94,19 @@ text_keymap.bind_key("del", "Delete", || s::text_ed_delete()); // ~~~ File Selection Keymap ~~~ -let file_selection_keymap = new_keymap(); +let file_selection_keymap = make_candidate_keymap(); file_selection_keymap.bind_key_for_regular_candidates("enter", "Open file", |path| s::open_doc(path)); -file_selection_keymap.bind_key("esc", "Exit menu", || s::escape()); -file_selection_keymap.bind_key("up", "Up", || s::menu_selection_up(), false); -file_selection_keymap.bind_key("down", "Down", || s::menu_selection_down(), false); -file_selection_keymap.bind_key("bksp", "Backspace", || s::menu_selection_backspace(), false); + +// ~~~ Default Layer ~~~ let layer = new_layer("default"); -layer.add_menu_keymap("file_selection", file_selection_keymap); + layer.add_mode_keymap("Tree", tree_keymap); layer.add_mode_keymap("Text", text_keymap); + +layer.add_menu_keymap("file_selection", file_selection_keymap); +// TODO: use local layers +layer.add_menu_keymap("node_selection", make_node_selection_keymap("json")); + s::register_layer(layer); s::add_global_layer("default"); diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 6422159..57729e3 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -4,7 +4,7 @@ use super::command::Command; use super::doc::Doc; use super::doc_set::{DocDisplayLabel, DocName, DocSet}; use super::Settings; -use crate::language::{LanguageSpec, NotationSetSpec, Storage}; +use crate::language::{Language, LanguageSpec, NotationSetSpec, Storage}; use crate::parsing::{Parse, ParseError}; use crate::pretty_doc::DocRef; use crate::tree::Node; @@ -92,6 +92,10 @@ impl Engine { Ok(notation_name) } + pub fn get_language(&self, name: &str) -> Result { + Ok(self.storage.language(name)?) + } + pub fn set_display_notation( &mut self, language_name: &str, @@ -253,12 +257,12 @@ impl Engine { * Editing * ***********/ - pub fn execute(&mut self, cmd: Command) -> Result<(), SynlessError> { + pub fn execute(&mut self, cmd: impl Into) -> Result<(), SynlessError> { let doc = self .doc_set .visible_doc_mut() .ok_or(DocError::NoVisibleDoc)?; - doc.execute(&mut self.storage, cmd, &mut self.clipboard)?; + doc.execute(&mut self.storage, cmd.into(), &mut self.clipboard)?; Ok(()) } diff --git a/src/language/interface.rs b/src/language/interface.rs index 646df4f..bfba5e9 100644 --- a/src/language/interface.rs +++ b/src/language/interface.rs @@ -134,6 +134,14 @@ impl Language { }) } + pub fn constructs(self, s: &Storage) -> impl Iterator { + let constructs = &grammar(s, self.language).constructs; + (&constructs).into_iter().map(move |id| Construct { + language: self.language, + construct: id, + }) + } + pub fn root_construct(self, s: &Storage) -> Construct { Construct { language: self.language, @@ -324,3 +332,15 @@ impl FixedSorts { } } } + +impl rhai::CustomType for Construct { + fn build(mut builder: rhai::TypeBuilder) { + builder.with_name("Construct"); + } +} + +impl rhai::CustomType for Language { + fn build(mut builder: rhai::TypeBuilder) { + builder.with_name("Language"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 7b97570..f5f485a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,8 @@ pub use engine::{DocName, Engine, Settings}; pub use frontends::Terminal; pub use keymap::{KeyProg, Keymap, Layer}; pub use language::{ - AritySpec, ConstructSpec, GrammarSpec, LanguageSpec, NotationSetSpec, SortSpec, Storage, + AritySpec, Construct, ConstructSpec, GrammarSpec, Language, LanguageSpec, NotationSetSpec, + SortSpec, Storage, }; pub use pretty_doc::DocRef; pub use runtime::Runtime; diff --git a/src/main.rs b/src/main.rs index 3ffd78f..a6a2f10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,8 @@ fn make_engine() -> rhai::Engine { engine.build_type::(); engine.build_type::(); engine.build_type::(); + engine.build_type::(); + engine.build_type::(); println!("Signatures:"); engine @@ -42,6 +44,7 @@ fn make_runtime() -> Rc>> { } fn run() -> Result<(), Box> { + // TODO: Log which rhai script failed to compile (instead of simple ?s) let mut engine = make_engine(); // Load internals_module.rhai diff --git a/src/runtime.rs b/src/runtime.rs index 0d52645..81b4e50 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,9 +1,10 @@ use crate::engine::{ - BookmarkCommand, Command, DocDisplayLabel, DocName, Engine, Settings, TextEdCommand, - TextNavCommand, TreeEdCommand, TreeNavCommand, + BookmarkCommand, DocDisplayLabel, DocName, Engine, Settings, TextEdCommand, TextNavCommand, + TreeEdCommand, TreeNavCommand, }; use crate::frontends::{Event, Frontend, Key}; use crate::keymap::{KeyLookupResult, KeyProg, Keymap, Layer, LayerManager, MenuSelectionCmd}; +use crate::language::{Construct, Language}; use crate::style::Style; use crate::tree::{Mode, Node}; use crate::util::{error, log, SynlessBug, SynlessError}; @@ -278,14 +279,25 @@ impl + 'static> Runtime { self.engine.load_language_ron(Path::new(path), &ron_string) } + pub fn get_language(&mut self, language_name: &str) -> Result { + self.engine.get_language(language_name) + } + + pub fn language_constructs(&mut self, language: Language) -> Vec { + language + .constructs(&self.engine.raw_storage()) + .map(rhai::Dynamic::from) + .collect() + } + + pub fn construct_name(&self, construct: Construct) -> String { + construct.name(self.engine.raw_storage()).to_owned() + } + /*********** * Editing * ***********/ - pub fn execute(&mut self, cmd: Command) -> Result<(), SynlessError> { - self.engine.execute(cmd) - } - pub fn undo(&mut self) -> Result<(), SynlessError> { self.engine.undo() } @@ -294,6 +306,11 @@ impl + 'static> Runtime { self.engine.redo() } + pub fn insert_node(&mut self, construct: Construct) -> Result<(), SynlessError> { + let node = Node::new(self.engine.raw_storage_mut(), construct); + self.engine.execute(TreeEdCommand::Insert(node)) + } + /*********** * Private * ***********/ @@ -325,7 +342,7 @@ impl + 'static> Runtime { Ok(None) } Some(KeyLookupResult::InsertChar(ch)) => { - self.execute(TextEdCommand::Insert(ch).into())?; + self.engine.execute(TextEdCommand::Insert(ch))?; self.display()?; Ok(None) } @@ -435,7 +452,7 @@ macro_rules! register { ($module:expr, $runtime:ident, $command:ident :: $variant:ident as $name:ident) => { let rt = $runtime.clone(); let closure = move || { - rt.borrow_mut().execute($command::$variant.into()) + rt.borrow_mut().engine.execute($command::$variant) .map_err(|err| Box::::from(err)) }; rhai::FuncRegistration::new(stringify!($name)) @@ -445,7 +462,7 @@ macro_rules! register { ($module:expr, $runtime:ident, $command:ident :: $variant:ident ($( $param:ident : $type:ty ),*) as $name:ident) => { let rt = $runtime.clone(); let closure = move | $( $param : $type ),* | { - rt.borrow_mut().execute($command::$variant( $( $param ),* ).into()) + rt.borrow_mut().engine.execute($command::$variant( $( $param ),* )) .map_err(|err| Box::::from(err)) }; rhai::FuncRegistration::new(stringify!($name)) @@ -486,8 +503,11 @@ impl + 'static> Runtime { // Languages register!(module, rt.load_language(path: &str)?); + register!(module, rt.get_language(language_name: &str)?); + register!(module, rt.language_constructs(language: Language)); + register!(module, rt.construct_name(construct: Construct)); - // Editing + // Editing: Tree Nav register!(module, rt, TreeNavCommand::Prev as tree_nav_prev); register!(module, rt, TreeNavCommand::First as tree_nav_first); register!(module, rt, TreeNavCommand::Next as tree_nav_next); @@ -506,22 +526,28 @@ impl + 'static> Runtime { register!(module, rt, TreeNavCommand::Parent as tree_nav_parent); register!(module, rt, TreeNavCommand::EnterText as tree_nav_enter_text); + // Editing: Tree Ed register!(module, rt, TreeEdCommand::Backspace as tree_ed_backspace); register!(module, rt, TreeEdCommand::Delete as tree_ed_delete); + register!(module, rt.insert_node(construct: Construct)?); + // Editing: Text Nav register!(module, rt, TextNavCommand::Left as text_nav_left); register!(module, rt, TextNavCommand::Right as text_nav_right); register!(module, rt, TextNavCommand::Beginning as text_nav_beginning); register!(module, rt, TextNavCommand::End as text_nav_end); register!(module, rt, TextNavCommand::ExitText as text_nav_exit); + // Editing: Text Ed register!(module, rt, TextEdCommand::Backspace as text_ed_backspace); register!(module, rt, TextEdCommand::Delete as text_ed_delete); register!(module, rt, TextEdCommand::Insert(ch: char) as text_ed_insert); + // Editing: Bookmark register!(module, rt, BookmarkCommand::Save(ch: char) as save_bookmark); register!(module, rt, BookmarkCommand::Goto(ch: char) as goto_bookmark); + // Editing: Meta register!(module, rt.undo()?); register!(module, rt.redo()?);