mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Make AST borrowed
This commit is contained in:
parent
16855121b6
commit
5fb5854ed8
File diff suppressed because it is too large
Load Diff
@ -105,22 +105,22 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the node can be cast to the given AST node.
|
/// Whether the node can be cast to the given AST node.
|
||||||
pub fn is<T: AstNode>(&self) -> bool {
|
pub fn is<'a, T: AstNode<'a>>(&'a self) -> bool {
|
||||||
self.cast::<T>().is_some()
|
self.cast::<T>().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to convert the node to a typed AST node.
|
/// Try to convert the node to a typed AST node.
|
||||||
pub fn cast<T: AstNode>(&self) -> Option<T> {
|
pub fn cast<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
|
||||||
T::from_untyped(self)
|
T::from_untyped(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the first child that can cast to the AST type `T`.
|
/// Cast the first child that can cast to the AST type `T`.
|
||||||
pub fn cast_first_match<T: AstNode>(&self) -> Option<T> {
|
pub fn cast_first_match<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
|
||||||
self.children().find_map(Self::cast)
|
self.children().find_map(Self::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the last child that can cast to the AST type `T`.
|
/// Cast the last child that can cast to the AST type `T`.
|
||||||
pub fn cast_last_match<T: AstNode>(&self) -> Option<T> {
|
pub fn cast_last_match<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
|
||||||
self.children().rev().find_map(Self::cast)
|
self.children().rev().find_map(Self::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +273,17 @@ impl SyntaxNode {
|
|||||||
Repr::Error(node) => node.error.span.number() + 1,
|
Repr::Error(node) => node.error.span.number() + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An arbitrary node just for filling a slot in memory.
|
||||||
|
///
|
||||||
|
/// In contrast to `default()`, this is a const fn.
|
||||||
|
pub(super) const fn arbitrary() -> Self {
|
||||||
|
Self(Repr::Leaf(LeafNode {
|
||||||
|
kind: SyntaxKind::Eof,
|
||||||
|
text: EcoString::new(),
|
||||||
|
span: Span::detached(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for SyntaxNode {
|
impl Debug for SyntaxNode {
|
||||||
@ -287,7 +298,7 @@ impl Debug for SyntaxNode {
|
|||||||
|
|
||||||
impl Default for SyntaxNode {
|
impl Default for SyntaxNode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::error("", "")
|
Self::arbitrary()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,6 +813,8 @@ impl<'a> LinkedNode<'a> {
|
|||||||
impl Deref for LinkedNode<'_> {
|
impl Deref for LinkedNode<'_> {
|
||||||
type Target = SyntaxNode;
|
type Target = SyntaxNode;
|
||||||
|
|
||||||
|
/// Dereference to a syntax node. Note that this shortens the lifetime, so
|
||||||
|
/// you may need to use [`get()`](Self::get) instead in some situations.
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.get()
|
self.get()
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt};
|
use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt};
|
||||||
use crate::syntax::ast::{self, AstNode, Expr, Ident};
|
use crate::syntax::ast::{self, AstNode};
|
||||||
use crate::syntax::{FileId, Span, SyntaxNode};
|
use crate::syntax::{FileId, Span, SyntaxNode};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ impl Func {
|
|||||||
match &self.repr {
|
match &self.repr {
|
||||||
Repr::Native(native) => Some(native.info.name),
|
Repr::Native(native) => Some(native.info.name),
|
||||||
Repr::Elem(func) => Some(func.info().name),
|
Repr::Elem(func) => Some(func.info().name),
|
||||||
Repr::Closure(closure) => closure.name.as_deref(),
|
Repr::Closure(closure) => closure.name(),
|
||||||
Repr::With(arc) => arc.0.name(),
|
Repr::With(arc) => arc.0.name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,36 +295,32 @@ pub struct ParamInfo {
|
|||||||
/// A user-defined closure.
|
/// A user-defined closure.
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
pub(super) struct Closure {
|
pub(super) struct Closure {
|
||||||
|
/// The closure's syntax node. Must be castable to `ast::Closure`.
|
||||||
|
pub node: SyntaxNode,
|
||||||
/// The source file where the closure was defined.
|
/// The source file where the closure was defined.
|
||||||
pub location: FileId,
|
pub location: FileId,
|
||||||
/// The name of the closure.
|
/// Default values of named parameters.
|
||||||
pub name: Option<Ident>,
|
pub defaults: Vec<Value>,
|
||||||
/// Captured values from outer scopes.
|
/// Captured values from outer scopes.
|
||||||
pub captured: Scope,
|
pub captured: Scope,
|
||||||
/// The list of parameters.
|
|
||||||
pub params: Vec<Param>,
|
|
||||||
/// The expression the closure should evaluate to.
|
|
||||||
pub body: Expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A closure parameter.
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub enum Param {
|
|
||||||
/// A positional parameter: `x`.
|
|
||||||
Pos(ast::Pattern),
|
|
||||||
/// A named parameter with a default value: `draw: false`.
|
|
||||||
Named(Ident, Value),
|
|
||||||
/// An argument sink: `..args`.
|
|
||||||
Sink(Option<Ident>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Closure {
|
impl Closure {
|
||||||
|
/// The name of the closure.
|
||||||
|
pub fn name(&self) -> Option<&str> {
|
||||||
|
self.node
|
||||||
|
.cast::<ast::Closure>()
|
||||||
|
.unwrap()
|
||||||
|
.name()
|
||||||
|
.map(|ident| ident.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
/// Call the function in the context with the arguments.
|
/// Call the function in the context with the arguments.
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn call(
|
fn call(
|
||||||
this: &Func,
|
func: &Func,
|
||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
@ -334,15 +330,15 @@ impl Closure {
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let closure = match &this.repr {
|
let Repr::Closure(this) = &func.repr else {
|
||||||
Repr::Closure(closure) => closure,
|
panic!("`this` must be a closure");
|
||||||
_ => panic!("`this` must be a closure"),
|
|
||||||
};
|
};
|
||||||
|
let closure = this.node.cast::<ast::Closure>().unwrap();
|
||||||
|
|
||||||
// Don't leak the scopes from the call site. Instead, we use the scope
|
// Don't leak the scopes from the call site. Instead, we use the scope
|
||||||
// of captured variables we collected earlier.
|
// of captured variables we collected earlier.
|
||||||
let mut scopes = Scopes::new(None);
|
let mut scopes = Scopes::new(None);
|
||||||
scopes.top = closure.captured.clone();
|
scopes.top = this.captured.clone();
|
||||||
|
|
||||||
// Prepare VT.
|
// Prepare VT.
|
||||||
let mut locator = Locator::chained(locator);
|
let mut locator = Locator::chained(locator);
|
||||||
@ -355,30 +351,34 @@ impl Closure {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Prepare VM.
|
// Prepare VM.
|
||||||
let mut vm = Vm::new(vt, route, closure.location, scopes);
|
let mut vm = Vm::new(vt, route, this.location, scopes);
|
||||||
vm.depth = depth;
|
vm.depth = depth;
|
||||||
|
|
||||||
// Provide the closure itself for recursive calls.
|
// Provide the closure itself for recursive calls.
|
||||||
if let Some(name) = &closure.name {
|
if let Some(name) = closure.name() {
|
||||||
vm.define(name.clone(), Value::Func(this.clone()));
|
vm.define(name, Value::Func(func.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the arguments according to the parameter list.
|
// Parse the arguments according to the parameter list.
|
||||||
let num_pos_params =
|
let num_pos_params = closure
|
||||||
closure.params.iter().filter(|p| matches!(p, Param::Pos(_))).count();
|
.params()
|
||||||
|
.children()
|
||||||
|
.filter(|p| matches!(p, ast::Param::Pos(_)))
|
||||||
|
.count();
|
||||||
let num_pos_args = args.to_pos().len();
|
let num_pos_args = args.to_pos().len();
|
||||||
let sink_size = num_pos_args.checked_sub(num_pos_params);
|
let sink_size = num_pos_args.checked_sub(num_pos_params);
|
||||||
|
|
||||||
let mut sink = None;
|
let mut sink = None;
|
||||||
let mut sink_pos_values = None;
|
let mut sink_pos_values = None;
|
||||||
for p in &closure.params {
|
let mut defaults = this.defaults.iter();
|
||||||
|
for p in closure.params().children() {
|
||||||
match p {
|
match p {
|
||||||
Param::Pos(pattern) => match pattern {
|
ast::Param::Pos(pattern) => match pattern {
|
||||||
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
||||||
vm.define(ident.clone(), args.expect::<Value>(ident)?)
|
vm.define(ident, args.expect::<Value>(&ident)?)
|
||||||
}
|
}
|
||||||
ast::Pattern::Normal(_) => unreachable!(),
|
ast::Pattern::Normal(_) => unreachable!(),
|
||||||
_ => {
|
pattern => {
|
||||||
super::define_pattern(
|
super::define_pattern(
|
||||||
&mut vm,
|
&mut vm,
|
||||||
pattern,
|
pattern,
|
||||||
@ -386,16 +386,18 @@ impl Closure {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Param::Sink(ident) => {
|
ast::Param::Sink(ident) => {
|
||||||
sink = ident.clone();
|
sink = ident.name();
|
||||||
if let Some(sink_size) = sink_size {
|
if let Some(sink_size) = sink_size {
|
||||||
sink_pos_values = Some(args.consume(sink_size)?);
|
sink_pos_values = Some(args.consume(sink_size)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Param::Named(ident, default) => {
|
ast::Param::Named(named) => {
|
||||||
|
let name = named.name();
|
||||||
|
let default = defaults.next().unwrap();
|
||||||
let value =
|
let value =
|
||||||
args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
|
args.named::<Value>(&name)?.unwrap_or_else(|| default.clone());
|
||||||
vm.define(ident.clone(), value);
|
vm.define(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,7 +414,7 @@ impl Closure {
|
|||||||
args.finish()?;
|
args.finish()?;
|
||||||
|
|
||||||
// Handle control flow.
|
// Handle control flow.
|
||||||
let result = closure.body.eval(&mut vm);
|
let result = closure.body().eval(&mut vm);
|
||||||
match vm.flow {
|
match vm.flow {
|
||||||
Some(FlowEvent::Return(_, Some(explicit))) => return Ok(explicit),
|
Some(FlowEvent::Return(_, Some(explicit))) => return Ok(explicit),
|
||||||
Some(FlowEvent::Return(_, None)) => {}
|
Some(FlowEvent::Return(_, None)) => {}
|
||||||
@ -483,7 +485,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
Some(ast::Expr::Closure(expr)) => {
|
Some(ast::Expr::Closure(expr)) => {
|
||||||
for param in expr.params().children() {
|
for param in expr.params().children() {
|
||||||
if let ast::Param::Named(named) = param {
|
if let ast::Param::Named(named) = param {
|
||||||
self.visit(named.expr().as_untyped());
|
self.visit(named.expr().to_untyped());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +508,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.visit(expr.body().as_untyped());
|
self.visit(expr.body().to_untyped());
|
||||||
self.internal.exit();
|
self.internal.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +516,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
// active after the body is evaluated.
|
// active after the body is evaluated.
|
||||||
Some(ast::Expr::Let(expr)) => {
|
Some(ast::Expr::Let(expr)) => {
|
||||||
if let Some(init) = expr.init() {
|
if let Some(init) = expr.init() {
|
||||||
self.visit(init.as_untyped());
|
self.visit(init.to_untyped());
|
||||||
}
|
}
|
||||||
|
|
||||||
for ident in expr.kind().idents() {
|
for ident in expr.kind().idents() {
|
||||||
@ -526,7 +528,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
// active after the iterable is evaluated but before the body is
|
// active after the iterable is evaluated but before the body is
|
||||||
// evaluated.
|
// evaluated.
|
||||||
Some(ast::Expr::For(expr)) => {
|
Some(ast::Expr::For(expr)) => {
|
||||||
self.visit(expr.iter().as_untyped());
|
self.visit(expr.iter().to_untyped());
|
||||||
self.internal.enter();
|
self.internal.enter();
|
||||||
|
|
||||||
let pattern = expr.pattern();
|
let pattern = expr.pattern();
|
||||||
@ -534,16 +536,16 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
self.bind(ident);
|
self.bind(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.visit(expr.body().as_untyped());
|
self.visit(expr.body().to_untyped());
|
||||||
self.internal.exit();
|
self.internal.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// An import contains items, but these are active only after the
|
// An import contains items, but these are active only after the
|
||||||
// path is evaluated.
|
// path is evaluated.
|
||||||
Some(ast::Expr::Import(expr)) => {
|
Some(ast::Expr::Import(expr)) => {
|
||||||
self.visit(expr.source().as_untyped());
|
self.visit(expr.source().to_untyped());
|
||||||
if let Some(ast::Imports::Items(items)) = expr.imports() {
|
if let Some(ast::Imports::Items(items)) = expr.imports() {
|
||||||
for item in items {
|
for item in items.idents() {
|
||||||
self.bind(item);
|
self.bind(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,14 +562,14 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
|
|
||||||
/// Bind a new internal variable.
|
/// Bind a new internal variable.
|
||||||
fn bind(&mut self, ident: ast::Ident) {
|
fn bind(&mut self, ident: ast::Ident) {
|
||||||
self.internal.top.define(ident.take(), Value::None);
|
self.internal.top.define(ident.get().clone(), Value::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Capture a variable if it isn't internal.
|
/// Capture a variable if it isn't internal.
|
||||||
fn capture(&mut self, ident: ast::Ident) {
|
fn capture(&mut self, ident: ast::Ident) {
|
||||||
if self.internal.get(&ident).is_err() {
|
if self.internal.get(&ident).is_err() {
|
||||||
if let Ok(value) = self.external.get(&ident) {
|
if let Ok(value) = self.external.get(&ident) {
|
||||||
self.captures.define_captured(ident.take(), value.clone());
|
self.captures.define_captured(ident.get().clone(), value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -576,7 +578,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
fn capture_in_math(&mut self, ident: ast::MathIdent) {
|
fn capture_in_math(&mut self, ident: ast::MathIdent) {
|
||||||
if self.internal.get(&ident).is_err() {
|
if self.internal.get(&ident).is_err() {
|
||||||
if let Ok(value) = self.external.get_in_math(&ident) {
|
if let Ok(value) = self.external.get_in_math(&ident) {
|
||||||
self.captures.define_captured(ident.take(), value.clone());
|
self.captures.define_captured(ident.get().clone(), value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -442,13 +442,13 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
|
|||||||
// "#import "path.typ": a, b, |".
|
// "#import "path.typ": a, b, |".
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(prev) = ctx.leaf.prev_sibling();
|
if let Some(prev) = ctx.leaf.prev_sibling();
|
||||||
if let Some(ast::Expr::Import(import)) = prev.cast();
|
if let Some(ast::Expr::Import(import)) = prev.get().cast();
|
||||||
if let Some(ast::Imports::Items(items)) = import.imports();
|
if let Some(ast::Imports::Items(items)) = import.imports();
|
||||||
if let Some(source) = prev.children().find(|child| child.is::<ast::Expr>());
|
if let Some(source) = prev.children().find(|child| child.is::<ast::Expr>());
|
||||||
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
|
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
|
||||||
then {
|
then {
|
||||||
ctx.from = ctx.cursor;
|
ctx.from = ctx.cursor;
|
||||||
import_item_completions(ctx, &items, &value);
|
import_item_completions(ctx, items, &value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,13 +460,13 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
|
|||||||
if let Some(parent) = ctx.leaf.parent();
|
if let Some(parent) = ctx.leaf.parent();
|
||||||
if parent.kind() == SyntaxKind::ImportItems;
|
if parent.kind() == SyntaxKind::ImportItems;
|
||||||
if let Some(grand) = parent.parent();
|
if let Some(grand) = parent.parent();
|
||||||
if let Some(ast::Expr::Import(import)) = grand.cast();
|
if let Some(ast::Expr::Import(import)) = grand.get().cast();
|
||||||
if let Some(ast::Imports::Items(items)) = import.imports();
|
if let Some(ast::Imports::Items(items)) = import.imports();
|
||||||
if let Some(source) = grand.children().find(|child| child.is::<ast::Expr>());
|
if let Some(source) = grand.children().find(|child| child.is::<ast::Expr>());
|
||||||
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
|
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
|
||||||
then {
|
then {
|
||||||
ctx.from = ctx.leaf.offset();
|
ctx.from = ctx.leaf.offset();
|
||||||
import_item_completions(ctx, &items, &value);
|
import_item_completions(ctx, items, &value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -475,9 +475,9 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add completions for all exports of a module.
|
/// Add completions for all exports of a module.
|
||||||
fn import_item_completions(
|
fn import_item_completions<'a>(
|
||||||
ctx: &mut CompletionContext,
|
ctx: &mut CompletionContext<'a>,
|
||||||
existing: &[ast::Ident],
|
existing: ast::ImportItems<'a>,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
) {
|
) {
|
||||||
let module = match value {
|
let module = match value {
|
||||||
@ -489,12 +489,12 @@ fn import_item_completions(
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if existing.is_empty() {
|
if existing.idents().next().is_none() {
|
||||||
ctx.snippet_completion("*", "*", "Import everything.");
|
ctx.snippet_completion("*", "*", "Import everything.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, value) in module.scope().iter() {
|
for (name, value) in module.scope().iter() {
|
||||||
if existing.iter().all(|ident| ident.as_str() != name) {
|
if existing.idents().all(|ident| ident.as_str() != name) {
|
||||||
ctx.value_completion(Some(name.clone()), value, false, None);
|
ctx.value_completion(Some(name.clone()), value, false, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -604,9 +604,9 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
|
|||||||
SyntaxKind::Named => parent.parent(),
|
SyntaxKind::Named => parent.parent(),
|
||||||
_ => Some(parent),
|
_ => Some(parent),
|
||||||
};
|
};
|
||||||
if let Some(args) = parent.cast::<ast::Args>();
|
if let Some(args) = parent.get().cast::<ast::Args>();
|
||||||
if let Some(grand) = parent.parent();
|
if let Some(grand) = parent.parent();
|
||||||
if let Some(expr) = grand.cast::<ast::Expr>();
|
if let Some(expr) = grand.get().cast::<ast::Expr>();
|
||||||
let set = matches!(expr, ast::Expr::Set(_));
|
let set = matches!(expr, ast::Expr::Set(_));
|
||||||
if let Some(callee) = match expr {
|
if let Some(callee) = match expr {
|
||||||
ast::Expr::FuncCall(call) => Some(call.callee()),
|
ast::Expr::FuncCall(call) => Some(call.callee()),
|
||||||
@ -634,13 +634,13 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if deciding.kind() == SyntaxKind::Colon;
|
if deciding.kind() == SyntaxKind::Colon;
|
||||||
if let Some(prev) = deciding.prev_leaf();
|
if let Some(prev) = deciding.prev_leaf();
|
||||||
if let Some(param) = prev.cast::<ast::Ident>();
|
if let Some(param) = prev.get().cast::<ast::Ident>();
|
||||||
then {
|
then {
|
||||||
if let Some(next) = deciding.next_leaf() {
|
if let Some(next) = deciding.next_leaf() {
|
||||||
ctx.from = ctx.cursor.min(next.offset());
|
ctx.from = ctx.cursor.min(next.offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
named_param_value_completions(ctx, &callee, ¶m);
|
named_param_value_completions(ctx, callee, ¶m);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -655,12 +655,15 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exclude arguments which are already present.
|
// Exclude arguments which are already present.
|
||||||
let exclude: Vec<_> = args.items().filter_map(|arg| match arg {
|
let exclude: Vec<_> = args
|
||||||
ast::Arg::Named(named) => Some(named.name()),
|
.items()
|
||||||
_ => None,
|
.filter_map(|arg| match arg {
|
||||||
}).collect();
|
ast::Arg::Named(named) => Some(named.name()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
param_completions(ctx, &callee, set, &exclude);
|
param_completions(ctx, callee, set, &exclude);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -669,11 +672,11 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add completions for the parameters of a function.
|
/// Add completions for the parameters of a function.
|
||||||
fn param_completions(
|
fn param_completions<'a>(
|
||||||
ctx: &mut CompletionContext,
|
ctx: &mut CompletionContext<'a>,
|
||||||
callee: &ast::Expr,
|
callee: ast::Expr<'a>,
|
||||||
set: bool,
|
set: bool,
|
||||||
exclude: &[ast::Ident],
|
exclude: &[ast::Ident<'a>],
|
||||||
) {
|
) {
|
||||||
let Some(func) = resolve_global_callee(ctx, callee) else { return };
|
let Some(func) = resolve_global_callee(ctx, callee) else { return };
|
||||||
let Some(info) = func.info() else { return };
|
let Some(info) = func.info() else { return };
|
||||||
@ -707,9 +710,9 @@ fn param_completions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add completions for the values of a named function parameter.
|
/// Add completions for the values of a named function parameter.
|
||||||
fn named_param_value_completions(
|
fn named_param_value_completions<'a>(
|
||||||
ctx: &mut CompletionContext,
|
ctx: &mut CompletionContext<'a>,
|
||||||
callee: &ast::Expr,
|
callee: ast::Expr<'a>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) {
|
) {
|
||||||
let Some(func) = resolve_global_callee(ctx, callee) else { return };
|
let Some(func) = resolve_global_callee(ctx, callee) else { return };
|
||||||
@ -732,10 +735,10 @@ fn named_param_value_completions(
|
|||||||
/// Resolve a callee expression to a global function.
|
/// Resolve a callee expression to a global function.
|
||||||
fn resolve_global_callee<'a>(
|
fn resolve_global_callee<'a>(
|
||||||
ctx: &CompletionContext<'a>,
|
ctx: &CompletionContext<'a>,
|
||||||
callee: &ast::Expr,
|
callee: ast::Expr<'a>,
|
||||||
) -> Option<&'a Func> {
|
) -> Option<&'a Func> {
|
||||||
let value = match callee {
|
let value = match callee {
|
||||||
ast::Expr::Ident(ident) => ctx.global.get(ident)?,
|
ast::Expr::Ident(ident) => ctx.global.get(&ident)?,
|
||||||
ast::Expr::FieldAccess(access) => match access.target() {
|
ast::Expr::FieldAccess(access) => match access.target() {
|
||||||
ast::Expr::Ident(target) => match ctx.global.get(&target)? {
|
ast::Expr::Ident(target) => match ctx.global.get(&target)? {
|
||||||
Value::Module(module) => module.get(&access.field()).ok()?,
|
Value::Module(module) => module.get(&access.field()).ok()?,
|
||||||
@ -1189,20 +1192,20 @@ impl<'a> CompletionContext<'a> {
|
|||||||
while let Some(node) = &ancestor {
|
while let Some(node) = &ancestor {
|
||||||
let mut sibling = Some(node.clone());
|
let mut sibling = Some(node.clone());
|
||||||
while let Some(node) = &sibling {
|
while let Some(node) = &sibling {
|
||||||
if let Some(v) = node.cast::<ast::LetBinding>() {
|
if let Some(v) = node.get().cast::<ast::LetBinding>() {
|
||||||
for ident in v.kind().idents() {
|
for ident in v.kind().idents() {
|
||||||
defined.insert(ident.take());
|
defined.insert(ident.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sibling = node.prev_sibling();
|
sibling = node.prev_sibling();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = node.parent() {
|
if let Some(parent) = node.parent() {
|
||||||
if let Some(v) = parent.cast::<ast::ForLoop>() {
|
if let Some(v) = parent.get().cast::<ast::ForLoop>() {
|
||||||
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
|
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
|
||||||
let pattern = v.pattern();
|
let pattern = v.pattern();
|
||||||
for ident in pattern.idents() {
|
for ident in pattern.idents() {
|
||||||
defined.insert(ident.take());
|
defined.insert(ident.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1233,7 +1236,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
self.completions.push(Completion {
|
self.completions.push(Completion {
|
||||||
kind: CompletionKind::Constant,
|
kind: CompletionKind::Constant,
|
||||||
label: name,
|
label: name.clone(),
|
||||||
apply: None,
|
apply: None,
|
||||||
detail: None,
|
detail: None,
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user