diff --git a/Cargo.lock b/Cargo.lock
index a01fb6398..0d62f6266 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2739,7 +2739,6 @@ dependencies = [
"ecow",
"flate2",
"fontdb",
- "if_chain",
"image",
"indexmap 2.0.0",
"log",
@@ -2841,6 +2840,18 @@ dependencies = [
"yaml-front-matter",
]
+[[package]]
+name = "typst-ide"
+version = "0.8.0"
+dependencies = [
+ "comemo",
+ "ecow",
+ "if_chain",
+ "serde",
+ "typst",
+ "unscanny",
+]
+
[[package]]
name = "typst-library"
version = "0.8.0"
diff --git a/crates/typst-docs/src/html.rs b/crates/typst-docs/src/html.rs
index c64649e3d..ea81fa5c9 100644
--- a/crates/typst-docs/src/html.rs
+++ b/crates/typst-docs/src/html.rs
@@ -215,7 +215,7 @@ impl<'a> Handler<'a> {
};
let root = parser(&code[1..code.len() - 1]);
- let html = typst::ide::highlight_html(&root);
+ let html = typst::syntax::highlight_html(&root);
*event = md::Event::Html(html.into());
}
@@ -370,7 +370,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
}
let root = typst::syntax::parse(&display);
- let highlighted = Html::new(typst::ide::highlight_html(&root));
+ let highlighted = Html::new(typst::syntax::highlight_html(&root));
if lang == "typ" {
return Html::new(format!("
{}
", highlighted.as_str()));
}
diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs
index 3018c244f..fa74a56e5 100644
--- a/crates/typst-docs/src/lib.rs
+++ b/crates/typst-docs/src/lib.rs
@@ -382,7 +382,7 @@ fn param_model(resolver: &dyn Resolver, info: &ParamInfo) -> ParamModel {
strings,
default: info.default.map(|default| {
let node = typst::syntax::parse_code(&default().repr());
- Html::new(typst::ide::highlight_html(&node))
+ Html::new(typst::syntax::highlight_html(&node))
}),
positional: info.positional,
named: info.named,
diff --git a/crates/typst-ide/Cargo.toml b/crates/typst-ide/Cargo.toml
new file mode 100644
index 000000000..ba3574f1f
--- /dev/null
+++ b/crates/typst-ide/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "typst-ide"
+description = "IDE functionality for Typst."
+categories = ["compilers", "science"]
+keywords = ["typst"]
+version.workspace = true
+rust-version.workspace = true
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+
+[lib]
+test = false
+doctest = false
+bench = false
+
+[dependencies]
+typst = { path = "../typst" }
+comemo = "0.3"
+ecow = { version = "0.1.2", features = ["serde"] }
+if_chain = "1"
+serde = { version = "1.0.184", features = ["derive"] }
+unscanny = "0.1"
diff --git a/crates/typst/src/ide/analyze.rs b/crates/typst-ide/src/analyze.rs
similarity index 91%
rename from crates/typst/src/ide/analyze.rs
rename to crates/typst-ide/src/analyze.rs
index 769e80adc..32f3df0d3 100644
--- a/crates/typst/src/ide/analyze.rs
+++ b/crates/typst-ide/src/analyze.rs
@@ -1,11 +1,10 @@
use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec};
-
-use crate::doc::Frame;
-use crate::eval::{Route, Scopes, Tracer, Value, Vm};
-use crate::model::{DelayedErrors, Introspector, Label, Locator, Vt};
-use crate::syntax::{ast, LinkedNode, Span, SyntaxKind};
-use crate::World;
+use typst::doc::Frame;
+use typst::eval::{Route, Scopes, Tracer, Value, Vm};
+use typst::model::{DelayedErrors, Introspector, Label, Locator, Vt};
+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 {
@@ -35,7 +34,7 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec {
let mut tracer = Tracer::new();
tracer.inspect(node.span());
- crate::compile(world, &mut tracer).ok();
+ typst::compile(world, &mut tracer).ok();
tracer.values()
}
@@ -65,7 +64,7 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option {
let route = Route::default();
let mut vm = Vm::new(vt, route.track(), Some(id), Scopes::new(Some(world.library())));
- crate::eval::import(&mut vm, source, Span::detached(), true)
+ typst::eval::import(&mut vm, source, Span::detached(), true)
.ok()
.map(Value::Module)
}
diff --git a/crates/typst/src/ide/complete.rs b/crates/typst-ide/src/complete.rs
similarity index 99%
rename from crates/typst/src/ide/complete.rs
rename to crates/typst-ide/src/complete.rs
index 27623fc21..dcf98a312 100644
--- a/crates/typst/src/ide/complete.rs
+++ b/crates/typst-ide/src/complete.rs
@@ -4,20 +4,20 @@ use std::collections::{BTreeSet, HashSet};
use ecow::{eco_format, EcoString};
use if_chain::if_chain;
use serde::{Deserialize, Serialize};
+use typst::doc::Frame;
+use typst::eval::{
+ format_str, AutoValue, CastInfo, Func, Library, NoneValue, Scope, Type, Value,
+};
+use typst::geom::Color;
+use typst::syntax::{
+ ast, is_id_continue, is_id_start, is_ident, LinkedNode, Source, SyntaxKind,
+};
+use typst::util::separated_list;
+use typst::World;
use unscanny::Scanner;
use super::analyze::analyze_labels;
use super::{analyze_expr, analyze_import, plain_docs_sentence, summarize_font_family};
-use crate::doc::Frame;
-use crate::eval::{
- format_str, AutoValue, CastInfo, Func, Library, NoneValue, Scope, Type, Value,
-};
-use crate::geom::Color;
-use crate::syntax::{
- ast, is_id_continue, is_id_start, is_ident, LinkedNode, Source, SyntaxKind,
-};
-use crate::util::separated_list;
-use crate::World;
/// Autocomplete a cursor position in a source file.
///
@@ -365,7 +365,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value) {
}
}
- for &(method, args) in crate::eval::mutable_methods_on(value.ty()) {
+ for &(method, args) in typst::eval::mutable_methods_on(value.ty()) {
ctx.completions.push(Completion {
kind: CompletionKind::Func,
label: method.into(),
@@ -378,7 +378,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value) {
})
}
- for &field in crate::eval::fields_on(value.ty()) {
+ for &field in typst::eval::fields_on(value.ty()) {
// Complete the field name along with its value. Notes:
// 1. No parentheses since function fields cannot currently be called
// with method syntax;
@@ -1136,7 +1136,7 @@ impl<'a> CompletionContext<'a> {
/// Add completions for a castable.
fn cast_completions(&mut self, cast: &'a CastInfo) {
// Prevent duplicate completions from appearing.
- if !self.seen_casts.insert(crate::util::hash128(cast)) {
+ if !self.seen_casts.insert(typst::util::hash128(cast)) {
return;
}
diff --git a/crates/typst/src/ide/jump.rs b/crates/typst-ide/src/jump.rs
similarity index 95%
rename from crates/typst/src/ide/jump.rs
rename to crates/typst-ide/src/jump.rs
index 34d51c070..a33e743c1 100644
--- a/crates/typst/src/ide/jump.rs
+++ b/crates/typst-ide/src/jump.rs
@@ -1,12 +1,11 @@
use std::num::NonZeroUsize;
use ecow::EcoString;
-
-use crate::doc::{Destination, Frame, FrameItem, Meta, Position};
-use crate::geom::{Geometry, Point, Size};
-use crate::model::Introspector;
-use crate::syntax::{FileId, LinkedNode, Source, Span, SyntaxKind};
-use crate::World;
+use typst::doc::{Destination, Frame, FrameItem, Meta, Position};
+use typst::geom::{Geometry, Point, Size};
+use typst::model::Introspector;
+use typst::syntax::{FileId, LinkedNode, Source, Span, SyntaxKind};
+use typst::World;
/// Where to [jump](jump_from_click) to.
#[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/typst/src/ide/mod.rs b/crates/typst-ide/src/lib.rs
similarity index 92%
rename from crates/typst/src/ide/mod.rs
rename to crates/typst-ide/src/lib.rs
index 4b08b66bb..3ab367dc8 100644
--- a/crates/typst/src/ide/mod.rs
+++ b/crates/typst-ide/src/lib.rs
@@ -2,22 +2,20 @@
mod analyze;
mod complete;
-mod highlight;
mod jump;
mod tooltip;
pub use self::analyze::analyze_labels;
pub use self::complete::{autocomplete, Completion, CompletionKind};
-pub use self::highlight::{highlight, highlight_html, Tag};
pub use self::jump::{jump_from_click, jump_from_cursor, Jump};
pub use self::tooltip::{tooltip, Tooltip};
use std::fmt::Write;
use ecow::{eco_format, EcoString};
+use typst::font::{FontInfo, FontStyle};
use self::analyze::*;
-use crate::font::{FontInfo, FontStyle};
/// Extract the first sentence of plain text of a piece of documentation.
///
@@ -80,8 +78,7 @@ fn summarize_font_family<'a>(variants: impl Iterator- ) -> Ec
}
let count = infos.len();
- let s = if count == 1 { "" } else { "s" };
- let mut detail = eco_format!("{count} variant{s}.");
+ let mut detail = eco_format!("{count} variant{}.", if count == 1 { "" } else { "s" });
if min_weight == max_weight {
write!(detail, " Weight {min_weight}.").unwrap();
diff --git a/crates/typst/src/ide/tooltip.rs b/crates/typst-ide/src/tooltip.rs
similarity index 95%
rename from crates/typst/src/ide/tooltip.rs
rename to crates/typst-ide/src/tooltip.rs
index b9dd17f73..8a418e0e8 100644
--- a/crates/typst/src/ide/tooltip.rs
+++ b/crates/typst-ide/src/tooltip.rs
@@ -1,18 +1,17 @@
use std::fmt::Write;
use ecow::{eco_format, EcoString};
-
use if_chain::if_chain;
+use typst::doc::Frame;
+use typst::eval::{CapturesVisitor, CastInfo, Tracer, Value};
+use typst::geom::{round_2, Length, Numeric};
+use typst::syntax::ast::{self, AstNode};
+use typst::syntax::{LinkedNode, Source, SyntaxKind};
+use typst::util::{pretty_comma_list, separated_list};
+use typst::World;
use super::analyze::analyze_labels;
use super::{analyze_expr, plain_docs_sentence, summarize_font_family};
-use crate::doc::Frame;
-use crate::eval::{CapturesVisitor, CastInfo, Tracer, Value};
-use crate::geom::{round_2, Length, Numeric};
-use crate::syntax::ast::{self, AstNode};
-use crate::syntax::{LinkedNode, Source, SyntaxKind};
-use crate::util::{pretty_comma_list, separated_list};
-use crate::World;
/// Describe the item under the cursor.
pub fn tooltip(
diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs
index 688e69d8f..dfd71275a 100644
--- a/crates/typst-library/src/text/raw.rs
+++ b/crates/typst-library/src/text/raw.rs
@@ -423,7 +423,7 @@ fn highlight_themed(
for child in node.children() {
let mut scopes = scopes.clone();
- if let Some(tag) = typst::ide::highlight(&child) {
+ if let Some(tag) = typst::syntax::highlight(&child) {
scopes.push(syntect::parsing::Scope::new(tag.tm_scope()).unwrap())
}
highlight_themed(&child, scopes, highlighter, f);
diff --git a/crates/typst/src/ide/highlight.rs b/crates/typst-syntax/src/highlight.rs
similarity index 99%
rename from crates/typst/src/ide/highlight.rs
rename to crates/typst-syntax/src/highlight.rs
index 197223f8a..e8ae613b6 100644
--- a/crates/typst/src/ide/highlight.rs
+++ b/crates/typst-syntax/src/highlight.rs
@@ -1,4 +1,4 @@
-use crate::syntax::{ast, LinkedNode, SyntaxKind, SyntaxNode};
+use crate::{ast, LinkedNode, SyntaxKind, SyntaxNode};
/// A syntax highlighting tag.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -405,7 +405,6 @@ mod tests {
use std::ops::Range;
use super::*;
- use crate::syntax::parse;
#[test]
fn test_highlighting() {
@@ -414,7 +413,7 @@ mod tests {
#[track_caller]
fn test(text: &str, goal: &[(Range, Tag)]) {
let mut vec = vec![];
- let root = parse(text);
+ let root = crate::parse(text);
highlight_tree(&mut vec, &LinkedNode::new(&root));
assert_eq!(vec, goal);
}
diff --git a/crates/typst-syntax/src/lib.rs b/crates/typst-syntax/src/lib.rs
index dec9a751e..4ee370969 100644
--- a/crates/typst-syntax/src/lib.rs
+++ b/crates/typst-syntax/src/lib.rs
@@ -3,6 +3,7 @@
pub mod ast;
mod file;
+mod highlight;
mod kind;
mod lexer;
mod node;
@@ -12,6 +13,7 @@ mod source;
mod span;
pub use self::file::{FileId, PackageSpec, PackageVersion, VirtualPath};
+pub use self::highlight::{highlight, highlight_html, Tag};
pub use self::kind::SyntaxKind;
pub use self::lexer::{is_id_continue, is_id_start, is_ident, is_newline};
pub use self::node::{LinkedChildren, LinkedNode, SyntaxError, SyntaxNode};
diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml
index 0775f67b6..630e7466f 100644
--- a/crates/typst/Cargo.toml
+++ b/crates/typst/Cargo.toml
@@ -25,7 +25,6 @@ comemo = "0.3"
ecow = { version = "0.1.2", features = ["serde"] }
flate2 = "1"
fontdb = { version = "0.14", default-features = false }
-if_chain = "1"
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
indexmap = { version = "2", features = ["serde"] }
log = "0.4"
diff --git a/crates/typst/src/eval/cast.rs b/crates/typst/src/eval/cast.rs
index 14413a616..0f9f63553 100644
--- a/crates/typst/src/eval/cast.rs
+++ b/crates/typst/src/eval/cast.rs
@@ -253,14 +253,12 @@ impl CastInfo {
msg.push_str(", found ");
write!(msg, "{}", found.ty()).unwrap();
}
- if_chain::if_chain! {
- if let Value::Int(i) = found;
- if parts.iter().any(|p| p == "length");
- if !matching_type;
- then {
+
+ if let Value::Int(i) = found {
+ if parts.iter().any(|p| p == "length") && !matching_type {
write!(msg, ": a length needs a unit - did you mean {i}pt?").unwrap();
}
- };
+ }
msg.into()
}
diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs
index d39a705ef..424e15ee1 100644
--- a/crates/typst/src/eval/mod.rs
+++ b/crates/typst/src/eval/mod.rs
@@ -50,10 +50,12 @@ pub use self::cast::{
pub use self::datetime::Datetime;
pub use self::dict::{dict, Dict};
pub use self::duration::Duration;
+pub use self::fields::fields_on;
pub use self::func::{
func, CapturesVisitor, Func, NativeFunc, NativeFuncData, ParamInfo,
};
pub use self::library::{set_lang_items, LangItems, Library};
+pub use self::methods::mutable_methods_on;
pub use self::module::Module;
pub use self::none::NoneValue;
pub use self::plugin::Plugin;
@@ -64,9 +66,6 @@ pub use self::tracer::Tracer;
pub use self::ty::{scope, ty, NativeType, NativeTypeData, Type};
pub use self::value::{Dynamic, Value};
-pub(crate) use self::fields::fields_on;
-pub(crate) use self::methods::mutable_methods_on;
-
use std::collections::HashSet;
use std::mem;
@@ -1829,7 +1828,7 @@ impl Eval for ast::ModuleInclude<'_> {
}
/// Process an import of a module relative to the current location.
-pub(crate) fn import(
+pub fn import(
vm: &mut Vm,
source: Value,
span: Span,
diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs
index 608abbccf..24a6311ea 100644
--- a/crates/typst/src/lib.rs
+++ b/crates/typst/src/lib.rs
@@ -46,7 +46,6 @@ pub mod doc;
pub mod export;
pub mod font;
pub mod geom;
-pub mod ide;
pub mod image;
pub mod model;