diff options
| author | Pg Biel <9021226+PgBiel@users.noreply.github.com> | 2023-08-30 08:36:02 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-30 13:36:02 +0200 |
| commit | 19b91d59d1467956f8175146a16b63d303eee0d7 (patch) | |
| tree | e2099ccec6fd1fa260597cb9700cbff43e747199 /crates/typst-syntax | |
| parent | 8a0dd88f1073f8fac9b2db022027eae3752dffd7 (diff) | |
Allow renaming imports with `as` (#1923)
Diffstat (limited to 'crates/typst-syntax')
| -rw-r--r-- | crates/typst-syntax/src/ast.rs | 71 | ||||
| -rw-r--r-- | crates/typst-syntax/src/kind.rs | 3 | ||||
| -rw-r--r-- | crates/typst-syntax/src/parser.rs | 14 |
3 files changed, 85 insertions, 3 deletions
diff --git a/crates/typst-syntax/src/ast.rs b/crates/typst-syntax/src/ast.rs index 7cf0cbd5..fd3bfebe 100644 --- a/crates/typst-syntax/src/ast.rs +++ b/crates/typst-syntax/src/ast.rs @@ -1988,6 +1988,15 @@ impl<'a> ModuleImport<'a> { _ => Option::None, }) } + + /// The name this module was assigned to, if it was renamed with `as` + /// (`renamed` in `import "..." as renamed`). + pub fn new_name(self) -> Option<Ident<'a>> { + self.0 + .children() + .skip_while(|child| child.kind() != SyntaxKind::As) + .find_map(SyntaxNode::cast) + } } /// The items that ought to be imported from a file. @@ -2005,9 +2014,65 @@ node! { } impl<'a> ImportItems<'a> { - /// The items to import from the module. - pub fn idents(self) -> impl DoubleEndedIterator<Item = Ident<'a>> { - self.0.children().filter_map(SyntaxNode::cast) + /// Returns an iterator over the items to import from the module. + pub fn iter(self) -> impl DoubleEndedIterator<Item = ImportItem<'a>> { + self.0.children().filter_map(|child| match child.kind() { + SyntaxKind::RenamedImportItem => child.cast().map(ImportItem::Renamed), + SyntaxKind::Ident => child.cast().map(ImportItem::Simple), + _ => Option::None, + }) + } +} + +/// An imported item, potentially renamed to another identifier. +#[derive(Debug, Copy, Clone, Hash)] +pub enum ImportItem<'a> { + /// A non-renamed import (the item's name in the scope is the same as its + /// name). + Simple(Ident<'a>), + /// A renamed import (the item was bound to a different name in the scope + /// than the one it was defined as). + Renamed(RenamedImportItem<'a>), +} + +impl<'a> ImportItem<'a> { + /// The original name of the imported item, at its source. This will be the + /// equal to the bound name if the item wasn't renamed with 'as'. + pub fn original_name(self) -> Ident<'a> { + match self { + Self::Simple(name) => name, + Self::Renamed(renamed_item) => renamed_item.original_name(), + } + } + + /// The name which this import item was bound to. Corresponds to the new + /// name, if it was renamed; otherwise, it's just its original name. + pub fn bound_name(self) -> Ident<'a> { + match self { + Self::Simple(name) => name, + Self::Renamed(renamed_item) => renamed_item.new_name(), + } + } +} + +node! { + /// A renamed import item: `a as d` + RenamedImportItem +} + +impl<'a> RenamedImportItem<'a> { + /// The original name of the imported item (`a` in `a as d`). + pub fn original_name(self) -> Ident<'a> { + self.0.cast_first_match().unwrap_or_default() + } + + /// The new name of the imported item (`d` in `a as d`). + pub fn new_name(self) -> Ident<'a> { + self.0 + .children() + .filter_map(SyntaxNode::cast) + .nth(1) + .unwrap_or_default() } } diff --git a/crates/typst-syntax/src/kind.rs b/crates/typst-syntax/src/kind.rs index 49119720..669ca0f2 100644 --- a/crates/typst-syntax/src/kind.rs +++ b/crates/typst-syntax/src/kind.rs @@ -242,6 +242,8 @@ pub enum SyntaxKind { ModuleImport, /// Items to import from a module: `a, b, c`. ImportItems, + /// A renamed import item: `a as d`. + RenamedImportItem, /// A module include: `include "chapter1.typ"`. ModuleInclude, /// A break from a loop: `break`. @@ -465,6 +467,7 @@ impl SyntaxKind { Self::ForLoop => "for-loop expression", Self::ModuleImport => "`import` expression", Self::ImportItems => "import items", + Self::RenamedImportItem => "renamed import item", Self::ModuleInclude => "`include` expression", Self::LoopBreak => "`break` expression", Self::LoopContinue => "`continue` expression", diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs index e0476ec8..306ac798 100644 --- a/crates/typst-syntax/src/parser.rs +++ b/crates/typst-syntax/src/parser.rs @@ -1138,6 +1138,12 @@ fn module_import(p: &mut Parser) { let m = p.marker(); p.assert(SyntaxKind::Import); code_expr(p); + if p.eat_if(SyntaxKind::As) { + // Allow renaming a full module import. + // If items are included, both the full module and the items are + // imported at the same time. + p.expect(SyntaxKind::Ident); + } if p.eat_if(SyntaxKind::Colon) && !p.eat_if(SyntaxKind::Star) { import_items(p); } @@ -1147,9 +1153,17 @@ fn module_import(p: &mut Parser) { fn import_items(p: &mut Parser) { let m = p.marker(); while !p.eof() && !p.at(SyntaxKind::Semicolon) { + let item_marker = p.marker(); if !p.eat_if(SyntaxKind::Ident) { p.unexpected(); } + + // Rename imported item. + if p.eat_if(SyntaxKind::As) { + p.expect(SyntaxKind::Ident); + p.wrap(item_marker, SyntaxKind::RenamedImportItem); + } + if p.current().is_terminator() { break; } |
