summaryrefslogtreecommitdiff
path: root/crates/typst-ide
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-01-29 15:20:30 +0100
committerGitHub <noreply@github.com>2025-01-29 14:20:30 +0000
commit1b2719c94c6422112508cfad24bdd9504541c363 (patch)
treef58682b5203b3448a560b2efd3f50c4c7d79372b /crates/typst-ide
parent9665eecdb62ee94cd9fcf4dfc61e2c70ba9391fb (diff)
Resolve bound name of bare import statically (#5773)
Diffstat (limited to 'crates/typst-ide')
-rw-r--r--crates/typst-ide/src/matchers.rs88
1 files changed, 49 insertions, 39 deletions
diff --git a/crates/typst-ide/src/matchers.rs b/crates/typst-ide/src/matchers.rs
index b92cbf55..ef8288f2 100644
--- a/crates/typst-ide/src/matchers.rs
+++ b/crates/typst-ide/src/matchers.rs
@@ -1,7 +1,7 @@
use ecow::EcoString;
use typst::foundations::{Module, Value};
use typst::syntax::ast::AstNode;
-use typst::syntax::{ast, LinkedNode, Span, SyntaxKind, SyntaxNode};
+use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
use crate::{analyze_import, IdeWorld};
@@ -30,38 +30,38 @@ pub fn named_items<T>(
if let Some(v) = node.cast::<ast::ModuleImport>() {
let imports = v.imports();
- let source = node
- .children()
- .find(|child| child.is::<ast::Expr>())
- .and_then(|source: LinkedNode| {
- Some((analyze_import(world, &source)?, source))
- });
- let source = source.as_ref();
+ let source = v.source();
+
+ let source_value = node
+ .find(source.span())
+ .and_then(|source| analyze_import(world, &source));
+ let source_value = source_value.as_ref();
+
+ let module = source_value.and_then(|value| match value {
+ Value::Module(module) => Some(module),
+ _ => None,
+ });
+
+ let name_and_span = match (imports, v.new_name()) {
+ // ```plain
+ // import "foo" as name
+ // import "foo" as name: ..
+ // ```
+ (_, Some(name)) => Some((name.get().clone(), name.span())),
+ // ```plain
+ // import "foo"
+ // ```
+ (None, None) => v.bare_name().ok().map(|name| (name, source.span())),
+ // ```plain
+ // import "foo": ..
+ // ```
+ (Some(..), None) => None,
+ };
// Seeing the module itself.
- if let Some((value, source)) = source {
- let site = match (imports, v.new_name()) {
- // ```plain
- // import "foo" as name;
- // import "foo" as name: ..;
- // ```
- (_, Some(name)) => Some(name.to_untyped()),
- // ```plain
- // import "foo";
- // ```
- (None, None) => Some(source.get()),
- // ```plain
- // import "foo": ..;
- // ```
- (Some(..), None) => None,
- };
-
- if let Some((site, value)) =
- site.zip(value.clone().cast::<Module>().ok())
- {
- if let Some(res) = recv(NamedItem::Module(&value, site)) {
- return Some(res);
- }
+ if let Some((name, span)) = name_and_span {
+ if let Some(res) = recv(NamedItem::Module(&name, span, module)) {
+ return Some(res);
}
}
@@ -75,7 +75,7 @@ pub fn named_items<T>(
// import "foo": *;
// ```
Some(ast::Imports::Wildcard) => {
- if let Some(scope) = source.and_then(|(value, _)| value.scope()) {
+ if let Some(scope) = source_value.and_then(Value::scope) {
for (name, value, span) in scope.iter() {
let item = NamedItem::Import(name, span, Some(value));
if let Some(res) = recv(item) {
@@ -92,7 +92,7 @@ pub fn named_items<T>(
let bound = item.bound_name();
let (span, value) = item.path().iter().fold(
- (bound.span(), source.map(|(value, _)| value)),
+ (bound.span(), source_value),
|(span, value), path_ident| {
let scope = value.and_then(|v| v.scope());
let span = scope
@@ -175,8 +175,8 @@ pub enum NamedItem<'a> {
Var(ast::Ident<'a>),
/// A function item.
Fn(ast::Ident<'a>),
- /// A (imported) module item.
- Module(&'a Module, &'a SyntaxNode),
+ /// A (imported) module.
+ Module(&'a EcoString, Span, Option<&'a Module>),
/// An imported item.
Import(&'a EcoString, Span, Option<&'a Value>),
}
@@ -186,7 +186,7 @@ impl<'a> NamedItem<'a> {
match self {
NamedItem::Var(ident) => ident.get(),
NamedItem::Fn(ident) => ident.get(),
- NamedItem::Module(value, _) => value.name(),
+ NamedItem::Module(name, _, _) => name,
NamedItem::Import(name, _, _) => name,
}
}
@@ -194,7 +194,7 @@ impl<'a> NamedItem<'a> {
pub(crate) fn value(&self) -> Option<Value> {
match self {
NamedItem::Var(..) | NamedItem::Fn(..) => None,
- NamedItem::Module(value, _) => Some(Value::Module((*value).clone())),
+ NamedItem::Module(_, _, value) => value.cloned().map(Value::Module),
NamedItem::Import(_, _, value) => value.cloned(),
}
}
@@ -202,7 +202,7 @@ impl<'a> NamedItem<'a> {
pub(crate) fn span(&self) -> Span {
match *self {
NamedItem::Var(name) | NamedItem::Fn(name) => name.span(),
- NamedItem::Module(_, site) => site.span(),
+ NamedItem::Module(_, span, _) => span,
NamedItem::Import(_, span, _) => span,
}
}
@@ -356,7 +356,17 @@ mod tests {
#[test]
fn test_named_items_import() {
- test("#import \"foo.typ\": a; #(a);", 2).must_include(["a"]);
+ test("#import \"foo.typ\"", 2).must_include(["foo"]);
+ test("#import \"foo.typ\" as bar", 2)
+ .must_include(["bar"])
+ .must_exclude(["foo"]);
+ }
+
+ #[test]
+ fn test_named_items_import_items() {
+ test("#import \"foo.typ\": a; #(a);", 2)
+ .must_include(["a"])
+ .must_exclude(["foo"]);
let world = TestWorld::new("#import \"foo.typ\": a.b; #(b);")
.with_source("foo.typ", "#import \"a.typ\"")