Remove green/red distinction
This commit is contained in:
parent
665ed12825
commit
08a6188123
@ -73,7 +73,7 @@ fn bench_highlight(iai: &mut Iai) {
|
||||
let source = ctx.sources.get(id);
|
||||
iai.run(|| {
|
||||
typst::syntax::highlight_node(
|
||||
source.red().as_ref(),
|
||||
source.root(),
|
||||
0 .. source.len_bytes(),
|
||||
&mut |_, _| {},
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{Scope, Scopes, Value};
|
||||
use crate::syntax::ast::{ClosureParam, Expr, Ident, Imports, TypedNode};
|
||||
use crate::syntax::RedRef;
|
||||
use crate::syntax::SyntaxNode;
|
||||
|
||||
/// A visitor that captures variable slots.
|
||||
pub struct CapturesVisitor<'a> {
|
||||
@ -39,7 +39,7 @@ impl<'a> CapturesVisitor<'a> {
|
||||
}
|
||||
|
||||
/// Visit any node and collect all captured variables.
|
||||
pub fn visit(&mut self, node: RedRef) {
|
||||
pub fn visit(&mut self, node: &SyntaxNode) {
|
||||
match node.cast() {
|
||||
// Every identifier is a potential variable that we need to capture.
|
||||
// Identifiers that shouldn't count as captures because they
|
||||
@ -62,7 +62,7 @@ impl<'a> CapturesVisitor<'a> {
|
||||
Some(Expr::Closure(expr)) => {
|
||||
for param in expr.params() {
|
||||
if let ClosureParam::Named(named) = param {
|
||||
self.visit(named.expr().as_red());
|
||||
self.visit(named.expr().as_untyped());
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,14 +74,14 @@ impl<'a> CapturesVisitor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.visit(expr.body().as_red());
|
||||
self.visit(expr.body().as_untyped());
|
||||
}
|
||||
|
||||
// A let expression contains a binding, but that binding is only
|
||||
// active after the body is evaluated.
|
||||
Some(Expr::Let(expr)) => {
|
||||
if let Some(init) = expr.init() {
|
||||
self.visit(init.as_red());
|
||||
self.visit(init.as_untyped());
|
||||
}
|
||||
self.bind(expr.binding());
|
||||
}
|
||||
@ -89,30 +89,30 @@ impl<'a> CapturesVisitor<'a> {
|
||||
// A show rule contains a binding, but that binding is only active
|
||||
// after the target has been evaluated.
|
||||
Some(Expr::Show(show)) => {
|
||||
self.visit(show.pattern().as_red());
|
||||
self.visit(show.pattern().as_untyped());
|
||||
if let Some(binding) = show.binding() {
|
||||
self.bind(binding);
|
||||
}
|
||||
self.visit(show.body().as_red());
|
||||
self.visit(show.body().as_untyped());
|
||||
}
|
||||
|
||||
// A for loop contains one or two bindings in its pattern. These are
|
||||
// active after the iterable is evaluated but before the body is
|
||||
// evaluated.
|
||||
Some(Expr::For(expr)) => {
|
||||
self.visit(expr.iter().as_red());
|
||||
self.visit(expr.iter().as_untyped());
|
||||
let pattern = expr.pattern();
|
||||
if let Some(key) = pattern.key() {
|
||||
self.bind(key);
|
||||
}
|
||||
self.bind(pattern.value());
|
||||
self.visit(expr.body().as_red());
|
||||
self.visit(expr.body().as_untyped());
|
||||
}
|
||||
|
||||
// An import contains items, but these are active only after the
|
||||
// path is evaluated.
|
||||
Some(Expr::Import(expr)) => {
|
||||
self.visit(expr.path().as_red());
|
||||
self.visit(expr.path().as_untyped());
|
||||
if let Imports::Items(items) = expr.imports() {
|
||||
for item in items {
|
||||
self.bind(item);
|
||||
@ -134,21 +134,17 @@ impl<'a> CapturesVisitor<'a> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::parse::parse;
|
||||
use crate::source::SourceId;
|
||||
use crate::syntax::RedNode;
|
||||
|
||||
#[track_caller]
|
||||
fn test(src: &str, result: &[&str]) {
|
||||
let green = parse(src);
|
||||
let red = RedNode::from_root(green, SourceId::from_raw(0));
|
||||
|
||||
let mut scopes = Scopes::new(None);
|
||||
scopes.top.define("x", 0);
|
||||
scopes.top.define("y", 0);
|
||||
scopes.top.define("z", 0);
|
||||
|
||||
let mut visitor = CapturesVisitor::new(&scopes);
|
||||
visitor.visit(red.as_ref());
|
||||
let root = parse(src);
|
||||
visitor.visit(&root);
|
||||
|
||||
let captures = visitor.finish();
|
||||
let mut names: Vec<_> = captures.iter().map(|(k, _)| k).collect();
|
||||
|
@ -698,7 +698,7 @@ impl Eval for ClosureExpr {
|
||||
// Collect captured variables.
|
||||
let captured = {
|
||||
let mut visitor = CapturesVisitor::new(&vm.scopes);
|
||||
visitor.visit(self.as_red());
|
||||
visitor.visit(self.as_untyped());
|
||||
visitor.finish()
|
||||
};
|
||||
|
||||
@ -770,7 +770,7 @@ impl Eval for ShowExpr {
|
||||
// Collect captured variables.
|
||||
let captured = {
|
||||
let mut visitor = CapturesVisitor::new(&vm.scopes);
|
||||
visitor.visit(self.as_red());
|
||||
visitor.visit(self.as_untyped());
|
||||
visitor.finish()
|
||||
};
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
//!
|
||||
//! # Steps
|
||||
//! - **Parsing:** The parsing step first transforms a plain string into an
|
||||
//! [iterator of tokens][tokens]. This token stream is [parsed] into a [green
|
||||
//! tree]. The green tree itself is untyped, but a typed layer over it is
|
||||
//! provided in the [AST] module.
|
||||
//! [iterator of tokens][tokens]. This token stream is [parsed] into a [syntax
|
||||
//! tree]. The tree itself is untyped, but a typed layer over it is provided
|
||||
//! in the [AST] module.
|
||||
//! - **Evaluation:** The next step is to [evaluate] the markup. This produces a
|
||||
//! [module], consisting of a scope of values that were exported by the code
|
||||
//! and [content], a hierarchical, styled representation with the contents of
|
||||
@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! [tokens]: parse::Tokens
|
||||
//! [parsed]: parse::parse
|
||||
//! [green tree]: syntax::GreenNode
|
||||
//! [syntax tree]: syntax::SyntaxNode
|
||||
//! [AST]: syntax::ast
|
||||
//! [evaluate]: eval::evaluate
|
||||
//! [module]: eval::Module
|
||||
|
@ -1,36 +1,39 @@
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::syntax::{Green, GreenNode, NodeKind};
|
||||
use crate::syntax::{InnerNode, NodeKind, SyntaxNode};
|
||||
|
||||
use super::{
|
||||
is_newline, parse, reparse_code_block, reparse_content_block, reparse_markup_elements,
|
||||
};
|
||||
|
||||
/// Refresh the given green node with as little parsing as possible.
|
||||
/// Refresh the given syntax node with as little parsing as possible.
|
||||
///
|
||||
/// Takes the new source, the range in the old source that was replaced and the
|
||||
/// length of the replacement.
|
||||
///
|
||||
/// Returns the range in the new source that was ultimately reparsed.
|
||||
pub fn reparse(
|
||||
green: &mut Arc<GreenNode>,
|
||||
root: &mut SyntaxNode,
|
||||
src: &str,
|
||||
replaced: Range<usize>,
|
||||
replacement_len: usize,
|
||||
) -> Range<usize> {
|
||||
Reparser { src, replaced, replacement_len }
|
||||
.reparse_step(Arc::make_mut(green), 0, true)
|
||||
.unwrap_or_else(|| {
|
||||
*green = parse(src);
|
||||
0 .. src.len()
|
||||
})
|
||||
if let SyntaxNode::Inner(inner) = root {
|
||||
let reparser = Reparser { src, replaced, replacement_len };
|
||||
if let Some(range) = reparser.reparse_step(Arc::make_mut(inner), 0, true) {
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
*root = parse(src);
|
||||
0 .. src.len()
|
||||
}
|
||||
|
||||
/// Allows partial refreshs of the [`Green`] node tree.
|
||||
/// Allows partial refreshs of the syntax tree.
|
||||
///
|
||||
/// This struct holds a description of a change. Its methods can be used to try
|
||||
/// and apply the change to a green tree.
|
||||
/// and apply the change to a syntax tree.
|
||||
struct Reparser<'a> {
|
||||
/// The new source code, with the change applied.
|
||||
src: &'a str,
|
||||
@ -44,12 +47,12 @@ impl Reparser<'_> {
|
||||
/// Try to reparse inside the given node.
|
||||
fn reparse_step(
|
||||
&self,
|
||||
green: &mut GreenNode,
|
||||
node: &mut InnerNode,
|
||||
mut offset: usize,
|
||||
outermost: bool,
|
||||
) -> Option<Range<usize>> {
|
||||
let is_markup = matches!(green.kind(), NodeKind::Markup(_));
|
||||
let original_count = green.children().len();
|
||||
let is_markup = matches!(node.kind(), NodeKind::Markup(_));
|
||||
let original_count = node.children().len();
|
||||
let original_offset = offset;
|
||||
|
||||
let mut search = SearchState::default();
|
||||
@ -62,8 +65,8 @@ impl Reparser<'_> {
|
||||
let mut child_outermost = false;
|
||||
|
||||
// Find the the first child in the range of children to reparse.
|
||||
for (i, child) in green.children().iter().enumerate() {
|
||||
let pos = GreenPos { idx: i, offset };
|
||||
for (i, child) in node.children().enumerate() {
|
||||
let pos = NodePos { idx: i, offset };
|
||||
let child_span = offset .. offset + child.len();
|
||||
|
||||
match search {
|
||||
@ -122,24 +125,24 @@ impl Reparser<'_> {
|
||||
// If we were looking for a non-whitespace element and hit the end of
|
||||
// the file here, we instead use EOF as the end of the span.
|
||||
if let SearchState::RequireNonTrivia(start) = search {
|
||||
search = SearchState::SpanFound(start, GreenPos {
|
||||
idx: green.children().len() - 1,
|
||||
offset: offset - green.children().last().unwrap().len(),
|
||||
search = SearchState::SpanFound(start, NodePos {
|
||||
idx: node.children().len() - 1,
|
||||
offset: offset - node.children().last().unwrap().len(),
|
||||
})
|
||||
}
|
||||
|
||||
if let SearchState::Contained(pos) = search {
|
||||
let child = &mut green.children_mut()[pos.idx];
|
||||
let child = &mut node.children_mut()[pos.idx];
|
||||
let prev_len = child.len();
|
||||
|
||||
if let Some(range) = match child {
|
||||
Green::Node(node) => {
|
||||
SyntaxNode::Inner(node) => {
|
||||
self.reparse_step(Arc::make_mut(node), pos.offset, child_outermost)
|
||||
}
|
||||
Green::Token(_) => None,
|
||||
SyntaxNode::Leaf(_) => None,
|
||||
} {
|
||||
let new_len = child.len();
|
||||
green.update_parent(new_len, prev_len);
|
||||
node.update_parent(new_len, prev_len);
|
||||
return Some(range);
|
||||
}
|
||||
|
||||
@ -154,7 +157,7 @@ impl Reparser<'_> {
|
||||
// treat it as a markup element.
|
||||
if let Some(func) = func {
|
||||
if let Some(result) = self.replace(
|
||||
green,
|
||||
node,
|
||||
func,
|
||||
pos.idx .. pos.idx + 1,
|
||||
superseded_span,
|
||||
@ -166,14 +169,14 @@ impl Reparser<'_> {
|
||||
}
|
||||
|
||||
// Save the current indent if this is a markup node and stop otherwise.
|
||||
let indent = match green.kind() {
|
||||
let indent = match node.kind() {
|
||||
NodeKind::Markup(n) => *n,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (mut start, end) = search.done()?;
|
||||
if let Some((ahead, ahead_at_start)) = ahead_nontrivia {
|
||||
let ahead_kind = green.children()[ahead.idx].kind();
|
||||
let ahead_kind = node.children().as_slice()[ahead.idx].kind();
|
||||
|
||||
if start.offset == self.replaced.start
|
||||
|| ahead_kind.only_at_start()
|
||||
@ -183,14 +186,14 @@ impl Reparser<'_> {
|
||||
at_start = ahead_at_start;
|
||||
}
|
||||
} else {
|
||||
start = GreenPos { idx: 0, offset: original_offset };
|
||||
start = NodePos { idx: 0, offset: original_offset };
|
||||
}
|
||||
|
||||
let superseded_span =
|
||||
start.offset .. end.offset + green.children()[end.idx].len();
|
||||
start.offset .. end.offset + node.children().as_slice()[end.idx].len();
|
||||
|
||||
self.replace(
|
||||
green,
|
||||
node,
|
||||
ReparseMode::MarkupElements(at_start, indent),
|
||||
start.idx .. end.idx + 1,
|
||||
superseded_span,
|
||||
@ -200,7 +203,7 @@ impl Reparser<'_> {
|
||||
|
||||
fn replace(
|
||||
&self,
|
||||
green: &mut GreenNode,
|
||||
node: &mut InnerNode,
|
||||
mode: ReparseMode,
|
||||
superseded_idx: Range<usize>,
|
||||
superseded_span: Range<usize>,
|
||||
@ -237,7 +240,7 @@ impl Reparser<'_> {
|
||||
&self.src[newborn_span.start ..],
|
||||
newborn_span.len(),
|
||||
differential,
|
||||
&green.children()[superseded_start ..],
|
||||
&node.children().as_slice()[superseded_start ..],
|
||||
at_start,
|
||||
indent,
|
||||
),
|
||||
@ -249,14 +252,14 @@ impl Reparser<'_> {
|
||||
return None;
|
||||
}
|
||||
|
||||
green.replace_children(superseded_start .. superseded_start + amount, newborns);
|
||||
node.replace_children(superseded_start .. superseded_start + amount, newborns);
|
||||
Some(newborn_span)
|
||||
}
|
||||
}
|
||||
|
||||
/// The position of a green node.
|
||||
/// The position of a syntax node.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
struct GreenPos {
|
||||
struct NodePos {
|
||||
/// The index in the parent node.
|
||||
idx: usize,
|
||||
/// The byte offset in the string.
|
||||
@ -272,15 +275,15 @@ enum SearchState {
|
||||
NoneFound,
|
||||
/// The search has concluded by finding a node that fully contains the
|
||||
/// modifications.
|
||||
Contained(GreenPos),
|
||||
Contained(NodePos),
|
||||
/// The search has found the start of the modified nodes.
|
||||
Inside(GreenPos),
|
||||
Inside(NodePos),
|
||||
/// The search has found the end of the modified nodes but the change
|
||||
/// touched its boundries so another non-trivia node is needed.
|
||||
RequireNonTrivia(GreenPos),
|
||||
RequireNonTrivia(NodePos),
|
||||
/// The search has concluded by finding a start and an end index for nodes
|
||||
/// with a pending reparse.
|
||||
SpanFound(GreenPos, GreenPos),
|
||||
SpanFound(NodePos, NodePos),
|
||||
}
|
||||
|
||||
impl Default for SearchState {
|
||||
@ -290,7 +293,7 @@ impl Default for SearchState {
|
||||
}
|
||||
|
||||
impl SearchState {
|
||||
fn done(self) -> Option<(GreenPos, GreenPos)> {
|
||||
fn done(self) -> Option<(NodePos, NodePos)> {
|
||||
match self {
|
||||
Self::NoneFound => None,
|
||||
Self::Contained(s) => Some((s, s)),
|
||||
|
@ -10,24 +10,20 @@ pub use parser::*;
|
||||
pub use tokens::*;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::syntax::ast::{Associativity, BinOp, UnOp};
|
||||
use crate::syntax::{ErrorPos, Green, GreenNode, NodeKind};
|
||||
use crate::syntax::{ErrorPos, NodeKind, SyntaxNode};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Parse a source file.
|
||||
pub fn parse(src: &str) -> Arc<GreenNode> {
|
||||
pub fn parse(src: &str) -> SyntaxNode {
|
||||
let mut p = Parser::new(src, TokenMode::Markup);
|
||||
markup(&mut p, true);
|
||||
match p.finish().into_iter().next() {
|
||||
Some(Green::Node(node)) => node,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
p.finish().into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Parse code directly, only used for syntax highlighting.
|
||||
pub fn parse_code(src: &str) -> Vec<Green> {
|
||||
pub fn parse_code(src: &str) -> Vec<SyntaxNode> {
|
||||
let mut p = Parser::new(src, TokenMode::Code);
|
||||
code(&mut p);
|
||||
p.finish()
|
||||
@ -40,7 +36,7 @@ fn reparse_code_block(
|
||||
prefix: &str,
|
||||
src: &str,
|
||||
end_pos: usize,
|
||||
) -> Option<(Vec<Green>, bool, usize)> {
|
||||
) -> Option<(Vec<SyntaxNode>, bool, usize)> {
|
||||
let mut p = Parser::with_prefix(prefix, src, TokenMode::Code);
|
||||
if !p.at(NodeKind::LeftBrace) {
|
||||
return None;
|
||||
@ -48,8 +44,8 @@ fn reparse_code_block(
|
||||
|
||||
code_block(&mut p);
|
||||
|
||||
let (mut green, terminated) = p.consume()?;
|
||||
let first = green.remove(0);
|
||||
let (mut node, terminated) = p.consume()?;
|
||||
let first = node.remove(0);
|
||||
if first.len() != end_pos {
|
||||
return None;
|
||||
}
|
||||
@ -64,7 +60,7 @@ fn reparse_content_block(
|
||||
prefix: &str,
|
||||
src: &str,
|
||||
end_pos: usize,
|
||||
) -> Option<(Vec<Green>, bool, usize)> {
|
||||
) -> Option<(Vec<SyntaxNode>, bool, usize)> {
|
||||
let mut p = Parser::with_prefix(prefix, src, TokenMode::Code);
|
||||
if !p.at(NodeKind::LeftBracket) {
|
||||
return None;
|
||||
@ -72,8 +68,8 @@ fn reparse_content_block(
|
||||
|
||||
content_block(&mut p);
|
||||
|
||||
let (mut green, terminated) = p.consume()?;
|
||||
let first = green.remove(0);
|
||||
let (mut node, terminated) = p.consume()?;
|
||||
let first = node.remove(0);
|
||||
if first.len() != end_pos {
|
||||
return None;
|
||||
}
|
||||
@ -89,13 +85,13 @@ fn reparse_markup_elements(
|
||||
src: &str,
|
||||
end_pos: usize,
|
||||
differential: isize,
|
||||
reference: &[Green],
|
||||
reference: &[SyntaxNode],
|
||||
mut at_start: bool,
|
||||
column: usize,
|
||||
) -> Option<(Vec<Green>, bool, usize)> {
|
||||
) -> Option<(Vec<SyntaxNode>, bool, usize)> {
|
||||
let mut p = Parser::with_prefix(prefix, src, TokenMode::Markup);
|
||||
|
||||
let mut node: Option<&Green> = None;
|
||||
let mut node: Option<&SyntaxNode> = None;
|
||||
let mut iter = reference.iter();
|
||||
let mut offset = differential;
|
||||
let mut replaced = 0;
|
||||
@ -683,7 +679,7 @@ fn dict(p: &mut Parser, marker: Marker) {
|
||||
kind if kind.is_paren() => Ok(()),
|
||||
NodeKind::Named | NodeKind::Keyed => {
|
||||
if let Some(NodeKind::Ident(key) | NodeKind::Str(key)) =
|
||||
x.children().first().map(|child| child.kind())
|
||||
x.children().next().map(|child| child.kind())
|
||||
{
|
||||
if !used.insert(key.clone()) {
|
||||
return Err("pair has duplicate key");
|
||||
@ -770,7 +766,7 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
|
||||
marker.filter_children(p, |x| match x.kind() {
|
||||
NodeKind::Named => {
|
||||
if let Some(NodeKind::Ident(ident)) =
|
||||
x.children().first().map(|child| child.kind())
|
||||
x.children().next().map(|child| child.kind())
|
||||
{
|
||||
if !used.insert(ident.clone()) {
|
||||
return Err("duplicate argument");
|
||||
|
@ -3,7 +3,7 @@ use std::mem;
|
||||
use std::ops::Range;
|
||||
|
||||
use super::{TokenMode, Tokens};
|
||||
use crate::syntax::{ErrorPos, Green, GreenData, GreenNode, NodeKind};
|
||||
use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A convenient token-based parser.
|
||||
@ -21,7 +21,7 @@ pub struct Parser<'s> {
|
||||
/// The stack of open groups.
|
||||
groups: Vec<GroupEntry>,
|
||||
/// The children of the currently built node.
|
||||
children: Vec<Green>,
|
||||
children: Vec<SyntaxNode>,
|
||||
/// Whether the last group was not correctly terminated.
|
||||
unterminated_group: bool,
|
||||
/// Whether a group terminator was found, that did not close a group.
|
||||
@ -54,14 +54,14 @@ impl<'s> Parser<'s> {
|
||||
}
|
||||
|
||||
/// End the parsing process and return the parsed children.
|
||||
pub fn finish(self) -> Vec<Green> {
|
||||
pub fn finish(self) -> Vec<SyntaxNode> {
|
||||
self.children
|
||||
}
|
||||
|
||||
/// End the parsing process and return the parsed children and whether the
|
||||
/// last token was terminated if all groups were terminated correctly or
|
||||
/// `None` otherwise.
|
||||
pub fn consume(self) -> Option<(Vec<Green>, bool)> {
|
||||
pub fn consume(self) -> Option<(Vec<SyntaxNode>, bool)> {
|
||||
self.terminated().then(|| (self.children, self.tokens.terminated()))
|
||||
}
|
||||
|
||||
@ -94,11 +94,11 @@ impl<'s> Parser<'s> {
|
||||
if self.tokens.mode() == TokenMode::Code {
|
||||
// Trailing trivia should not be wrapped into the new node.
|
||||
let idx = self.children.len();
|
||||
self.children.push(Green::default());
|
||||
self.children.push(SyntaxNode::default());
|
||||
self.children.extend(children.drain(until.0 ..));
|
||||
self.children[idx] = GreenNode::with_children(kind, children).into();
|
||||
self.children[idx] = InnerNode::with_children(kind, children).into();
|
||||
} else {
|
||||
self.children.push(GreenNode::with_children(kind, children).into());
|
||||
self.children.push(InnerNode::with_children(kind, children).into());
|
||||
}
|
||||
|
||||
output
|
||||
@ -291,7 +291,7 @@ impl<'s> Parser<'s> {
|
||||
if group_mode == TokenMode::Code {
|
||||
let start = self.trivia_start().0;
|
||||
target = self.current_start
|
||||
- self.children[start ..].iter().map(Green::len).sum::<usize>();
|
||||
- self.children[start ..].iter().map(SyntaxNode::len).sum::<usize>();
|
||||
self.children.truncate(start);
|
||||
}
|
||||
|
||||
@ -314,7 +314,7 @@ impl<'s> Parser<'s> {
|
||||
fn bump(&mut self) {
|
||||
let kind = self.current.take().unwrap();
|
||||
let len = self.tokens.cursor() - self.current_start;
|
||||
self.children.push(GreenData::new(kind, len).into());
|
||||
self.children.push(NodeData::new(kind, len).into());
|
||||
self.current_start = self.tokens.cursor();
|
||||
self.current = self.tokens.next();
|
||||
}
|
||||
@ -399,7 +399,7 @@ impl Parser<'_> {
|
||||
pub fn expected_at(&mut self, marker: Marker, what: &str) {
|
||||
let msg = format_eco!("expected {}", what);
|
||||
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||
self.children.insert(marker.0, GreenData::new(error, 0).into());
|
||||
self.children.insert(marker.0, NodeData::new(error, 0).into());
|
||||
}
|
||||
|
||||
/// Eat the current token and add an error that it is not the expected
|
||||
@ -422,12 +422,12 @@ pub struct Marker(usize);
|
||||
|
||||
impl Marker {
|
||||
/// Peek at the child directly before the marker.
|
||||
pub fn before<'a>(self, p: &'a Parser) -> Option<&'a Green> {
|
||||
pub fn before<'a>(self, p: &'a Parser) -> Option<&'a SyntaxNode> {
|
||||
p.children.get(self.0.checked_sub(1)?)
|
||||
}
|
||||
|
||||
/// Peek at the child directly after the marker.
|
||||
pub fn after<'a>(self, p: &'a Parser) -> Option<&'a Green> {
|
||||
pub fn after<'a>(self, p: &'a Parser) -> Option<&'a SyntaxNode> {
|
||||
p.children.get(self.0)
|
||||
}
|
||||
|
||||
@ -455,13 +455,13 @@ impl Marker {
|
||||
let until = p.trivia_start();
|
||||
let children = p.children.drain(self.0 .. until.0).collect();
|
||||
p.children
|
||||
.insert(self.0, GreenNode::with_children(kind, children).into());
|
||||
.insert(self.0, InnerNode::with_children(kind, children).into());
|
||||
}
|
||||
|
||||
/// Wrap all children that do not fulfill the predicate in error nodes.
|
||||
pub fn filter_children<F>(self, p: &mut Parser, mut f: F)
|
||||
where
|
||||
F: FnMut(&Green) -> Result<(), &'static str>,
|
||||
F: FnMut(&SyntaxNode) -> Result<(), &'static str>,
|
||||
{
|
||||
for child in &mut p.children[self.0 ..] {
|
||||
// Don't expose errors.
|
||||
@ -482,7 +482,7 @@ impl Marker {
|
||||
}
|
||||
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||
let inner = mem::take(child);
|
||||
*child = GreenNode::with_child(error, inner).into();
|
||||
*child = InnerNode::with_child(error, inner).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use crate::diag::TypResult;
|
||||
use crate::loading::{FileHash, Loader};
|
||||
use crate::parse::{is_newline, parse, reparse};
|
||||
use crate::syntax::ast::Markup;
|
||||
use crate::syntax::{GreenNode, RedNode, Span};
|
||||
use crate::syntax::{Span, SyntaxNode};
|
||||
use crate::util::{PathExt, StrExt};
|
||||
|
||||
#[cfg(feature = "codespan-reporting")]
|
||||
@ -151,7 +151,7 @@ pub struct SourceFile {
|
||||
path: PathBuf,
|
||||
src: String,
|
||||
lines: Vec<Line>,
|
||||
root: Arc<GreenNode>,
|
||||
root: SyntaxNode,
|
||||
rev: usize,
|
||||
}
|
||||
|
||||
@ -178,27 +178,21 @@ impl SourceFile {
|
||||
/// Create a source file with the same synthetic span for all nodes.
|
||||
pub fn synthesized(src: impl Into<String>, span: Span) -> Self {
|
||||
let mut file = Self::detached(src);
|
||||
Arc::make_mut(&mut file.root).synthesize(Arc::new(span));
|
||||
file.root.synthesize(Arc::new(span));
|
||||
file.id = span.source;
|
||||
file
|
||||
}
|
||||
|
||||
/// The root node of the file's untyped green tree.
|
||||
pub fn root(&self) -> &Arc<GreenNode> {
|
||||
/// The root node of the file's untyped syntax tree.
|
||||
pub fn root(&self) -> &SyntaxNode {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// The root red node of the file's untyped red tree.
|
||||
pub fn red(&self) -> RedNode {
|
||||
RedNode::from_root(self.root.clone(), self.id)
|
||||
}
|
||||
|
||||
/// The root node of the file's typed abstract syntax tree.
|
||||
pub fn ast(&self) -> TypResult<Markup> {
|
||||
let red = self.red();
|
||||
let errors = red.errors();
|
||||
let errors = self.root.errors();
|
||||
if errors.is_empty() {
|
||||
Ok(red.cast().unwrap())
|
||||
Ok(self.root.cast().unwrap())
|
||||
} else {
|
||||
Err(Box::new(errors))
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
//! A typed layer over the red-green tree.
|
||||
//! A typed layer over the untyped syntax tree.
|
||||
//!
|
||||
//! The AST is rooted in the [`Markup`] node.
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
|
||||
use super::{Green, GreenData, NodeKind, RedNode, RedRef, Span, Spanned};
|
||||
use super::{NodeData, NodeKind, Span, Spanned, SyntaxNode};
|
||||
use crate::geom::{AngleUnit, LengthUnit};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A typed AST node.
|
||||
pub trait TypedNode: Sized {
|
||||
/// Convert from a red node to a typed node.
|
||||
fn from_red(value: RedRef) -> Option<Self>;
|
||||
/// Convert a node into its typed variant.
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self>;
|
||||
|
||||
/// A reference to the underlying red node.
|
||||
fn as_red(&self) -> RedRef<'_>;
|
||||
/// A reference to the underlying syntax node.
|
||||
fn as_untyped(&self) -> &SyntaxNode;
|
||||
|
||||
/// The source code location.
|
||||
fn span(&self) -> Span {
|
||||
self.as_red().span()
|
||||
self.as_untyped().span()
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,19 +34,19 @@ macro_rules! node {
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
#[repr(transparent)]
|
||||
$(#[$attr])*
|
||||
pub struct $name(RedNode);
|
||||
pub struct $name(SyntaxNode);
|
||||
|
||||
impl TypedNode for $name {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
if matches!(node.kind(), $variants) {
|
||||
Some(Self(node.own()))
|
||||
Some(Self(node.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
self.0.as_ref()
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -77,7 +77,10 @@ impl Markup {
|
||||
NodeKind::Strong => node.cast().map(MarkupNode::Strong),
|
||||
NodeKind::Emph => node.cast().map(MarkupNode::Emph),
|
||||
NodeKind::Raw(raw) => Some(MarkupNode::Raw(raw.as_ref().clone())),
|
||||
NodeKind::Math(math) => Some(MarkupNode::Math(Spanned::new(math.as_ref().clone(), node.span()))),
|
||||
NodeKind::Math(math) => Some(MarkupNode::Math(Spanned::new(
|
||||
math.as_ref().clone(),
|
||||
node.span(),
|
||||
))),
|
||||
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
|
||||
NodeKind::List => node.cast().map(MarkupNode::List),
|
||||
NodeKind::Enum => node.cast().map(MarkupNode::Enum),
|
||||
@ -279,7 +282,7 @@ pub enum Expr {
|
||||
}
|
||||
|
||||
impl TypedNode for Expr {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(_) => node.cast().map(Self::Ident),
|
||||
NodeKind::CodeBlock => node.cast().map(Self::Code),
|
||||
@ -309,33 +312,33 @@ impl TypedNode for Expr {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Lit(v) => v.as_red(),
|
||||
Self::Code(v) => v.as_red(),
|
||||
Self::Content(v) => v.as_red(),
|
||||
Self::Ident(v) => v.as_red(),
|
||||
Self::Array(v) => v.as_red(),
|
||||
Self::Dict(v) => v.as_red(),
|
||||
Self::Group(v) => v.as_red(),
|
||||
Self::Unary(v) => v.as_red(),
|
||||
Self::Binary(v) => v.as_red(),
|
||||
Self::FieldAccess(v) => v.as_red(),
|
||||
Self::FuncCall(v) => v.as_red(),
|
||||
Self::MethodCall(v) => v.as_red(),
|
||||
Self::Closure(v) => v.as_red(),
|
||||
Self::Let(v) => v.as_red(),
|
||||
Self::Set(v) => v.as_red(),
|
||||
Self::Show(v) => v.as_red(),
|
||||
Self::Wrap(v) => v.as_red(),
|
||||
Self::If(v) => v.as_red(),
|
||||
Self::While(v) => v.as_red(),
|
||||
Self::For(v) => v.as_red(),
|
||||
Self::Import(v) => v.as_red(),
|
||||
Self::Include(v) => v.as_red(),
|
||||
Self::Break(v) => v.as_red(),
|
||||
Self::Continue(v) => v.as_red(),
|
||||
Self::Return(v) => v.as_red(),
|
||||
Self::Lit(v) => v.as_untyped(),
|
||||
Self::Code(v) => v.as_untyped(),
|
||||
Self::Content(v) => v.as_untyped(),
|
||||
Self::Ident(v) => v.as_untyped(),
|
||||
Self::Array(v) => v.as_untyped(),
|
||||
Self::Dict(v) => v.as_untyped(),
|
||||
Self::Group(v) => v.as_untyped(),
|
||||
Self::Unary(v) => v.as_untyped(),
|
||||
Self::Binary(v) => v.as_untyped(),
|
||||
Self::FieldAccess(v) => v.as_untyped(),
|
||||
Self::FuncCall(v) => v.as_untyped(),
|
||||
Self::MethodCall(v) => v.as_untyped(),
|
||||
Self::Closure(v) => v.as_untyped(),
|
||||
Self::Let(v) => v.as_untyped(),
|
||||
Self::Set(v) => v.as_untyped(),
|
||||
Self::Show(v) => v.as_untyped(),
|
||||
Self::Wrap(v) => v.as_untyped(),
|
||||
Self::If(v) => v.as_untyped(),
|
||||
Self::While(v) => v.as_untyped(),
|
||||
Self::For(v) => v.as_untyped(),
|
||||
Self::Import(v) => v.as_untyped(),
|
||||
Self::Include(v) => v.as_untyped(),
|
||||
Self::Break(v) => v.as_untyped(),
|
||||
Self::Continue(v) => v.as_untyped(),
|
||||
Self::Return(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -429,7 +432,7 @@ node! {
|
||||
impl CodeBlock {
|
||||
/// The list of expressions contained in the block.
|
||||
pub fn exprs(&self) -> impl Iterator<Item = Expr> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
self.0.children().filter_map(SyntaxNode::cast)
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,7 +468,7 @@ node! {
|
||||
impl ArrayExpr {
|
||||
/// The array items.
|
||||
pub fn items(&self) -> impl Iterator<Item = ArrayItem> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
self.0.children().filter_map(SyntaxNode::cast)
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,17 +482,17 @@ pub enum ArrayItem {
|
||||
}
|
||||
|
||||
impl TypedNode for ArrayItem {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Spread => node.cast_first_child().map(Self::Spread),
|
||||
_ => node.cast().map(Self::Pos),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Pos(v) => v.as_red(),
|
||||
Self::Spread(v) => v.as_red(),
|
||||
Self::Pos(v) => v.as_untyped(),
|
||||
Self::Spread(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -502,7 +505,7 @@ node! {
|
||||
impl DictExpr {
|
||||
/// The named dictionary items.
|
||||
pub fn items(&self) -> impl Iterator<Item = DictItem> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
self.0.children().filter_map(SyntaxNode::cast)
|
||||
}
|
||||
}
|
||||
|
||||
@ -518,7 +521,7 @@ pub enum DictItem {
|
||||
}
|
||||
|
||||
impl TypedNode for DictItem {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Named => node.cast().map(Self::Named),
|
||||
NodeKind::Keyed => node.cast().map(Self::Keyed),
|
||||
@ -527,11 +530,11 @@ impl TypedNode for DictItem {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Named(v) => v.as_red(),
|
||||
Self::Keyed(v) => v.as_red(),
|
||||
Self::Spread(v) => v.as_red(),
|
||||
Self::Named(v) => v.as_untyped(),
|
||||
Self::Keyed(v) => v.as_untyped(),
|
||||
Self::Spread(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -895,7 +898,7 @@ node! {
|
||||
impl CallArgs {
|
||||
/// The positional and named arguments.
|
||||
pub fn items(&self) -> impl Iterator<Item = CallArg> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
self.0.children().filter_map(SyntaxNode::cast)
|
||||
}
|
||||
}
|
||||
|
||||
@ -911,7 +914,7 @@ pub enum CallArg {
|
||||
}
|
||||
|
||||
impl TypedNode for CallArg {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Named => node.cast().map(Self::Named),
|
||||
NodeKind::Spread => node.cast_first_child().map(Self::Spread),
|
||||
@ -919,11 +922,11 @@ impl TypedNode for CallArg {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Pos(v) => v.as_red(),
|
||||
Self::Named(v) => v.as_red(),
|
||||
Self::Spread(v) => v.as_red(),
|
||||
Self::Pos(v) => v.as_untyped(),
|
||||
Self::Named(v) => v.as_untyped(),
|
||||
Self::Spread(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -948,7 +951,7 @@ impl ClosureExpr {
|
||||
.find(|x| x.kind() == &NodeKind::ClosureParams)
|
||||
.expect("closure is missing parameter list")
|
||||
.children()
|
||||
.filter_map(RedRef::cast)
|
||||
.filter_map(SyntaxNode::cast)
|
||||
}
|
||||
|
||||
/// The body of the closure.
|
||||
@ -969,7 +972,7 @@ pub enum ClosureParam {
|
||||
}
|
||||
|
||||
impl TypedNode for ClosureParam {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(_) => node.cast().map(Self::Pos),
|
||||
NodeKind::Named => node.cast().map(Self::Named),
|
||||
@ -978,11 +981,11 @@ impl TypedNode for ClosureParam {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_red(&self) -> RedRef<'_> {
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Pos(v) => v.as_red(),
|
||||
Self::Named(v) => v.as_red(),
|
||||
Self::Sink(v) => v.as_red(),
|
||||
Self::Pos(v) => v.as_untyped(),
|
||||
Self::Named(v) => v.as_untyped(),
|
||||
Self::Sink(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1007,7 +1010,7 @@ impl LetExpr {
|
||||
/// The expression the binding is initialized with.
|
||||
pub fn init(&self) -> Option<Expr> {
|
||||
if self.0.cast_first_child::<Ident>().is_some() {
|
||||
self.0.children().filter_map(RedRef::cast).nth(1)
|
||||
self.0.children().filter_map(SyntaxNode::cast).nth(1)
|
||||
} else {
|
||||
// This is a let .. with expression.
|
||||
self.0.cast_first_child()
|
||||
@ -1042,7 +1045,7 @@ impl ShowExpr {
|
||||
pub fn binding(&self) -> Option<Ident> {
|
||||
let mut children = self.0.children();
|
||||
children
|
||||
.find_map(RedRef::cast)
|
||||
.find_map(SyntaxNode::cast)
|
||||
.filter(|_| children.any(|child| child.kind() == &NodeKind::Colon))
|
||||
}
|
||||
|
||||
@ -1052,7 +1055,7 @@ impl ShowExpr {
|
||||
.children()
|
||||
.rev()
|
||||
.skip_while(|child| child.kind() != &NodeKind::As)
|
||||
.find_map(RedRef::cast)
|
||||
.find_map(SyntaxNode::cast)
|
||||
.expect("show rule is missing pattern")
|
||||
}
|
||||
|
||||
@ -1094,14 +1097,14 @@ impl IfExpr {
|
||||
pub fn if_body(&self) -> Expr {
|
||||
self.0
|
||||
.children()
|
||||
.filter_map(RedRef::cast)
|
||||
.filter_map(SyntaxNode::cast)
|
||||
.nth(1)
|
||||
.expect("if expression is missing body")
|
||||
}
|
||||
|
||||
/// The expression to evaluate if the condition is false.
|
||||
pub fn else_body(&self) -> Option<Expr> {
|
||||
self.0.children().filter_map(RedRef::cast).nth(2)
|
||||
self.0.children().filter_map(SyntaxNode::cast).nth(2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1152,7 +1155,7 @@ node! {
|
||||
impl ForPattern {
|
||||
/// The key part of the pattern: index for arrays, name for dictionaries.
|
||||
pub fn key(&self) -> Option<Ident> {
|
||||
let mut children = self.0.children().filter_map(RedRef::cast);
|
||||
let mut children = self.0.children().filter_map(SyntaxNode::cast);
|
||||
let key = children.next();
|
||||
if children.next().is_some() { key } else { None }
|
||||
}
|
||||
@ -1176,7 +1179,7 @@ impl ImportExpr {
|
||||
.find_map(|node| match node.kind() {
|
||||
NodeKind::Star => Some(Imports::Wildcard),
|
||||
NodeKind::ImportItems => {
|
||||
let items = node.children().filter_map(RedRef::cast).collect();
|
||||
let items = node.children().filter_map(SyntaxNode::cast).collect();
|
||||
Some(Imports::Items(items))
|
||||
}
|
||||
_ => None,
|
||||
@ -1241,8 +1244,8 @@ node! {
|
||||
impl Ident {
|
||||
/// Take out the contained [`EcoString`].
|
||||
pub fn take(self) -> EcoString {
|
||||
match self.0.green {
|
||||
Green::Token(GreenData { kind: NodeKind::Ident(id), .. }) => id,
|
||||
match self.0 {
|
||||
SyntaxNode::Leaf(NodeData { kind: NodeKind::Ident(id), .. }) => id,
|
||||
_ => panic!("identifier is of wrong kind"),
|
||||
}
|
||||
}
|
||||
@ -1252,8 +1255,8 @@ impl Deref for Ident {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0.green {
|
||||
Green::Token(GreenData { kind: NodeKind::Ident(id), .. }) => id,
|
||||
match &self.0 {
|
||||
SyntaxNode::Leaf(NodeData { kind: NodeKind::Ident(id), .. }) => id,
|
||||
_ => panic!("identifier is of wrong kind"),
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,12 @@ use std::sync::Arc;
|
||||
use syntect::highlighting::{Color, FontStyle, Highlighter, Style, Theme};
|
||||
use syntect::parsing::Scope;
|
||||
|
||||
use super::{GreenNode, NodeKind, RedNode, RedRef};
|
||||
use super::{InnerNode, NodeKind, SyntaxNode};
|
||||
use crate::parse::TokenMode;
|
||||
use crate::source::SourceId;
|
||||
|
||||
/// Provide highlighting categories for the descendants of a node that fall into
|
||||
/// a range.
|
||||
pub fn highlight_node<F>(node: RedRef, range: Range<usize>, f: &mut F)
|
||||
pub fn highlight_node<F>(node: &SyntaxNode, range: Range<usize>, f: &mut F)
|
||||
where
|
||||
F: FnMut(Range<usize>, Category),
|
||||
{
|
||||
@ -36,20 +35,21 @@ where
|
||||
TokenMode::Markup => crate::parse::parse(text),
|
||||
TokenMode::Code => {
|
||||
let children = crate::parse::parse_code(text);
|
||||
Arc::new(GreenNode::with_children(NodeKind::CodeBlock, children))
|
||||
SyntaxNode::Inner(Arc::new(InnerNode::with_children(
|
||||
NodeKind::CodeBlock,
|
||||
children,
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
let root = RedNode::from_root(root, SourceId::from_raw(0));
|
||||
let highlighter = Highlighter::new(&theme);
|
||||
|
||||
highlight_themed_impl(text, root.as_ref(), vec![], &highlighter, f);
|
||||
highlight_themed_impl(text, &root, vec![], &highlighter, f);
|
||||
}
|
||||
|
||||
/// Recursive implementation for returning syntect styles.
|
||||
fn highlight_themed_impl<F>(
|
||||
text: &str,
|
||||
node: RedRef,
|
||||
node: &SyntaxNode,
|
||||
scopes: Vec<Scope>,
|
||||
highlighter: &Highlighter,
|
||||
f: &mut F,
|
||||
@ -178,7 +178,11 @@ pub enum Category {
|
||||
impl Category {
|
||||
/// Determine the highlighting category of a node given its parent and its
|
||||
/// index in its siblings.
|
||||
pub fn determine(child: RedRef, parent: RedRef, i: usize) -> Option<Category> {
|
||||
pub fn determine(
|
||||
child: &SyntaxNode,
|
||||
parent: &SyntaxNode,
|
||||
i: usize,
|
||||
) -> Option<Category> {
|
||||
match child.kind() {
|
||||
NodeKind::LeftBrace => Some(Category::Bracket),
|
||||
NodeKind::RightBrace => Some(Category::Bracket),
|
||||
@ -262,7 +266,7 @@ impl Category {
|
||||
if parent
|
||||
.children()
|
||||
.filter(|c| matches!(c.kind(), NodeKind::Ident(_)))
|
||||
.map(RedRef::span)
|
||||
.map(SyntaxNode::span)
|
||||
.nth(1)
|
||||
.map_or(false, |span| span == child.span()) =>
|
||||
{
|
||||
@ -359,7 +363,7 @@ mod tests {
|
||||
let mut vec = vec![];
|
||||
let source = SourceFile::detached(src);
|
||||
let full = 0 .. src.len();
|
||||
highlight_node(source.red().as_ref(), full, &mut |range, category| {
|
||||
highlight_node(source.root(), full, &mut |range, category| {
|
||||
vec.push((range, category));
|
||||
});
|
||||
assert_eq!(vec, goal);
|
||||
|
@ -14,24 +14,23 @@ pub use span::*;
|
||||
|
||||
use self::ast::{MathNode, RawNode, TypedNode, Unit};
|
||||
use crate::diag::Error;
|
||||
use crate::source::SourceId;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// An inner or leaf node in the untyped green tree.
|
||||
/// An inner or leaf node in the untyped syntax tree.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub enum Green {
|
||||
pub enum SyntaxNode {
|
||||
/// A reference-counted inner node.
|
||||
Node(Arc<GreenNode>),
|
||||
/// A terminal, owned token.
|
||||
Token(GreenData),
|
||||
Inner(Arc<InnerNode>),
|
||||
/// A leaf token.
|
||||
Leaf(NodeData),
|
||||
}
|
||||
|
||||
impl Green {
|
||||
impl SyntaxNode {
|
||||
/// Returns the metadata of the node.
|
||||
fn data(&self) -> &GreenData {
|
||||
pub fn data(&self) -> &NodeData {
|
||||
match self {
|
||||
Green::Node(n) => &n.data,
|
||||
Green::Token(t) => t,
|
||||
SyntaxNode::Inner(n) => &n.data,
|
||||
SyntaxNode::Leaf(t) => t,
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,106 +44,146 @@ impl Green {
|
||||
self.data().len()
|
||||
}
|
||||
|
||||
/// Whether the node or its children contain an error.
|
||||
pub fn erroneous(&self) -> bool {
|
||||
match self {
|
||||
Self::Node(node) => node.erroneous,
|
||||
Self::Token(data) => data.kind.is_error(),
|
||||
}
|
||||
/// The span of the node.
|
||||
pub fn span(&self) -> Span {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(&self) -> &[Green] {
|
||||
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
||||
match self {
|
||||
Green::Node(n) => n.children(),
|
||||
Green::Token(_) => &[],
|
||||
SyntaxNode::Inner(n) => n.children(),
|
||||
SyntaxNode::Leaf(_) => [].iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the node is a leaf node in the green tree.
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
match self {
|
||||
Green::Node(n) => n.children().is_empty(),
|
||||
Green::Token(_) => true,
|
||||
/// Returns all leaf descendants of this node (may include itself).
|
||||
///
|
||||
/// This method is slow and only intended for testing.
|
||||
pub fn leafs(&self) -> Vec<Self> {
|
||||
if match self {
|
||||
SyntaxNode::Inner(n) => n.children().len() == 0,
|
||||
SyntaxNode::Leaf(_) => true,
|
||||
} {
|
||||
vec![self.clone()]
|
||||
} else {
|
||||
self.children().flat_map(Self::leafs).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the node or its children contain an error.
|
||||
pub fn erroneous(&self) -> bool {
|
||||
match self {
|
||||
Self::Inner(node) => node.erroneous,
|
||||
Self::Leaf(data) => data.kind.is_error(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors(&self) -> Vec<Error> {
|
||||
if !self.erroneous() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
match self.kind() {
|
||||
NodeKind::Error(..) => todo!(),
|
||||
_ => self
|
||||
.children()
|
||||
.filter(|node| node.erroneous())
|
||||
.flat_map(|node| node.errors())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(&self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
T::from_untyped(self)
|
||||
}
|
||||
|
||||
/// Get the first child that can cast to some AST type.
|
||||
pub fn cast_first_child<T: TypedNode>(&self) -> Option<T> {
|
||||
self.children().find_map(Self::cast)
|
||||
}
|
||||
|
||||
/// Get the last child that can cast to some AST type.
|
||||
pub fn cast_last_child<T: TypedNode>(&self) -> Option<T> {
|
||||
self.children().rev().find_map(Self::cast)
|
||||
}
|
||||
|
||||
/// Change the type of the node.
|
||||
pub fn convert(&mut self, kind: NodeKind) {
|
||||
match self {
|
||||
Self::Node(node) => {
|
||||
Self::Inner(node) => {
|
||||
let node = Arc::make_mut(node);
|
||||
node.erroneous |= kind.is_error();
|
||||
node.data.kind = kind;
|
||||
}
|
||||
Self::Token(data) => data.kind = kind,
|
||||
Self::Leaf(data) => data.kind = kind,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a synthetic span for the node and all its children.
|
||||
pub fn synthesize(&mut self, span: Arc<Span>) {
|
||||
match self {
|
||||
Green::Node(n) => Arc::make_mut(n).synthesize(span),
|
||||
Green::Token(t) => t.synthesize(span),
|
||||
SyntaxNode::Inner(n) => Arc::make_mut(n).synthesize(span),
|
||||
SyntaxNode::Leaf(t) => t.synthesize(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Green {
|
||||
impl Default for SyntaxNode {
|
||||
fn default() -> Self {
|
||||
Self::Token(GreenData::new(NodeKind::None, 0))
|
||||
Self::Leaf(NodeData::new(NodeKind::None, 0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Green {
|
||||
impl Debug for SyntaxNode {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Node(node) => node.fmt(f),
|
||||
Self::Token(token) => token.fmt(f),
|
||||
Self::Inner(node) => node.fmt(f),
|
||||
Self::Leaf(token) => token.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An inner node in the untyped green tree.
|
||||
/// An inner node in the untyped syntax tree.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct GreenNode {
|
||||
pub struct InnerNode {
|
||||
/// Node metadata.
|
||||
data: GreenData,
|
||||
data: NodeData,
|
||||
/// This node's children, losslessly make up this node.
|
||||
children: Vec<Green>,
|
||||
children: Vec<SyntaxNode>,
|
||||
/// Whether this node or any of its children are erroneous.
|
||||
erroneous: bool,
|
||||
}
|
||||
|
||||
impl GreenNode {
|
||||
impl InnerNode {
|
||||
/// Creates a new node with the given kind and a single child.
|
||||
pub fn with_child(kind: NodeKind, child: impl Into<Green>) -> Self {
|
||||
pub fn with_child(kind: NodeKind, child: impl Into<SyntaxNode>) -> Self {
|
||||
Self::with_children(kind, vec![child.into()])
|
||||
}
|
||||
|
||||
/// Creates a new node with the given kind and children.
|
||||
pub fn with_children(kind: NodeKind, children: Vec<Green>) -> Self {
|
||||
pub fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self {
|
||||
let mut erroneous = kind.is_error();
|
||||
let len = children
|
||||
.iter()
|
||||
.inspect(|c| erroneous |= c.erroneous())
|
||||
.map(Green::len)
|
||||
.map(SyntaxNode::len)
|
||||
.sum();
|
||||
|
||||
Self {
|
||||
data: GreenData::new(kind, len),
|
||||
data: NodeData::new(kind, len),
|
||||
children,
|
||||
erroneous,
|
||||
}
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(&self) -> &[Green] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
/// The node's metadata.
|
||||
fn data(&self) -> &GreenData {
|
||||
pub fn data(&self) -> &NodeData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
@ -158,6 +197,11 @@ impl GreenNode {
|
||||
self.data().len()
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
|
||||
self.children.iter()
|
||||
}
|
||||
|
||||
/// Set a synthetic span for the node and all its children.
|
||||
pub fn synthesize(&mut self, span: Arc<Span>) {
|
||||
self.data.synthesize(span.clone());
|
||||
@ -167,7 +211,7 @@ impl GreenNode {
|
||||
}
|
||||
|
||||
/// The node's children, mutably.
|
||||
pub(crate) fn children_mut(&mut self) -> &mut [Green] {
|
||||
pub(crate) fn children_mut(&mut self) -> &mut [SyntaxNode] {
|
||||
&mut self.children
|
||||
}
|
||||
|
||||
@ -175,42 +219,44 @@ impl GreenNode {
|
||||
pub(crate) fn replace_children(
|
||||
&mut self,
|
||||
range: Range<usize>,
|
||||
replacement: Vec<Green>,
|
||||
replacement: Vec<SyntaxNode>,
|
||||
) {
|
||||
let superseded = &self.children[range.clone()];
|
||||
let superseded_len: usize = superseded.iter().map(Green::len).sum();
|
||||
let replacement_len: usize = replacement.iter().map(Green::len).sum();
|
||||
let superseded_len: usize = superseded.iter().map(SyntaxNode::len).sum();
|
||||
let replacement_len: usize = replacement.iter().map(SyntaxNode::len).sum();
|
||||
|
||||
// If we're erroneous, but not due to the superseded range, then we will
|
||||
// still be erroneous after the replacement.
|
||||
let still_erroneous = self.erroneous && !superseded.iter().any(Green::erroneous);
|
||||
let still_erroneous =
|
||||
self.erroneous && !superseded.iter().any(SyntaxNode::erroneous);
|
||||
|
||||
self.children.splice(range, replacement);
|
||||
self.data.len = self.data.len + replacement_len - superseded_len;
|
||||
self.erroneous = still_erroneous || self.children.iter().any(Green::erroneous);
|
||||
self.erroneous =
|
||||
still_erroneous || self.children.iter().any(SyntaxNode::erroneous);
|
||||
}
|
||||
|
||||
/// Update the length of this node given the old and new length of
|
||||
/// replaced children.
|
||||
pub(crate) fn update_parent(&mut self, new_len: usize, old_len: usize) {
|
||||
self.data.len = self.data.len() + new_len - old_len;
|
||||
self.erroneous = self.children.iter().any(Green::erroneous);
|
||||
self.erroneous = self.children.iter().any(SyntaxNode::erroneous);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GreenNode> for Green {
|
||||
fn from(node: GreenNode) -> Self {
|
||||
impl From<InnerNode> for SyntaxNode {
|
||||
fn from(node: InnerNode) -> Self {
|
||||
Arc::new(node).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<GreenNode>> for Green {
|
||||
fn from(node: Arc<GreenNode>) -> Self {
|
||||
Self::Node(node)
|
||||
impl From<Arc<InnerNode>> for SyntaxNode {
|
||||
fn from(node: Arc<InnerNode>) -> Self {
|
||||
Self::Inner(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GreenNode {
|
||||
impl Debug for InnerNode {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.data.fmt(f)?;
|
||||
if !self.children.is_empty() {
|
||||
@ -223,20 +269,18 @@ impl Debug for GreenNode {
|
||||
|
||||
/// Data shared between inner and leaf nodes.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct GreenData {
|
||||
pub struct NodeData {
|
||||
/// What kind of node this is (each kind would have its own struct in a
|
||||
/// strongly typed AST).
|
||||
kind: NodeKind,
|
||||
/// The byte length of the node in the source.
|
||||
len: usize,
|
||||
/// A synthetic span for the node, usually this is `None`.
|
||||
span: Option<Arc<Span>>,
|
||||
}
|
||||
|
||||
impl GreenData {
|
||||
impl NodeData {
|
||||
/// Create new node metadata.
|
||||
pub fn new(kind: NodeKind, len: usize) -> Self {
|
||||
Self { len, kind, span: None }
|
||||
Self { len, kind }
|
||||
}
|
||||
|
||||
/// The type of the node.
|
||||
@ -250,271 +294,26 @@ impl GreenData {
|
||||
}
|
||||
|
||||
/// Set a synthetic span for the node.
|
||||
pub fn synthesize(&mut self, span: Arc<Span>) {
|
||||
self.span = Some(span)
|
||||
pub fn synthesize(&mut self, _: Arc<Span>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GreenData> for Green {
|
||||
fn from(token: GreenData) -> Self {
|
||||
Self::Token(token)
|
||||
impl From<NodeData> for SyntaxNode {
|
||||
fn from(token: NodeData) -> Self {
|
||||
Self::Leaf(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GreenData {
|
||||
impl Debug for NodeData {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}: {}", self.kind, self.len)
|
||||
}
|
||||
}
|
||||
|
||||
/// A owned wrapper for a green node with span information.
|
||||
///
|
||||
/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST node.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct RedNode {
|
||||
id: SourceId,
|
||||
offset: usize,
|
||||
green: Green,
|
||||
}
|
||||
|
||||
impl RedNode {
|
||||
/// Create a new red node from a root [`GreenNode`].
|
||||
pub fn from_root(root: Arc<GreenNode>, id: SourceId) -> Self {
|
||||
Self { id, offset: 0, green: root.into() }
|
||||
}
|
||||
|
||||
/// Convert to a borrowed representation.
|
||||
pub fn as_ref(&self) -> RedRef<'_> {
|
||||
RedRef {
|
||||
id: self.id,
|
||||
offset: self.offset,
|
||||
green: &self.green,
|
||||
}
|
||||
}
|
||||
|
||||
/// The node's metadata.
|
||||
pub fn data(&self) -> &GreenData {
|
||||
self.as_ref().data()
|
||||
}
|
||||
|
||||
/// The type of the node.
|
||||
pub fn kind(&self) -> &NodeKind {
|
||||
self.as_ref().kind()
|
||||
}
|
||||
|
||||
/// The length of the node.
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_ref().len()
|
||||
}
|
||||
|
||||
/// The span of the node.
|
||||
pub fn span(&self) -> Span {
|
||||
self.as_ref().span()
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors(&self) -> Vec<Error> {
|
||||
self.as_ref().errors()
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
self.as_ref().cast()
|
||||
}
|
||||
|
||||
/// The children of the node.
|
||||
pub fn children(&self) -> Children<'_> {
|
||||
self.as_ref().children()
|
||||
}
|
||||
|
||||
/// Get the first child that can cast to some AST type.
|
||||
pub fn cast_first_child<T: TypedNode>(&self) -> Option<T> {
|
||||
self.as_ref().cast_first_child()
|
||||
}
|
||||
|
||||
/// Get the last child that can cast to some AST type.
|
||||
pub fn cast_last_child<T: TypedNode>(&self) -> Option<T> {
|
||||
self.as_ref().cast_last_child()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RedNode {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.as_ref().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed wrapper for a [`GreenNode`] with span information.
|
||||
///
|
||||
/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node.
|
||||
#[derive(Copy, Clone, PartialEq, Hash)]
|
||||
pub struct RedRef<'a> {
|
||||
id: SourceId,
|
||||
offset: usize,
|
||||
green: &'a Green,
|
||||
}
|
||||
|
||||
impl<'a> RedRef<'a> {
|
||||
/// Convert to an owned representation.
|
||||
pub fn own(self) -> RedNode {
|
||||
RedNode {
|
||||
id: self.id,
|
||||
offset: self.offset,
|
||||
green: self.green.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The node's metadata.
|
||||
pub fn data(self) -> &'a GreenData {
|
||||
self.green.data()
|
||||
}
|
||||
|
||||
/// The type of the node.
|
||||
pub fn kind(self) -> &'a NodeKind {
|
||||
self.green.kind()
|
||||
}
|
||||
|
||||
/// The length of the node.
|
||||
pub fn len(self) -> usize {
|
||||
self.green.len()
|
||||
}
|
||||
|
||||
/// The span of the node.
|
||||
pub fn span(self) -> Span {
|
||||
match self.data().span.as_deref() {
|
||||
Some(&span) => span,
|
||||
None => Span::new(self.id, self.offset, self.offset + self.len()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the node is a leaf node.
|
||||
pub fn is_leaf(self) -> bool {
|
||||
self.green.is_leaf()
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors(self) -> Vec<Error> {
|
||||
if !self.green.erroneous() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
match self.kind() {
|
||||
NodeKind::Error(pos, msg) => {
|
||||
let mut span = self.span();
|
||||
if self.data().span.is_none() {
|
||||
span = match pos {
|
||||
ErrorPos::Start => span.at_start(),
|
||||
ErrorPos::Full => span,
|
||||
ErrorPos::End => span.at_end(),
|
||||
};
|
||||
}
|
||||
|
||||
vec![Error::new(span, msg.to_string())]
|
||||
}
|
||||
_ => self
|
||||
.children()
|
||||
.filter(|red| red.green.erroneous())
|
||||
.flat_map(|red| red.errors())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all leaf descendants of this node (may include itself).
|
||||
pub fn leafs(self) -> Vec<Self> {
|
||||
if self.is_leaf() {
|
||||
vec![self]
|
||||
} else {
|
||||
self.children().flat_map(Self::leafs).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
T::from_red(self)
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(self) -> Children<'a> {
|
||||
let children = match &self.green {
|
||||
Green::Node(node) => node.children(),
|
||||
Green::Token(_) => &[],
|
||||
};
|
||||
|
||||
Children {
|
||||
id: self.id,
|
||||
iter: children.iter(),
|
||||
front: self.offset,
|
||||
back: self.offset + self.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the first child that can cast to some AST type.
|
||||
pub fn cast_first_child<T: TypedNode>(self) -> Option<T> {
|
||||
self.children().find_map(RedRef::cast)
|
||||
}
|
||||
|
||||
/// Get the last child that can cast to some AST type.
|
||||
pub fn cast_last_child<T: TypedNode>(self) -> Option<T> {
|
||||
self.children().rev().find_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RedRef<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}: {:?}", self.kind(), self.span())?;
|
||||
let mut children = self.children().peekable();
|
||||
if children.peek().is_some() {
|
||||
f.write_str(" ")?;
|
||||
f.debug_list().entries(children.map(RedRef::own)).finish()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the children of a red node.
|
||||
pub struct Children<'a> {
|
||||
id: SourceId,
|
||||
iter: std::slice::Iter<'a, Green>,
|
||||
front: usize,
|
||||
back: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Children<'a> {
|
||||
type Item = RedRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|green| {
|
||||
let offset = self.front;
|
||||
self.front += green.len();
|
||||
RedRef { id: self.id, offset, green }
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for Children<'_> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|green| {
|
||||
self.back -= green.len();
|
||||
RedRef { id: self.id, offset: self.back, green }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for Children<'_> {}
|
||||
|
||||
/// All syntactical building blocks that can be part of a Typst document.
|
||||
///
|
||||
/// Can be emitted as a token by the tokenizer or as part of a green node by
|
||||
/// Can be emitted as a token by the tokenizer or as part of a syntax node by
|
||||
/// the parser.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum NodeKind {
|
||||
|
@ -487,8 +487,7 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool {
|
||||
ok &= apply(start .. end, supplement);
|
||||
}
|
||||
|
||||
let red = SourceFile::detached(src).red();
|
||||
let leafs = red.as_ref().leafs();
|
||||
let leafs = typst::parse::parse(src).leafs();
|
||||
let leaf_start = leafs[pick(0 .. leafs.len())].span().start;
|
||||
let supplement = supplements[pick(0 .. supplements.len())];
|
||||
ok &= apply(leaf_start .. leaf_start, supplement);
|
||||
|
Loading…
x
Reference in New Issue
Block a user