dev: reduce a bundle of ts usage in query crate (#72)

This commit is contained in:
Myriad-Dreamin 2024-03-18 23:00:27 +08:00 committed by GitHub
parent c6509325a8
commit cc1f418423
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 204 additions and 117 deletions

View file

@ -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);

View file

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

View file

@ -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)
}

View file

@ -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>)> {

View file

@ -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())
}

View file

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

View file

@ -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));

View file

@ -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()));

View file

@ -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()?;

View file

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

View file

@ -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),

View file

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

View file

@ -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::*;

View file

@ -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)))
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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:\\")