Skip to content

Commit

Permalink
feat: Loading docs, loading languages, opening menus from Rhai
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpombrio committed Apr 18, 2024
1 parent 6753ea4 commit 5733ef7
Show file tree
Hide file tree
Showing 25 changed files with 390 additions and 100 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ron = "0.8.1"
[dependencies.partial-pretty-printer]
#path = "../ppp/"
git = "https://github.com/justinpombrio/partial-pretty-printer"
version = "0.7.0"
version = "0.8.0"
features = ["serialization"]
[dependencies.no-nonsense-flamegraphs]
version = "0.2.*"
Expand Down
3 changes: 2 additions & 1 deletion data/json_lang.ron
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// TODO: styles

LanguageSpec(
name: "Json",
name: "json",
file_extensions: [".json"],
grammar: GrammarSpec(
constructs: [
ConstructSpec(
Expand Down
3 changes: 2 additions & 1 deletion data/keyhints_lang.ron
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
LanguageSpec(
name: "Keyhints",
name: "keyhints",
file_extensions: [],
grammar: GrammarSpec(
constructs: [
ConstructSpec(
Expand Down
1 change: 1 addition & 0 deletions data/pokemon.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion data/selection_lang.ron
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
LanguageSpec(
name: "SelectionMenu",
name: "selection_menu",
file_extensions: [],
grammar: GrammarSpec(
constructs: [
ConstructSpec(
Expand Down
23 changes: 22 additions & 1 deletion scripts/base_module.rhai
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
fn block() {
loop {
let keyprog = synless_internals::block_on_key();
try {
synless_internals::display();
let keyprog = synless_internals::block_on_key();
} catch (err) {
log_caught_error(err);
s::abort();
}
if keyprog.close_menu {
s::close_menu();
return call(keyprog.prog);
Expand All @@ -9,6 +15,21 @@ fn block() {
}
}

fn log_caught_error(err) {
if type_of(err) == "SynlessError" {
let category = err.category;
let msg = err.message;
s::log_error(`${category}: ${msg}`);
} else {
if type_of(err) == "map" && "message" in err {
let msg = err.message;
s::log_error(`Rhai: ${msg}`);
} else {
s::log_error(`Thrown: ${err}`);
};
}
}

fn abort() {
synless_internals::prepare_to_abort();
exit();
Expand Down
2 changes: 2 additions & 0 deletions scripts/init.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
s::load_language("data/json_lang.ron");
s::open_doc("data/pokemon.json");
17 changes: 3 additions & 14 deletions scripts/main.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,9 @@ loop {
try {
s::block(); // ignoring return value
} catch (err) {
if type_of(err) == "SynlessError" {
let category = err.category;
let msg = err.message;
s::log_error(`${category}: ${msg}`);
if err.category == "Abort" {
s::abort();
}
} else {
if type_of(err) == "map" && "message" in err {
let msg = err.message;
s::log_error(`Rhai: ${msg}`);
} else {
s::log_error(`Thrown: ${err}`);
};
log_caught_error(err);
if type_of(err) == "SynlessError" && err.category == "Abort" {
s::abort();
}
s::close_menu();
}
Expand Down
14 changes: 12 additions & 2 deletions src/engine/doc_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,20 @@ impl DocSet {
}
}

pub fn visible_doc(&self) -> Option<&DocName> {
pub fn visible_doc_name(&self) -> Option<&DocName> {
self.visible_doc.as_ref()
}

pub fn visible_doc(&self) -> Option<&Doc> {
let doc_index = *self.name_to_doc.get(self.visible_doc.as_ref()?)?;
Some(self.docs.get(doc_index).bug())
}

pub fn visible_doc_mut(&mut self) -> Option<&mut Doc> {
let doc_index = *self.name_to_doc.get(self.visible_doc.as_ref()?)?;
Some(self.docs.get_mut(doc_index).bug())
}

pub fn get_doc(&self, doc_name: &DocName) -> Option<&Doc> {
let doc_index = *self.name_to_doc.get(doc_name)?;
Some(self.docs.get(doc_index).bug())
Expand All @@ -142,7 +152,7 @@ impl DocSet {

let (doc, opts) = match label {
DocDisplayLabel::Visible => {
let doc = self.get_doc(self.visible_doc()?)?;
let doc = self.get_doc(self.visible_doc_name()?)?;
let (focus_path, focus_target) = doc.cursor().path_from_root(s);
let options = pane::PrintingOptions {
focus_path,
Expand Down
43 changes: 37 additions & 6 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(clippy::module_inception)]

use super::command::Command;
use super::doc::Doc;
use super::doc_set::{DocDisplayLabel, DocName, DocSet};
use super::Settings;
Expand All @@ -21,6 +22,8 @@ pub enum DocError {
DocNotFound(DocName),
#[error("Document '{0}' is already open")]
DocAlreadyOpen(DocName),
#[error("There is no visible doc to act on")]
NoVisibleDoc,
}

impl From<DocError> for SynlessError {
Expand Down Expand Up @@ -114,6 +117,21 @@ impl Engine {
Ok(())
}

pub fn register_file_extension(
&mut self,
extension: String,
language_name: &str,
) -> Result<(), SynlessError> {
let lang = self.storage.language(language_name)?;
self.storage.register_file_extension(extension, lang);
Ok(())
}

pub fn lookup_file_extension(&self, extension: &str) -> Option<&str> {
let language = self.storage.lookup_file_extension(extension)?;
Some(language.name(&self.storage))
}

/***********
* Parsers *
***********/
Expand Down Expand Up @@ -147,8 +165,8 @@ impl Engine {
Ok(())
}

pub fn visible_doc(&self) -> Option<&DocName> {
self.doc_set.visible_doc()
pub fn visible_doc_name(&self) -> Option<&DocName> {
self.doc_set.visible_doc_name()
}

pub fn get_doc(&self, doc_name: &DocName) -> Option<&Doc> {
Expand All @@ -165,7 +183,7 @@ impl Engine {

pub fn load_doc_from_sexpr(
&self,
doc_name: &DocName,
doc_name: DocName,
_source: &str,
) -> Result<(), SynlessError> {
todo!()
Expand All @@ -177,7 +195,7 @@ impl Engine {

pub fn load_doc_from_source(
&mut self,
doc_name: &DocName,
doc_name: DocName,
language_name: &str,
source: &str,
) -> Result<(), SynlessError> {
Expand All @@ -187,8 +205,8 @@ impl Engine {
.ok_or_else(|| error!(Language, "No parser for language {}", language_name))?;
let root_node = parser.parse(&mut self.storage, &doc_name.to_string(), source)?;
let doc = Doc::new(&self.storage, root_node).bug_msg("Invalid root");
if !self.doc_set.add_doc(doc_name.to_owned(), doc) {
return Err(DocError::DocAlreadyOpen(doc_name.to_owned()).into());
if !self.doc_set.add_doc(doc_name.clone(), doc) {
return Err(DocError::DocAlreadyOpen(doc_name).into());
}
Ok(())
}
Expand Down Expand Up @@ -216,6 +234,19 @@ impl Engine {
.get_content(&self.storage, label, &self.settings)
}

/***********
* Editing *
***********/

pub fn execute(&mut self, cmd: Command) -> Result<(), SynlessError> {
let doc = self
.doc_set
.visible_doc_mut()
.ok_or(DocError::NoVisibleDoc)?;
doc.execute(&mut self.storage, cmd, &mut self.clipboard)?;
Ok(())
}

/**********************
* Raw Storage Access *
**********************/
Expand Down
6 changes: 2 additions & 4 deletions src/frontends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ use std::error::Error;

impl<W: Error + 'static, E: Error + 'static> From<pane::PaneError<W, E>> for SynlessError {
fn from(error: pane::PaneError<W, E>) -> SynlessError {
use pane::PaneError::{
InvalidUseOfDynamic, MissingDocument, PrettyWindowError, PrintingError,
};
use pane::PaneError::{InvalidUseOfDynamic, PrettyWindowError, PrintingError};

match &error {
InvalidUseOfDynamic | MissingDocument(_) => error!(Frontend, "{}", error.to_string()),
InvalidUseOfDynamic => error!(Frontend, "{}", error.to_string()),
PrettyWindowError(err) => error!(Frontend, "{}", err.to_string()),
PrintingError(err) => error!(Printing, "{}", err.to_string()),
}
Expand Down
10 changes: 2 additions & 8 deletions src/keymap/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ impl rhai::CustomType for KeyProg {
///
/// - If you add a general binding and candidate-specific binding with the same key, the
/// candidate-specific binding takes priority.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Keymap {
/// If the user types `Key`, execute `KeyProgSpec`.
general_bindings: OrderedMap<Key, KeyProgSpec>,
Expand All @@ -242,13 +242,7 @@ impl Keymap {
****************/

pub fn new() -> Keymap {
Keymap {
general_bindings: OrderedMap::new(),
special_bindings: OrderedMap::new(),
regular_bindings: OrderedMap::new(),
regular_candidates: Vec::new(),
custom_bindings: OrderedMap::new(),
}
Keymap::default()
}

/// Take the union of the two keymaps, with bindings in `other` overriding those in `self`.
Expand Down
53 changes: 37 additions & 16 deletions src/keymap/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::frontends::Key;
use crate::language::Storage;
use crate::tree::Mode;
use crate::tree::Node;
use crate::util::IndexedMap;
use crate::util::{error, IndexedMap, SynlessError};
use std::collections::HashMap;

type LayerIndex = usize;
Expand Down Expand Up @@ -100,15 +100,19 @@ impl LayerManager {

/// Add a global keymap layer to the top of the global layer stack. Returns `Err` if the layer
/// has not been registered.
pub fn add_global_layer(&mut self, layer_name: &str) -> Result<(), ()> {
pub fn add_global_layer(&mut self, layer_name: &str) -> Result<(), SynlessError> {
add_layer(&self.layers, &mut self.global_layers, layer_name)?;
self.cached_composite_layers.clear();
Ok(())
}

/// Add a keymap layer to the top of the given document's local layer stack. Returns `Err` if
/// the layer has not been registered.
pub fn add_local_layer(&mut self, doc_name: &DocName, layer_name: &str) -> Result<(), ()> {
pub fn add_local_layer(
&mut self,
doc_name: &DocName,
layer_name: &str,
) -> Result<(), SynlessError> {
let mut local_layers = self.local_layers.entry(doc_name.to_owned()).or_default();
add_layer(&self.layers, local_layers, layer_name)?;
self.cached_composite_layers.clear();
Expand All @@ -117,15 +121,19 @@ impl LayerManager {

/// Remove a global keymap layer from wherever it is in the global layer stack. Returns `Err`
/// if the layer has not been registered.
pub fn remove_global_layer(&mut self, layer_name: &str) -> Result<(), ()> {
pub fn remove_global_layer(&mut self, layer_name: &str) -> Result<(), SynlessError> {
remove_layer(&self.layers, &mut self.global_layers, layer_name)?;
self.cached_composite_layers.clear();
Ok(())
}

/// Remove a keymap layer from wherever it is in the given document's local layer stack.
/// Returns `Err` if the layer has not been registered.
pub fn remove_local_layer(&mut self, doc_name: &DocName, layer_name: &str) -> Result<(), ()> {
pub fn remove_local_layer(
&mut self,
doc_name: &DocName,
layer_name: &str,
) -> Result<(), SynlessError> {
let mut local_layers = self.local_layers.entry(doc_name.to_owned()).or_default();
remove_layer(&self.layers, local_layers, layer_name)?;
self.cached_composite_layers.clear();
Expand Down Expand Up @@ -161,17 +169,16 @@ impl LayerManager {
/// Open the named menu. If `dynamic_keymap` is `Some`, layer it on top of the existing keymaps
/// for the menu. Returns `false` and does nothing if there's no menu to open (this happens
/// when none of the layers have a menu of this name and `dynamic_keymap` is `None`).
#[must_use]
pub fn open_menu(
&mut self,
doc_name: Option<&DocName>,
menu_name: String,
dynamic_keymap: Option<Keymap>,
) -> bool {
) -> Result<(), SynlessError> {
let composite_layer = self.composite_layer(doc_name);
let label = KeymapLabel::Menu(menu_name.clone());
let menu = match (dynamic_keymap, composite_layer.keymaps.get(&label)) {
(None, None) => return false,
(None, None) => return Err(error!(Keymap, "No keymap for menu '{menu_name}'")),
(Some(keymap), None) => Menu::new(menu_name, keymap),
(Some(dyn_keymap), Some(composite_keymap)) => {
let mut keymap = composite_keymap.to_owned();
Expand All @@ -181,7 +188,7 @@ impl LayerManager {
(None, Some(keymap)) => Menu::new(menu_name, keymap.to_owned()),
};
self.active_menu = Some(menu);
true
Ok(())
}

pub fn close_menu(&mut self) {
Expand All @@ -190,12 +197,16 @@ impl LayerManager {

/// Manipulate the menu's candidate selection. Returns `false` and does nothing if there is no
/// menu open, or it does not have candidate selection.
#[must_use]
pub fn edit_menu_selection(&mut self, cmd: MenuSelectionCmd) -> bool {
if let Some(menu) = &mut self.active_menu {
pub fn edit_menu_selection(&mut self, cmd: MenuSelectionCmd) -> Result<(), SynlessError> {
let is_ok = if let Some(menu) = &mut self.active_menu {
menu.execute(cmd)
} else {
false
};
if is_ok {
Ok(())
} else {
Err(error!(Keymap, "No selection menu to edit"))
}
}

Expand Down Expand Up @@ -277,8 +288,13 @@ fn add_layer(
layers: &IndexedMap<Layer>,
active_layers: &mut Vec<LayerIndex>,
layer_name: &str,
) -> Result<(), ()> {
let layer_index = layers.id(layer_name).ok_or(())?;
) -> Result<(), SynlessError> {
let layer_index = layers.id(layer_name).ok_or_else(|| {
error!(
Keymap,
"Layer {layer_name} cannot be added because it has not been registered"
)
})?;
active_layers.retain(|i| *i != layer_index); // remove lower priority duplicate
active_layers.push(layer_index);
Ok(())
Expand All @@ -288,8 +304,13 @@ fn remove_layer(
layers: &IndexedMap<Layer>,
active_layers: &mut Vec<LayerIndex>,
layer_name: &str,
) -> Result<(), ()> {
let layer_index = layers.id(layer_name).ok_or(())?;
) -> Result<(), SynlessError> {
let layer_index = layers.id(layer_name).ok_or_else(|| {
error!(
Keymap,
"Layer {layer_name} cannot be removed because it has not been registered"
)
})?;
active_layers.retain(|i| *i != layer_index);
Ok(())
}
Loading

0 comments on commit 5733ef7

Please sign in to comment.