mirror of
https://github.com/typst/typst
synced 2025-05-16 18:15:29 +08:00
decorator -> annotation
This commit is contained in:
parent
0fb417ddb6
commit
fb77128423
@ -81,33 +81,33 @@ impl<'a> Markup<'a> {
|
||||
}
|
||||
|
||||
node! {
|
||||
DecoratorName
|
||||
AnnotationName
|
||||
}
|
||||
|
||||
impl<'a> DecoratorName<'a> {
|
||||
/// Get the decorator name.
|
||||
impl<'a> AnnotationName<'a> {
|
||||
/// Get the annotation name.
|
||||
pub fn get(self) -> &'a EcoString {
|
||||
self.0.text()
|
||||
}
|
||||
|
||||
/// Get the decorator name as a string slice.
|
||||
/// Get the annotation name as a string slice.
|
||||
pub fn as_str(self) -> &'a str {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// A decorator: `/! allow("warning")`.
|
||||
Decorator
|
||||
/// An annotation: `/! allow("warning")`.
|
||||
Annotation
|
||||
}
|
||||
|
||||
impl<'a> Decorator<'a> {
|
||||
/// The name of the decorator, e.g. `allow`.
|
||||
pub fn name(self) -> DecoratorName<'a> {
|
||||
impl<'a> Annotation<'a> {
|
||||
/// The name of the annotation, e.g. `allow`.
|
||||
pub fn name(self) -> AnnotationName<'a> {
|
||||
self.0.cast_first_match().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The decorator's arguments. Currently, they are always strings.
|
||||
/// The annotation's arguments. Currently, they are always strings.
|
||||
pub fn arguments(self) -> impl DoubleEndedIterator<Item = Str<'a>> {
|
||||
self.0.children().filter_map(Str::from_untyped)
|
||||
}
|
||||
|
@ -286,9 +286,9 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
|
||||
SyntaxKind::Destructuring => None,
|
||||
SyntaxKind::DestructAssignment => None,
|
||||
|
||||
SyntaxKind::Decorator => None,
|
||||
SyntaxKind::DecoratorMarker => None,
|
||||
SyntaxKind::DecoratorName => None,
|
||||
SyntaxKind::Annotation => None,
|
||||
SyntaxKind::AnnotationMarker => None,
|
||||
SyntaxKind::AnnotationName => None,
|
||||
|
||||
SyntaxKind::LineComment => Some(Tag::Comment),
|
||||
SyntaxKind::BlockComment => Some(Tag::Comment),
|
||||
|
@ -13,8 +13,8 @@ pub enum SyntaxKind {
|
||||
LineComment,
|
||||
/// A block comment: `/* ... */`.
|
||||
BlockComment,
|
||||
/// A decorator: `// @allow("warning")`.
|
||||
Decorator,
|
||||
/// An annotation: `// @allow("warning")`.
|
||||
Annotation,
|
||||
|
||||
/// The contents of a file or content block.
|
||||
Markup,
|
||||
@ -281,10 +281,10 @@ pub enum SyntaxKind {
|
||||
/// A destructuring assignment expression: `(x, y) = (1, 2)`.
|
||||
DestructAssignment,
|
||||
|
||||
/// A decorator's marker: `// @`.
|
||||
DecoratorMarker,
|
||||
/// A decorator's name: `allow`.
|
||||
DecoratorName,
|
||||
/// An annotation's marker: `// @`.
|
||||
AnnotationMarker,
|
||||
/// An annotation's name: `allow`.
|
||||
AnnotationName,
|
||||
}
|
||||
|
||||
impl SyntaxKind {
|
||||
@ -366,7 +366,7 @@ impl SyntaxKind {
|
||||
| Self::BlockComment
|
||||
| Self::Space
|
||||
| Self::Parbreak
|
||||
| Self::Decorator
|
||||
| Self::Annotation
|
||||
)
|
||||
}
|
||||
|
||||
@ -382,7 +382,7 @@ impl SyntaxKind {
|
||||
Self::Error => "syntax error",
|
||||
Self::LineComment => "line comment",
|
||||
Self::BlockComment => "block comment",
|
||||
Self::Decorator => "decorator",
|
||||
Self::Annotation => "annotation",
|
||||
Self::Markup => "markup",
|
||||
Self::Text => "text",
|
||||
Self::Space => "space",
|
||||
@ -510,8 +510,8 @@ impl SyntaxKind {
|
||||
Self::FuncReturn => "`return` expression",
|
||||
Self::Destructuring => "destructuring pattern",
|
||||
Self::DestructAssignment => "destructuring assignment expression",
|
||||
Self::DecoratorMarker => "decorator marker",
|
||||
Self::DecoratorName => "decorator name",
|
||||
Self::AnnotationMarker => "annotation marker",
|
||||
Self::AnnotationName => "annotation name",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ impl Lexer<'_> {
|
||||
/// syntax node returned might not always be a leaf, but could actually
|
||||
/// come with a subtree (could be an inner node). This happens when it is
|
||||
/// preferred to perform parsing at the character level instead of at the
|
||||
/// token level, as seen, for example, in [`decorator`](Lexer::decorator).
|
||||
/// token level, as seen, for example, in [`annotation`](Lexer::annotation).
|
||||
pub fn next(&mut self) -> SyntaxNode {
|
||||
if self.mode == LexMode::Raw {
|
||||
let Some((kind, end)) = self.raw.pop() else {
|
||||
@ -120,7 +120,7 @@ impl Lexer<'_> {
|
||||
let token = match self.s.eat() {
|
||||
Some(c) if is_space(c, self.mode) => self.whitespace(start, c),
|
||||
Some('/') if self.s.eat_if('/') => {
|
||||
return self.line_comment_or_decorator(start);
|
||||
return self.line_comment_or_annotation(start);
|
||||
}
|
||||
Some('/') if self.s.eat_if('*') => self.block_comment(),
|
||||
Some('*') if self.s.eat_if('/') => {
|
||||
@ -185,14 +185,14 @@ impl Lexer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a decorator if the line comment has the form
|
||||
/// Parses an annotation if the line comment has the form
|
||||
/// `// @something`
|
||||
///
|
||||
/// Otherwise, parses a regular line comment.
|
||||
fn line_comment_or_decorator(&mut self, start: usize) -> SyntaxNode {
|
||||
fn line_comment_or_annotation(&mut self, start: usize) -> SyntaxNode {
|
||||
self.s.eat_while(is_inline_whitespace);
|
||||
if self.s.eat_if('@') {
|
||||
return self.decorator(start);
|
||||
return self.annotation(start);
|
||||
}
|
||||
self.s.eat_until(is_newline);
|
||||
self.emit_token(SyntaxKind::LineComment, start)
|
||||
@ -224,18 +224,18 @@ impl Lexer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Decorator lexing and auxiliary methods.
|
||||
/// Annotation lexing and auxiliary methods.
|
||||
impl Lexer<'_> {
|
||||
/// Lexes and parses a decorator into a complete syntax subtree.
|
||||
/// Lexes and parses an annotation into a complete syntax subtree.
|
||||
///
|
||||
/// The lexer is fully responsible for the decorator, as it is simpler to
|
||||
/// The lexer is fully responsible for the annotation, as it is simpler to
|
||||
/// parse them at the character level, given they follow a very simple
|
||||
/// and rigid structure, in the form
|
||||
/// `/! decorator-name("string argument1", "string argument2")`
|
||||
/// `/! annotation-name("string argument1", "string argument2")`
|
||||
/// with optional whitespaces and comments between arguments.
|
||||
fn decorator(&mut self, start: usize) -> SyntaxNode {
|
||||
fn annotation(&mut self, start: usize) -> SyntaxNode {
|
||||
// Start by lexing the marker.
|
||||
let marker = self.emit_token(SyntaxKind::DecoratorMarker, start);
|
||||
let marker = self.emit_token(SyntaxKind::AnnotationMarker, start);
|
||||
let mut subtree = vec![marker];
|
||||
|
||||
let current_start = self.s.cursor();
|
||||
@ -245,35 +245,35 @@ impl Lexer<'_> {
|
||||
subtree.push(self.emit_token(SyntaxKind::Space, current_start));
|
||||
}
|
||||
|
||||
// Lex the decorator name.
|
||||
// Lex the annotation name.
|
||||
let current_start = self.s.cursor();
|
||||
if !self.s.eat_if(is_id_start) {
|
||||
self.s.eat_until(is_newline);
|
||||
subtree.push(self.emit_error("expected identifier", current_start));
|
||||
|
||||
// Return a single error node until the end of the decorator.
|
||||
return SyntaxNode::inner(SyntaxKind::Decorator, subtree);
|
||||
// Return a single error node until the end of the annotation.
|
||||
return SyntaxNode::inner(SyntaxKind::Annotation, subtree);
|
||||
}
|
||||
|
||||
let decorator_name = self.decorator_name(current_start);
|
||||
subtree.push(self.emit_token(decorator_name, current_start));
|
||||
let annotation_name = self.annotation_name(current_start);
|
||||
subtree.push(self.emit_token(annotation_name, current_start));
|
||||
|
||||
// Left parenthesis before decorator arguments.
|
||||
// Left parenthesis before annotation arguments.
|
||||
let current_start = self.s.cursor();
|
||||
if !self.s.eat_if('(') {
|
||||
self.s.eat_until(is_newline);
|
||||
subtree.push(self.emit_error("expected opening paren", current_start));
|
||||
|
||||
// Return a single error node until the end of the decorator.
|
||||
return SyntaxNode::inner(SyntaxKind::Decorator, subtree);
|
||||
// Return a single error node until the end of the annotation.
|
||||
return SyntaxNode::inner(SyntaxKind::Annotation, subtree);
|
||||
}
|
||||
|
||||
subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
|
||||
|
||||
// Decorator arguments:
|
||||
// Annotation arguments:
|
||||
// Keep reading until we find a right parenthesis or newline. We have
|
||||
// to check the newline before eating (through '.peek()') to ensure it
|
||||
// is not considered part of the decorator.
|
||||
// is not considered part of the annotation.
|
||||
let mut current_start = self.s.cursor();
|
||||
let mut expecting_comma = false;
|
||||
let mut finished = false;
|
||||
@ -284,9 +284,9 @@ impl Lexer<'_> {
|
||||
SyntaxKind::Space
|
||||
}
|
||||
Some('/') if self.s.eat_if('/') => {
|
||||
let node = self.line_comment_or_decorator(current_start);
|
||||
if node.kind() == SyntaxKind::Decorator {
|
||||
self.error("cannot have multiple decorators per line")
|
||||
let node = self.line_comment_or_annotation(current_start);
|
||||
if node.kind() == SyntaxKind::Annotation {
|
||||
self.error("cannot have multiple annotations per line")
|
||||
} else {
|
||||
subtree.push(node);
|
||||
current_start = self.s.cursor();
|
||||
@ -298,7 +298,7 @@ impl Lexer<'_> {
|
||||
// After we finished specifying arguments, there must only
|
||||
// be whitespaces until the line ends.
|
||||
self.s.eat_until(char::is_whitespace);
|
||||
self.error("expected end of decorator")
|
||||
self.error("expected end of annotation")
|
||||
}
|
||||
Some('"') if expecting_comma => {
|
||||
self.s.eat_until(|c| c == ',' || c == ')' || is_newline(c));
|
||||
@ -306,7 +306,7 @@ impl Lexer<'_> {
|
||||
}
|
||||
Some('"') => {
|
||||
expecting_comma = true;
|
||||
self.decorator_string()
|
||||
self.annotation_string()
|
||||
}
|
||||
Some(',') if expecting_comma => {
|
||||
expecting_comma = false;
|
||||
@ -318,7 +318,7 @@ impl Lexer<'_> {
|
||||
SyntaxKind::RightParen
|
||||
}
|
||||
Some(c) => self.error(eco_format!(
|
||||
"the character '{c}' is not valid in a decorator"
|
||||
"the character '{c}' is not valid in an annotation"
|
||||
)),
|
||||
None => break,
|
||||
};
|
||||
@ -334,34 +334,34 @@ impl Lexer<'_> {
|
||||
subtree.push(self.emit_error("expected closing paren", self.s.cursor()));
|
||||
}
|
||||
|
||||
SyntaxNode::inner(SyntaxKind::Decorator, subtree)
|
||||
SyntaxNode::inner(SyntaxKind::Annotation, subtree)
|
||||
}
|
||||
|
||||
/// Lexes a decorator name.
|
||||
/// Lexes an annotation name.
|
||||
///
|
||||
/// A decorator name is an identifier within a specific subset of allowed
|
||||
/// identifiers. Currently, `allow` is the only valid decorator name.
|
||||
fn decorator_name(&mut self, start: usize) -> SyntaxKind {
|
||||
/// An annotation name is an identifier within a specific subset of allowed
|
||||
/// identifiers. Currently, `allow` is the only valid annotation name.
|
||||
fn annotation_name(&mut self, start: usize) -> SyntaxKind {
|
||||
self.s.eat_while(is_id_continue);
|
||||
let ident = self.s.from(start);
|
||||
|
||||
if ident == "allow" {
|
||||
SyntaxKind::DecoratorName
|
||||
SyntaxKind::AnnotationName
|
||||
} else {
|
||||
let error = self.error(eco_format!("invalid decorator name"));
|
||||
let error = self.error(eco_format!("invalid annotation name"));
|
||||
self.hint("must be 'allow'");
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
/// Lexes a string in a decorator.
|
||||
/// Lexes a string in an annotation.
|
||||
///
|
||||
/// Currently, such strings only allow a very restricted set of characters.
|
||||
/// These restrictions may be lifted in the future.
|
||||
fn decorator_string(&mut self) -> SyntaxKind {
|
||||
// TODO: Allow more characters in decorators' strings, perhaps allowing
|
||||
fn annotation_string(&mut self) -> SyntaxKind {
|
||||
// TODO: Allow more characters in annotations' strings, perhaps allowing
|
||||
// newlines somehow.
|
||||
// Could perhaps use one //! per line so we can break a decorator into
|
||||
// Could perhaps use one //! per line so we can break an annotation into
|
||||
// multiple lines in a sensible way.
|
||||
let start = self.s.cursor();
|
||||
self.s.eat_while(|c| !is_newline(c) && c != '"');
|
||||
@ -371,9 +371,9 @@ impl Lexer<'_> {
|
||||
return self.error("unclosed string");
|
||||
}
|
||||
|
||||
if let Some(c) = content.chars().find(|c| !is_valid_in_decorator_string(*c)) {
|
||||
if let Some(c) = content.chars().find(|c| !is_valid_in_annotation_string(*c)) {
|
||||
return self
|
||||
.error(eco_format!("invalid character '{c}' in a decorator's string"));
|
||||
.error(eco_format!("invalid character '{c}' in an annotation's string"));
|
||||
}
|
||||
|
||||
SyntaxKind::Str
|
||||
@ -1160,9 +1160,9 @@ fn is_valid_in_label_literal(c: char) -> bool {
|
||||
is_id_continue(c) || matches!(c, ':' | '.')
|
||||
}
|
||||
|
||||
/// Whether a character can be part of a string in a decorator.
|
||||
/// Whether a character can be part of a string in an annotation.
|
||||
#[inline]
|
||||
fn is_valid_in_decorator_string(c: char) -> bool {
|
||||
fn is_valid_in_annotation_string(c: char) -> bool {
|
||||
is_id_continue(c) || c == '@' || c == '/'
|
||||
}
|
||||
|
||||
|
@ -818,15 +818,15 @@ impl<'a> LinkedNode<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the first sibling decorator node at the line above this node.
|
||||
/// This is done by moving backwards, checking for decorators, until we hit
|
||||
/// Get the first sibling annotation node at the line above this node.
|
||||
/// This is done by moving backwards, checking for annotations, until we hit
|
||||
/// a second newline (that is, we only check, at most, the line before this
|
||||
/// node).
|
||||
pub fn prev_attached_decorator(&self) -> Option<Self> {
|
||||
pub fn prev_attached_annotation(&self) -> Option<Self> {
|
||||
let mut cursor = self.prev_sibling_inner()?;
|
||||
let mut newlines = cursor.capped_newlines();
|
||||
while newlines < 2 {
|
||||
if cursor.kind() == SyntaxKind::Decorator {
|
||||
if cursor.kind() == SyntaxKind::Annotation {
|
||||
return Some(cursor);
|
||||
}
|
||||
|
||||
@ -834,8 +834,8 @@ impl<'a> LinkedNode<'a> {
|
||||
newlines += cursor.capped_newlines();
|
||||
}
|
||||
|
||||
// Decorators are attached if they're in the previous line.
|
||||
// If we counted at least two newlines, no decorators are attached to
|
||||
// Annotations are attached if they're in the previous line.
|
||||
// If we counted at least two newlines, no annotations are attached to
|
||||
// this node.
|
||||
None
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ fn markup_expr(p: &mut Parser, at_start: &mut bool) {
|
||||
| SyntaxKind::Parbreak
|
||||
| SyntaxKind::LineComment
|
||||
| SyntaxKind::BlockComment
|
||||
| SyntaxKind::Decorator => {
|
||||
| SyntaxKind::Annotation => {
|
||||
p.eat();
|
||||
return;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ pub const MARKUP_EXPR: SyntaxSet = SyntaxSet::new()
|
||||
.add(SyntaxKind::Parbreak)
|
||||
.add(SyntaxKind::LineComment)
|
||||
.add(SyntaxKind::BlockComment)
|
||||
.add(SyntaxKind::Decorator)
|
||||
.add(SyntaxKind::Annotation)
|
||||
.add(SyntaxKind::Text)
|
||||
.add(SyntaxKind::Linebreak)
|
||||
.add(SyntaxKind::Escape)
|
||||
|
@ -542,7 +542,7 @@ pub fn deduplicate_and_suppress_warnings(
|
||||
|
||||
/// Checks if a given warning is suppressed given one span it has a tracepoint
|
||||
/// in. If one of the ancestors of the node where the warning occurred has a
|
||||
/// warning suppression decorator sibling right before it suppressing this
|
||||
/// warning suppression annotation sibling right before it suppressing this
|
||||
/// particular warning, the warning is considered suppressed.
|
||||
fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier) -> bool {
|
||||
// Don't suppress detached warnings.
|
||||
@ -556,13 +556,13 @@ fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier)
|
||||
// Walk the parent nodes to check for a warning suppression in the
|
||||
// previous line.
|
||||
while let Some(node) = searched_node {
|
||||
let mut searched_decorator = node.prev_attached_decorator();
|
||||
while let Some(sibling) = searched_decorator {
|
||||
let decorator = sibling.cast::<ast::Decorator>().unwrap();
|
||||
if check_decorator_suppresses_warning(decorator, identifier) {
|
||||
let mut searched_annotation = node.prev_attached_annotation();
|
||||
while let Some(sibling) = searched_annotation {
|
||||
let annotation = sibling.cast::<ast::Annotation>().unwrap();
|
||||
if check_annotation_suppresses_warning(annotation, identifier) {
|
||||
return true;
|
||||
}
|
||||
searched_decorator = sibling.prev_attached_decorator();
|
||||
searched_annotation = sibling.prev_attached_annotation();
|
||||
}
|
||||
searched_node = node.parent();
|
||||
}
|
||||
@ -570,17 +570,17 @@ fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier)
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if an 'allow' decorator would cause a warning with a particular
|
||||
/// Checks if an 'allow' annotation would cause a warning with a particular
|
||||
/// identifier to be suppressed.
|
||||
fn check_decorator_suppresses_warning(
|
||||
decorator: ast::Decorator,
|
||||
fn check_annotation_suppresses_warning(
|
||||
annotation: ast::Annotation,
|
||||
warning: &Identifier,
|
||||
) -> bool {
|
||||
if decorator.name().as_str() != "allow" {
|
||||
if annotation.name().as_str() != "allow" {
|
||||
return false;
|
||||
}
|
||||
|
||||
for argument in decorator.arguments() {
|
||||
for argument in annotation.arguments() {
|
||||
if warning.name() == argument.get() {
|
||||
return true;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Test decorators.
|
||||
// Test annotations.
|
||||
|
||||
--- basic-decorators ---
|
||||
--- basic-annotations ---
|
||||
|
||||
// @allow()
|
||||
// @allow("A")
|
||||
@ -19,7 +19,7 @@
|
||||
h(#0em)
|
||||
$
|
||||
|
||||
--- decorator-comments ---
|
||||
--- annotation-comments ---
|
||||
|
||||
// @allow("abc") // this is ok
|
||||
|
||||
@ -31,22 +31,22 @@ $
|
||||
this is ok
|
||||
*/, "abc")
|
||||
|
||||
--- decorator-strings ---
|
||||
--- annotation-strings ---
|
||||
|
||||
// @allow("@some/thing-there123")
|
||||
|
||||
--- unknown-decorator ---
|
||||
// Error: 2:5-2:13 invalid decorator name
|
||||
--- unknown-annotation ---
|
||||
// Error: 2:5-2:13 invalid annotation name
|
||||
// Hint: 2:5-2:13 must be 'allow'
|
||||
|
||||
// @whatever()
|
||||
|
||||
--- invalid-decorator-syntax ---
|
||||
// Error: 2:11-2:12 the character '*' is not valid in a decorator
|
||||
--- invalid-annotation-syntax ---
|
||||
// Error: 2:11-2:12 the character '*' is not valid in an annotation
|
||||
|
||||
// @allow(*)
|
||||
|
||||
// Error: 2:11-2:12 the character '5' is not valid in a decorator
|
||||
// Error: 2:11-2:12 the character '5' is not valid in an annotation
|
||||
|
||||
// @allow(5)
|
||||
|
||||
@ -63,12 +63,12 @@ this is ok
|
||||
|
||||
// @allow("abc
|
||||
|
||||
// Error: 2:18-2:21 expected end of decorator
|
||||
// Error: 2:18-2:21 expected end of annotation
|
||||
|
||||
// @allow("abc") abc
|
||||
|
||||
// Error: 2:17-2:22 expected comma
|
||||
// Error: 2:24-2:27 expected end of decorator
|
||||
// Error: 2:24-2:27 expected end of annotation
|
||||
|
||||
// @allow("abc" "abc") abc
|
||||
|
||||
@ -80,23 +80,23 @@ this is ok
|
||||
|
||||
// @allow(, "abc", "abc", "abc")
|
||||
|
||||
--- invalid-decorator-strings ---
|
||||
--- invalid-annotation-strings ---
|
||||
|
||||
// Error: 2:11-2:16 invalid character ' ' in a decorator's string
|
||||
// Error: 2:11-2:16 invalid character ' ' in an annotation's string
|
||||
|
||||
// @allow("a b")
|
||||
|
||||
// Error: 2:11-2:19 invalid character '|' in a decorator's string
|
||||
// Error: 2:11-2:19 invalid character '|' in an annotation's string
|
||||
|
||||
// @allow("aaaaa|")
|
||||
|
||||
// TODO: Why does this print / instead of \?
|
||||
// Error: 2:11-2:19 invalid character '/' in a decorator's string
|
||||
// Error: 2:11-2:19 invalid character '/' in an annotation's string
|
||||
|
||||
// @allow("aaaaa\")
|
||||
|
||||
--- invalid-decorator-in-decorator ---
|
||||
// Error: 2:18-2:34 cannot have multiple decorators per line
|
||||
--- invalid-annotation-in-annotation ---
|
||||
// Error: 2:18-2:34 cannot have multiple annotations per line
|
||||
|
||||
// @allow("aaa") // @allow("bbb")
|
||||
|
Loading…
x
Reference in New Issue
Block a user