Extract IDE crate

This commit is contained in:
Laurenz 2023-09-26 17:12:18 +02:00
parent 0d39fa021f
commit d7928a8ea3
16 changed files with 86 additions and 60 deletions

13
Cargo.lock generated
View File

@ -2739,7 +2739,6 @@ dependencies = [
"ecow", "ecow",
"flate2", "flate2",
"fontdb", "fontdb",
"if_chain",
"image", "image",
"indexmap 2.0.0", "indexmap 2.0.0",
"log", "log",
@ -2841,6 +2840,18 @@ dependencies = [
"yaml-front-matter", "yaml-front-matter",
] ]
[[package]]
name = "typst-ide"
version = "0.8.0"
dependencies = [
"comemo",
"ecow",
"if_chain",
"serde",
"typst",
"unscanny",
]
[[package]] [[package]]
name = "typst-library" name = "typst-library"
version = "0.8.0" version = "0.8.0"

View File

@ -215,7 +215,7 @@ impl<'a> Handler<'a> {
}; };
let root = parser(&code[1..code.len() - 1]); 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()); *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 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" { if lang == "typ" {
return Html::new(format!("<pre>{}</pre>", highlighted.as_str())); return Html::new(format!("<pre>{}</pre>", highlighted.as_str()));
} }

View File

@ -382,7 +382,7 @@ fn param_model(resolver: &dyn Resolver, info: &ParamInfo) -> ParamModel {
strings, strings,
default: info.default.map(|default| { default: info.default.map(|default| {
let node = typst::syntax::parse_code(&default().repr()); 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, positional: info.positional,
named: info.named, named: info.named,

View File

@ -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"

View File

@ -1,11 +1,10 @@
use comemo::Track; use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec}; use ecow::{eco_vec, EcoString, EcoVec};
use typst::doc::Frame;
use crate::doc::Frame; use typst::eval::{Route, Scopes, Tracer, Value, Vm};
use crate::eval::{Route, Scopes, Tracer, Value, Vm}; use typst::model::{DelayedErrors, Introspector, Label, Locator, Vt};
use crate::model::{DelayedErrors, Introspector, Label, Locator, Vt}; use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
use crate::syntax::{ast, LinkedNode, Span, SyntaxKind}; use typst::World;
use crate::World;
/// Try to determine a set of possible values for an expression. /// Try to determine a set of possible values for an expression.
pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> { pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> {
@ -35,7 +34,7 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> {
let mut tracer = Tracer::new(); let mut tracer = Tracer::new();
tracer.inspect(node.span()); tracer.inspect(node.span());
crate::compile(world, &mut tracer).ok(); typst::compile(world, &mut tracer).ok();
tracer.values() tracer.values()
} }
@ -65,7 +64,7 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
let route = Route::default(); let route = Route::default();
let mut vm = Vm::new(vt, route.track(), Some(id), Scopes::new(Some(world.library()))); 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() .ok()
.map(Value::Module) .map(Value::Module)
} }

View File

@ -4,20 +4,20 @@ use std::collections::{BTreeSet, HashSet};
use ecow::{eco_format, EcoString}; use ecow::{eco_format, EcoString};
use if_chain::if_chain; use if_chain::if_chain;
use serde::{Deserialize, Serialize}; 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 unscanny::Scanner;
use super::analyze::analyze_labels; use super::analyze::analyze_labels;
use super::{analyze_expr, analyze_import, plain_docs_sentence, summarize_font_family}; 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. /// 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 { ctx.completions.push(Completion {
kind: CompletionKind::Func, kind: CompletionKind::Func,
label: method.into(), 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: // Complete the field name along with its value. Notes:
// 1. No parentheses since function fields cannot currently be called // 1. No parentheses since function fields cannot currently be called
// with method syntax; // with method syntax;
@ -1136,7 +1136,7 @@ impl<'a> CompletionContext<'a> {
/// Add completions for a castable. /// Add completions for a castable.
fn cast_completions(&mut self, cast: &'a CastInfo) { fn cast_completions(&mut self, cast: &'a CastInfo) {
// Prevent duplicate completions from appearing. // Prevent duplicate completions from appearing.
if !self.seen_casts.insert(crate::util::hash128(cast)) { if !self.seen_casts.insert(typst::util::hash128(cast)) {
return; return;
} }

View File

@ -1,12 +1,11 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use ecow::EcoString; use ecow::EcoString;
use typst::doc::{Destination, Frame, FrameItem, Meta, Position};
use crate::doc::{Destination, Frame, FrameItem, Meta, Position}; use typst::geom::{Geometry, Point, Size};
use crate::geom::{Geometry, Point, Size}; use typst::model::Introspector;
use crate::model::Introspector; use typst::syntax::{FileId, LinkedNode, Source, Span, SyntaxKind};
use crate::syntax::{FileId, LinkedNode, Source, Span, SyntaxKind}; use typst::World;
use crate::World;
/// Where to [jump](jump_from_click) to. /// Where to [jump](jump_from_click) to.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]

View File

@ -2,22 +2,20 @@
mod analyze; mod analyze;
mod complete; mod complete;
mod highlight;
mod jump; mod jump;
mod tooltip; mod tooltip;
pub use self::analyze::analyze_labels; pub use self::analyze::analyze_labels;
pub use self::complete::{autocomplete, Completion, CompletionKind}; 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::jump::{jump_from_click, jump_from_cursor, Jump};
pub use self::tooltip::{tooltip, Tooltip}; pub use self::tooltip::{tooltip, Tooltip};
use std::fmt::Write; use std::fmt::Write;
use ecow::{eco_format, EcoString}; use ecow::{eco_format, EcoString};
use typst::font::{FontInfo, FontStyle};
use self::analyze::*; use self::analyze::*;
use crate::font::{FontInfo, FontStyle};
/// Extract the first sentence of plain text of a piece of documentation. /// Extract the first sentence of plain text of a piece of documentation.
/// ///
@ -80,8 +78,7 @@ fn summarize_font_family<'a>(variants: impl Iterator<Item = &'a FontInfo>) -> Ec
} }
let count = infos.len(); let count = infos.len();
let s = if count == 1 { "" } else { "s" }; let mut detail = eco_format!("{count} variant{}.", if count == 1 { "" } else { "s" });
let mut detail = eco_format!("{count} variant{s}.");
if min_weight == max_weight { if min_weight == max_weight {
write!(detail, " Weight {min_weight}.").unwrap(); write!(detail, " Weight {min_weight}.").unwrap();

View File

@ -1,18 +1,17 @@
use std::fmt::Write; use std::fmt::Write;
use ecow::{eco_format, EcoString}; use ecow::{eco_format, EcoString};
use if_chain::if_chain; 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::analyze_labels;
use super::{analyze_expr, plain_docs_sentence, summarize_font_family}; 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. /// Describe the item under the cursor.
pub fn tooltip( pub fn tooltip(

View File

@ -423,7 +423,7 @@ fn highlight_themed<F>(
for child in node.children() { for child in node.children() {
let mut scopes = scopes.clone(); 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()) scopes.push(syntect::parsing::Scope::new(tag.tm_scope()).unwrap())
} }
highlight_themed(&child, scopes, highlighter, f); highlight_themed(&child, scopes, highlighter, f);

View File

@ -1,4 +1,4 @@
use crate::syntax::{ast, LinkedNode, SyntaxKind, SyntaxNode}; use crate::{ast, LinkedNode, SyntaxKind, SyntaxNode};
/// A syntax highlighting tag. /// A syntax highlighting tag.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@ -405,7 +405,6 @@ mod tests {
use std::ops::Range; use std::ops::Range;
use super::*; use super::*;
use crate::syntax::parse;
#[test] #[test]
fn test_highlighting() { fn test_highlighting() {
@ -414,7 +413,7 @@ mod tests {
#[track_caller] #[track_caller]
fn test(text: &str, goal: &[(Range<usize>, Tag)]) { fn test(text: &str, goal: &[(Range<usize>, Tag)]) {
let mut vec = vec![]; let mut vec = vec![];
let root = parse(text); let root = crate::parse(text);
highlight_tree(&mut vec, &LinkedNode::new(&root)); highlight_tree(&mut vec, &LinkedNode::new(&root));
assert_eq!(vec, goal); assert_eq!(vec, goal);
} }

View File

@ -3,6 +3,7 @@
pub mod ast; pub mod ast;
mod file; mod file;
mod highlight;
mod kind; mod kind;
mod lexer; mod lexer;
mod node; mod node;
@ -12,6 +13,7 @@ mod source;
mod span; mod span;
pub use self::file::{FileId, PackageSpec, PackageVersion, VirtualPath}; pub use self::file::{FileId, PackageSpec, PackageVersion, VirtualPath};
pub use self::highlight::{highlight, highlight_html, Tag};
pub use self::kind::SyntaxKind; pub use self::kind::SyntaxKind;
pub use self::lexer::{is_id_continue, is_id_start, is_ident, is_newline}; pub use self::lexer::{is_id_continue, is_id_start, is_ident, is_newline};
pub use self::node::{LinkedChildren, LinkedNode, SyntaxError, SyntaxNode}; pub use self::node::{LinkedChildren, LinkedNode, SyntaxError, SyntaxNode};

View File

@ -25,7 +25,6 @@ comemo = "0.3"
ecow = { version = "0.1.2", features = ["serde"] } ecow = { version = "0.1.2", features = ["serde"] }
flate2 = "1" flate2 = "1"
fontdb = { version = "0.14", default-features = false } fontdb = { version = "0.14", default-features = false }
if_chain = "1"
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] } image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
log = "0.4" log = "0.4"

View File

@ -253,14 +253,12 @@ impl CastInfo {
msg.push_str(", found "); msg.push_str(", found ");
write!(msg, "{}", found.ty()).unwrap(); write!(msg, "{}", found.ty()).unwrap();
} }
if_chain::if_chain! {
if let Value::Int(i) = found; if let Value::Int(i) = found {
if parts.iter().any(|p| p == "length"); if parts.iter().any(|p| p == "length") && !matching_type {
if !matching_type;
then {
write!(msg, ": a length needs a unit - did you mean {i}pt?").unwrap(); write!(msg, ": a length needs a unit - did you mean {i}pt?").unwrap();
} }
}; }
msg.into() msg.into()
} }

View File

@ -50,10 +50,12 @@ pub use self::cast::{
pub use self::datetime::Datetime; pub use self::datetime::Datetime;
pub use self::dict::{dict, Dict}; pub use self::dict::{dict, Dict};
pub use self::duration::Duration; pub use self::duration::Duration;
pub use self::fields::fields_on;
pub use self::func::{ pub use self::func::{
func, CapturesVisitor, Func, NativeFunc, NativeFuncData, ParamInfo, func, CapturesVisitor, Func, NativeFunc, NativeFuncData, ParamInfo,
}; };
pub use self::library::{set_lang_items, LangItems, Library}; pub use self::library::{set_lang_items, LangItems, Library};
pub use self::methods::mutable_methods_on;
pub use self::module::Module; pub use self::module::Module;
pub use self::none::NoneValue; pub use self::none::NoneValue;
pub use self::plugin::Plugin; 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::ty::{scope, ty, NativeType, NativeTypeData, Type};
pub use self::value::{Dynamic, Value}; 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::collections::HashSet;
use std::mem; use std::mem;
@ -1829,7 +1828,7 @@ impl Eval for ast::ModuleInclude<'_> {
} }
/// Process an import of a module relative to the current location. /// Process an import of a module relative to the current location.
pub(crate) fn import( pub fn import(
vm: &mut Vm, vm: &mut Vm,
source: Value, source: Value,
span: Span, span: Span,

View File

@ -46,7 +46,6 @@ pub mod doc;
pub mod export; pub mod export;
pub mod font; pub mod font;
pub mod geom; pub mod geom;
pub mod ide;
pub mod image; pub mod image;
pub mod model; pub mod model;