summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/eval/mod.rs19
-rw-r--r--crates/typst/src/eval/value.rs10
-rw-r--r--crates/typst/src/ide/complete.rs38
3 files changed, 51 insertions, 16 deletions
diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs
index cd1f29c6..d39a705e 100644
--- a/crates/typst/src/eval/mod.rs
+++ b/crates/typst/src/eval/mod.rs
@@ -1743,21 +1743,17 @@ impl Eval for ast::ModuleImport<'_> {
let new_name = self.new_name();
let imports = self.imports();
- let name = match &source {
+ match &source {
Value::Func(func) => {
- func.scope()
- .ok_or("cannot import from user-defined functions")
- .at(source_span)?;
- func.name().unwrap_or_default().into()
+ if func.scope().is_none() {
+ bail!(source_span, "cannot import from user-defined functions");
+ }
}
- Value::Type(ty) => ty.short_name().into(),
+ Value::Type(_) => {}
other => {
- let module = import(vm, other.clone(), source_span, true)?;
- let name = module.name().clone();
- source = Value::Module(module);
- name
+ source = Value::Module(import(vm, other.clone(), source_span, true)?);
}
- };
+ }
if let Some(new_name) = &new_name {
if let ast::Expr::Ident(ident) = self.source() {
@@ -1779,6 +1775,7 @@ impl Eval for ast::ModuleImport<'_> {
None => {
// Only import here if there is no rename.
if new_name.is_none() {
+ let name: EcoString = source.name().unwrap().into();
vm.scopes.top.define(name, source);
}
}
diff --git a/crates/typst/src/eval/value.rs b/crates/typst/src/eval/value.rs
index 776608b3..69edcdec 100644
--- a/crates/typst/src/eval/value.rs
+++ b/crates/typst/src/eval/value.rs
@@ -168,6 +168,16 @@ impl Value {
}
}
+ /// The name, if this is a function, type, or module.
+ pub fn name(&self) -> Option<&str> {
+ match self {
+ Self::Func(func) => func.name(),
+ Self::Type(ty) => Some(ty.short_name()),
+ Self::Module(module) => Some(module.name()),
+ _ => None,
+ }
+ }
+
/// Try to extract documentation for the value.
pub fn docs(&self) -> Option<&'static str> {
match self {
diff --git a/crates/typst/src/ide/complete.rs b/crates/typst/src/ide/complete.rs
index 59a6c930..9c1796ab 100644
--- a/crates/typst/src/ide/complete.rs
+++ b/crates/typst/src/ide/complete.rs
@@ -1195,6 +1195,7 @@ impl<'a> CompletionContext<'a> {
}
/// Add completions for definitions that are available at the cursor.
+ ///
/// Filters the global/math scope with the given filter.
fn scope_completions(&mut self, parens: bool, filter: impl Fn(&Value) -> bool) {
let mut defined = BTreeSet::new();
@@ -1203,20 +1204,47 @@ impl<'a> CompletionContext<'a> {
while let Some(node) = &ancestor {
let mut sibling = Some(node.clone());
while let Some(node) = &sibling {
- if let Some(v) = node.get().cast::<ast::LetBinding>() {
+ if let Some(v) = node.cast::<ast::LetBinding>() {
for ident in v.kind().idents() {
- defined.insert(ident.get());
+ defined.insert(ident.get().clone());
+ }
+ }
+
+ if let Some(v) = node.cast::<ast::ModuleImport>() {
+ let imports = v.imports();
+ match imports {
+ None | Some(ast::Imports::Wildcard) => {
+ if let Some(value) = node
+ .children()
+ .find(|child| child.is::<ast::Expr>())
+ .and_then(|source| analyze_import(self.world, &source))
+ {
+ if imports.is_none() {
+ defined.extend(value.name().map(Into::into));
+ } else if let Some(scope) = value.scope() {
+ for (name, _) in scope.iter() {
+ defined.insert(name.clone());
+ }
+ }
+ }
+ }
+ Some(ast::Imports::Items(items)) => {
+ for item in items.iter() {
+ defined.insert(item.bound_name().get().clone());
+ }
+ }
}
}
+
sibling = node.prev_sibling();
}
if let Some(parent) = node.parent() {
- if let Some(v) = parent.get().cast::<ast::ForLoop>() {
+ if let Some(v) = parent.cast::<ast::ForLoop>() {
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
let pattern = v.pattern();
for ident in pattern.idents() {
- defined.insert(ident.get());
+ defined.insert(ident.get().clone());
}
}
}
@@ -1247,7 +1275,7 @@ impl<'a> CompletionContext<'a> {
if !name.is_empty() {
self.completions.push(Completion {
kind: CompletionKind::Constant,
- label: name.clone(),
+ label: name,
apply: None,
detail: None,
});