mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-19 02:35:00 +00:00
feat: encode and use workspace information into PackageSpec
(#1187)
* feat: remove an unused API * feat: encode workspace information into `PackageSpec` * feat: remove unused real_path * feat: remove unused mtime * feat: add ResolveAccessModel * feat: implement id overlay semantics * feat: remove mtime checking in overlay model * feat: remove mtime checking in notify model * feat: format ids * fix: cases * feat: resolve root by world * dev: add untitled root * fix: warnings * fix: a wrong usage * fix: snapshots * fix: tests
This commit is contained in:
parent
a25d208124
commit
56714675b7
49 changed files with 835 additions and 774 deletions
|
@ -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<Func>;
|
||||
|
@ -76,16 +76,13 @@ pub trait LspWorldExt {
|
|||
impl LspWorldExt for tinymist_project::LspWorld {
|
||||
fn file_id_by_path(&self, path: &Path) -> FileResult<FileId> {
|
||||
// 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<Source> {
|
||||
|
@ -94,10 +91,10 @@ impl LspWorldExt for tinymist_project::LspWorld {
|
|||
}
|
||||
|
||||
fn uri_for_id(&self, fid: FileId) -> Result<Url, FileError> {
|
||||
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()
|
||||
|
|
|
@ -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<Vec<CompletionItem>> {
|
||||
let id = self.cursor.source.id();
|
||||
if id.package().is_some() {
|
||||
if WorkspaceResolver::is_package_file(id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TypstFileId> {
|
||||
pub fn depended_source_files(&self) -> EcoVec<TypstFileId> {
|
||||
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<TypstFileId> {
|
||||
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<tinymist_std::ImmutPath> {
|
||||
pub fn depended_files(&self) -> EcoVec<TypstFileId> {
|
||||
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<PathBuf, FileError> {
|
||||
pub fn path_for_id(&self, id: TypstFileId) -> Result<PathResolution, FileError> {
|
||||
self.world.path_for_id(id)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<Mutex<FileId>> =
|
||||
LazyLock::new(|| Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ"))));
|
||||
|
||||
pub(crate) fn convert_docs(ctx: &SharedContext, content: &str) -> StrResult<EcoString> {
|
||||
static DOCS_LIB: LazyLock<Arc<Scopes<Value>>> =
|
||||
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();
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PositionEncoding> for lsp_types::PositionEncodingKind {
|
|||
const UNTITLED_ROOT: &str = "/untitled";
|
||||
static EMPTY_URL: LazyLock<Url> = LazyLock::new(|| Url::parse("file://").unwrap());
|
||||
|
||||
/// Convert a path to a URL.
|
||||
pub fn untitled_url(path: &Path) -> anyhow::Result<Url> {
|
||||
Ok(Url::parse(&format!("untitled:{}", path.display()))?)
|
||||
}
|
||||
|
||||
/// Convert a path to a URL.
|
||||
pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
|
||||
if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) {
|
||||
|
@ -50,7 +56,7 @@ pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
|
|||
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<Url> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Convert a path resolution to a URL.
|
||||
pub fn path_res_to_url(path: PathResolution) -> anyhow::Result<Url> {
|
||||
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" {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -80,15 +80,7 @@ struct ReferencesWorker<'a> {
|
|||
|
||||
impl ReferencesWorker<'_> {
|
||||
fn label_root(mut self) -> Option<Vec<LspLocation>> {
|
||||
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)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<T>(
|
|||
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::<Vec<_>>();
|
||||
|
||||
let world = verse.snapshot();
|
||||
|
@ -115,20 +120,19 @@ pub fn compile_doc_for_test(
|
|||
ctx: &mut LocalContext,
|
||||
properties: &HashMap<&str, &str>,
|
||||
) -> Option<VersionedDocument> {
|
||||
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<T>(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)
|
||||
|
|
|
@ -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())
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue