summaryrefslogtreecommitdiff
path: root/crates/typst-ide/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-11-10 12:15:28 +0100
committerLaurenz <laurmaedje@gmail.com>2024-11-13 10:21:40 +0100
commita5a4b0b72fcf948bc58dd5800eefbabb1d67a43d (patch)
tree3860f05b073a26b25584734a7b9aa61a67c9c61e /crates/typst-ide/src
parent03ba5a0cb123fbfcb36dddfd1de4d710fef09a98 (diff)
Introduce `IdeWorld` trait
Diffstat (limited to 'crates/typst-ide/src')
-rw-r--r--crates/typst-ide/src/analyze.rs11
-rw-r--r--crates/typst-ide/src/complete.rs8
-rw-r--r--crates/typst-ide/src/definition.rs7
-rw-r--r--crates/typst-ide/src/jump.rs7
-rw-r--r--crates/typst-ide/src/lib.rs31
-rw-r--r--crates/typst-ide/src/matchers.rs5
-rw-r--r--crates/typst-ide/src/tooltip.rs16
7 files changed, 59 insertions, 26 deletions
diff --git a/crates/typst-ide/src/analyze.rs b/crates/typst-ide/src/analyze.rs
index 75ffaede..ce7fe478 100644
--- a/crates/typst-ide/src/analyze.rs
+++ b/crates/typst-ide/src/analyze.rs
@@ -5,12 +5,13 @@ use typst::foundations::{Context, Label, Scopes, Styles, Value};
use typst::introspection::Introspector;
use typst::model::{BibliographyElem, Document};
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
-use typst::World;
use typst_eval::Vm;
+use crate::IdeWorld;
+
/// Try to determine a set of possible values for an expression.
pub fn analyze_expr(
- world: &dyn World,
+ world: &dyn IdeWorld,
node: &LinkedNode,
) -> EcoVec<(Value, Option<Styles>)> {
let Some(expr) = node.cast::<ast::Expr>() else {
@@ -38,7 +39,7 @@ pub fn analyze_expr(
}
}
- return typst::trace(world, node.span());
+ return typst::trace(world.upcast(), node.span());
}
};
@@ -46,7 +47,7 @@ pub fn analyze_expr(
}
/// Try to load a module from the current source file.
-pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
+pub fn analyze_import(world: &dyn IdeWorld, source: &LinkedNode) -> Option<Value> {
// Use span in the node for resolving imports with relative paths.
let source_span = source.span();
let (source, _) = analyze_expr(world, source).into_iter().next()?;
@@ -59,7 +60,7 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
let mut sink = Sink::new();
let engine = Engine {
routines: &typst::ROUTINES,
- world: world.track(),
+ world: world.upcast().track(),
introspector: introspector.track(),
traced: traced.track(),
sink: sink.track_mut(),
diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs
index 2a606e4a..6ea0a212 100644
--- a/crates/typst-ide/src/complete.rs
+++ b/crates/typst-ide/src/complete.rs
@@ -15,12 +15,11 @@ use typst::syntax::{
};
use typst::text::RawElem;
use typst::visualize::Color;
-use typst::World;
use unscanny::Scanner;
use crate::{
analyze_expr, analyze_import, analyze_labels, named_items, plain_docs_sentence,
- summarize_font_family,
+ summarize_font_family, IdeWorld,
};
/// Autocomplete a cursor position in a source file.
@@ -35,7 +34,7 @@ use crate::{
/// the autocompletions. Label completions, for instance, are only generated
/// when the document is available.
pub fn autocomplete(
- world: &dyn World,
+ world: &dyn IdeWorld,
document: Option<&Document>,
source: &Source,
cursor: usize,
@@ -1023,7 +1022,7 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) {
/// Context for autocompletion.
struct CompletionContext<'a> {
- world: &'a (dyn World + 'a),
+ world: &'a (dyn IdeWorld + 'a),
document: Option<&'a Document>,
global: &'a Scope,
math: &'a Scope,
@@ -1042,6 +1041,7 @@ impl<'a> CompletionContext<'a> {
/// Create a new autocompletion context.
fn new(
world: &'a (dyn World + 'a),
+ world: &'a (dyn IdeWorld + 'a),
document: Option<&'a Document>,
source: &'a Source,
leaf: &'a LinkedNode<'a>,
diff --git a/crates/typst-ide/src/definition.rs b/crates/typst-ide/src/definition.rs
index 4323226d..a8286554 100644
--- a/crates/typst-ide/src/definition.rs
+++ b/crates/typst-ide/src/definition.rs
@@ -3,9 +3,10 @@ use typst::foundations::{Label, Module, Selector, Value};
use typst::model::Document;
use typst::syntax::ast::AstNode;
use typst::syntax::{ast, LinkedNode, Side, Source, Span, SyntaxKind};
-use typst::World;
-use crate::{analyze_import, deref_target, named_items, DerefTarget, NamedItem};
+use crate::{
+ analyze_import, deref_target, named_items, DerefTarget, IdeWorld, NamedItem,
+};
/// Find the definition of the item under the cursor.
///
@@ -13,7 +14,7 @@ use crate::{analyze_import, deref_target, named_items, DerefTarget, NamedItem};
/// the definition search. Label definitions, for instance, are only generated
/// when the document is available.
pub fn definition(
- world: &dyn World,
+ world: &dyn IdeWorld,
document: Option<&Document>,
source: &Source,
cursor: usize,
diff --git a/crates/typst-ide/src/jump.rs b/crates/typst-ide/src/jump.rs
index e48db986..5d270f04 100644
--- a/crates/typst-ide/src/jump.rs
+++ b/crates/typst-ide/src/jump.rs
@@ -4,7 +4,8 @@ use typst::layout::{Frame, FrameItem, Point, Position, Size};
use typst::model::{Destination, Document, Url};
use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind};
use typst::visualize::Geometry;
-use typst::World;
+
+use crate::IdeWorld;
/// Where to [jump](jump_from_click) to.
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -18,7 +19,7 @@ pub enum Jump {
}
impl Jump {
- fn from_span(world: &dyn World, span: Span) -> Option<Self> {
+ fn from_span(world: &dyn IdeWorld, span: Span) -> Option<Self> {
let id = span.id()?;
let source = world.source(id).ok()?;
let node = source.find(span)?;
@@ -28,7 +29,7 @@ impl Jump {
/// Determine where to jump to based on a click in a frame.
pub fn jump_from_click(
- world: &dyn World,
+ world: &dyn IdeWorld,
document: &Document,
frame: &Frame,
click: Point,
diff --git a/crates/typst-ide/src/lib.rs b/crates/typst-ide/src/lib.rs
index 4c1542fd..f66999b4 100644
--- a/crates/typst-ide/src/lib.rs
+++ b/crates/typst-ide/src/lib.rs
@@ -17,7 +17,30 @@ pub use self::tooltip::{tooltip, Tooltip};
use std::fmt::Write;
use ecow::{eco_format, EcoString};
+use typst::syntax::package::PackageSpec;
use typst::text::{FontInfo, FontStyle};
+use typst::World;
+
+/// Extends the `World` for IDE functionality.
+pub trait IdeWorld: World {
+ /// Turn into a normal [`World`].
+ ///
+ /// This is necessary because trait upcasting is experimental in Rust.
+ /// See: https://github.com/rust-lang/rust/issues/65991
+ ///
+ /// Implementors can simply return `self`.
+ fn upcast(&self) -> &dyn World;
+
+ /// A list of all available packages and optionally descriptions for them.
+ ///
+ /// This function is **optional** to implement. It enhances the user
+ /// experience by enabling autocompletion for packages. Details about
+ /// packages from the `@preview` namespace are available from
+ /// `https://packages.typst.org/preview/index.json`.
+ fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
+ &[]
+ }
+}
/// Extract the first sentence of plain text of a piece of documentation.
///
@@ -107,6 +130,8 @@ mod tests {
use typst::utils::{singleton, LazyHash};
use typst::{Library, World};
+ use crate::IdeWorld;
+
/// A world for IDE testing.
pub struct TestWorld {
pub main: Source,
@@ -193,6 +218,12 @@ mod tests {
}
}
+ impl IdeWorld for TestWorld {
+ fn upcast(&self) -> &dyn World {
+ self
+ }
+ }
+
/// Extra methods for [`Source`].
pub trait SourceExt {
/// Negative cursors index from the back.
diff --git a/crates/typst-ide/src/matchers.rs b/crates/typst-ide/src/matchers.rs
index 1daec819..dd7dfd1f 100644
--- a/crates/typst-ide/src/matchers.rs
+++ b/crates/typst-ide/src/matchers.rs
@@ -2,13 +2,12 @@ use ecow::EcoString;
use typst::foundations::{Module, Value};
use typst::syntax::ast::AstNode;
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind, SyntaxNode};
-use typst::World;
-use crate::analyze_import;
+use crate::{analyze_import, IdeWorld};
/// Find the named items starting from the given position.
pub fn named_items<T>(
- world: &dyn World,
+ world: &dyn IdeWorld,
position: LinkedNode,
mut recv: impl FnMut(NamedItem) -> Option<T>,
) -> Option<T> {
diff --git a/crates/typst-ide/src/tooltip.rs b/crates/typst-ide/src/tooltip.rs
index 0936f278..ade453e7 100644
--- a/crates/typst-ide/src/tooltip.rs
+++ b/crates/typst-ide/src/tooltip.rs
@@ -9,12 +9,11 @@ use typst::model::Document;
use typst::syntax::ast::AstNode;
use typst::syntax::{ast, LinkedNode, Side, Source, SyntaxKind};
use typst::utils::{round_with_precision, Numeric};
-use typst::World;
use typst_eval::CapturesVisitor;
use crate::{
analyze_expr, analyze_import, analyze_labels, plain_docs_sentence,
- summarize_font_family,
+ summarize_font_family, IdeWorld,
};
/// Describe the item under the cursor.
@@ -23,7 +22,7 @@ use crate::{
/// the tooltips. Label tooltips, for instance, are only generated when the
/// document is available.
pub fn tooltip(
- world: &dyn World,
+ world: &dyn IdeWorld,
document: Option<&Document>,
source: &Source,
cursor: usize,
@@ -52,7 +51,7 @@ pub enum Tooltip {
}
/// Tooltip for a hovered expression.
-fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
+fn expr_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
let mut ancestor = leaf;
while !ancestor.is::<ast::Expr>() {
ancestor = ancestor.parent()?;
@@ -112,8 +111,9 @@ fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
}
/// Tooltips for imports.
-fn import_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
+fn import_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! {
+ if leaf.kind() == SyntaxKind::Star;
if let Some(parent) = leaf.parent();
if let Some(import) = parent.cast::<ast::ModuleImport>();
if let Some(node) = parent.find(import.source().span());
@@ -192,7 +192,7 @@ fn label_tooltip(document: &Document, leaf: &LinkedNode) -> Option<Tooltip> {
}
/// Tooltips for components of a named parameter.
-fn named_param_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
+fn named_param_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
let (func, named) = if_chain! {
// Ensure that we are in a named pair in the arguments to a function
// call or set rule.
@@ -249,7 +249,7 @@ fn find_string_doc(info: &CastInfo, string: &str) -> Option<&'static str> {
}
/// Tooltip for font.
-fn font_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
+fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! {
// Ensure that we are on top of a string.
if let Some(string) = leaf.cast::<ast::Str>();
@@ -320,7 +320,7 @@ mod tests {
fn test_with_world(world: &TestWorld, cursor: isize, side: Side) -> Response {
let source = &world.main;
let doc = typst::compile(&world).output.ok();
- tooltip(&world, doc.as_ref(), source, source.cursor(cursor), side)
+ tooltip(world, doc.as_ref(), source, source.cursor(cursor), side)
}
#[test]