summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Fischler <Mafii@users.noreply.github.com>2023-07-05 16:25:26 +0200
committerGitHub <noreply@github.com>2023-07-05 16:25:26 +0200
commit5bdad06d9202ab2af728778ab84acc385c439c64 (patch)
tree4aa31178ae52727a5faf7bbd784a784ebdfae5e2
parentd37217aaa4b5029d7ec8405739b8deda4792696f (diff)
Parser hints infrastructure (#1570)
-rw-r--r--crates/typst/src/syntax/node.rs19
-rw-r--r--crates/typst/src/syntax/parser.rs12
-rw-r--r--tests/typ/compiler/for.typ3
3 files changed, 31 insertions, 3 deletions
diff --git a/crates/typst/src/syntax/node.rs b/crates/typst/src/syntax/node.rs
index 6a66416d..3bbecb6c 100644
--- a/crates/typst/src/syntax/node.rs
+++ b/crates/typst/src/syntax/node.rs
@@ -135,6 +135,13 @@ impl SyntaxNode {
}
}
+ /// Adds a user-presentable hint if this is an error node.
+ pub fn hint(&mut self, hint: impl Into<EcoString>) {
+ if let Repr::Error(error) = &mut self.0 {
+ Arc::make_mut(error).hint(hint);
+ }
+ }
+
/// The error messages for this node and its descendants.
pub fn errors(&self) -> Vec<SourceError> {
if !self.erroneous() {
@@ -142,7 +149,8 @@ impl SyntaxNode {
}
if let Repr::Error(error) = &self.0 {
- vec![SourceError::new(error.span, error.message.clone())]
+ vec![SourceError::new(error.span, error.message.clone())
+ .with_hints(error.hints.to_owned())]
} else {
self.children()
.filter(|node| node.erroneous())
@@ -539,6 +547,9 @@ struct ErrorNode {
text: EcoString,
/// The node's span.
span: Span,
+ /// Additonal hints to the user, indicating how this error could be avoided
+ /// or worked around.
+ hints: Vec<EcoString>,
}
impl ErrorNode {
@@ -548,6 +559,7 @@ impl ErrorNode {
message: message.into(),
text: text.into(),
span: Span::detached(),
+ hints: vec![],
}
}
@@ -555,6 +567,11 @@ impl ErrorNode {
fn len(&self) -> usize {
self.text.len()
}
+
+ /// Add a user-presentable hint to this error node.
+ fn hint(&mut self, hint: impl Into<EcoString>) {
+ self.hints.push(hint.into());
+ }
}
impl Debug for ErrorNode {
diff --git a/crates/typst/src/syntax/parser.rs b/crates/typst/src/syntax/parser.rs
index 54670df5..4849ff79 100644
--- a/crates/typst/src/syntax/parser.rs
+++ b/crates/typst/src/syntax/parser.rs
@@ -1066,7 +1066,8 @@ fn for_loop(p: &mut Parser) {
p.assert(SyntaxKind::For);
pattern(p);
if p.at(SyntaxKind::Comma) {
- p.expected("keyword `in` - did you mean to use a destructuring pattern?");
+ p.expected("keyword `in`");
+ p.hint("did you mean to use a destructuring pattern?");
if !p.eat_if(SyntaxKind::Ident) {
p.eat_if(SyntaxKind::Underscore);
}
@@ -1613,6 +1614,15 @@ impl<'s> Parser<'s> {
self.skip();
}
+ // Adds a hint to the last node, if the last node is an error.
+ fn hint(&mut self, hint: impl Into<EcoString>) {
+ self.unskip();
+ if let Some(last) = self.nodes.last_mut() {
+ last.hint(hint);
+ }
+ self.skip();
+ }
+
fn expected_at(&mut self, m: Marker, thing: &str) {
let message = eco_format!("expected {}", thing);
let error = SyntaxNode::error(message, "");
diff --git a/tests/typ/compiler/for.typ b/tests/typ/compiler/for.typ
index 7a0d9601..b2a2287d 100644
--- a/tests/typ/compiler/for.typ
+++ b/tests/typ/compiler/for.typ
@@ -92,7 +92,8 @@
---
// Destructuring without parentheses.
-// Error: 7 expected keyword `in` - did you mean to use a destructuring pattern?
+// Error: 7 expected keyword `in`
+// Hint: 7 did you mean to use a destructuring pattern?
#for k, v in (a: 4, b: 5) {
dont-care
}