diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-02-27 11:05:16 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-27 10:05:16 +0000 |
| commit | 145723b1ef4fa23f1f6665b8907dfe79d0bf83cf (patch) | |
| tree | 02a7de661ddd5dafa75dfce3e3c8b45a7333b9dc /crates/typst-ide/src | |
| parent | e9ee00a7c0df083663ff5ccca162238b88525e14 (diff) | |
New context system (#3497)
Diffstat (limited to 'crates/typst-ide/src')
| -rw-r--r-- | crates/typst-ide/src/analyze.rs | 52 | ||||
| -rw-r--r-- | crates/typst-ide/src/complete.rs | 50 | ||||
| -rw-r--r-- | crates/typst-ide/src/tooltip.rs | 8 |
3 files changed, 77 insertions, 33 deletions
diff --git a/crates/typst-ide/src/analyze.rs b/crates/typst-ide/src/analyze.rs index e78f0c12..1fef1b49 100644 --- a/crates/typst-ide/src/analyze.rs +++ b/crates/typst-ide/src/analyze.rs @@ -2,32 +2,36 @@ use comemo::Track; use ecow::{eco_vec, EcoString, EcoVec}; use typst::engine::{Engine, Route}; use typst::eval::{Tracer, Vm}; -use typst::foundations::{Label, Scopes, Value}; +use typst::foundations::{Context, Label, Scopes, Styles, Value}; use typst::introspection::{Introspector, Locator}; use typst::model::{BibliographyElem, Document}; use typst::syntax::{ast, LinkedNode, Span, SyntaxKind}; use typst::World; /// Try to determine a set of possible values for an expression. -pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> { - match node.cast::<ast::Expr>() { - Some(ast::Expr::None(_)) => eco_vec![Value::None], - Some(ast::Expr::Auto(_)) => eco_vec![Value::Auto], - Some(ast::Expr::Bool(v)) => eco_vec![Value::Bool(v.get())], - Some(ast::Expr::Int(v)) => eco_vec![Value::Int(v.get())], - Some(ast::Expr::Float(v)) => eco_vec![Value::Float(v.get())], - Some(ast::Expr::Numeric(v)) => eco_vec![Value::numeric(v.get())], - Some(ast::Expr::Str(v)) => eco_vec![Value::Str(v.get().into())], +pub fn analyze_expr( + world: &dyn World, + node: &LinkedNode, +) -> EcoVec<(Value, Option<Styles>)> { + let Some(expr) = node.cast::<ast::Expr>() else { + return eco_vec![]; + }; - Some(ast::Expr::FieldAccess(access)) => { - let Some(child) = node.children().next() else { return eco_vec![] }; - analyze_expr(world, &child) - .into_iter() - .filter_map(|target| target.field(&access.field()).ok()) - .collect() - } + let val = match expr { + ast::Expr::None(_) => Value::None, + ast::Expr::Auto(_) => Value::Auto, + ast::Expr::Bool(v) => Value::Bool(v.get()), + ast::Expr::Int(v) => Value::Int(v.get()), + ast::Expr::Float(v) => Value::Float(v.get()), + ast::Expr::Numeric(v) => Value::numeric(v.get()), + ast::Expr::Str(v) => Value::Str(v.get().into()), + _ => { + if node.kind() == SyntaxKind::Contextual { + if let Some(child) = node.children().last() { + return analyze_expr(world, &child); + } + } - Some(_) => { if let Some(parent) = node.parent() { if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 { return analyze_expr(world, parent); @@ -37,16 +41,16 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> { let mut tracer = Tracer::new(); tracer.inspect(node.span()); typst::compile(world, &mut tracer).ok(); - tracer.values() + return tracer.values(); } + }; - _ => eco_vec![], - } + eco_vec![(val, None)] } /// Try to load a module from the current source file. pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> { - let source = analyze_expr(world, source).into_iter().next()?; + let (source, _) = analyze_expr(world, source).into_iter().next()?; if source.scope().is_some() { return Some(source); } @@ -62,7 +66,9 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> { tracer: tracer.track_mut(), }; - let mut vm = Vm::new(engine, Scopes::new(Some(world.library())), Span::detached()); + let context = Context::none(); + let mut vm = + Vm::new(engine, &context, Scopes::new(Some(world.library())), Span::detached()); typst::eval::import(&mut vm, source, Span::detached(), true) .ok() .map(Value::Module) diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs index b58a9bcc..94679552 100644 --- a/crates/typst-ide/src/complete.rs +++ b/crates/typst-ide/src/complete.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use serde::{Deserialize, Serialize}; use typst::foundations::{ fields_on, format_str, mutable_methods_on, repr, AutoValue, CastInfo, Func, Label, - NoneValue, Repr, Scope, Type, Value, + NoneValue, Repr, Scope, StyleChain, Styles, Type, Value, }; use typst::model::Document; use typst::syntax::{ @@ -135,6 +135,17 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool { } } + // Behind a half-completed context block: "#context |". + if_chain! { + if let Some(prev) = ctx.leaf.prev_leaf(); + if prev.kind() == SyntaxKind::Context; + then { + ctx.from = ctx.cursor; + code_completions(ctx, false); + return true; + } + } + // Directly after a raw block. let mut s = Scanner::new(ctx.text); s.jump(ctx.leaf.offset()); @@ -333,10 +344,10 @@ fn complete_field_accesses(ctx: &mut CompletionContext) -> bool { if prev.is::<ast::Expr>(); if prev.parent_kind() != Some(SyntaxKind::Markup) || prev.prev_sibling_kind() == Some(SyntaxKind::Hash); - if let Some(value) = analyze_expr(ctx.world, &prev).into_iter().next(); + if let Some((value, styles)) = analyze_expr(ctx.world, &prev).into_iter().next(); then { ctx.from = ctx.cursor; - field_access_completions(ctx, &value); + field_access_completions(ctx, &value, &styles); return true; } } @@ -348,10 +359,10 @@ fn complete_field_accesses(ctx: &mut CompletionContext) -> bool { if prev.kind() == SyntaxKind::Dot; if let Some(prev_prev) = prev.prev_sibling(); if prev_prev.is::<ast::Expr>(); - if let Some(value) = analyze_expr(ctx.world, &prev_prev).into_iter().next(); + if let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next(); then { ctx.from = ctx.leaf.offset(); - field_access_completions(ctx, &value); + field_access_completions(ctx, &value, &styles); return true; } } @@ -360,7 +371,11 @@ fn complete_field_accesses(ctx: &mut CompletionContext) -> bool { } /// Add completions for all fields on a value. -fn field_access_completions(ctx: &mut CompletionContext, value: &Value) { +fn field_access_completions( + ctx: &mut CompletionContext, + value: &Value, + styles: &Option<Styles>, +) { for (name, value) in value.ty().scope().iter() { ctx.value_completion(Some(name.clone()), value, true, None); } @@ -421,6 +436,23 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value) { ctx.value_completion(Some(name.clone().into()), value, false, None); } } + Value::Func(func) => { + // Autocomplete get rules. + if let Some((elem, styles)) = func.element().zip(styles.as_ref()) { + for param in elem.params().iter().filter(|param| !param.required) { + if let Some(value) = elem.field_id(param.name).and_then(|id| { + elem.field_from_styles(id, StyleChain::new(styles)) + }) { + ctx.value_completion( + Some(param.name.into()), + &value, + false, + None, + ); + } + } + } + } Value::Plugin(plugin) => { for name in plugin.iter() { ctx.completions.push(Completion { @@ -863,6 +895,12 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) { ); ctx.snippet_completion( + "context expression", + "context ${}", + "Provides contextual data.", + ); + + ctx.snippet_completion( "let binding", "let ${name} = ${value}", "Saves a value in a variable.", diff --git a/crates/typst-ide/src/tooltip.rs b/crates/typst-ide/src/tooltip.rs index 67614b9e..2f04be87 100644 --- a/crates/typst-ide/src/tooltip.rs +++ b/crates/typst-ide/src/tooltip.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use ecow::{eco_format, EcoString}; use if_chain::if_chain; use typst::eval::{CapturesVisitor, Tracer}; -use typst::foundations::{repr, CastInfo, Repr, Value}; +use typst::foundations::{repr, Capturer, CastInfo, Repr, Value}; use typst::layout::Length; use typst::model::Document; use typst::syntax::{ast, LinkedNode, Source, SyntaxKind}; @@ -59,7 +59,7 @@ fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> { let values = analyze_expr(world, ancestor); - if let [value] = values.as_slice() { + if let [(value, _)] = values.as_slice() { if let Some(docs) = value.docs() { return Some(Tooltip::Text(plain_docs_sentence(docs))); } @@ -78,7 +78,7 @@ fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> { let mut last = None; let mut pieces: Vec<EcoString> = vec![]; let mut iter = values.iter(); - for value in (&mut iter).take(Tracer::MAX_VALUES - 1) { + for (value, _) in (&mut iter).take(Tracer::MAX_VALUES - 1) { if let Some((prev, count)) = &mut last { if *prev == value { *count += 1; @@ -120,7 +120,7 @@ fn closure_tooltip(leaf: &LinkedNode) -> Option<Tooltip> { } // Analyze the closure's captures. - let mut visitor = CapturesVisitor::new(None); + let mut visitor = CapturesVisitor::new(None, Capturer::Function); visitor.visit(parent); let captures = visitor.finish(); |
