Skip to content

Commit

Permalink
Implement PrettyDoc (mostly)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpombrio committed Mar 14, 2024
1 parent e708740 commit 2917d7f
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 33 deletions.
78 changes: 58 additions & 20 deletions src/language/ast.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use super::forest::{Forest, NodeIndex};
use super::language_set::{
Arity, Construct, DocCondition, Language, LanguageSet, StyleLabel, ValidNotation,
};
use super::language_set::{Arity, Construct, Language, LanguageSet};
use super::text::Text;
use crate::infra::{bug, SynlessBug};
use crate::style::{Condition, StyleLabel, ValidNotation};
use partial_pretty_printer as ppp;
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct AstId(usize);
pub struct AstId(usize);

struct AstNode {
id: AstId,
Expand All @@ -22,12 +21,19 @@ pub struct DocStorage {
next_id: AstId,
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ast(NodeIndex);

// TODO: doc
pub struct Bookmark(NodeIndex);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Location {
InText(Ast, usize),
After(Ast),
BeforeFirstChild(Ast),
}

impl DocStorage {
fn next_id(&mut self) -> AstId {
let id = self.next_id.0;
Expand All @@ -36,6 +42,17 @@ impl DocStorage {
}
}

impl Location {
pub fn cursor_halves(self, s: &DocStorage) -> (Option<Ast>, Option<Ast>) {
match self {
Location::InText(..) => (None, None),
Location::After(left_sibling) => (Some(left_sibling), left_sibling.next_sibling(s)),
Location::BeforeFirstChild(parent) => (None, parent.first_child(s)),
}
}
}

// TODO: put these methods in any order whatsoever
impl Ast {
pub fn new_hole(s: &mut DocStorage, lang: Language) -> Ast {
Ast::new(s, lang.hole_construct(&s.language_set))
Expand Down Expand Up @@ -76,6 +93,10 @@ impl Ast {
}
}

pub fn id(self, s: &DocStorage) -> AstId {
s.forest.data(self.0).id
}

/// Determine the number of siblings that this node has, including itself.
pub fn num_siblings(&self, s: &DocStorage) -> usize {
if let Some(parent) = s.forest.parent(self.0) {
Expand All @@ -98,15 +119,29 @@ impl Ast {

/// Return the number of children this node has. For a Fixed node, this is
/// its arity. For a Listy node, this is its current number of children.
/// For text, this is considered 0.
fn num_children(self, s: &DocStorage) -> usize {
s.forest.num_children(self.0)
/// For text, this is None.
pub fn num_children(self, s: &DocStorage) -> Option<usize> {
if s.forest.data(self.0).text.is_some() {
None
} else {
Some(s.forest.num_children(self.0))
}
}

pub fn parent(self, s: &DocStorage) -> Option<Ast> {
s.forest.parent(self.0).map(Ast)
}

pub fn first_child(self, s: &DocStorage) -> Option<Ast> {
s.forest.first_child(self.0).map(Ast)
}

pub fn last_child(self, s: &DocStorage) -> Option<Ast> {
s.forest
.first_child(self.0)
.map(|n| Ast(s.forest.last_sibling(n)))
}

pub fn next_sibling(self, s: &DocStorage) -> Option<Ast> {
s.forest.next(self.0).map(Ast)
}
Expand Down Expand Up @@ -183,22 +218,25 @@ impl Ast {
s.forest.data(self.0).construct.arity(&s.language_set)
}

/// Borrow the text of a texty node. Panics if not texty.
pub fn text(self, s: &DocStorage) -> &Text {
pub fn is_comment_or_ws(self, s: &DocStorage) -> bool {
s.forest
.data(self.0)
.text
.as_ref()
.bug_msg("Ast::text() - not texty")
.construct
.is_comment_or_ws(&s.language_set)
}

/// Mutably borrow the text of a texty node. Panics if not texty.
pub fn text_mut(self, s: &mut DocStorage) -> &mut Text {
s.forest
.data_mut(self.0)
.text
.as_mut()
.bug_msg("Ast::text_mut() - not texty")
pub fn notation(self, s: &DocStorage) -> &ValidNotation {
s.forest.data(self.0).construct.notation(&s.language_set)
}

/// Borrow the text of a texty node.
pub fn text(self, s: &DocStorage) -> Option<&Text> {
s.forest.data(self.0).text.as_ref()
}

/// Mutably borrow the text of a texty node.
pub fn text_mut(self, s: &mut DocStorage) -> Option<&mut Text> {
s.forest.data_mut(self.0).text.as_mut()
}

/// Go to this node's `n`'th child.
Expand Down
3 changes: 1 addition & 2 deletions src/language/forest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,12 +778,11 @@ mod forest_tests {
}

#[test]
#[should_panic(expected = "Forest - can't swap overlapping nodes")]
fn test_swap_cycle() {
let mut f = Forest::<u32>::new(0);
let parent = f.new_node(0);
let child = f.new_node(0);
f.insert_first_child(parent, child);
f.swap(parent, child);
assert!(!f.swap(parent, child));
}
}
15 changes: 10 additions & 5 deletions src/language/language_set.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::LanguageError;
use crate::infra::bug;
use crate::style::ValidNotation;
use bit_set::BitSet;
use partial_pretty_printer as ppp;
use std::collections::HashMap;
Expand All @@ -23,6 +24,7 @@ const HOLE_NAME: &str = "$hole";
pub struct ConstructSpec {
pub name: String,
pub arity: AritySpec,
pub is_comment_or_ws: bool,
// TODO: https://github.com/justinpombrio/synless/issues/88
pub key: Option<char>,
}
Expand Down Expand Up @@ -67,6 +69,7 @@ type NotationSetId = usize;
struct ConstructCompiled {
name: String,
arity: ArityCompiled,
is_comment_or_ws: bool,
key: Option<char>,
}

Expand Down Expand Up @@ -94,6 +97,7 @@ struct GrammarCompiled {
keymap: HashMap<char, ConstructId>,
}

// TODO: need to be able to add a NotationSet! It should inject notation for holes.
struct LanguageCompiled {
grammar: GrammarCompiled,
notation_sets_by_name: HashMap<String, NotationSetId>,
Expand All @@ -111,11 +115,6 @@ struct NotationSetCompiled {
* Public Interface *
********************************************/

// TODO: These will need more data, and should be moved somewhere.
pub type StyleLabel = u32;
pub type DocCondition = ();
pub type ValidNotation = ppp::ValidNotation<StyleLabel, DocCondition>;

/// The (unique) collection of all loaded [`Language`]s.
pub struct LanguageSet {
languages_by_name: HashMap<String, LanguageId>,
Expand Down Expand Up @@ -301,6 +300,10 @@ impl Construct {
pub fn notation(self, l: &LanguageSet) -> &ValidNotation {
self.language().current_notation_set(l).notation(l, self)
}

pub fn is_comment_or_ws(self, l: &LanguageSet) -> bool {
l.grammar(self.language).constructs[self.construct].is_comment_or_ws
}
}

impl FixedSorts {
Expand Down Expand Up @@ -408,6 +411,7 @@ impl GrammarBuilder {
self.add_construct(ConstructSpec {
name: HOLE_NAME.to_owned(),
arity: AritySpec::Fixed(Vec::new()),
is_comment_or_ws: false,
key: None,
})
}
Expand Down Expand Up @@ -501,6 +505,7 @@ impl GrammarBuilder {
grammar.constructs.push(ConstructCompiled {
name: construct.name.clone(),
arity,
is_comment_or_ws: construct.is_comment_or_ws,
key: construct.key,
});
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions src/language/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod text;

use std::fmt;

pub use ast::{Ast, AstId, DocStorage, Location};

#[derive(thiserror::Error, fmt::Debug)]
pub enum LanguageError {
#[error("Missing notation for construct '{0}'")]
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

mod infra;
mod language;
mod pretty_doc;
mod style;

fn main() {
Expand Down
158 changes: 158 additions & 0 deletions src/pretty_doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use crate::infra::SynlessBug;
use crate::language::{Ast, AstId, DocStorage, Location};
use crate::style::{Condition, CursorHalf, Style, StyleLabel, ValidNotation};
use partial_pretty_printer as ppp;
use std::fmt;

// TODO: handle text char highlighting

#[derive(Clone, Copy)]
pub struct DocRef<'d> {
storage: &'d DocStorage,
cursor_pos: Location,
left_cursor: Option<Ast>,
right_cursor: Option<Ast>,
ast: Ast,
}

impl<'d> DocRef<'d> {
pub fn new(storage: &'d DocStorage, cursor_pos: Location, ast: Ast) -> DocRef<'d> {
let (left_cursor, right_cursor) = cursor_pos.cursor_halves(storage);
DocRef {
storage,
cursor_pos,
left_cursor,
right_cursor,
ast,
}
}

fn with_ast(self, ast: Ast) -> Self {
DocRef {
storage: self.storage,
cursor_pos: self.cursor_pos,
left_cursor: self.left_cursor,
right_cursor: self.right_cursor,
ast,
}
}
}

impl<'d> ppp::PrettyDoc<'d> for DocRef<'d> {
type Id = AstId;
type Style = Style;
type StyleLabel = StyleLabel;
type Condition = Condition;

fn id(self) -> AstId {
self.ast.id(self.storage)
}

fn notation(self) -> &'d ValidNotation {
self.ast.notation(self.storage)
}

fn condition(self, condition: &Condition) -> bool {
match condition {
Condition::IsEmptyText => self
.ast
.text(self.storage)
.map(|text| text.as_str().is_empty())
.unwrap_or(false),
Condition::IsCommentOrWs => self.ast.is_comment_or_ws(self.storage),
Condition::NeedsSeparator => {
if self.ast.is_comment_or_ws(self.storage) {
return false;
}
let mut sibling = self.ast;
while let Some(next_sibling) = sibling.next_sibling(self.storage) {
if !next_sibling.is_comment_or_ws(self.storage) {
return true;
}
sibling = next_sibling;
}
false
}
}
}

fn lookup_style(self, style_label: StyleLabel) -> Style {
match style_label {
StyleLabel::Open => {
let mut style = Style::default();
if self.cursor_pos == Location::BeforeFirstChild(self.ast) {
style.cursor = Some(CursorHalf::Left)
}
style
}
StyleLabel::Close => {
let mut style = Style::default();
let at_end = match self.cursor_pos {
Location::InText(..) => todo!(),
Location::BeforeFirstChild(parent) => {
parent == self.ast && self.ast.first_child(self.storage).is_none()
}
Location::After(sibling) => self.ast.last_child(self.storage) == Some(sibling),
};
// TODO: perhaps rewrite as:
// if self.ast.gap_after_children(self.storage) == self.cursor_pos
if at_end {
style.cursor = Some(CursorHalf::Right)
}
style
}
StyleLabel::Properties {
color,
bold,
italic,
underlined,
priority,
} => Style {
color: color.map(|x| (x, priority)),
bold: bold.map(|x| (x, priority)),
italic: italic.map(|x| (x, priority)),
underlined: underlined.map(|x| (x, priority)),
cursor: None,
},
}
}

fn node_style(self) -> Style {
let mut style = Style::default();
if self.left_cursor == Some(self.ast) {
style.cursor = Some(CursorHalf::Left);
} else if self.right_cursor == Some(self.ast) {
style.cursor = Some(CursorHalf::Right);
}
style
}

fn num_children(self) -> Option<usize> {
self.ast.num_children(self.storage)
}

fn unwrap_text(self) -> &'d str {
self.ast.text(self.storage).bug().as_str()
}

fn unwrap_child(self, i: usize) -> Self {
let child = self.ast.nth_child(self.storage, i);
self.with_ast(child)
}

fn unwrap_last_child(self) -> Self {
let last_child = self.ast.last_child(self.storage).bug();
self.with_ast(last_child)
}

fn unwrap_prev_sibling(self, _: Self, _: usize) -> Self {
let sibling = self.ast.prev_sibling(self.storage).bug();
self.with_ast(sibling)
}
}

impl<'d> fmt::Debug for DocRef<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DocRef({:?}, {:?})", self.ast, self.cursor_pos)
}
}
Loading

0 comments on commit 2917d7f

Please sign in to comment.