diff --git a/Cargo.lock b/Cargo.lock index d95c4f26..acbf2570 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4213,6 +4213,7 @@ dependencies = [ name = "tinymist-vfs" version = "0.12.18" dependencies = [ + "comemo", "indexmap 2.7.0", "js-sys", "log", diff --git a/crates/tinymist-project/src/lock.rs b/crates/tinymist-project/src/lock.rs index 98b0a151..2b0dcb2c 100644 --- a/crates/tinymist-project/src/lock.rs +++ b/crates/tinymist-project/src/lock.rs @@ -9,7 +9,7 @@ use crate::LspWorld; /// Make a new project lock updater. pub fn update_lock(world: &LspWorld) -> Option { - let root = world.workspace_root()?; + let root = world.entry_state().workspace_root()?; Some(ProjectLockUpdater { root, updates: vec![], diff --git a/crates/tinymist-project/src/watch.rs b/crates/tinymist-project/src/watch.rs index decb22d9..9d81f8cf 100644 --- a/crates/tinymist-project/src/watch.rs +++ b/crates/tinymist-project/src/watch.rs @@ -18,17 +18,14 @@ use typst::diag::{EcoString, FileError, FileResult}; use crate::vfs::{ notify::{FileChangeSet, FileSnapshot, FilesystemEvent, NotifyMessage, UpstreamUpdateEvent}, system::SystemAccessModel, - AccessModel, Bytes, + Bytes, PathAccessModel, }; use tinymist_std::ImmutPath; type WatcherPair = (RecommendedWatcher, mpsc::UnboundedReceiver); type NotifyEvent = notify::Result; type FileEntry = (/* key */ ImmutPath, /* value */ FileSnapshot); -type NotifyFilePair = FileResult<( - /* mtime */ tinymist_std::time::Time, - /* content */ Bytes, -)>; +type NotifyFilePair = FileResult; /// The state of a watched file. /// @@ -290,10 +287,7 @@ impl NotifyActor { changeset.may_insert(self.notify_entry_update(path.clone(), Some(meta))); } else { - let watched = meta.and_then(|meta| { - let content = self.inner.content(path)?; - Ok((meta.modified().unwrap(), content)) - }); + let watched = self.inner.content(path); changeset.inserts.push((path.clone(), watched.into())); } } @@ -409,12 +403,8 @@ impl NotifyActor { return None; } - // Check meta, path, and content - - // Get meta, real path and ignore errors - let mtime = meta.modified().ok()?; - - let mut file = self.inner.content(&path).map(|it| (mtime, it)); + // Check path and content + let mut file = self.inner.content(&path); // Check state in fast path: compare state, return None on not sending // the file change @@ -456,7 +446,7 @@ impl NotifyActor { } }, // Compare content for transitional the state - (Some(Ok((prev_tick, prev_content))), Ok((next_tick, next_content))) => { + (Some(Ok(prev_content)), Ok(next_content)) => { // So far it is accurately no change for the file, skip it if prev_content == next_content { return None; @@ -488,27 +478,6 @@ impl NotifyActor { // Otherwise, we push the diff to the consumer. WatchState::EmptyOrRemoval { .. } => {} } - - // We have found a change, however, we need to check whether the - // mtime is changed. Generally, the mtime should be changed. - // However, It is common that editor (VSCode) to change the - // mtime after writing - // - // this condition should be never happen, but we still check it - // - // There will be cases that user change content of a file and - // then also modify the mtime of the file, so we need to check - // `next_tick == prev_tick`: Whether mtime is changed. - // `matches!(entry.state, WatchState::Fresh)`: Whether the file - // is fresh. We have not submit the file to the compiler, so - // that is ok. - if next_tick == prev_tick && matches!(entry.state, WatchState::Stable) { - // this is necessary to invalidate our mtime-based cache - *next_tick = prev_tick - .checked_add(std::time::Duration::from_micros(1)) - .unwrap(); - log::warn!("same content but mtime is different...: {:?} content: prev:{:?} v.s. curr:{:?}", path, prev_content, next_content); - }; } }; diff --git a/crates/tinymist-project/src/world.rs b/crates/tinymist-project/src/world.rs index 758956f6..10878f36 100644 --- a/crates/tinymist-project/src/world.rs +++ b/crates/tinymist-project/src/world.rs @@ -4,6 +4,7 @@ pub use tinymist_std::error::prelude; pub use tinymist_world as base; pub use tinymist_world::args::*; pub use tinymist_world::config::CompileFontOpts; +use tinymist_world::package::RegistryPathMapper; pub use tinymist_world::vfs; pub use tinymist_world::{entry::*, EntryOpts, EntryState}; pub use tinymist_world::{font, package, CompilerUniverse, CompilerWorld, Revising, TaskInputs}; @@ -132,15 +133,30 @@ impl LspUniverseBuilder { font_resolver: Arc, package_registry: HttpRegistry, ) -> ZResult { + let registry = Arc::new(package_registry); + let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); + Ok(LspUniverse::new_raw( entry, Some(inputs), - Vfs::new(SystemAccessModel {}), - package_registry, + Vfs::new(resolver, SystemAccessModel {}), + registry, font_resolver, )) } + /// Resolve fonts from given options. + pub fn only_embedded_fonts() -> ZResult { + let mut searcher = SystemFontSearcher::new(); + searcher.resolve_opts(CompileFontOpts { + font_profile_cache_path: Default::default(), + font_paths: vec![], + no_system_fonts: true, + with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(), + })?; + Ok(searcher.into()) + } + /// Resolve fonts from given options. pub fn resolve_fonts(args: CompileFontArgs) -> ZResult { let mut searcher = SystemFontSearcher::new(); diff --git a/crates/tinymist-query/Cargo.toml b/crates/tinymist-query/Cargo.toml index 9cdcef10..c8f82ce9 100644 --- a/crates/tinymist-query/Cargo.toml +++ b/crates/tinymist-query/Cargo.toml @@ -59,7 +59,7 @@ hashbrown.workspace = true triomphe.workspace = true base64.workspace = true typlite.workspace = true -tinymist-world.workspace = true +tinymist-world = { workspace = true, features = ["no-content-hint"] } tinymist-project.workspace = true tinymist-analysis.workspace = true tinymist-derive.workspace = true diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index a1699bd6..69104fd3 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -24,7 +24,8 @@ pub mod signature; pub use signature::*; pub mod semantic_tokens; pub use semantic_tokens::*; -use typst::syntax::{Source, VirtualPath}; +use tinymist_world::vfs::WorkspaceResolver; +use typst::syntax::Source; use typst::World; mod post_tyck; mod tyck; @@ -40,12 +41,11 @@ pub use global::*; use ecow::eco_format; use lsp_types::Url; -use tinymist_world::EntryReader; use typst::diag::{FileError, FileResult}; use typst::foundations::{Func, Value}; use typst::syntax::FileId; -use crate::path_to_url; +use crate::path_res_to_url; pub(crate) trait ToFunc { fn to_func(&self) -> Option; @@ -76,16 +76,13 @@ pub trait LspWorldExt { impl LspWorldExt for tinymist_project::LspWorld { fn file_id_by_path(&self, path: &Path) -> FileResult { // todo: source in packages - let root = self.workspace_root().ok_or_else(|| { - let reason = eco_format!("workspace root not found"); - FileError::Other(Some(reason)) - })?; - let relative_path = path.strip_prefix(&root).map_err(|_| { - let reason = eco_format!("access denied, path: {path:?}, root: {root:?}"); - FileError::Other(Some(reason)) - })?; - - Ok(FileId::new(None, VirtualPath::new(relative_path))) + match self.id_for_path(path) { + Some(id) => Ok(id), + None => WorkspaceResolver::file_with_parent_root(path).ok_or_else(|| { + let reason = eco_format!("invalid path: {path:?}"); + FileError::Other(Some(reason)) + }), + } } fn source_by_path(&self, path: &Path) -> FileResult { @@ -94,10 +91,10 @@ impl LspWorldExt for tinymist_project::LspWorld { } fn uri_for_id(&self, fid: FileId) -> Result { - self.path_for_id(fid).and_then(|path| { - path_to_url(&path) - .map_err(|err| FileError::Other(Some(eco_format!("convert to url: {err:?}")))) - }) + let res = path_res_to_url(self.path_for_id(fid)?); + + log::info!("uri_for_id: {fid:?} -> {res:?}"); + res.map_err(|err| FileError::Other(Some(eco_format!("convert to url: {err:?}")))) } } @@ -133,6 +130,7 @@ mod matcher_tests { mod expr_tests { use tinymist_std::path::unix_slash; + use tinymist_world::vfs::WorkspaceResolver; use typst::syntax::Source; use crate::syntax::{Expr, RefExpr}; @@ -150,8 +148,10 @@ mod expr_tests { let fid = if let Some(fid) = decl.file_id() { let vpath = fid.vpath().as_rooted_path(); match fid.package() { - Some(package) => format!(" in {package:?}{}", unix_slash(vpath)), - None => format!(" in {}", unix_slash(vpath)), + Some(package) if WorkspaceResolver::is_package_file(fid) => { + format!(" in {package:?}{}", unix_slash(vpath)) + } + Some(_) | None => format!(" in {}", unix_slash(vpath)), } } else { "".to_string() diff --git a/crates/tinymist-query/src/analysis/completion/path.rs b/crates/tinymist-query/src/analysis/completion/path.rs index eb0e9c4e..c99202d7 100644 --- a/crates/tinymist-query/src/analysis/completion/path.rs +++ b/crates/tinymist-query/src/analysis/completion/path.rs @@ -1,10 +1,12 @@ //! Completion of paths (string literal). +use tinymist_world::vfs::WorkspaceResolver; + use super::*; impl CompletionPair<'_, '_, '_> { pub fn complete_path(&mut self, preference: &PathPreference) -> Option> { let id = self.cursor.source.id(); - if id.package().is_some() { + if WorkspaceResolver::is_package_file(id) { return None; } diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 3ff9c74b..248e7f45 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -11,6 +11,7 @@ use rustc_hash::FxHashMap; use tinymist_project::LspWorld; use tinymist_std::debug_loc::DataSource; use tinymist_std::hash::{hash128, FxDashMap}; +use tinymist_world::vfs::{PathResolution, WorkspaceResolver}; use tinymist_world::{EntryReader, WorldDeps, DETACHED_ENTRY}; use typst::diag::{eco_format, At, FileError, FileResult, SourceResult, StrResult}; use typst::engine::{Route, Sink, Traced}; @@ -315,9 +316,9 @@ impl LocalContext { self.caches .completion_files .get_or_init(|| { - if let Some(root) = self.world.workspace_root() { + if let Some(root) = self.world.entry_state().workspace_root() { scan_workspace_files(&root, PathPreference::Special.ext_matcher(), |path| { - TypstFileId::new(None, VirtualPath::new(path)) + WorkspaceResolver::workspace_file(Some(&root), VirtualPath::new(path)) }) } else { vec![] @@ -357,7 +358,7 @@ impl LocalContext { } /// Get all depended files in the workspace, inclusively. - pub fn depended_source_files(&self) -> Vec { + pub fn depended_source_files(&self) -> EcoVec { let mut ids = self.depended_files(); let preference = PathPreference::Source { allow_package: false, @@ -368,22 +369,10 @@ impl LocalContext { /// Get all depended file ids of a compilation, inclusively. /// Note: must be called after compilation. - pub fn depended_files(&self) -> Vec { - let mut ids = vec![]; - for dep in self.depended_paths() { - if let Ok(ref_fid) = self.file_id_by_path(&dep) { - ids.push(ref_fid); - } - } - ids - } - - /// Get depended paths of a compilation. - /// Note: must be called after compilation. - pub(crate) fn depended_paths(&self) -> EcoVec { + pub fn depended_files(&self) -> EcoVec { let mut deps = EcoVec::new(); - self.world.iter_dependencies(&mut |path| { - deps.push(path); + self.world.iter_dependencies(&mut |file_id| { + deps.push(file_id); }); deps @@ -585,7 +574,7 @@ impl SharedContext { } /// Resolve the real path for a file id. - pub fn path_for_id(&self, id: TypstFileId) -> Result { + pub fn path_for_id(&self, id: TypstFileId) -> Result { self.world.path_for_id(id) } diff --git a/crates/tinymist-query/src/analysis/link_expr.rs b/crates/tinymist-query/src/analysis/link_expr.rs index de22d23c..0fd35a77 100644 --- a/crates/tinymist-query/src/analysis/link_expr.rs +++ b/crates/tinymist-query/src/analysis/link_expr.rs @@ -40,9 +40,8 @@ impl LinkTarget { LinkTarget::Url(url) => Some(url.as_ref().clone()), LinkTarget::Path(id, path) => { // Avoid creating new ids here. - let base = id.vpath().join(path.as_str()); let root = ctx.path_for_id(id.join("/")).ok()?; - crate::path_to_url(&base.resolve(&root)?).ok() + crate::path_res_to_url(root.join(path).ok()?).ok() } } } diff --git a/crates/tinymist-query/src/analysis/prelude.rs b/crates/tinymist-query/src/analysis/prelude.rs index 423f1e7d..95ee2853 100644 --- a/crates/tinymist-query/src/analysis/prelude.rs +++ b/crates/tinymist-query/src/analysis/prelude.rs @@ -2,7 +2,7 @@ pub use core::fmt; pub use std::collections::{BTreeMap, HashMap}; pub use std::hash::{Hash, Hasher}; pub use std::ops::Range; -pub use std::path::{Path, PathBuf}; +pub use std::path::Path; pub use std::sync::{Arc, LazyLock}; pub use comemo::Track; diff --git a/crates/tinymist-query/src/completion.rs b/crates/tinymist-query/src/completion.rs index a56b461c..76213e59 100644 --- a/crates/tinymist-query/src/completion.rs +++ b/crates/tinymist-query/src/completion.rs @@ -169,7 +169,7 @@ mod tests { let mut results = vec![]; for s in rng.clone() { let request = CompletionRequest { - path: ctx.path_for_id(id).unwrap(), + path: ctx.path_for_id(id).unwrap().as_path().to_owned(), position: ctx.to_lsp_pos(s, &source), explicit: false, trigger_character, diff --git a/crates/tinymist-query/src/diagnostics.rs b/crates/tinymist-query/src/diagnostics.rs index fa3b7db2..c198bc47 100644 --- a/crates/tinymist-query/src/diagnostics.rs +++ b/crates/tinymist-query/src/diagnostics.rs @@ -1,5 +1,4 @@ use tinymist_project::LspWorld; -use tinymist_world::EntryReader; use typst::syntax::Span; use crate::{prelude::*, LspWorldExt}; @@ -54,19 +53,10 @@ fn convert_diagnostic( ctx: &LocalDiagContext, typst_diagnostic: &TypstDiagnostic, ) -> anyhow::Result<(Url, Diagnostic)> { - let uri; - let lsp_range; - if let Some((id, span)) = diagnostic_span_id(typst_diagnostic) { - uri = ctx.uri_for_id(id)?; - let source = ctx.source(id)?; - lsp_range = diagnostic_range(&source, span, ctx.position_encoding); - } else { - let root = ctx - .workspace_root() - .ok_or_else(|| anyhow::anyhow!("no workspace root"))?; - uri = path_to_url(&root)?; - lsp_range = LspRange::default(); - }; + let (id, span) = diagnostic_span_id(ctx, typst_diagnostic); + let uri = ctx.uri_for_id(id)?; + let source = ctx.source(id)?; + let lsp_range = diagnostic_range(&source, span, ctx.position_encoding); let lsp_severity = diagnostic_severity(typst_diagnostic.severity); @@ -131,10 +121,14 @@ fn diagnostic_related_information( Ok(tracepoints) } -fn diagnostic_span_id(typst_diagnostic: &TypstDiagnostic) -> Option<(TypstFileId, Span)> { +fn diagnostic_span_id( + ctx: &LocalDiagContext, + typst_diagnostic: &TypstDiagnostic, +) -> (TypstFileId, Span) { iter::once(typst_diagnostic.span) .chain(typst_diagnostic.trace.iter().map(|trace| trace.span)) .find_map(|span| Some((span.id()?, span))) + .unwrap_or_else(|| (ctx.main(), Span::detached())) } fn diagnostic_range( diff --git a/crates/tinymist-query/src/docs/convert.rs b/crates/tinymist-query/src/docs/convert.rs index 37a54eaf..fb56457b 100644 --- a/crates/tinymist-query/src/docs/convert.rs +++ b/crates/tinymist-query/src/docs/convert.rs @@ -1,37 +1,29 @@ +use std::path::Path; use std::sync::{Arc, LazyLock}; use ecow::{eco_format, EcoString}; -use parking_lot::Mutex; -use tinymist_world::{EntryState, ShadowApi, TaskInputs}; +use tinymist_world::{EntryReader, ShadowApi, TaskInputs}; use typlite::scopes::Scopes; use typlite::value::Value; use typlite::TypliteFeat; +use typst::diag::StrResult; use typst::foundations::Bytes; -use typst::{ - diag::StrResult, - syntax::{FileId, VirtualPath}, -}; +use typst::World; use crate::analysis::SharedContext; -// Unfortunately, we have only 65536 possible file ids and we cannot revoke -// them. So we share a global file id for all docs conversion. -static DOCS_CONVERT_ID: LazyLock> = - LazyLock::new(|| Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ")))); - pub(crate) fn convert_docs(ctx: &SharedContext, content: &str) -> StrResult { static DOCS_LIB: LazyLock>> = LazyLock::new(|| Arc::new(typlite::library::docstring_lib())); - let conv_id = DOCS_CONVERT_ID.lock(); - let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap(); - let entry = entry.select_in_workspace(*conv_id); + let entry = ctx.world.entry_state(); + let entry = entry.select_in_workspace(Path::new("__tinymist_docs__.typ")); let mut w = ctx.world.task(TaskInputs { entry: Some(entry), inputs: None, }); - w.map_shadow_by_id(*conv_id, Bytes::from(content.as_bytes().to_owned()))?; + w.map_shadow_by_id(w.main(), Bytes::from(content.as_bytes().to_owned()))?; // todo: bad performance w.source_db.take_state(); diff --git a/crates/tinymist-query/src/document_metrics.rs b/crates/tinymist-query/src/document_metrics.rs index 9ddeebc3..09949cef 100644 --- a/crates/tinymist-query/src/document_metrics.rs +++ b/crates/tinymist-query/src/document_metrics.rs @@ -215,7 +215,7 @@ impl DocumentMetricsWorker<'_> { let column = source.byte_to_column(byte_index)?; let filepath = self.ctx.path_for_id(file_id).ok()?; - let filepath_str = filepath.to_string_lossy().to_string(); + let filepath_str = filepath.as_path().display().to_string(); Some((filepath_str, line as u32 + 1, column as u32 + 1)) } diff --git a/crates/tinymist-query/src/entry.rs b/crates/tinymist-query/src/entry.rs index 12a0e866..4606bdad 100644 --- a/crates/tinymist-query/src/entry.rs +++ b/crates/tinymist-query/src/entry.rs @@ -1,7 +1,7 @@ use anyhow::bail; use tinymist_std::ImmutPath; use tinymist_world::EntryState; -use typst::syntax::{FileId, VirtualPath}; +use typst::syntax::VirtualPath; /// Entry resolver #[derive(Debug, Default, Clone)] @@ -60,14 +60,14 @@ impl EntryResolver { (Some(entry), Some(root)) => match entry.strip_prefix(&root) { Ok(stripped) => Some(EntryState::new_rooted( root, - Some(FileId::new(None, VirtualPath::new(stripped))), + Some(VirtualPath::new(stripped)), )), Err(err) => { log::info!("Entry is not in root directory: err {err:?}: entry: {entry:?}, root: {root:?}"); - EntryState::new_rootless(entry) + EntryState::new_rooted_by_parent(entry) } }, - (Some(entry), None) => EntryState::new_rootless(entry), + (Some(entry), None) => EntryState::new_rooted_by_parent(entry), (None, Some(root)) => Some(EntryState::new_workspace(root)), (None, None) => None, }; @@ -106,6 +106,8 @@ impl EntryResolver { #[cfg(test)] #[cfg(any(windows, unix, target_os = "macos"))] mod entry_tests { + use tinymist_world::vfs::WorkspaceResolver; + use super::*; use std::path::Path; @@ -127,7 +129,10 @@ mod entry_tests { assert_eq!(entry.root(), Some(ImmutPath::from(root_path))); assert_eq!( entry.main(), - Some(FileId::new(None, VirtualPath::new("main.typ"))) + Some(WorkspaceResolver::workspace_file( + entry.root().as_ref(), + VirtualPath::new("main.typ") + )) ); } @@ -152,7 +157,10 @@ mod entry_tests { assert_eq!(entry.root(), Some(ImmutPath::from(root_path))); assert_eq!( entry.main(), - Some(FileId::new(None, VirtualPath::new("main.typ"))) + Some(WorkspaceResolver::workspace_file( + entry.root().as_ref(), + VirtualPath::new("main.typ") + )) ); } @@ -166,7 +174,10 @@ mod entry_tests { assert_eq!(entry.root(), Some(ImmutPath::from(root2_path))); assert_eq!( entry.main(), - Some(FileId::new(None, VirtualPath::new("main.typ"))) + Some(WorkspaceResolver::workspace_file( + entry.root().as_ref(), + VirtualPath::new("main.typ") + )) ); } } diff --git a/crates/tinymist-query/src/lsp_typst_boundary.rs b/crates/tinymist-query/src/lsp_typst_boundary.rs index 5c1d92cb..a80989f9 100644 --- a/crates/tinymist-query/src/lsp_typst_boundary.rs +++ b/crates/tinymist-query/src/lsp_typst_boundary.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use tinymist_std::path::PathClean; +use tinymist_world::vfs::PathResolution; use typst::syntax::Source; use crate::prelude::*; @@ -42,6 +43,11 @@ impl From for lsp_types::PositionEncodingKind { const UNTITLED_ROOT: &str = "/untitled"; static EMPTY_URL: LazyLock = LazyLock::new(|| Url::parse("file://").unwrap()); +/// Convert a path to a URL. +pub fn untitled_url(path: &Path) -> anyhow::Result { + Ok(Url::parse(&format!("untitled:{}", path.display()))?) +} + /// Convert a path to a URL. pub fn path_to_url(path: &Path) -> anyhow::Result { if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) { @@ -50,7 +56,7 @@ pub fn path_to_url(path: &Path) -> anyhow::Result { return Ok(EMPTY_URL.clone()); } - return Ok(Url::parse(&format!("untitled:{}", untitled.display()))?); + return untitled_url(untitled); } Url::from_file_path(path).or_else(|never| { @@ -60,6 +66,14 @@ pub fn path_to_url(path: &Path) -> anyhow::Result { }) } +/// Convert a path resolution to a URL. +pub fn path_res_to_url(path: PathResolution) -> anyhow::Result { + match path { + PathResolution::Rootless(path) => untitled_url(path.as_rooted_path()), + PathResolution::Resolved(path) => path_to_url(&path), + } +} + /// Convert a URL to a path. pub fn url_to_path(uri: Url) -> PathBuf { if uri.scheme() == "file" { diff --git a/crates/tinymist-query/src/prepare_rename.rs b/crates/tinymist-query/src/prepare_rename.rs index 1843626c..eb09c44b 100644 --- a/crates/tinymist-query/src/prepare_rename.rs +++ b/crates/tinymist-query/src/prepare_rename.rs @@ -1,3 +1,5 @@ +use tinymist_world::vfs::WorkspaceResolver; + use crate::{ analysis::Definition, prelude::*, @@ -65,7 +67,7 @@ pub(crate) fn prepare_renaming( let name = def.name().clone(); let (def_fid, _def_range) = def.location(ctx.shared()).clone()?; - if def_fid.package().is_some() { + if WorkspaceResolver::is_package_file(def_fid) { crate::log_debug_ct!( "prepare_rename: {name} is in a package {pkg:?}", pkg = def_fid.package(), diff --git a/crates/tinymist-query/src/references.rs b/crates/tinymist-query/src/references.rs index 2e5fb05e..f5ca5f88 100644 --- a/crates/tinymist-query/src/references.rs +++ b/crates/tinymist-query/src/references.rs @@ -80,15 +80,7 @@ struct ReferencesWorker<'a> { impl ReferencesWorker<'_> { fn label_root(mut self) -> Option> { - let mut ids = vec![]; - - for dep in self.ctx.ctx.depended_paths() { - if let Ok(ref_fid) = self.ctx.ctx.file_id_by_path(&dep) { - ids.push(ref_fid); - } - } - - for ref_fid in ids { + for ref_fid in self.ctx.ctx.depended_files() { self.file(ref_fid)?; } diff --git a/crates/tinymist-query/src/rename.rs b/crates/tinymist-query/src/rename.rs index b274816b..a7e9ae76 100644 --- a/crates/tinymist-query/src/rename.rs +++ b/crates/tinymist-query/src/rename.rs @@ -59,7 +59,8 @@ impl StatefulRequest for RenameRequest { }; let def_fid = def.location(ctx.shared())?.0; - let old_path = ctx.path_for_id(def_fid).ok()?; + // todo: rename in untitled files + let old_path = ctx.path_for_id(def_fid).ok()?.to_err().ok()?; let rename_loc = Path::new(ref_path_str.as_str()); let diff = pathdiff::diff_paths(Path::new(&new_path_str), rename_loc)?; diff --git a/crates/tinymist-query/src/symbol.rs b/crates/tinymist-query/src/symbol.rs index 51924463..0c628115 100644 --- a/crates/tinymist-query/src/symbol.rs +++ b/crates/tinymist-query/src/symbol.rs @@ -36,11 +36,11 @@ impl SemanticRequest for SymbolRequest { let mut symbols = vec![]; - for path in ctx.depended_paths() { - let Ok(source) = ctx.source_by_path(&path) else { + for id in ctx.depended_files() { + let Ok(source) = ctx.source_by_id(id) else { continue; }; - let uri = path_to_url(&path).unwrap(); + let uri = ctx.uri_for_id(id).unwrap(); let res = get_lexical_hierarchy(&source, LexicalScopeKind::Symbol).map(|symbols| { filter_document_symbols( &symbols, diff --git a/crates/tinymist-query/src/tests.rs b/crates/tinymist-query/src/tests.rs index 3e14b4cb..72a9b17c 100644 --- a/crates/tinymist-query/src/tests.rs +++ b/crates/tinymist-query/src/tests.rs @@ -12,18 +12,18 @@ use once_cell::sync::Lazy; use serde_json::{ser::PrettyFormatter, Serializer, Value}; use tinymist_project::CompileFontArgs; use tinymist_world::package::PackageSpec; +use tinymist_world::vfs::WorkspaceResolver; use tinymist_world::EntryState; use tinymist_world::TaskInputs; use tinymist_world::{EntryManager, EntryReader, ShadowApi}; use typst::foundations::Bytes; use typst::syntax::ast::{self, AstNode}; -use typst::syntax::{FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath}; +use typst::syntax::{LinkedNode, Source, SyntaxKind, VirtualPath}; pub use insta::assert_snapshot; pub use serde::Serialize; pub use serde_json::json; pub use tinymist_project::{LspUniverse, LspUniverseBuilder}; -use typst::World; use typst_shim::syntax::LinkedNodeExt; use crate::syntax::find_module_level_docs; @@ -57,11 +57,16 @@ pub fn run_with_ctx( path: PathBuf, f: &impl Fn(&mut LocalContext, PathBuf) -> T, ) -> T { - let root = verse.workspace_root().unwrap(); + let root = verse.entry_state().workspace_root().unwrap(); let paths = verse .shadow_paths() .into_iter() - .map(|path| TypstFileId::new(None, VirtualPath::new(path.strip_prefix(&root).unwrap()))) + .map(|path| { + WorkspaceResolver::workspace_file( + Some(&root), + VirtualPath::new(path.strip_prefix(&root).unwrap()), + ) + }) .collect::>(); let world = verse.snapshot(); @@ -115,20 +120,19 @@ pub fn compile_doc_for_test( ctx: &mut LocalContext, properties: &HashMap<&str, &str>, ) -> Option { - let main_id = properties.get("compile").and_then(|v| match v.trim() { - "true" => Some(ctx.world.main()), - "false" => None, + let entry = match properties.get("compile")?.trim() { + "true" => ctx.world.entry_state(), + "false" => return None, path if path.ends_with(".typ") => { - let vp = VirtualPath::new(path); - Some(TypstFileId::new(None, vp)) + ctx.world.entry_state().select_in_workspace(Path::new(path)) } - _ => panic!("invalid value for 'compile' property: {v}"), - })?; + v => panic!("invalid value for 'compile' property: {v}"), + }; let mut world = Cow::Borrowed(&ctx.world); - if main_id != ctx.world.main() { + if entry != ctx.world.entry_state() { world = Cow::Owned(world.task(TaskInputs { - entry: Some(world.entry_state().select_in_workspace(main_id)), + entry: Some(entry), ..Default::default() })); } @@ -192,10 +196,7 @@ pub fn run_with_sources(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu verse .mutate_entry(EntryState::new_rooted( root.as_path().into(), - Some(TypstFileId::new( - None, - VirtualPath::new(pw.strip_prefix(root).unwrap()), - )), + Some(VirtualPath::new(pw.strip_prefix(root).unwrap())), )) .unwrap(); f(&mut verse, pw) diff --git a/crates/tinymist-query/src/workspace_label.rs b/crates/tinymist-query/src/workspace_label.rs index dbcacf02..5c51acab 100644 --- a/crates/tinymist-query/src/workspace_label.rs +++ b/crates/tinymist-query/src/workspace_label.rs @@ -25,10 +25,9 @@ impl SemanticRequest for WorkspaceLabelRequest { let Ok(source) = ctx.source_by_id(fid) else { continue; }; - let Ok(path) = ctx.path_for_id(fid) else { + let Ok(uri) = ctx.uri_for_id(fid) else { continue; }; - let uri = path_to_url(&path).unwrap(); let res = get_lexical_hierarchy(&source, LexicalScopeKind::Symbol).map(|hierarchy| { filter_document_labels(&hierarchy, &source, &uri, ctx.position_encoding()) }); diff --git a/crates/tinymist-vfs/Cargo.toml b/crates/tinymist-vfs/Cargo.toml index 39359c23..75c2d939 100644 --- a/crates/tinymist-vfs/Cargo.toml +++ b/crates/tinymist-vfs/Cargo.toml @@ -15,6 +15,7 @@ tinymist-std = { workspace = true, features = ["typst"] } parking_lot.workspace = true nohash-hasher.workspace = true indexmap.workspace = true +comemo.workspace = true log.workspace = true rpds = "1" diff --git a/crates/tinymist-vfs/src/browser.rs b/crates/tinymist-vfs/src/browser.rs index c403c440..0c7bf8df 100644 --- a/crates/tinymist-vfs/src/browser.rs +++ b/crates/tinymist-vfs/src/browser.rs @@ -1,10 +1,9 @@ use std::path::Path; -use tinymist_std::ImmutPath; use typst::diag::{FileError, FileResult}; use wasm_bindgen::prelude::*; -use crate::{AccessModel, Bytes, Time}; +use crate::{Bytes, PathAccessModel}; /// Provides proxy access model from typst compiler to some JavaScript /// implementation. @@ -23,24 +22,7 @@ pub struct ProxyAccessModel { pub read_all_fn: js_sys::Function, } -impl AccessModel for ProxyAccessModel { - fn mtime(&self, src: &Path) -> FileResult