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;