From 72f4c543cced0741f3349896cc96bda90a0c69ac Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 23 Sep 2023 01:35:03 +0200 Subject: [PATCH] Scope completions for imports --- crates/typst/src/eval/mod.rs | 19 +++++++--------- crates/typst/src/eval/value.rs | 10 +++++++++ crates/typst/src/ide/complete.rs | 38 +++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs index cd1f29c66..d39a705ef 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 776608b3a..69edcdec1 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 59a6c9300..9c1796abe 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::() { + if let Some(v) = node.cast::() { for ident in v.kind().idents() { - defined.insert(ident.get()); + defined.insert(ident.get().clone()); } } + + if let Some(v) = node.cast::() { + let imports = v.imports(); + match imports { + None | Some(ast::Imports::Wildcard) => { + if let Some(value) = node + .children() + .find(|child| child.is::()) + .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::() { + if let Some(v) = parent.cast::() { 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, });