From cc1f418423453dff68c7013957c3740a45b6ae4b Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:00:27 +0800 Subject: [PATCH] dev: reduce a bundle of ts usage in query crate (#72) --- Cargo.lock | 5 ++ Cargo.toml | 14 ++++++ crates/tinymist-query/Cargo.toml | 6 ++- crates/tinymist-query/src/analysis.rs | 14 +++--- crates/tinymist-query/src/analysis/def_use.rs | 6 +-- crates/tinymist-query/src/analysis/global.rs | 49 ++++++++++++------ .../src/analysis/track_values.rs | 2 +- crates/tinymist-query/src/completion.rs | 23 +++++---- crates/tinymist-query/src/diagnostics.rs | 2 +- crates/tinymist-query/src/document_symbol.rs | 4 +- crates/tinymist-query/src/folding_range.rs | 2 +- crates/tinymist-query/src/goto_declaration.rs | 2 +- crates/tinymist-query/src/goto_definition.rs | 6 +-- crates/tinymist-query/src/hover.rs | 21 ++++---- crates/tinymist-query/src/inlay_hint.rs | 8 +-- crates/tinymist-query/src/lib.rs | 12 ++++- crates/tinymist-query/src/prelude.rs | 15 ++---- crates/tinymist-query/src/references.rs | 7 ++- crates/tinymist-query/src/rename.rs | 2 +- crates/tinymist-query/src/syntax/import.rs | 12 +++-- .../src/syntax/lexical_hierarchy.rs | 8 ++- crates/tinymist-query/src/syntax/module.rs | 4 +- crates/tinymist-query/src/tests.rs | 46 +++++++++-------- crates/tinymist/Cargo.toml | 1 + crates/tinymist/src/actor/typst.rs | 50 ++++++++++++++++--- 25 files changed, 204 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfe749738..e59fbf14d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,8 +414,10 @@ checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.4", ] @@ -3616,6 +3618,7 @@ dependencies = [ "anyhow", "async-trait", "cargo_metadata", + "chrono", "clap", "clap_builder", "clap_complete", @@ -3656,6 +3659,7 @@ version = "0.11.0" dependencies = [ "anyhow", "comemo", + "ecow", "ena", "fxhash", "indexmap 2.2.5", @@ -3666,6 +3670,7 @@ dependencies = [ "lsp-types", "once_cell", "parking_lot", + "reflexo", "regex", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 87a17e68c..9b6959cde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,10 +44,23 @@ typst = "0.11.0" typst-ide = "0.11.0" typst-pdf = "0.11.0" typst-assets = { git = "https://github.com/typst/typst-assets", rev = "4d1211a" } +reflexo = { version = "0.4.2-rc8", default-features = false, features = [ + "flat-vector", +] } typst-ts-core = { version = "0.4.2-rc8" } typst-ts-compiler = { version = "0.4.2-rc8" } typst-preview = { git = "https://github.com/Enter-tainer/typst-preview", rev = "18630ebda22339109ef675a885787f4fc8731ba8" } +# [features] +# rkyv = ["dep:rkyv", "rkyv/alloc", "rkyv/archive_le"] +# rkyv-validation = ["dep:rkyv", "rkyv/validation"] +# flat-vector = ["rkyv", "rkyv-validation"] + +# __web = ["dep:wasm-bindgen", "dep:js-sys"] +# web = ["__web"] +# full = ["web", "flat-vector"] +# default = ["full"] + lsp-server = "0.7.6" lsp-types = { version = "=0.95.0", features = ["proposed"] } crossbeam-channel = "0.5.12" @@ -113,6 +126,7 @@ typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = " # typst-syntax = { path = "../typst/crates/typst-syntax" } typst-ts-svg-exporter = { git = "https://github.com/Myriad-Dreamin/typst.ts", rev = "2fc877de0a4bcd7a8f057933546a97348e9621c7", package = "typst-ts-svg-exporter" } +reflexo = { git = "https://github.com/Myriad-Dreamin/typst.ts", rev = "2fc877de0a4bcd7a8f057933546a97348e9621c7", package = "reflexo" } typst-ts-core = { git = "https://github.com/Myriad-Dreamin/typst.ts", rev = "2fc877de0a4bcd7a8f057933546a97348e9621c7", package = "typst-ts-core" } typst-ts-compiler = { git = "https://github.com/Myriad-Dreamin/typst.ts", rev = "2fc877de0a4bcd7a8f057933546a97348e9621c7", package = "typst-ts-compiler" } diff --git a/crates/tinymist-query/Cargo.toml b/crates/tinymist-query/Cargo.toml index 39017f8ef..84fba87b9 100644 --- a/crates/tinymist-query/Cargo.toml +++ b/crates/tinymist-query/Cargo.toml @@ -28,16 +28,18 @@ fxhash.workspace = true toml.workspace = true walkdir.workspace = true indexmap.workspace = true +ecow.workspace = true typst.workspace = true typst-ide.workspace = true -typst-ts-core = { version = "0.4.2-rc6", default-features = false, features = [ +reflexo.workspace = true +typst-ts-compiler.workspace = true +typst-ts-core = { version = "0.4.2-rc8", default-features = false, features = [ "flat-vector", "vector-bbox", "no-content-hint", ] } -typst-ts-compiler.workspace = true lsp-types.workspace = true diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 71f16290e..60884f7b4 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -8,9 +8,9 @@ pub use global::*; #[cfg(test)] mod module_tests { + use ecow::EcoVec; + use reflexo::path::unix_slash; use serde_json::json; - use typst_ts_core::path::unix_slash; - use typst_ts_core::typst::prelude::EcoVec; use crate::prelude::*; use crate::syntax::module::*; @@ -18,7 +18,7 @@ mod module_tests { #[test] fn test() { - snapshot_testing2("modules", &|ctx, _| { + snapshot_testing("modules", &|ctx, _| { fn ids(ids: EcoVec<TypstFileId>) -> Vec<String> { let mut ids: Vec<String> = ids .into_iter() @@ -66,14 +66,14 @@ mod lexical_hierarchy_tests { use def_use::DefUseSnapshot; use crate::analysis::def_use; - use crate::prelude::*; + // use crate::prelude::*; use crate::syntax::lexical_hierarchy; use crate::tests::*; #[test] fn scope() { - snapshot_testing("lexical_hierarchy", &|world, path| { - let source = get_suitable_source_in_workspace(world, &path).unwrap(); + snapshot_testing("lexical_hierarchy", &|ctx, path| { + let source = ctx.source_by_path(&path).unwrap(); let result = lexical_hierarchy::get_lexical_hierarchy( source, @@ -87,7 +87,7 @@ mod lexical_hierarchy_tests { #[test] fn test_def_use() { fn def_use(set: &str) { - snapshot_testing2(set, &|ctx, path| { + snapshot_testing(set, &|ctx, path| { let source = ctx.source_by_path(&path).unwrap(); let result = ctx.def_use(source); diff --git a/crates/tinymist-query/src/analysis/def_use.rs b/crates/tinymist-query/src/analysis/def_use.rs index 0af602cff..2a5e52891 100644 --- a/crates/tinymist-query/src/analysis/def_use.rs +++ b/crates/tinymist-query/src/analysis/def_use.rs @@ -7,9 +7,11 @@ use std::{ }; use log::info; +use reflexo::path::unix_slash; +pub use reflexo::vector::ir::DefId; use serde::Serialize; +use typst::syntax::FileId as TypstFileId; use typst::syntax::Source; -use typst_ts_core::{path::unix_slash, TypstFileId}; use super::SearchCtx; use crate::syntax::{ @@ -18,8 +20,6 @@ use crate::syntax::{ }; use crate::{adt::snapshot_map::SnapshotMap, syntax::LexicalModKind}; -pub use typst_ts_core::vector::ir::DefId; - /// The type namespace of def-use relations /// /// The symbols from different namespaces are not visible to each other. diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 435893eb1..67949e413 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -1,17 +1,22 @@ use std::{ collections::{HashMap, HashSet}, - path::Path, + path::{Path, PathBuf}, sync::Arc, }; use once_cell::sync::OnceCell; +use reflexo::{cow_mut::CowMut, ImmutPath}; +use typst::syntax::FileId as TypstFileId; use typst::{ diag::{eco_format, FileError, FileResult}, syntax::{Source, VirtualPath}, World, }; -use typst_ts_compiler::{service::WorkspaceProvider, TypstSystemWorld}; -use typst_ts_core::{cow_mut::CowMut, ImmutPath, TypstFileId}; +use typst_ts_compiler::package::Registry; +use typst_ts_compiler::TypstSystemWorld; +// use typst_ts_compiler::TypstSystemWorld; +// use typst_ts_compiler::{service::WorkspaceProvider, TypstSystemWorld}; +// use typst_ts_core::{cow_mut::CowMut, ImmutPath}; use super::{get_def_use_inner, DefUseInfo}; use crate::{ @@ -57,10 +62,11 @@ pub struct Analysis { /// changes. pub root: ImmutPath, /// The position encoding for the workspace. - position_encoding: PositionEncoding, + pub position_encoding: PositionEncoding, } /// A cache for all level of analysis results of a module. +#[derive(Default)] pub struct AnalysisCaches { modules: HashMap<TypstFileId, ModuleAnalysisCache>, root_files: OnceCell<Vec<TypstFileId>>, @@ -78,18 +84,11 @@ pub struct AnalysisContext<'a> { impl<'w> AnalysisContext<'w> { /// Create a new analysis context. - pub fn new(world: &'w TypstSystemWorld, encoding: PositionEncoding) -> Self { + pub fn new(world: &'w TypstSystemWorld, a: Analysis) -> Self { Self { world, - analysis: CowMut::Owned(Analysis { - root: world.workspace_root(), - position_encoding: encoding, - }), - caches: AnalysisCaches { - modules: HashMap::new(), - root_files: OnceCell::new(), - module_deps: OnceCell::new(), - }, + analysis: CowMut::Owned(a), + caches: AnalysisCaches::default(), } } @@ -117,6 +116,24 @@ impl<'w> AnalysisContext<'w> { } } + /// Resolve the real path for a file id. + pub fn path_for_id(&self, id: TypstFileId) -> Result<PathBuf, FileError> { + if id.vpath().as_rootless_path() == Path::new("-") { + return Ok(PathBuf::from("-")); + } + + // Determine the root path relative to which the file path + // will be resolved. + let root = match id.package() { + Some(spec) => self.world.registry.resolve(spec)?, + None => self.analysis.root.clone(), + }; + + // Join the path to the root. If it tries to escape, deny + // access. Note: It can still escape via symlinks. + id.vpath().resolve(&root).ok_or(FileError::AccessDenied) + } + /// Get the source of a file by file id. pub fn source_by_id(&mut self, id: TypstFileId) -> FileResult<Source> { self.get_mut(id); @@ -173,6 +190,10 @@ impl<'w> AnalysisContext<'w> { lsp_to_typst::range(position, self.analysis.position_encoding, src) } + pub fn to_lsp_pos(&self, typst_offset: usize, src: &Source) -> LspPosition { + typst_to_lsp::offset_to_position(typst_offset, self.analysis.position_encoding, src) + } + pub fn to_lsp_range(&self, position: TypstRange, src: &Source) -> LspRange { typst_to_lsp::range(position, src, self.analysis.position_encoding) } diff --git a/crates/tinymist-query/src/analysis/track_values.rs b/crates/tinymist-query/src/analysis/track_values.rs index 80342edb7..3612b5d29 100644 --- a/crates/tinymist-query/src/analysis/track_values.rs +++ b/crates/tinymist-query/src/analysis/track_values.rs @@ -1,6 +1,7 @@ //! Dynamic analysis of an expression or import statement. use comemo::Track; +use ecow::*; use typst::engine::{Engine, Route}; use typst::eval::{Tracer, Vm}; use typst::foundations::{Context, Label, Scopes, Styles, Value}; @@ -8,7 +9,6 @@ use typst::introspection::{Introspector, Locator}; use typst::model::{BibliographyElem, Document}; use typst::syntax::{ast, LinkedNode, Span, SyntaxKind}; use typst::World; -use typst_ts_core::typst::prelude::*; /// Try to determine a set of possible values for an expression. pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<(Value, Option<Styles>)> { diff --git a/crates/tinymist-query/src/completion.rs b/crates/tinymist-query/src/completion.rs index 378373948..367081c5c 100644 --- a/crates/tinymist-query/src/completion.rs +++ b/crates/tinymist-query/src/completion.rs @@ -1,4 +1,4 @@ -use crate::prelude::*; +use crate::{prelude::*, StatefulRequest}; #[derive(Debug, Clone)] pub struct CompletionRequest { @@ -7,16 +7,17 @@ pub struct CompletionRequest { pub explicit: bool, } -impl CompletionRequest { - pub fn request( +impl StatefulRequest for CompletionRequest { + type Response = CompletionResponse; + + fn request( self, - world: &TypstSystemWorld, + ctx: &mut AnalysisContext, doc: Option<VersionedDocument>, - position_encoding: PositionEncoding, - ) -> Option<CompletionResponse> { + ) -> Option<Self::Response> { let doc = doc.as_ref().map(|doc| doc.document.as_ref()); - let source = get_suitable_source_in_workspace(world, &self.path).ok()?; - let cursor = lsp_to_typst::position(self.position, position_encoding, &source)?; + let source = ctx.source_by_path(&self.path).ok()?; + let cursor = ctx.to_typst_pos(self.position, &source)?; // Please see <https://github.com/nvarner/typst-lsp/commit/2d66f26fb96ceb8e485f492e5b81e9db25c3e8ec> // @@ -33,10 +34,10 @@ impl CompletionRequest { // assume that the completion is not explicit. let explicit = false; - let (offset, completions) = typst_ide::autocomplete(world, doc, &source, cursor, explicit)?; + let (offset, completions) = + typst_ide::autocomplete(ctx.world, doc, &source, cursor, explicit)?; - let lsp_start_position = - typst_to_lsp::offset_to_position(offset, position_encoding, &source); + let lsp_start_position = ctx.to_lsp_pos(offset, &source); let replace_range = LspRange::new(lsp_start_position, self.position); Some(typst_to_lsp::completions(&completions, replace_range).into()) } diff --git a/crates/tinymist-query/src/diagnostics.rs b/crates/tinymist-query/src/diagnostics.rs index 588c9b185..c5e685e52 100644 --- a/crates/tinymist-query/src/diagnostics.rs +++ b/crates/tinymist-query/src/diagnostics.rs @@ -101,7 +101,7 @@ fn diagnostic_related_information( Ok(tracepoints) } -fn diagnostic_span_id(typst_diagnostic: &TypstDiagnostic) -> Option<(FileId, TypstSpan)> { +fn diagnostic_span_id(typst_diagnostic: &TypstDiagnostic) -> Option<(TypstFileId, TypstSpan)> { iter::once(typst_diagnostic.span) .chain(typst_diagnostic.trace.iter().map(|trace| trace.span)) .find_map(|span| Some((span.id()?, span))) diff --git a/crates/tinymist-query/src/document_symbol.rs b/crates/tinymist-query/src/document_symbol.rs index badc70e72..200369132 100644 --- a/crates/tinymist-query/src/document_symbol.rs +++ b/crates/tinymist-query/src/document_symbol.rs @@ -57,10 +57,10 @@ mod tests { #[test] fn test() { - snapshot_testing("document_symbols", &|world, path| { + snapshot_testing("document_symbols", &|ctx, path| { let request = DocumentSymbolRequest { path: path.clone() }; - let source = get_suitable_source_in_workspace(world, &path).unwrap(); + let source = ctx.source_by_path(&path).unwrap(); let result = request.request(source, PositionEncoding::Utf16); assert_snapshot!(JsonRepr::new_redacted(result.unwrap(), &REDACT_LOC)); diff --git a/crates/tinymist-query/src/folding_range.rs b/crates/tinymist-query/src/folding_range.rs index 50ceb0168..0d2a8e571 100644 --- a/crates/tinymist-query/src/folding_range.rs +++ b/crates/tinymist-query/src/folding_range.rs @@ -124,7 +124,7 @@ mod tests { line_folding_only: true, }; - let source = get_suitable_source_in_workspace(world, &path).unwrap(); + let source = world.source_by_path(&path).unwrap(); let result = request.request(source, PositionEncoding::Utf16); assert_snapshot!(JsonRepr::new_pure(result.unwrap())); diff --git a/crates/tinymist-query/src/goto_declaration.rs b/crates/tinymist-query/src/goto_declaration.rs index 8be4ad7f9..957bf290f 100644 --- a/crates/tinymist-query/src/goto_declaration.rs +++ b/crates/tinymist-query/src/goto_declaration.rs @@ -38,7 +38,7 @@ impl SyntaxRequest for GotoDeclarationRequest { let ref_id = source.id(); let ref_source = &source; - let span_path = ctx.world.path_for_id(ref_id).ok()?; + let span_path = ctx.path_for_id(ref_id).ok()?; let range = ctx.to_lsp_range(ref_range, ref_source); let uri = Url::from_file_path(span_path).ok()?; diff --git a/crates/tinymist-query/src/goto_definition.rs b/crates/tinymist-query/src/goto_definition.rs index 026d4af79..ee0f61474 100644 --- a/crates/tinymist-query/src/goto_definition.rs +++ b/crates/tinymist-query/src/goto_definition.rs @@ -2,7 +2,7 @@ use std::ops::Range; use log::debug; use typst::foundations::Value; -use typst_ts_core::TypstFileId; +use typst::syntax::FileId as TypstFileId; use crate::{ prelude::*, @@ -53,7 +53,7 @@ impl SyntaxRequest for GotoDefinitionRequest { let def = find_definition(ctx, source.clone(), deref_target)?; - let span_path = ctx.world.path_for_id(def.fid).ok()?; + let span_path = ctx.path_for_id(def.fid).ok()?; let uri = Url::from_file_path(span_path).ok()?; let span_source = ctx.source_by_id(def.fid).ok()?; @@ -221,7 +221,7 @@ mod tests { #[test] fn test() { - snapshot_testing2("goto_definition", &|world, path| { + snapshot_testing("goto_definition", &|world, path| { let source = world.source_by_path(&path).unwrap(); let request = GotoDefinitionRequest { diff --git a/crates/tinymist-query/src/hover.rs b/crates/tinymist-query/src/hover.rs index 96b86617b..cac8b76fb 100644 --- a/crates/tinymist-query/src/hover.rs +++ b/crates/tinymist-query/src/hover.rs @@ -1,4 +1,4 @@ -use crate::prelude::*; +use crate::{prelude::*, StatefulRequest}; #[derive(Debug, Clone)] pub struct HoverRequest { @@ -6,24 +6,25 @@ pub struct HoverRequest { pub position: LspPosition, } -impl HoverRequest { - pub fn request( +impl StatefulRequest for HoverRequest { + type Response = Hover; + + fn request( self, - world: &TypstSystemWorld, + ctx: &mut AnalysisContext, doc: Option<VersionedDocument>, - position_encoding: PositionEncoding, - ) -> Option<Hover> { + ) -> Option<Self::Response> { let doc = doc.as_ref().map(|doc| doc.document.as_ref()); - let source = get_suitable_source_in_workspace(world, &self.path).ok()?; - let offset = lsp_to_typst::position(self.position, position_encoding, &source)?; + let source = ctx.source_by_path(&self.path).ok()?; + let offset = ctx.to_typst_pos(self.position, &source)?; // the typst's cursor is 1-based, so we need to add 1 to the offset let cursor = offset + 1; - let typst_tooltip = typst_ide::tooltip(world, doc, &source, cursor)?; + let typst_tooltip = typst_ide::tooltip(ctx.world, doc, &source, cursor)?; let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - let range = typst_to_lsp::range(ast_node.range(), &source, position_encoding); + let range = ctx.to_lsp_range(ast_node.range(), &source); Some(Hover { contents: typst_to_lsp::tooltip(&typst_tooltip), diff --git a/crates/tinymist-query/src/inlay_hint.rs b/crates/tinymist-query/src/inlay_hint.rs index 9892c1611..936ab132d 100644 --- a/crates/tinymist-query/src/inlay_hint.rs +++ b/crates/tinymist-query/src/inlay_hint.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, ops::Range}; +use ecow::eco_vec; use log::debug; use lsp_types::{InlayHintKind, InlayHintLabel}; use typst::{ @@ -7,7 +8,6 @@ use typst::{ syntax::SyntaxNode, util::LazyHash, }; -use typst_ts_core::typst::prelude::eco_vec; use crate::{prelude::*, SyntaxRequest}; @@ -68,7 +68,7 @@ impl SyntaxRequest for InlayHintRequest { } fn inlay_hint( - world: &TypstSystemWorld, + world: &dyn World, source: &Source, range: Range<usize>, encoding: PositionEncoding, @@ -76,7 +76,7 @@ fn inlay_hint( const SMART: InlayHintConfig = InlayHintConfig::smart(); struct InlayHintWorker<'a> { - world: &'a TypstSystemWorld, + world: &'a dyn World, source: &'a Source, range: Range<usize>, encoding: PositionEncoding, @@ -661,7 +661,7 @@ mod tests { #[test] fn smart() { - snapshot_testing2("inlay_hints", &|ctx, path| { + snapshot_testing("inlay_hints", &|ctx, path| { let source = ctx.source_by_path(&path).unwrap(); let request = InlayHintRequest { diff --git a/crates/tinymist-query/src/lib.rs b/crates/tinymist-query/src/lib.rs index 5182b5965..059c00a0d 100644 --- a/crates/tinymist-query/src/lib.rs +++ b/crates/tinymist-query/src/lib.rs @@ -7,7 +7,7 @@ pub(crate) mod diagnostics; use std::sync::Arc; pub use analysis::AnalysisContext; -use typst_ts_core::TypstDocument; +use typst::model::Document as TypstDocument; pub use diagnostics::*; pub(crate) mod code_lens; @@ -62,6 +62,16 @@ pub trait SyntaxRequest { fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response>; } +pub trait StatefulRequest { + type Response; + + fn request( + self, + ctx: &mut AnalysisContext, + v: Option<VersionedDocument>, + ) -> Option<Self::Response>; +} + mod polymorphic { use super::prelude::*; use super::*; diff --git a/crates/tinymist-query/src/prelude.rs b/crates/tinymist-query/src/prelude.rs index 73f189d31..525f7002b 100644 --- a/crates/tinymist-query/src/prelude.rs +++ b/crates/tinymist-query/src/prelude.rs @@ -15,17 +15,18 @@ pub use lsp_types::{ SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp, SignatureInformation, SymbolInformation, Url, WorkspaceEdit, }; +pub use reflexo::vector::ir::DefId; pub use serde_json::Value as JsonValue; pub use typst::diag::{EcoString, FileError, FileResult, Tracepoint}; pub use typst::foundations::{Func, ParamInfo, Value}; +pub use typst::syntax::FileId as TypstFileId; pub use typst::syntax::{ ast::{self, AstNode}, - FileId, LinkedNode, Source, Spanned, SyntaxKind, VirtualPath, + LinkedNode, Source, Spanned, SyntaxKind, }; pub use typst::World; -use typst_ts_compiler::service::WorkspaceProvider; +// use typst_ts_compiler::service::WorkspaceProvider; pub use typst_ts_compiler::TypstSystemWorld; -pub use typst_ts_core::TypstFileId; pub use crate::analysis::{analyze_expr, AnalysisContext}; pub use crate::lsp_typst_boundary::{ @@ -33,11 +34,3 @@ pub use crate::lsp_typst_boundary::{ TypstDiagnostic, TypstSeverity, TypstSpan, }; pub use crate::VersionedDocument; - -pub fn get_suitable_source_in_workspace(w: &TypstSystemWorld, p: &Path) -> FileResult<Source> { - // todo: source in packages - let relative_path = p - .strip_prefix(&w.workspace_root()) - .map_err(|_| FileError::NotFound(p.to_owned()))?; - w.source(TypstFileId::new(None, VirtualPath::new(relative_path))) -} diff --git a/crates/tinymist-query/src/references.rs b/crates/tinymist-query/src/references.rs index 8c34ee62b..ca5f1620a 100644 --- a/crates/tinymist-query/src/references.rs +++ b/crates/tinymist-query/src/references.rs @@ -1,5 +1,4 @@ use log::debug; -use typst_ts_core::vector::ir::DefId; use crate::{ prelude::*, @@ -116,7 +115,7 @@ pub(crate) fn find_references_root( position_encoding: PositionEncoding, ) -> Option<Vec<LspLocation>> { let def_source = ctx.source_by_id(def_fid).ok()?; - let def_path = ctx.world.path_for_id(def_fid).ok()?; + let def_path = ctx.path_for_id(def_fid).ok()?; let uri = Url::from_file_path(def_path).ok()?; // todo: reuse uri, range to location @@ -140,7 +139,7 @@ pub(crate) fn find_references_root( let ref_source = ctx.ctx.source_by_id(ref_fid).ok()?; let def_use = ctx.ctx.def_use(ref_source.clone())?; - let uri = ctx.ctx.world.path_for_id(ref_fid).ok()?; + let uri = ctx.ctx.path_for_id(ref_fid).ok()?; let uri = Url::from_file_path(uri).ok()?; let mut redefines = vec![]; @@ -176,7 +175,7 @@ mod tests { #[test] fn test() { // goto_definition - snapshot_testing2("references", &|world, path| { + snapshot_testing("references", &|world, path| { let source = world.source_by_path(&path).unwrap(); let request = ReferencesRequest { diff --git a/crates/tinymist-query/src/rename.rs b/crates/tinymist-query/src/rename.rs index 79e5256cf..075925836 100644 --- a/crates/tinymist-query/src/rename.rs +++ b/crates/tinymist-query/src/rename.rs @@ -47,7 +47,7 @@ impl SyntaxRequest for RenameRequest { let def_loc = { let def_source = ctx.source_by_id(lnk.fid).ok()?; - let span_path = ctx.world.path_for_id(lnk.fid).ok()?; + let span_path = ctx.path_for_id(lnk.fid).ok()?; let uri = Url::from_file_path(span_path).ok()?; let Some(range) = lnk.name_range else { diff --git a/crates/tinymist-query/src/syntax/import.rs b/crates/tinymist-query/src/syntax/import.rs index 052659403..a9f8e1114 100644 --- a/crates/tinymist-query/src/syntax/import.rs +++ b/crates/tinymist-query/src/syntax/import.rs @@ -1,7 +1,11 @@ use std::path::Path; -use typst::syntax::{ast, package::PackageManifest, LinkedNode, Source, SyntaxKind, VirtualPath}; -use typst_ts_core::{package::PackageSpec, typst::prelude::EcoVec, TypstFileId}; +use ecow::EcoVec; +use typst::syntax::{ + ast, + package::{PackageManifest, PackageSpec}, + FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath, +}; use crate::prelude::*; @@ -13,7 +17,7 @@ fn resolve_id_by_path( if import_path.starts_with('@') { let spec = import_path.parse::<PackageSpec>().ok()?; // Evaluate the manifest. - let manifest_id = FileId::new(Some(spec.clone()), VirtualPath::new("typst.toml")); + let manifest_id = TypstFileId::new(Some(spec.clone()), VirtualPath::new("typst.toml")); let bytes = world.file(manifest_id).ok()?; let string = std::str::from_utf8(&bytes).map_err(FileError::from).ok()?; let manifest: PackageManifest = toml::from_str(string).ok()?; @@ -80,7 +84,7 @@ pub fn find_imports(world: &dyn World, source: &Source) -> EcoVec<TypstFileId> { struct ImportWorker<'a> { world: &'a dyn World, current: TypstFileId, - imports: EcoVec<(FileId, LinkedNode<'a>)>, + imports: EcoVec<(TypstFileId, LinkedNode<'a>)>, } impl<'a> ImportWorker<'a> { diff --git a/crates/tinymist-query/src/syntax/lexical_hierarchy.rs b/crates/tinymist-query/src/syntax/lexical_hierarchy.rs index a1a9076b8..23986ef26 100644 --- a/crates/tinymist-query/src/syntax/lexical_hierarchy.rs +++ b/crates/tinymist-query/src/syntax/lexical_hierarchy.rs @@ -4,21 +4,19 @@ use std::{ }; use anyhow::{anyhow, Context}; +use ecow::{eco_vec, EcoVec}; use log::info; use lsp_types::SymbolKind; +use reflexo::error::prelude::*; use serde::{Deserialize, Serialize}; use typst::{ syntax::{ ast::{self, AstNode}, + package::PackageSpec, LinkedNode, Source, SyntaxKind, }, util::LazyHash, }; -use typst_ts_core::{ - error::prelude::WithContext, - package::PackageSpec, - typst::prelude::{eco_vec, EcoVec}, -}; use super::IdentRef; diff --git a/crates/tinymist-query/src/syntax/module.rs b/crates/tinymist-query/src/syntax/module.rs index b24db6557..3b6af4f10 100644 --- a/crates/tinymist-query/src/syntax/module.rs +++ b/crates/tinymist-query/src/syntax/module.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::Path, sync::Once}; -use typst::syntax::VirtualPath; -use typst_ts_core::{typst::prelude::EcoVec, TypstFileId}; +use ecow::EcoVec; +use typst::syntax::{FileId as TypstFileId, VirtualPath}; use crate::prelude::AnalysisContext; diff --git a/crates/tinymist-query/src/tests.rs b/crates/tinymist-query/src/tests.rs index bb732e94a..92ae2911c 100644 --- a/crates/tinymist-query/src/tests.rs +++ b/crates/tinymist-query/src/tests.rs @@ -20,9 +20,11 @@ use typst_ts_core::{config::CompileOpts, Bytes, TypstFileId}; pub use insta::assert_snapshot; pub use typst_ts_compiler::TypstSystemWorld; -use crate::{prelude::AnalysisContext, typst_to_lsp, LspPosition, PositionEncoding}; +use crate::{ + analysis::Analysis, prelude::AnalysisContext, typst_to_lsp, LspPosition, PositionEncoding, +}; -pub fn snapshot_testing(name: &str, f: &impl Fn(&mut TypstSystemWorld, PathBuf)) { +pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf)) { let mut settings = insta::Settings::new(); settings.set_prepend_module_to_snapshot(false); settings.set_snapshot_path(format!("fixtures/{name}/snaps")); @@ -31,29 +33,31 @@ pub fn snapshot_testing(name: &str, f: &impl Fn(&mut TypstSystemWorld, PathBuf)) insta::glob!(&glob_path, |path| { let contents = std::fs::read_to_string(path).unwrap(); - run_with_sources(&contents, f); + run_with_sources(&contents, |w: &mut TypstSystemWorld, p| { + let paths = w + .shadow_paths() + .into_iter() + .map(|p| { + TypstFileId::new( + None, + VirtualPath::new(p.strip_prefix(w.workspace_root()).unwrap()), + ) + }) + .collect::<Vec<_>>(); + let mut ctx = AnalysisContext::new( + w, + Analysis { + root: w.workspace_root(), + position_encoding: PositionEncoding::Utf16, + }, + ); + ctx.test_files(|| paths); + f(&mut ctx, p); + }); }); }); } -pub fn snapshot_testing2(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf)) { - snapshot_testing(name, &|w, p| { - let paths = w - .shadow_paths() - .into_iter() - .map(|p| { - TypstFileId::new( - None, - VirtualPath::new(p.strip_prefix(w.workspace_root()).unwrap()), - ) - }) - .collect::<Vec<_>>(); - let mut ctx = AnalysisContext::new(w, PositionEncoding::Utf16); - ctx.test_files(|| paths); - f(&mut ctx, p); - }); -} - pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, PathBuf) -> T) -> T { let root = if cfg!(windows) { PathBuf::from("C:\\") diff --git a/crates/tinymist/Cargo.toml b/crates/tinymist/Cargo.toml index 51bcd2469..52a0eff3d 100644 --- a/crates/tinymist/Cargo.toml +++ b/crates/tinymist/Cargo.toml @@ -53,6 +53,7 @@ lsp-server.workspace = true crossbeam-channel.workspace = true lsp-types.workspace = true dhat = { version = "0.3.3", optional = true } +chrono = { version = "0.4" } [features] default = ["cli", "preview"] diff --git a/crates/tinymist/src/actor/typst.rs b/crates/tinymist/src/actor/typst.rs index 2d09c6eba..962a80d1f 100644 --- a/crates/tinymist/src/actor/typst.rs +++ b/crates/tinymist/src/actor/typst.rs @@ -11,8 +11,9 @@ use log::{debug, error, info, trace, warn}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use tinymist_query::{ - analysis::AnalysisContext, CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, - FoldRequestFeature, OnExportRequest, OnSaveExportRequest, PositionEncoding, SyntaxRequest, + analysis::{Analysis, AnalysisContext}, + CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature, + OnExportRequest, OnSaveExportRequest, PositionEncoding, StatefulRequest, SyntaxRequest, VersionedDocument, }; use tokio::sync::{broadcast, mpsc, oneshot, watch}; @@ -204,8 +205,7 @@ impl<'a> fmt::Debug for ShowOpts<'a> { macro_rules! query_state { ($self:ident, $method:ident, $req:expr) => {{ - let enc = $self.position_encoding; - let res = $self.steal_state(move |w, doc| $req.request(w, doc, enc)); + let res = $self.steal_state(move |w, doc| $req.request(w, doc)); res.map(CompilerQueryResponse::$method) }}; } @@ -664,11 +664,39 @@ impl CompileActor { fn steal_state<T: Send + Sync + 'static>( &self, - f: impl FnOnce(&TypstSystemWorld, Option<VersionedDocument>) -> T + Send + Sync + 'static, + f: impl FnOnce(&mut AnalysisContext, Option<VersionedDocument>) -> T + Send + Sync + 'static, ) -> anyhow::Result<T> { - let fut = self.steal(move |compiler| f(compiler.compiler.world(), compiler.success_doc())); + let enc = self.position_encoding; + + self.steal(move |compiler| { + // todo: record analysis + let doc = compiler.success_doc(); + let w = compiler.compiler.world_mut(); + + let Some(main) = w.main else { + log::error!("TypstActor: main file is not set"); + return Err(anyhow!("main file is not set")); + }; + w.source(main).map_err(|err| { + log::info!("TypstActor: failed to prepare main file: {:?}", err); + anyhow!("failed to get source: {err}") + })?; + w.prepare_env(&mut Default::default()).map_err(|err| { + log::error!("TypstActor: failed to prepare env: {:?}", err); + anyhow!("failed to prepare env") + })?; - Ok(fut?) + Ok(f( + &mut AnalysisContext::new( + w, + Analysis { + root: w.root.clone(), + position_encoding: enc, + }, + ), + doc, + )) + })? } fn steal_world<T: Send + Sync + 'static>( @@ -694,7 +722,13 @@ impl CompileActor { anyhow!("failed to prepare env") })?; - Ok(f(&mut AnalysisContext::new(compiler.compiler.world(), enc))) + Ok(f(&mut AnalysisContext::new( + w, + Analysis { + root: w.root.clone(), + position_encoding: enc, + }, + ))) })? } }