From 860e9bd818c539094c79efaf1c0010cda95b7568 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 3 Apr 2024 16:08:15 -0400 Subject: [PATCH 1/2] Add Engine and DocSet, use FocusTarget, bump ppp. Hooray for the elimination of "virtual text children"! --- Cargo.toml | 5 +- src/engine/doc.rs | 17 ++++- src/engine/doc_set.rs | 148 ++++++++++++++++++++++++++++++++++++++ src/engine/engine.rs | 104 +++++++++++++++++++++++++++ src/engine/mod.rs | 21 ++++++ src/language/compiled.rs | 11 ++- src/language/interface.rs | 72 +++++++++++++++++-- src/language/mod.rs | 2 + src/language/specs.rs | 9 +-- src/language/storage.rs | 27 +++---- src/pretty_doc.rs | 141 ++++++++++-------------------------- src/style.rs | 9 +-- src/tree/forest.rs | 1 + src/tree/location.rs | 26 +++++++ src/tree/node.rs | 11 ++- tests/tests.rs | 10 +-- 16 files changed, 466 insertions(+), 148 deletions(-) create mode 100644 src/engine/doc_set.rs create mode 100644 src/engine/engine.rs diff --git a/Cargo.toml b/Cargo.toml index 3df02df..c30ab6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,12 @@ bit-set = "0.5" typed-arena = "2.0" generational-arena = "0.2" crossterm = "0.27.0" +serde = { version = "1.0", features = ["derive"] } +ron = "0.8.1" [dependencies.partial-pretty-printer] git = "https://github.com/justinpombrio/partial-pretty-printer" - version = "0.4.0" + version = "0.5.1" + features = ["serialization"] [dependencies.no-nonsense-flamegraphs] version = "0.2.*" git = "https://github.com/justinpombrio/no-nonsense-flamegraphs" diff --git a/src/engine/doc.rs b/src/engine/doc.rs index 4efe599..239a51e 100644 --- a/src/engine/doc.rs +++ b/src/engine/doc.rs @@ -3,6 +3,7 @@ use super::doc_command::{ TextNavCommand, TreeEdCommand, TreeNavCommand, }; use crate::language::Storage; +use crate::pretty_doc::DocRef; use crate::tree::{Bookmark, Location, Mode, Node}; use crate::util::{bug_assert, SynlessBug}; use std::collections::HashMap; @@ -42,6 +43,7 @@ pub enum DocError { EmptyClipboard, } +#[derive(Debug)] pub struct Doc { cursor: Location, recent: Option, @@ -61,6 +63,18 @@ impl Doc { } } + pub fn doc_ref_source<'d>(&self, s: &'d Storage) -> DocRef<'d> { + DocRef::new(s, self.cursor, self.cursor.root_node(s), true) + } + + pub fn doc_ref_display<'d>(&self, s: &'d Storage) -> DocRef<'d> { + DocRef::new(s, self.cursor, self.cursor.root_node(s), false) + } + + pub fn cursor(&self) -> Location { + self.cursor + } + pub fn mode(&self) -> Mode { self.cursor.mode() } @@ -78,7 +92,7 @@ impl Doc { DocCommand::Ed(cmd) => execute_ed(s, cmd, &mut self.cursor)?, DocCommand::Clipboard(cmd) => execute_clipboard(s, cmd, &mut self.cursor, clipboard)?, DocCommand::Nav(cmd) => { - execute_nav(s, cmd, &mut self.cursor, &mut self.bookmarks, clipboard)?; + execute_nav(s, cmd, &mut self.cursor, &mut self.bookmarks)?; Vec::new() } }; @@ -191,7 +205,6 @@ fn execute_nav( cmd: NavCommand, cursor: &mut Location, bookmarks: &mut HashMap, - clipboard: &mut Vec, ) -> Result<(), DocError> { match cmd { NavCommand::Tree(cmd) => execute_tree_nav(s, cmd, cursor), diff --git a/src/engine/doc_set.rs b/src/engine/doc_set.rs new file mode 100644 index 0000000..674f0e3 --- /dev/null +++ b/src/engine/doc_set.rs @@ -0,0 +1,148 @@ +use super::doc::Doc; +use super::Settings; +use crate::language::Storage; +use crate::pretty_doc::DocRef; +use crate::util::SynlessBug; +use partial_pretty_printer as ppp; +use partial_pretty_printer::pane; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +type DocIndex = usize; + +/// Label for documents that might be displayed on the screen. +/// +/// Sample PaneNotation, and its corresponding DocLabels: +/// +/// ```text +/// +----------------------------+ +/// | doc1 |*doc2*| doc3 | | +/// +----------------------------+ +/// | | +/// | This is the visible doc. | +/// | | +/// +----------------------------+ +/// | doc2.rs 27,1 | +/// +----------------------------+ +/// |i->insert h->left | +/// |s->save l->right | +/// +----------------------------+ +/// +/// +----------------------------+ +/// | Aux(tab_bar) | +/// +----------------------------+ +/// | | +/// | Visible | +/// | | +/// +----------------------------+ +/// | Meta(name) Meta(linecol) | +/// +----------------------------+ +/// | | +/// | Aux(key_hints) | +/// +----------------------------+ +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum DocLabel { + /// A "real" document that the user is viewing and editing. + Visible, + /// An auto-generated doc containing info about the `Visible` doc, for use in a status bar. + Metadata(String), + /// An auto-generated doc used to implement UI elements like menus. + Auxilliary(String), +} + +#[derive(Debug)] +pub struct DocSet { + file_path_to_doc: HashMap, + /// INVARIANT: DocLabel::Visible is always present. + label_to_doc: HashMap, + /// DocIndex -> Doc + docs: Vec, +} + +impl DocSet { + pub fn new(starting_doc: Doc) -> DocSet { + let mut doc_set = DocSet { + file_path_to_doc: HashMap::new(), + label_to_doc: HashMap::new(), + docs: Vec::new(), + }; + let starting_doc_index = doc_set.insert_doc(starting_doc); + doc_set + .label_to_doc + .insert(DocLabel::Visible, starting_doc_index); + doc_set + } + + pub fn visible_doc(&self) -> &Doc { + let doc_index = *self + .label_to_doc + .get(&DocLabel::Visible) + .bug_msg("VisibleDoc not found"); + self.docs.get(doc_index).bug() + } + + pub fn metadata_doc(&self, name: &str) -> Option<&Doc> { + let doc_index = *self + .label_to_doc + .get(&DocLabel::Metadata(name.to_owned()))?; + Some(self.docs.get(doc_index).bug()) + } + + pub fn auxilliary_doc(&self, name: &str) -> Option<&Doc> { + let doc_index = *self + .label_to_doc + .get(&DocLabel::Auxilliary(name.to_owned()))?; + Some(self.docs.get(doc_index).bug()) + } + + pub fn file_doc(&self, file_path: &Path) -> Option<&Doc> { + let doc_index = *self.file_path_to_doc.get(file_path)?; + Some(self.docs.get(doc_index).bug()) + } + + pub fn get_content<'s>( + &self, + s: &'s Storage, + label: DocLabel, + settings: &Settings, + ) -> Option<(DocRef<'s>, pane::PrintingOptions)> { + let meta_and_aux_options = pane::PrintingOptions { + focus_path: vec![], + focus_target: ppp::FocusTarget::Start, + focus_height: 0.0, + width_strategy: pane::WidthStrategy::Full, + set_focus: false, + }; + + let (doc, opts) = match label { + DocLabel::Visible => { + let doc = self.visible_doc(); + let (focus_path, focus_target) = doc.cursor().path_from_root(s); + let options = pane::PrintingOptions { + focus_path, + focus_target, + focus_height: settings.focus_height, + width_strategy: pane::WidthStrategy::NoMoreThan(settings.max_doc_width), + set_focus: true, + }; + (doc, options) + } + DocLabel::Metadata(name) => { + let doc = self.metadata_doc(&name)?; + (doc, meta_and_aux_options) + } + DocLabel::Auxilliary(name) => { + let doc = self.auxilliary_doc(&name)?; + (doc, meta_and_aux_options) + } + }; + Some((doc.doc_ref_display(s), opts)) + } + + fn insert_doc(&mut self, doc: Doc) -> usize { + let doc_index = self.docs.len(); + self.docs.push(doc); + doc_index + } +} diff --git a/src/engine/engine.rs b/src/engine/engine.rs new file mode 100644 index 0000000..3ef041d --- /dev/null +++ b/src/engine/engine.rs @@ -0,0 +1,104 @@ +#![allow(clippy::module_inception)] + +use super::doc::Doc; +use super::doc_set::{DocLabel, DocSet}; +use super::Settings; +use crate::language::{Language, LanguageError, LanguageSpec, NotationSetSpec, Storage}; +use crate::pretty_doc::{DocRef, PrettyDocError}; +use crate::style::Style; +use crate::tree::{Bookmark, Node}; +use crate::util::SynlessBug; +use partial_pretty_printer as ppp; +use partial_pretty_printer::pane; +use std::path::Path; + +// TODO: think about error types +#[derive(thiserror::Error, Debug)] +pub enum EngineError { + #[error("Did not find doc named '{0}'")] + DocNotFound(String), + #[error("{0}")] + PrintingError(#[from] ppp::PrintingError), + #[error("{0}")] + LanguageError(#[from] LanguageError), +} + +#[derive(Debug)] +pub struct Engine { + storage: Storage, + doc_set: DocSet, + clipboard: Vec, + settings: Settings, +} + +impl Engine { + pub fn new(settings: Settings) -> Engine { + todo!() + } + + /************* + * Languages * + *************/ + + pub fn add_language(&mut self, language_spec: LanguageSpec) -> Result<(), EngineError> { + self.storage.add_language(language_spec)?; + Ok(()) + } + + pub fn set_display_notation( + &mut self, + language_name: &str, + notation_set: NotationSetSpec, + ) -> Result<(), EngineError> { + let notation_set_name = notation_set.name.clone(); + let lang = self.storage.language(language_name)?; + lang.add_notation(&mut self.storage, notation_set)?; + lang.set_display_notation(&mut self.storage, ¬ation_set_name)?; + Ok(()) + } + + pub fn set_source_notation( + &mut self, + language_name: &str, + notation_set: NotationSetSpec, + ) -> Result<(), EngineError> { + let notation_set_name = notation_set.name.clone(); + let lang = self.storage.language(language_name)?; + lang.add_notation(&mut self.storage, notation_set)?; + lang.set_source_notation(&mut self.storage, ¬ation_set_name)?; + Ok(()) + } + + pub fn unset_source_notation(&mut self, language_name: &str) -> Result<(), EngineError> { + let lang = self.storage.language(language_name)?; + lang.unset_source_notation(&mut self.storage)?; + Ok(()) + } + + /****************** + * Doc Management * + ******************/ + + pub fn make_empty_doc(&mut self, doc_name: &str, language: Language) { + todo!() + } + + /************ + * Printing * + ************/ + + fn print_source(&self, doc_path: &Path) -> Result { + let doc = self + .doc_set + .file_doc(doc_path) + .ok_or_else(|| EngineError::DocNotFound(doc_path.to_string_lossy().into_owned()))?; + let doc_ref = doc.doc_ref_source(&self.storage); + let source = ppp::pretty_print_to_string(doc_ref, self.settings.source_width)?; + Ok(source) + } + + fn get_content(&self, label: DocLabel) -> Option<(DocRef, pane::PrintingOptions)> { + self.doc_set + .get_content(&self.storage, label, &self.settings) + } +} diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 164332c..a1c78eb 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,2 +1,23 @@ mod doc; mod doc_command; +mod doc_set; +mod engine; + +use partial_pretty_printer as ppp; + +#[derive(Debug, Clone)] +pub struct Settings { + source_width: ppp::Width, + max_doc_width: ppp::Width, + focus_height: f32, +} + +impl Settings { + fn default() -> Settings { + Settings { + source_width: 100, + max_doc_width: 80, + focus_height: 0.5, + } + } +} diff --git a/src/language/compiled.rs b/src/language/compiled.rs index 8ad5a32..fdef96e 100644 --- a/src/language/compiled.rs +++ b/src/language/compiled.rs @@ -37,6 +37,7 @@ pub enum ArityCompiled { #[derive(Debug)] pub struct SortCompiled(pub BitSet); +#[derive(Debug)] pub struct GrammarCompiled { pub constructs: IndexedMap, /// SortId -> SortCompiled @@ -48,13 +49,16 @@ pub struct GrammarCompiled { pub keymap: HashMap, } +#[derive(Debug)] pub struct LanguageCompiled { pub name: String, pub grammar: GrammarCompiled, pub notation_sets: IndexedMap, - pub current_notation_set: NotationSetId, + pub source_notation: Option, + pub display_notation: NotationSetId, } +#[derive(Debug)] pub struct NotationSetCompiled { pub name: String, /// ConstructId -> ValidNotation @@ -74,7 +78,8 @@ pub fn compile_language(language_spec: LanguageSpec) -> Result Result { diff --git a/src/language/interface.rs b/src/language/interface.rs index 1ae0497..f59f7b2 100644 --- a/src/language/interface.rs +++ b/src/language/interface.rs @@ -102,7 +102,7 @@ impl Language { s.languages[self.language].notation_sets.names() } - pub fn get_notation_set(self, s: &Storage, name: &str) -> Option { + pub fn notation(self, s: &Storage, name: &str) -> Option { s.languages[self.language] .notation_sets .id(name) @@ -112,10 +112,17 @@ impl Language { }) } - pub fn current_notation_set(self, s: &Storage) -> NotationSet { + pub fn source_notation(self, s: &Storage) -> Option { + Some(NotationSet { + language: self.language, + notation_set: s.languages[self.language].source_notation?, + }) + } + + pub fn display_notation(self, s: &Storage) -> NotationSet { NotationSet { language: self.language, - notation_set: s.languages[self.language].current_notation_set, + notation_set: s.languages[self.language].display_notation, } } @@ -134,6 +141,57 @@ impl Language { } } + pub fn add_notation( + self, + s: &mut Storage, + notation_set: NotationSetSpec, + ) -> Result<(), LanguageError> { + let notation_set = compile_notation_set(notation_set, grammar(s, self.language))?; + s.languages[self.language] + .notation_sets + .insert(notation_set.name.clone(), notation_set) + .map_err(|name| LanguageError::DuplicateNotationSet(self.name(s).to_owned(), name)) + } + + pub fn set_display_notation( + self, + s: &mut Storage, + notation_set_name: &str, + ) -> Result<(), LanguageError> { + let notation_set_id = self.notation_set_id(s, notation_set_name)?; + s.languages[self.language].display_notation = notation_set_id; + Ok(()) + } + + pub fn set_source_notation( + self, + s: &mut Storage, + notation_set_name: &str, + ) -> Result<(), LanguageError> { + let notation_set_id = self.notation_set_id(s, notation_set_name)?; + s.languages[self.language].source_notation = Some(notation_set_id); + Ok(()) + } + + pub fn unset_source_notation(self, s: &mut Storage) -> Result<(), LanguageError> { + s.languages[self.language].source_notation = None; + Ok(()) + } + + fn notation_set_id(self, s: &Storage, notation_set_name: &str) -> Result { + if let Some(id) = s.languages[self.language] + .notation_sets + .id(notation_set_name) + { + Ok(id) + } else { + Err(LanguageError::UndefinedNotationSet( + self.name(s).to_owned(), + notation_set_name.to_owned(), + )) + } + } + pub(super) fn from_id(id: LanguageId) -> Language { Language { language: id } } @@ -205,8 +263,12 @@ impl Construct { } } - pub fn notation(self, s: &Storage) -> &ValidNotation { - self.language().current_notation_set(s).notation(s, self) + pub fn display_notation(self, s: &Storage) -> &ValidNotation { + self.language().display_notation(s).notation(s, self) + } + + pub fn source_notation(self, s: &Storage) -> Option<&ValidNotation> { + Some(self.language().source_notation(s)?.notation(s, self)) } pub fn is_comment_or_ws(self, s: &Storage) -> bool { diff --git a/src/language/mod.rs b/src/language/mod.rs index b2503bf..15f0a81 100644 --- a/src/language/mod.rs +++ b/src/language/mod.rs @@ -46,4 +46,6 @@ pub enum LanguageError { DuplicateLanguage(String), #[error("Name '{0}' is not a known language")] UndefinedLanguage(String), + #[error("Name '{1}' is not a known notation set for language '{0}'")] + UndefinedNotationSet(String, String), } diff --git a/src/language/specs.rs b/src/language/specs.rs index 0e144f6..90909ef 100644 --- a/src/language/specs.rs +++ b/src/language/specs.rs @@ -1,7 +1,8 @@ use crate::style::Notation; +use serde::{Deserialize, Serialize}; /// A kind of node that can appear in a document. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConstructSpec { pub name: String, pub arity: AritySpec, @@ -11,11 +12,11 @@ pub struct ConstructSpec { } /// A set of constructs. Can both include and be included by other sorts. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)] pub struct SortSpec(pub Vec); /// The sorts of children that a node is allowed to contain. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum AritySpec { /// Designates a pure text node. Texty, @@ -29,7 +30,7 @@ pub enum AritySpec { /// Describes the structure of a language, e.g. which constructs can appear /// in which positions. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct GrammarSpec { pub constructs: Vec, pub sorts: Vec<(String, SortSpec)>, diff --git a/src/language/storage.rs b/src/language/storage.rs index 55c3859..90afe60 100644 --- a/src/language/storage.rs +++ b/src/language/storage.rs @@ -1,4 +1,4 @@ -use super::compiled::{compile_language, compile_notation_set, LanguageCompiled}; +use super::compiled::{compile_language, LanguageCompiled}; use super::interface::Language; use super::specs::{LanguageSpec, NotationSetSpec}; use super::LanguageError; @@ -6,6 +6,7 @@ use crate::tree::NodeForest; use crate::util::IndexedMap; /// Stores all documents and languages. +#[derive(Debug)] pub struct Storage { pub(super) languages: IndexedMap, pub(crate) node_forest: NodeForest, @@ -26,24 +27,12 @@ impl Storage { .map_err(LanguageError::DuplicateLanguage) } - pub fn add_notation_set( - &mut self, - language_name: &str, - notation_set: NotationSetSpec, - ) -> Result<(), LanguageError> { - if let Some(language) = self.languages.get_by_name_mut(language_name) { - let notation_set = compile_notation_set(notation_set, &language.grammar)?; - language - .notation_sets - .insert(notation_set.name.clone(), notation_set) - .map_err(|name| LanguageError::DuplicateNotationSet(language_name.to_owned(), name)) - } else { - Err(LanguageError::UndefinedLanguage(language_name.to_owned())) - } - } - - pub fn get_language(&self, name: &str) -> Option { - Some(Language::from_id(self.languages.id(name)?)) + pub fn language(&self, name: &str) -> Result { + let language_id = self + .languages + .id(name) + .ok_or_else(|| LanguageError::UndefinedLanguage(name.to_owned()))?; + Ok(Language::from_id(language_id)) } } diff --git a/src/pretty_doc.rs b/src/pretty_doc.rs index 2c1911f..9a6bf64 100644 --- a/src/pretty_doc.rs +++ b/src/pretty_doc.rs @@ -9,14 +9,6 @@ use partial_pretty_printer as ppp; use std::fmt; use std::sync::OnceLock; -fn get_text_notation() -> &'static ValidNotation { - static TEXT_NOTATION: OnceLock = OnceLock::new(); - TEXT_NOTATION.get_or_init(|| { - let notation = ppp::Notation::Text; - notation.validate().bug() - }) -} - #[derive(thiserror::Error, Debug)] pub enum PrettyDocError { #[error("No source notation available for language '{0}'")] @@ -28,58 +20,45 @@ pub struct DocRef<'d> { storage: &'d Storage, cursor_loc: Location, node: Node, - text_pos: Option, + use_source_notation: bool, } impl<'d> DocRef<'d> { - pub fn new(storage: &'d Storage, cursor_loc: Location, node: Node) -> DocRef<'d> { + pub fn new( + storage: &'d Storage, + cursor_loc: Location, + node: Node, + use_source_notation: bool, + ) -> DocRef<'d> { DocRef { storage, cursor_loc, node, - text_pos: None, - } - } - - fn on_virtual_text_parent(&self) -> bool { - self.node.text(self.storage).is_some() && self.text_pos.is_none() - } - - /// Char index to split this node's text at - fn text_index(&self) -> usize { - if let Some((node, char_pos)) = self.cursor_loc.text_pos() { - if node == self.node { - char_pos - } else { - 0 - } - } else { - 0 + use_source_notation, } } } impl<'d> ppp::PrettyDoc<'d> for DocRef<'d> { - type Id = (NodeId, u16); + type Id = NodeId; type Style = Style; type StyleLabel = StyleLabel; type Condition = Condition; type Error = PrettyDocError; - fn id(self) -> Result<(NodeId, u16), Self::Error> { - let id = self.node.id(self.storage); - Ok(match self.text_pos { - None => (id, 0), - Some(CursorHalf::Left) => (id, 1), - Some(CursorHalf::Right) => (id, 2), - }) + fn id(self) -> Result { + Ok(self.node.id(self.storage)) } fn notation(self) -> Result<&'d ValidNotation, Self::Error> { - Ok(match self.text_pos { - None => self.node.notation(self.storage), - Some(_) => get_text_notation(), - }) + if self.use_source_notation { + self.node.source_notation(self.storage).ok_or_else(|| { + let lang = self.node.language(self.storage); + PrettyDocError::NoSourceNotation(lang.name(self.storage).to_owned()) + }) + } else { + Ok(self.node.display_notation(self.storage)) + } } fn condition(self, condition: &Condition) -> Result { @@ -145,93 +124,51 @@ impl<'d> ppp::PrettyDoc<'d> for DocRef<'d> { } fn node_style(self) -> Result { - Ok(if self.text_pos.is_some() { - Style::default() - } else if self.cursor_loc.left_node(self.storage) == Some(self.node) { + let style = if self.cursor_loc.left_node(self.storage) == Some(self.node) { LEFT_CURSOR_STYLE } else if self.cursor_loc.right_node(self.storage) == Some(self.node) { RIGHT_CURSOR_STYLE } else { Style::default() - }) + }; + Ok(style) } fn num_children(self) -> Result, Self::Error> { - Ok(if self.on_virtual_text_parent() { - Some(2) - } else { - self.node.num_children(self.storage) - }) + Ok(self.node.num_children(self.storage)) } fn unwrap_text(self) -> Result<&'d str, Self::Error> { - let text = &self.node.text(self.storage).bug(); - Ok(match self.text_pos { - None => bug!("DocRef::unwrap_text non-virtual text"), - Some(CursorHalf::Left) => text.as_split_str(self.text_index()).0, - Some(CursorHalf::Right) => text.as_split_str(self.text_index()).1, - }) + Ok(self.node.text(self.storage).bug().as_str()) } fn unwrap_child(self, i: usize) -> Result { - if self.on_virtual_text_parent() { - let cursor_half = match i { - 0 => CursorHalf::Left, - 1 => CursorHalf::Right, - _ => bug!("DocRef::unwrap_child virtual text child OOB"), - }; - Ok(DocRef { - text_pos: Some(cursor_half), - ..self - }) - } else { - let child = self.node.nth_child(self.storage, i).bug(); - Ok(DocRef { - node: child, - ..self - }) - } + let child = self.node.nth_child(self.storage, i).bug(); + Ok(DocRef { + node: child, + ..self + }) } fn unwrap_last_child(self) -> Result { - if self.on_virtual_text_parent() { - Ok(DocRef { - text_pos: Some(CursorHalf::Right), - ..self - }) - } else { - let last_child = self.node.last_child(self.storage).bug(); - Ok(DocRef { - node: last_child, - ..self - }) - } + let last_child = self.node.last_child(self.storage).bug(); + Ok(DocRef { + node: last_child, + ..self + }) } fn unwrap_prev_sibling(self, _: Self, _: usize) -> Result { - Ok(match self.text_pos { - Some(CursorHalf::Left) => bug!("unwrap_prev_sibling: virtual text OOB"), - Some(CursorHalf::Right) => DocRef { - text_pos: Some(CursorHalf::Left), - ..self - }, - None => { - let sibling = self.node.prev_sibling(self.storage).bug(); - DocRef { - node: sibling, - ..self - } - } + let sibling = self.node.prev_sibling(self.storage).bug(); + Ok(DocRef { + node: sibling, + ..self }) } } impl<'d> fmt::Debug for DocRef<'d> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "DocRef({:?}, {:?}, {:?})", - self.node, self.cursor_loc, self.text_pos - ) + write!(f, "DocRef({:?}, {:?})", self.node, self.cursor_loc) } } diff --git a/src/style.rs b/src/style.rs index 0b87c62..b9b4726 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,5 +1,6 @@ use crate::util::SynlessBug; use partial_pretty_printer as ppp; +use serde::{Deserialize, Serialize}; pub const HOLE_STYLE: Style = Style { is_hole: true, @@ -42,7 +43,7 @@ pub struct Style { pub is_hole: bool, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum Priority { High, Low, @@ -54,7 +55,7 @@ pub enum CursorHalf { Right, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum StyleLabel { Open, Close, @@ -69,7 +70,7 @@ pub enum StyleLabel { } // TODO: doc -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum Condition { IsEmptyText, IsCommentOrWs, @@ -87,7 +88,7 @@ pub struct Rgb { pub blue: u8, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum Base16Color { /// Default Background Base00, diff --git a/src/tree/forest.rs b/src/tree/forest.rs index 978b062..6be389a 100644 --- a/src/tree/forest.rs +++ b/src/tree/forest.rs @@ -22,6 +22,7 @@ pub type NodeIndex = generational_arena::Index; /// - Preventing "use after free" (see the note on deletion above). /// Along the same lines, preventing cycles at compile time. /// - Removing the need to pass the `Forest` in to every method call. +#[derive(Debug)] pub struct Forest { // TODO: Try making roots linked in a cycle internally arena: Arena>, diff --git a/src/tree/location.rs b/src/tree/location.rs index 913b315..6327b45 100644 --- a/src/tree/location.rs +++ b/src/tree/location.rs @@ -1,6 +1,7 @@ use super::node::Node; use crate::language::{Arity, Storage}; use crate::util::{bug, SynlessBug}; +use partial_pretty_printer as ppp; // The node in this LocationInner may not be valid (may have been deleted!) #[derive(Debug, Clone, Copy)] @@ -111,6 +112,31 @@ impl Location { } } + /// Find a path from the root node to a node near this location, together with + /// a `FocusTarget` specifying where this location is relative to that node. + pub fn path_from_root(self, s: &Storage) -> (Vec, ppp::FocusTarget) { + use LocationInner::*; + + let mut path_to_root = Vec::new(); + let (mut node, target) = match self.0 { + BeforeNode(node) => (node, ppp::FocusTarget::Start), + AfterNode(node) => (node, ppp::FocusTarget::End), + // NOTE: This relies on the node's notation containing a `Notation::FocusMark`. + BelowNode(node) => (node, ppp::FocusTarget::Mark), + InText(node, char_pos) => (node, ppp::FocusTarget::Text(char_pos)), + }; + while let Some(parent) = node.parent(s) { + path_to_root.push(node.sibling_index(s)); + node = parent; + } + let path_from_root = { + let mut path = path_to_root; + path.reverse(); + path + }; + (path_from_root, target) + } + /************** * Navigation * **************/ diff --git a/src/tree/node.rs b/src/tree/node.rs index 12072cb..5f55611 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -9,6 +9,7 @@ use std::fmt; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct NodeId(usize); +#[derive(Debug)] pub struct NodeForest { forest: forest::Forest, next_id: usize, @@ -167,8 +168,12 @@ impl Node { s.forest().data(self.0).construct.is_comment_or_ws(s) } - pub fn notation(self, s: &Storage) -> &ValidNotation { - s.forest().data(self.0).construct.notation(s) + pub fn display_notation(self, s: &Storage) -> &ValidNotation { + s.forest().data(self.0).construct.display_notation(s) + } + + pub fn source_notation(self, s: &Storage) -> Option<&ValidNotation> { + s.forest().data(self.0).construct.source_notation(s) } pub fn is_texty(self, s: &Storage) -> bool { @@ -401,7 +406,7 @@ impl Node { let mut clone_data = |data: &NodeData| -> NodeData { NodeData { id: inc_id(next_id), - construct: data.construct.clone(), + construct: data.construct, text: data.text.clone(), } }; diff --git a/tests/tests.rs b/tests/tests.rs index 5ba64d3..bddc022 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -7,7 +7,7 @@ use synless::{ // e.g. example.com?p1=v1,p2=v2,p3,p4=v4 fn urllang() -> LanguageSpec { use ppp::notation_constructors::{ - child, count, empty, fold, indent, left, lit, nl, right, Count, Fold, + child, count, empty, fold, indent, left, lit, nl, right, text, Count, Fold, }; LanguageSpec { @@ -54,7 +54,7 @@ fn urllang() -> LanguageSpec { default_notation_set: NotationSetSpec { name: "Testlang_notation".to_owned(), notations: vec![ - ("String".to_owned(), child(0) + child(1)), + ("String".to_owned(), text()), ("Equals".to_owned(), child(0) + lit("=") + child(1)), ("Url".to_owned(), child(0) + child(1)), ( @@ -84,7 +84,7 @@ fn urllang() -> LanguageSpec { } fn node_with_text(s: &mut Storage, lang_name: &str, construct_name: &str, text: &str) -> Node { - let lang = s.get_language(lang_name).unwrap(); + let lang = s.language(lang_name).unwrap(); let construct = lang.get_construct(s, construct_name).unwrap(); Node::with_text(s, construct, text.to_owned()).unwrap() } @@ -95,7 +95,7 @@ fn node_with_children( construct_name: &str, children: impl IntoIterator, ) -> Node { - let lang = s.get_language(lang_name).unwrap(); + let lang = s.language(lang_name).unwrap(); let construct = lang.get_construct(s, construct_name).unwrap(); Node::with_children(s, construct, children).unwrap() } @@ -117,7 +117,7 @@ fn test_doc_ref() { let params = node_with_children(&mut s, "urllang", "Params", [eq_1, eq_2, done]); let url = node_with_children(&mut s, "urllang", "Url", [domain, params]); - let doc_ref = DocRef::new(&s, Location::after(&s, url), url); + let doc_ref = DocRef::new(&s, Location::after(&s, url), url, false); let actual = match ppp::pretty_print_to_string(doc_ref, 80) { Ok(actual) => actual, From 2769962d29edb4c535c3873ddf5ff653fc972c3d Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 4 Apr 2024 13:03:29 -0400 Subject: [PATCH 2/2] minor: PR review --- src/engine/doc.rs | 4 ++-- src/engine/doc_set.rs | 4 ++-- src/engine/engine.rs | 24 +++++++++++++++--------- src/engine/mod.rs | 8 ++++---- src/language/compiled.rs | 2 +- src/language/interface.rs | 8 ++++---- src/language/specs.rs | 2 +- src/pretty_doc.rs | 35 ++++++++++++++++++++--------------- tests/tests.rs | 4 ++-- 9 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/engine/doc.rs b/src/engine/doc.rs index 239a51e..2388129 100644 --- a/src/engine/doc.rs +++ b/src/engine/doc.rs @@ -64,11 +64,11 @@ impl Doc { } pub fn doc_ref_source<'d>(&self, s: &'d Storage) -> DocRef<'d> { - DocRef::new(s, self.cursor, self.cursor.root_node(s), true) + DocRef::new_source(s, self.cursor, self.cursor.root_node(s)) } pub fn doc_ref_display<'d>(&self, s: &'d Storage) -> DocRef<'d> { - DocRef::new(s, self.cursor, self.cursor.root_node(s), false) + DocRef::new_display(s, self.cursor, self.cursor.root_node(s)) } pub fn cursor(&self) -> Location { diff --git a/src/engine/doc_set.rs b/src/engine/doc_set.rs index 674f0e3..74c79e9 100644 --- a/src/engine/doc_set.rs +++ b/src/engine/doc_set.rs @@ -43,7 +43,7 @@ type DocIndex = usize; /// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum DocLabel { - /// A "real" document that the user is viewing and editing. + /// The "real" document that the user is viewing and editing. Visible, /// An auto-generated doc containing info about the `Visible` doc, for use in a status bar. Metadata(String), @@ -123,7 +123,7 @@ impl DocSet { focus_path, focus_target, focus_height: settings.focus_height, - width_strategy: pane::WidthStrategy::NoMoreThan(settings.max_doc_width), + width_strategy: pane::WidthStrategy::NoMoreThan(settings.max_display_width), set_focus: true, }; (doc, options) diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 3ef041d..9c3ee59 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -45,27 +45,33 @@ impl Engine { Ok(()) } + pub fn add_notation( + &mut self, + language_name: &str, + notation: NotationSetSpec, + ) -> Result<(), EngineError> { + let lang = self.storage.language(language_name)?; + lang.add_notation(&mut self.storage, notation)?; + Ok(()) + } + pub fn set_display_notation( &mut self, language_name: &str, - notation_set: NotationSetSpec, + notation_name: &str, ) -> Result<(), EngineError> { - let notation_set_name = notation_set.name.clone(); let lang = self.storage.language(language_name)?; - lang.add_notation(&mut self.storage, notation_set)?; - lang.set_display_notation(&mut self.storage, ¬ation_set_name)?; + lang.set_display_notation(&mut self.storage, notation_name)?; Ok(()) } pub fn set_source_notation( &mut self, language_name: &str, - notation_set: NotationSetSpec, + notation_name: &str, ) -> Result<(), EngineError> { - let notation_set_name = notation_set.name.clone(); let lang = self.storage.language(language_name)?; - lang.add_notation(&mut self.storage, notation_set)?; - lang.set_source_notation(&mut self.storage, ¬ation_set_name)?; + lang.set_source_notation(&mut self.storage, notation_name)?; Ok(()) } @@ -93,7 +99,7 @@ impl Engine { .file_doc(doc_path) .ok_or_else(|| EngineError::DocNotFound(doc_path.to_string_lossy().into_owned()))?; let doc_ref = doc.doc_ref_source(&self.storage); - let source = ppp::pretty_print_to_string(doc_ref, self.settings.source_width)?; + let source = ppp::pretty_print_to_string(doc_ref, self.settings.max_source_width)?; Ok(source) } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index a1c78eb..c4fd662 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -7,16 +7,16 @@ use partial_pretty_printer as ppp; #[derive(Debug, Clone)] pub struct Settings { - source_width: ppp::Width, - max_doc_width: ppp::Width, + max_source_width: ppp::Width, + max_display_width: ppp::Width, focus_height: f32, } impl Settings { fn default() -> Settings { Settings { - source_width: 100, - max_doc_width: 80, + max_source_width: 100, + max_display_width: 120, focus_height: 0.5, } } diff --git a/src/language/compiled.rs b/src/language/compiled.rs index fdef96e..8eed71d 100644 --- a/src/language/compiled.rs +++ b/src/language/compiled.rs @@ -68,7 +68,7 @@ pub struct NotationSetCompiled { pub fn compile_language(language_spec: LanguageSpec) -> Result { let grammar = language_spec.grammar.compile()?; - let notation_set = compile_notation_set(language_spec.default_notation_set, &grammar)?; + let notation_set = compile_notation_set(language_spec.default_notation, &grammar)?; let mut notation_sets = IndexedMap::new(); notation_sets .insert(notation_set.name.to_owned(), notation_set) diff --git a/src/language/interface.rs b/src/language/interface.rs index f59f7b2..39bd2e3 100644 --- a/src/language/interface.rs +++ b/src/language/interface.rs @@ -98,7 +98,7 @@ impl Language { }) } - pub fn notation_set_names(self, s: &Storage) -> impl ExactSizeIterator + '_ { + pub fn notation_names(self, s: &Storage) -> impl ExactSizeIterator + '_ { s.languages[self.language].notation_sets.names() } @@ -158,7 +158,7 @@ impl Language { s: &mut Storage, notation_set_name: &str, ) -> Result<(), LanguageError> { - let notation_set_id = self.notation_set_id(s, notation_set_name)?; + let notation_set_id = self.notation_id(s, notation_set_name)?; s.languages[self.language].display_notation = notation_set_id; Ok(()) } @@ -168,7 +168,7 @@ impl Language { s: &mut Storage, notation_set_name: &str, ) -> Result<(), LanguageError> { - let notation_set_id = self.notation_set_id(s, notation_set_name)?; + let notation_set_id = self.notation_id(s, notation_set_name)?; s.languages[self.language].source_notation = Some(notation_set_id); Ok(()) } @@ -178,7 +178,7 @@ impl Language { Ok(()) } - fn notation_set_id(self, s: &Storage, notation_set_name: &str) -> Result { + fn notation_id(self, s: &Storage, notation_set_name: &str) -> Result { if let Some(id) = s.languages[self.language] .notation_sets .id(notation_set_name) diff --git a/src/language/specs.rs b/src/language/specs.rs index 90909ef..2251a0d 100644 --- a/src/language/specs.rs +++ b/src/language/specs.rs @@ -52,5 +52,5 @@ pub struct NotationSetSpec { pub struct LanguageSpec { pub name: String, pub grammar: GrammarSpec, - pub default_notation_set: NotationSetSpec, + pub default_notation: NotationSetSpec, } diff --git a/src/pretty_doc.rs b/src/pretty_doc.rs index 9a6bf64..3a095a2 100644 --- a/src/pretty_doc.rs +++ b/src/pretty_doc.rs @@ -24,17 +24,21 @@ pub struct DocRef<'d> { } impl<'d> DocRef<'d> { - pub fn new( - storage: &'d Storage, - cursor_loc: Location, - node: Node, - use_source_notation: bool, - ) -> DocRef<'d> { + pub fn new_display(storage: &'d Storage, cursor_loc: Location, node: Node) -> DocRef<'d> { DocRef { storage, cursor_loc, node, - use_source_notation, + use_source_notation: false, + } + } + + pub fn new_source(storage: &'d Storage, cursor_loc: Location, node: Node) -> DocRef<'d> { + DocRef { + storage, + cursor_loc, + node, + use_source_notation: true, } } } @@ -142,26 +146,23 @@ impl<'d> ppp::PrettyDoc<'d> for DocRef<'d> { Ok(self.node.text(self.storage).bug().as_str()) } - fn unwrap_child(self, i: usize) -> Result { - let child = self.node.nth_child(self.storage, i).bug(); + fn unwrap_child(self, n: usize) -> Result { Ok(DocRef { - node: child, + node: self.node.nth_child(self.storage, n).bug(), ..self }) } fn unwrap_last_child(self) -> Result { - let last_child = self.node.last_child(self.storage).bug(); Ok(DocRef { - node: last_child, + node: self.node.last_child(self.storage).bug(), ..self }) } fn unwrap_prev_sibling(self, _: Self, _: usize) -> Result { - let sibling = self.node.prev_sibling(self.storage).bug(); Ok(DocRef { - node: sibling, + node: self.node.prev_sibling(self.storage).bug(), ..self }) } @@ -169,6 +170,10 @@ impl<'d> ppp::PrettyDoc<'d> for DocRef<'d> { impl<'d> fmt::Debug for DocRef<'d> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DocRef({:?}, {:?})", self.node, self.cursor_loc) + write!( + f, + "DocRef({:?}, {:?}, {})", + self.node, self.cursor_loc, self.use_source_notation + ) } } diff --git a/tests/tests.rs b/tests/tests.rs index bddc022..6618125 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -51,7 +51,7 @@ fn urllang() -> LanguageSpec { )], root_sort: SortSpec(vec!["Url".to_owned()]), }, - default_notation_set: NotationSetSpec { + default_notation: NotationSetSpec { name: "Testlang_notation".to_owned(), notations: vec![ ("String".to_owned(), text()), @@ -117,7 +117,7 @@ fn test_doc_ref() { let params = node_with_children(&mut s, "urllang", "Params", [eq_1, eq_2, done]); let url = node_with_children(&mut s, "urllang", "Url", [domain, params]); - let doc_ref = DocRef::new(&s, Location::after(&s, url), url, false); + let doc_ref = DocRef::new_display(&s, Location::after(&s, url), url); let actual = match ppp::pretty_print_to_string(doc_ref, 80) { Ok(actual) => actual,