summaryrefslogtreecommitdiff
path: root/crates/typst-ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-ide')
-rw-r--r--crates/typst-ide/src/analyze.rs52
-rw-r--r--crates/typst-ide/src/complete.rs50
-rw-r--r--crates/typst-ide/src/tooltip.rs8
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();