mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 09:52:27 +00:00
dev: stateful requests now accept snapshot (#1581)
* dev: stateful requests now accept snapshot * dev: add some convenient methods * dev: remove unused latest_doc state * dev: use graph * docs: comment * fix: bad flag
This commit is contained in:
parent
5b42231a77
commit
10ec787cc9
29 changed files with 383 additions and 504 deletions
|
@ -1,70 +1,72 @@
|
|||
//! Project compiler for tinymist.
|
||||
|
||||
use core::fmt;
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use ecow::{eco_vec, EcoVec};
|
||||
use tinymist_std::error::prelude::Result;
|
||||
use tinymist_std::typst::{TypstHtmlDocument, TypstPagedDocument};
|
||||
use tinymist_std::{typst::TypstDocument, ImmutPath};
|
||||
use tinymist_world::vfs::notify::{
|
||||
FilesystemEvent, MemoryEvent, NotifyDeps, NotifyMessage, UpstreamUpdateEvent,
|
||||
};
|
||||
use tinymist_world::vfs::{FileId, FsProvider, RevisingVfs, WorkspaceResolver};
|
||||
use tinymist_world::{
|
||||
CompileSnapshot, CompilerFeat, CompilerUniverse, EntryReader, EntryState, ExportSignal,
|
||||
ProjectInsId, TaskInputs, WorldComputeGraph, WorldDeps,
|
||||
CompileSnapshot, CompilerFeat, CompilerUniverse, DiagnosticsTask, EntryReader, EntryState,
|
||||
ExportSignal, FlagTask, HtmlCompilationTask, PagedCompilationTask, ProjectInsId, TaskInputs,
|
||||
WorldComputeGraph, WorldDeps,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use typst::diag::{SourceDiagnostic, SourceResult, Warned};
|
||||
|
||||
/// A compiled artifact.
|
||||
pub struct CompiledArtifact<F: CompilerFeat> {
|
||||
/// The used snapshot.
|
||||
pub snap: CompileSnapshot<F>,
|
||||
/// The used compute graph.
|
||||
pub graph: Arc<WorldComputeGraph<F>>,
|
||||
/// The diagnostics of the document.
|
||||
pub warnings: EcoVec<SourceDiagnostic>,
|
||||
pub diag: Arc<DiagnosticsTask>,
|
||||
/// The compiled document.
|
||||
pub doc: SourceResult<TypstDocument>,
|
||||
pub doc: Option<TypstDocument>,
|
||||
/// The depended files.
|
||||
pub deps: OnceLock<EcoVec<FileId>>,
|
||||
}
|
||||
|
||||
impl<F: CompilerFeat> fmt::Display for CompiledArtifact<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let rev = self.world.revision();
|
||||
write!(f, "CompiledArtifact({:?}, rev={rev:?})", self.id)
|
||||
let rev = self.graph.snap.world.revision();
|
||||
write!(f, "CompiledArtifact({:?}, rev={rev:?})", self.graph.snap.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: CompilerFeat> std::ops::Deref for CompiledArtifact<F> {
|
||||
type Target = CompileSnapshot<F>;
|
||||
type Target = Arc<WorldComputeGraph<F>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.snap
|
||||
&self.graph
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: CompilerFeat> Clone for CompiledArtifact<F> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
snap: self.snap.clone(),
|
||||
graph: self.graph.clone(),
|
||||
doc: self.doc.clone(),
|
||||
warnings: self.warnings.clone(),
|
||||
diag: self.diag.clone(),
|
||||
deps: self.deps.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: CompilerFeat> CompiledArtifact<F> {
|
||||
/// Returns the project id.
|
||||
pub fn id(&self) -> &ProjectInsId {
|
||||
&self.graph.snap.id
|
||||
}
|
||||
|
||||
/// Returns the last successfully compiled document.
|
||||
pub fn success_doc(&self) -> Option<TypstDocument> {
|
||||
self.doc
|
||||
.as_ref()
|
||||
.ok()
|
||||
.cloned()
|
||||
.or_else(|| self.snap.success_doc.clone())
|
||||
}
|
||||
|
@ -73,7 +75,7 @@ impl<F: CompilerFeat> CompiledArtifact<F> {
|
|||
pub fn depended_files(&self) -> &EcoVec<FileId> {
|
||||
self.deps.get_or_init(|| {
|
||||
let mut deps = EcoVec::default();
|
||||
self.world.iter_dependencies(&mut |f| {
|
||||
self.graph.snap.world.iter_dependencies(&mut |f| {
|
||||
deps.push(f);
|
||||
});
|
||||
|
||||
|
@ -82,108 +84,52 @@ impl<F: CompilerFeat> CompiledArtifact<F> {
|
|||
}
|
||||
|
||||
/// Runs the compiler and returns the compiled document.
|
||||
pub fn from_snapshot(snap: CompileSnapshot<F>) -> CompiledArtifact<F> {
|
||||
let is_html = snap.world.library.features.is_enabled(typst::Feature::Html);
|
||||
pub fn from_graph(graph: Arc<WorldComputeGraph<F>>) -> CompiledArtifact<F> {
|
||||
let is_html = graph.library().features.is_enabled(typst::Feature::Html);
|
||||
|
||||
if is_html {
|
||||
Self::from_snapshot_inner::<TypstHtmlDocument>(snap)
|
||||
let _ = graph.provide::<FlagTask<HtmlCompilationTask>>(Ok(FlagTask::flag(is_html)));
|
||||
let _ = graph.provide::<FlagTask<PagedCompilationTask>>(Ok(FlagTask::flag(!is_html)));
|
||||
let doc = if is_html {
|
||||
graph.shared_compile_html().expect("html").map(From::from)
|
||||
} else {
|
||||
Self::from_snapshot_inner::<TypstPagedDocument>(snap)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the compiler and returns the compiled document.
|
||||
fn from_snapshot_inner<D>(mut snap: CompileSnapshot<F>) -> CompiledArtifact<F>
|
||||
where
|
||||
D: typst::Document + 'static,
|
||||
Arc<D>: Into<TypstDocument>,
|
||||
{
|
||||
snap.world.set_is_compiling(true);
|
||||
let res = ::typst::compile::<D>(&snap.world);
|
||||
snap.world.set_is_compiling(false);
|
||||
|
||||
Self::from_snapshot_result(
|
||||
snap,
|
||||
Warned {
|
||||
output: res.output.map(Arc::new),
|
||||
warnings: res.warnings,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Runs the compiler and returns the compiled document.
|
||||
pub fn from_snapshot_result<D>(
|
||||
snap: CompileSnapshot<F>,
|
||||
res: Warned<SourceResult<Arc<D>>>,
|
||||
) -> CompiledArtifact<F>
|
||||
where
|
||||
D: typst::Document + 'static,
|
||||
Arc<D>: Into<TypstDocument>,
|
||||
{
|
||||
let is_html_compilation = TypeId::of::<D>() == TypeId::of::<TypstHtmlDocument>();
|
||||
|
||||
let warned = match res.output {
|
||||
Ok(doc) => Ok(Warned {
|
||||
output: doc,
|
||||
warnings: res.warnings,
|
||||
}),
|
||||
Err(diags) => match (res.warnings.is_empty(), diags.is_empty()) {
|
||||
(true, true) => Err(diags),
|
||||
(true, false) => Err(diags),
|
||||
(false, true) => Err(res.warnings),
|
||||
(false, false) => {
|
||||
let mut warnings = res.warnings;
|
||||
warnings.extend(diags);
|
||||
Err(warnings)
|
||||
}
|
||||
},
|
||||
};
|
||||
let (doc, warnings) = match warned {
|
||||
Ok(doc) => (Ok(doc.output.into()), doc.warnings),
|
||||
Err(err) => (Err(err), EcoVec::default()),
|
||||
};
|
||||
|
||||
let exclude_html_warnings = if !is_html_compilation {
|
||||
warnings
|
||||
} else if warnings.len() == 1
|
||||
&& warnings[0]
|
||||
.message
|
||||
.starts_with("html export is under active development")
|
||||
{
|
||||
EcoVec::new()
|
||||
} else {
|
||||
warnings
|
||||
graph.shared_compile().expect("paged").map(From::from)
|
||||
};
|
||||
|
||||
CompiledArtifact {
|
||||
snap,
|
||||
diag: graph.shared_diagnostics().expect("diag"),
|
||||
graph,
|
||||
doc,
|
||||
warnings: exclude_html_warnings,
|
||||
deps: OnceLock::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns error diagnostics.
|
||||
pub fn errors(&self) -> Option<&EcoVec<SourceDiagnostic>> {
|
||||
self.doc.as_ref().err()
|
||||
/// Returns the error count.
|
||||
pub fn error_cnt(&self) -> usize {
|
||||
self.diag.error_cnt()
|
||||
}
|
||||
|
||||
/// Returns warning diagnostics.
|
||||
pub fn warnings(&self) -> &EcoVec<SourceDiagnostic> {
|
||||
&self.warnings
|
||||
/// Returns the warning count.
|
||||
pub fn warning_cnt(&self) -> usize {
|
||||
self.diag.warning_cnt()
|
||||
}
|
||||
|
||||
/// Returns the diagnostics.
|
||||
pub fn diagnostics(&self) -> impl Iterator<Item = &typst::diag::SourceDiagnostic> {
|
||||
self.diag.diagnostics()
|
||||
}
|
||||
|
||||
/// Returns whether there are any errors.
|
||||
pub fn has_errors(&self) -> bool {
|
||||
self.errors().is_some_and(|e| !e.is_empty())
|
||||
self.error_cnt() > 0
|
||||
}
|
||||
|
||||
/// Returns whether there are any warnings.
|
||||
pub fn diagnostics(&self) -> impl Iterator<Item = &SourceDiagnostic> {
|
||||
self.errors()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.chain(self.warnings.iter())
|
||||
/// Sets the signal.
|
||||
pub fn with_signal(mut self, signal: ExportSignal) -> Self {
|
||||
let mut snap = self.snap.clone();
|
||||
snap.signal = signal;
|
||||
|
||||
self.graph = self.graph.snapshot_unsafe(snap);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +261,7 @@ impl<F: CompilerFeat> fmt::Debug for Interrupt<F> {
|
|||
match self {
|
||||
Interrupt::Compile(id) => write!(f, "Compile({id:?})"),
|
||||
Interrupt::Settle(id) => write!(f, "Settle({id:?})"),
|
||||
Interrupt::Compiled(artifact) => write!(f, "Compiled({:?})", artifact.id),
|
||||
Interrupt::Compiled(artifact) => write!(f, "Compiled({:?})", artifact.id()),
|
||||
Interrupt::ChangeTask(id, change) => {
|
||||
write!(f, "ChangeTask({id:?}, entry={:?})", change.entry.is_some())
|
||||
}
|
||||
|
@ -472,7 +418,7 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
|
|||
}
|
||||
|
||||
/// Creates a snapshot of the primary project.
|
||||
pub fn snapshot(&mut self) -> CompileSnapshot<F> {
|
||||
pub fn snapshot(&mut self) -> Arc<WorldComputeGraph<F>> {
|
||||
self.primary.snapshot()
|
||||
}
|
||||
|
||||
|
@ -500,7 +446,6 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
|
|||
snapshot: None,
|
||||
handler,
|
||||
compilation: OnceLock::default(),
|
||||
latest_doc: None,
|
||||
latest_success_doc: None,
|
||||
deps: Default::default(),
|
||||
committed_revision: 0,
|
||||
|
@ -591,7 +536,8 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
|
|||
proj.reason.see(reason_by_entry_change());
|
||||
}
|
||||
Interrupt::Compiled(artifact) => {
|
||||
let proj = Self::find_project(&mut self.primary, &mut self.dedicates, &artifact.id);
|
||||
let proj =
|
||||
Self::find_project(&mut self.primary, &mut self.dedicates, artifact.id());
|
||||
|
||||
let processed = proj.process_compile(artifact);
|
||||
|
||||
|
@ -636,7 +582,6 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
|
|||
}
|
||||
|
||||
// Reset the watch state and document state.
|
||||
proj.latest_doc = None;
|
||||
proj.latest_success_doc = None;
|
||||
}
|
||||
|
||||
|
@ -796,8 +741,8 @@ pub struct ProjectInsState<F: CompilerFeat, Ext> {
|
|||
pub verse: CompilerUniverse<F>,
|
||||
/// The reason to compile.
|
||||
pub reason: CompileReasons,
|
||||
/// The latest snapshot.
|
||||
snapshot: Option<CompileSnapshot<F>>,
|
||||
/// The latest compute graph (snapshot).
|
||||
snapshot: Option<Arc<WorldComputeGraph<F>>>,
|
||||
/// The latest compilation.
|
||||
pub compilation: OnceLock<CompiledArtifact<F>>,
|
||||
/// The compilation handle.
|
||||
|
@ -805,8 +750,6 @@ pub struct ProjectInsState<F: CompilerFeat, Ext> {
|
|||
/// The file dependencies.
|
||||
deps: EcoVec<ImmutPath>,
|
||||
|
||||
/// The latest compiled document.
|
||||
pub(crate) latest_doc: Option<TypstDocument>,
|
||||
/// The latest successly compiled document.
|
||||
latest_success_doc: Option<TypstDocument>,
|
||||
|
||||
|
@ -815,9 +758,9 @@ pub struct ProjectInsState<F: CompilerFeat, Ext> {
|
|||
|
||||
impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
||||
/// Creates a snapshot of the project.
|
||||
pub fn snapshot(&mut self) -> CompileSnapshot<F> {
|
||||
pub fn snapshot(&mut self) -> Arc<WorldComputeGraph<F>> {
|
||||
match self.snapshot.as_ref() {
|
||||
Some(snap) if snap.world.revision() == self.verse.revision => snap.clone(),
|
||||
Some(snap) if snap.world().revision() == self.verse.revision => snap.clone(),
|
||||
_ => {
|
||||
let snap = self.make_snapshot();
|
||||
self.snapshot = Some(snap.clone());
|
||||
|
@ -826,9 +769,9 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
}
|
||||
}
|
||||
|
||||
fn make_snapshot(&self) -> CompileSnapshot<F> {
|
||||
fn make_snapshot(&self) -> Arc<WorldComputeGraph<F>> {
|
||||
let world = self.verse.snapshot();
|
||||
CompileSnapshot {
|
||||
let snap = CompileSnapshot {
|
||||
id: self.id.clone(),
|
||||
world,
|
||||
signal: ExportSignal {
|
||||
|
@ -837,7 +780,8 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
by_fs_events: self.reason.by_fs_events,
|
||||
},
|
||||
success_doc: self.latest_success_doc.clone(),
|
||||
}
|
||||
};
|
||||
WorldComputeGraph::new(snap)
|
||||
}
|
||||
|
||||
/// Compile the document once if there is any reason and the entry is
|
||||
|
@ -854,9 +798,8 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
let snap = self.snapshot();
|
||||
self.reason = Default::default();
|
||||
Some(move || {
|
||||
let compiled = WorldComputeGraph::new(snap);
|
||||
compute(&compiled);
|
||||
compiled
|
||||
compute(&snap);
|
||||
snap
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -880,33 +823,33 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
/// Compile the document once.
|
||||
fn run_compile(
|
||||
h: Arc<dyn CompileHandler<F, Ext>>,
|
||||
snap: CompileSnapshot<F>,
|
||||
graph: Arc<WorldComputeGraph<F>>,
|
||||
) -> impl FnOnce() -> CompiledArtifact<F> {
|
||||
let start = tinymist_std::time::now();
|
||||
|
||||
// todo unwrap main id
|
||||
let id = snap.world.main_id().unwrap();
|
||||
let revision = snap.world.revision().get();
|
||||
let id = graph.world().main_id().unwrap();
|
||||
let revision = graph.world().revision().get();
|
||||
|
||||
h.status(
|
||||
revision,
|
||||
&snap.id,
|
||||
&graph.snap.id,
|
||||
CompileReport::Stage(id, "compiling", start),
|
||||
);
|
||||
|
||||
move || {
|
||||
let compiled = CompiledArtifact::from_snapshot(snap);
|
||||
let compiled = CompiledArtifact::from_graph(graph);
|
||||
|
||||
let elapsed = start.elapsed().unwrap_or_default();
|
||||
let rep = match &compiled.doc {
|
||||
Ok(..) => CompileReport::CompileSuccess(id, compiled.warnings.len(), elapsed),
|
||||
Err(err) => CompileReport::CompileError(id, err.len(), elapsed),
|
||||
Some(..) => CompileReport::CompileSuccess(id, compiled.warning_cnt(), elapsed),
|
||||
None => CompileReport::CompileError(id, compiled.error_cnt(), elapsed),
|
||||
};
|
||||
|
||||
// todo: we need to check revision for really concurrent compilation
|
||||
log_compile_report(&rep);
|
||||
|
||||
h.status(revision, &compiled.id, rep);
|
||||
h.status(revision, compiled.id(), rep);
|
||||
h.notify_compile(&compiled);
|
||||
|
||||
compiled
|
||||
|
@ -921,11 +864,10 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
}
|
||||
|
||||
// Update state.
|
||||
let doc = artifact.doc.ok();
|
||||
let doc = artifact.doc.clone();
|
||||
self.committed_revision = compiled_revision;
|
||||
self.latest_doc.clone_from(&doc);
|
||||
if doc.is_some() {
|
||||
self.latest_success_doc.clone_from(&self.latest_doc);
|
||||
self.latest_success_doc = doc;
|
||||
}
|
||||
|
||||
// Notify the new file dependencies.
|
||||
|
@ -938,7 +880,7 @@ impl<F: CompilerFeat, Ext: 'static> ProjectInsState<F, Ext> {
|
|||
|
||||
self.deps = deps.clone();
|
||||
|
||||
let mut world = artifact.snap.world;
|
||||
let mut world = world.clone();
|
||||
|
||||
let is_primary = self.id == ProjectInsId("primary".into());
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ use std::{borrow::Cow, sync::Arc};
|
|||
use tinymist_std::error::prelude::*;
|
||||
use tinymist_std::{bail, ImmutPath};
|
||||
use tinymist_task::ExportTarget;
|
||||
use tinymist_world::args::*;
|
||||
use tinymist_world::config::CompileFontOpts;
|
||||
use tinymist_world::font::system::SystemFontSearcher;
|
||||
use tinymist_world::package::{http::HttpRegistry, RegistryPathMapper};
|
||||
use tinymist_world::vfs::{system::SystemAccessModel, Vfs};
|
||||
use tinymist_world::{args::*, WorldComputeGraph};
|
||||
use tinymist_world::{
|
||||
CompileSnapshot, CompilerFeat, CompilerUniverse, CompilerWorld, EntryOpts, EntryState,
|
||||
};
|
||||
|
@ -42,6 +42,8 @@ pub type LspWorld = CompilerWorld<LspCompilerFeat>;
|
|||
pub type LspCompileSnapshot = CompileSnapshot<LspCompilerFeat>;
|
||||
/// LSP compiled artifact.
|
||||
pub type LspCompiledArtifact = CompiledArtifact<LspCompilerFeat>;
|
||||
/// LSP compute graph.
|
||||
pub type LspComputeGraph = Arc<WorldComputeGraph<LspCompilerFeat>>;
|
||||
/// LSP interrupt.
|
||||
pub type LspInterrupt = Interrupt<LspCompilerFeat>;
|
||||
/// Immutable prehashed reference to dictionary.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Linked definition analysis
|
||||
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
use typst::foundations::{IntoValue, Label, Selector, Type};
|
||||
use typst::introspection::Introspector;
|
||||
use typst::model::BibliographyElem;
|
||||
|
@ -7,7 +8,6 @@ use typst::model::BibliographyElem;
|
|||
use super::{prelude::*, InsTy, SharedContext};
|
||||
use crate::syntax::{Decl, DeclExpr, Expr, ExprInfo, SyntaxClass, VarClass};
|
||||
use crate::ty::DocSource;
|
||||
use crate::VersionedDocument;
|
||||
|
||||
/// A linked definition in the source code
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
|
@ -59,7 +59,7 @@ impl Definition {
|
|||
pub fn definition(
|
||||
ctx: &Arc<SharedContext>,
|
||||
source: &Source,
|
||||
document: Option<&VersionedDocument>,
|
||||
document: Option<&TypstDocument>,
|
||||
syntax: SyntaxClass,
|
||||
) -> Option<Definition> {
|
||||
match syntax {
|
||||
|
@ -81,7 +81,7 @@ pub fn definition(
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
let introspector = &document?.document.introspector();
|
||||
let introspector = &document?.introspector();
|
||||
bib_definition(ctx, introspector, name)
|
||||
.or_else(|| ref_definition(introspector, name, ref_expr))
|
||||
}
|
||||
|
|
|
@ -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_std::typst::TypstDocument;
|
||||
use tinymist_world::vfs::{PathResolution, WorkspaceResolver};
|
||||
use tinymist_world::{EntryReader, DETACHED_ENTRY};
|
||||
use typst::diag::{eco_format, At, FileError, FileResult, SourceResult, StrResult};
|
||||
|
@ -37,7 +38,6 @@ use crate::syntax::{
|
|||
use crate::upstream::{tooltip_, Tooltip};
|
||||
use crate::{
|
||||
ColorTheme, CompilerQueryRequest, LspPosition, LspRange, LspWorldExt, PositionEncoding,
|
||||
VersionedDocument,
|
||||
};
|
||||
|
||||
use super::TypeEnv;
|
||||
|
@ -195,7 +195,7 @@ pub trait PeriscopeProvider {
|
|||
fn periscope_at(
|
||||
&self,
|
||||
_ctx: &mut LocalContext,
|
||||
_doc: VersionedDocument,
|
||||
_doc: &TypstDocument,
|
||||
_pos: Position,
|
||||
) -> Option<String> {
|
||||
None
|
||||
|
@ -796,7 +796,7 @@ impl SharedContext {
|
|||
pub(crate) fn def_of_span(
|
||||
self: &Arc<Self>,
|
||||
source: &Source,
|
||||
doc: Option<&VersionedDocument>,
|
||||
doc: Option<&TypstDocument>,
|
||||
span: Span,
|
||||
) -> Option<Definition> {
|
||||
let syntax = self.classify_span(source, span)?;
|
||||
|
@ -814,7 +814,7 @@ impl SharedContext {
|
|||
pub(crate) fn def_of_syntax(
|
||||
self: &Arc<Self>,
|
||||
source: &Source,
|
||||
doc: Option<&VersionedDocument>,
|
||||
doc: Option<&TypstDocument>,
|
||||
syntax: SyntaxClass,
|
||||
) -> Option<Definition> {
|
||||
definition(self, source, doc, syntax)
|
||||
|
|
|
@ -41,11 +41,7 @@ pub struct CompletionRequest {
|
|||
impl StatefulRequest for CompletionRequest {
|
||||
type Response = CompletionList;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
// These trigger characters are for completion on positional arguments,
|
||||
// which follows the configuration item
|
||||
// `tinymist.completion.triggerOnSnippetPlaceholders`.
|
||||
|
@ -55,7 +51,7 @@ impl StatefulRequest for CompletionRequest {
|
|||
return None;
|
||||
}
|
||||
|
||||
let document = doc.as_ref().map(|doc| &doc.document);
|
||||
let document = graph.snap.success_doc.as_ref();
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
|
||||
|
||||
|
@ -134,7 +130,7 @@ mod tests {
|
|||
let mut includes = HashSet::new();
|
||||
let mut excludes = HashSet::new();
|
||||
|
||||
let doc = compile_doc_for_test(ctx, &properties);
|
||||
let graph = compile_doc_for_test(ctx, &properties);
|
||||
|
||||
for kk in properties.get("contains").iter().flat_map(|v| v.split(',')) {
|
||||
// split first char
|
||||
|
@ -188,7 +184,7 @@ mod tests {
|
|||
trigger_character,
|
||||
};
|
||||
let result = request
|
||||
.request(ctx, doc.clone())
|
||||
.request(ctx, graph.clone())
|
||||
.map(|list| CompletionList {
|
||||
is_incomplete: list.is_incomplete,
|
||||
items: get_items(list.items),
|
||||
|
|
|
@ -98,13 +98,8 @@ pub struct DocumentMetricsRequest {
|
|||
impl StatefulRequest for DocumentMetricsRequest {
|
||||
type Response = DocumentMetricsResponse;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
let doc = doc?;
|
||||
let doc = doc.document;
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.as_ref()?;
|
||||
|
||||
let mut worker = DocumentMetricsWorker {
|
||||
ctx,
|
||||
|
@ -113,7 +108,7 @@ impl StatefulRequest for DocumentMetricsRequest {
|
|||
font_info: Default::default(),
|
||||
};
|
||||
|
||||
worker.work(&doc)?;
|
||||
worker.work(doc)?;
|
||||
|
||||
let font_info = worker.compute()?;
|
||||
let span_info = SpanInfo {
|
||||
|
|
|
@ -26,16 +26,13 @@ pub struct GotoDefinitionRequest {
|
|||
impl StatefulRequest for GotoDefinitionRequest {
|
||||
type Response = GotoDefinitionResponse;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.as_ref();
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let syntax = ctx.classify_for_decl(&source, self.position)?;
|
||||
let origin_selection_range = ctx.to_lsp_range(syntax.node().range(), &source);
|
||||
|
||||
let def = ctx.def_of_syntax(&source, doc.as_ref(), syntax)?;
|
||||
let def = ctx.def_of_syntax(&source, doc, syntax)?;
|
||||
|
||||
let (fid, def_range) = def.location(ctx.shared())?;
|
||||
let uri = ctx.uri_for_id(fid).ok()?;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use core::fmt::{self, Write};
|
||||
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
use typst::foundations::repr::separated_list;
|
||||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
|
@ -26,11 +27,8 @@ pub struct HoverRequest {
|
|||
impl StatefulRequest for HoverRequest {
|
||||
type Response = Hover;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.clone();
|
||||
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
|
||||
|
@ -80,7 +78,7 @@ impl StatefulRequest for HoverRequest {
|
|||
struct HoverWorker<'a> {
|
||||
ctx: &'a mut LocalContext,
|
||||
source: Source,
|
||||
doc: Option<VersionedDocument>,
|
||||
doc: Option<TypstDocument>,
|
||||
cursor: usize,
|
||||
def: Vec<String>,
|
||||
value: Vec<String>,
|
||||
|
@ -250,19 +248,19 @@ impl HoverWorker<'_> {
|
|||
// Preview results
|
||||
let provider = self.ctx.analysis.periscope.clone()?;
|
||||
let doc = self.doc.as_ref()?;
|
||||
let position = jump_from_cursor(&doc.document, &self.source, self.cursor);
|
||||
let position = jump_from_cursor(doc, &self.source, self.cursor);
|
||||
let position = position.or_else(|| {
|
||||
for idx in 1..100 {
|
||||
let next_cursor = self.cursor + idx;
|
||||
if next_cursor < self.source.text().len() {
|
||||
let position = jump_from_cursor(&doc.document, &self.source, next_cursor);
|
||||
let position = jump_from_cursor(doc, &self.source, next_cursor);
|
||||
if position.is_some() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
let prev_cursor = self.cursor.checked_sub(idx);
|
||||
if let Some(prev_cursor) = prev_cursor {
|
||||
let position = jump_from_cursor(&doc.document, &self.source, prev_cursor);
|
||||
let position = jump_from_cursor(doc, &self.source, prev_cursor);
|
||||
if position.is_some() {
|
||||
return position;
|
||||
}
|
||||
|
@ -274,7 +272,7 @@ impl HoverWorker<'_> {
|
|||
|
||||
log::info!("telescope position: {position:?}");
|
||||
|
||||
let preview_content = provider.periscope_at(self.ctx, doc.clone(), position?)?;
|
||||
let preview_content = provider.periscope_at(self.ctx, doc, position?)?;
|
||||
self.preview.push(preview_content);
|
||||
Some(())
|
||||
}
|
||||
|
@ -390,15 +388,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("hover", &|world, path| {
|
||||
let source = world.source_by_path(&path).unwrap();
|
||||
snapshot_testing("hover", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let request = HoverRequest {
|
||||
path: path.clone(),
|
||||
position: find_test_position(&source),
|
||||
};
|
||||
|
||||
let result = request.request(world, None);
|
||||
let snap = WorldComputeGraph::from_world(ctx.world.clone());
|
||||
let result = request.request(ctx, snap);
|
||||
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,97 +7,88 @@
|
|||
//! code. Currently it provides:
|
||||
//! + language queries defined by the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
|
||||
|
||||
mod adt;
|
||||
pub use analysis::{CompletionFeat, LocalContext, LocalContextGuard, LspWorldExt};
|
||||
pub use completion::{CompletionRequest, PostfixSnippet};
|
||||
pub use typlite::ColorTheme;
|
||||
pub use upstream::with_vm;
|
||||
|
||||
pub use code_action::*;
|
||||
pub use code_context::*;
|
||||
pub use code_lens::*;
|
||||
pub use color_presentation::*;
|
||||
pub use diagnostics::*;
|
||||
pub use document_color::*;
|
||||
pub use document_highlight::*;
|
||||
pub use document_link::*;
|
||||
pub use document_metrics::*;
|
||||
pub use document_symbol::*;
|
||||
pub use folding_range::*;
|
||||
pub use goto_declaration::*;
|
||||
pub use goto_definition::*;
|
||||
pub use hover::*;
|
||||
pub use inlay_hint::*;
|
||||
pub use jump::*;
|
||||
pub use lsp_typst_boundary::*;
|
||||
pub use on_enter::*;
|
||||
pub use prepare_rename::*;
|
||||
pub use references::*;
|
||||
pub use rename::*;
|
||||
pub use selection_range::*;
|
||||
pub use semantic_tokens_delta::*;
|
||||
pub use semantic_tokens_full::*;
|
||||
pub use signature_help::*;
|
||||
pub use symbol::*;
|
||||
pub use will_rename_files::*;
|
||||
pub use workspace_label::*;
|
||||
|
||||
pub mod analysis;
|
||||
pub mod docs;
|
||||
pub mod package;
|
||||
pub mod syntax;
|
||||
pub mod testing;
|
||||
pub mod ty;
|
||||
mod upstream;
|
||||
|
||||
pub use analysis::{CompletionFeat, LocalContext, LocalContextGuard, LspWorldExt};
|
||||
pub use completion::PostfixSnippet;
|
||||
pub use upstream::with_vm;
|
||||
|
||||
mod diagnostics;
|
||||
pub use diagnostics::*;
|
||||
mod code_action;
|
||||
pub use code_action::*;
|
||||
mod code_context;
|
||||
pub use code_context::*;
|
||||
mod code_lens;
|
||||
pub use code_lens::*;
|
||||
mod completion;
|
||||
pub use completion::CompletionRequest;
|
||||
mod color_presentation;
|
||||
pub use color_presentation::*;
|
||||
mod document_color;
|
||||
pub use document_color::*;
|
||||
mod document_highlight;
|
||||
pub use document_highlight::*;
|
||||
mod document_symbol;
|
||||
pub use document_symbol::*;
|
||||
mod document_link;
|
||||
pub use document_link::*;
|
||||
mod workspace_label;
|
||||
pub use workspace_label::*;
|
||||
mod document_metrics;
|
||||
pub use document_metrics::*;
|
||||
mod folding_range;
|
||||
pub use folding_range::*;
|
||||
mod goto_declaration;
|
||||
pub use goto_declaration::*;
|
||||
mod goto_definition;
|
||||
pub use goto_definition::*;
|
||||
mod hover;
|
||||
pub use hover::*;
|
||||
mod inlay_hint;
|
||||
pub use inlay_hint::*;
|
||||
mod jump;
|
||||
pub use jump::*;
|
||||
mod will_rename_files;
|
||||
pub use will_rename_files::*;
|
||||
mod rename;
|
||||
pub use rename::*;
|
||||
mod selection_range;
|
||||
pub use selection_range::*;
|
||||
mod semantic_tokens_full;
|
||||
pub use semantic_tokens_full::*;
|
||||
mod semantic_tokens_delta;
|
||||
pub use semantic_tokens_delta::*;
|
||||
mod signature_help;
|
||||
pub use signature_help::*;
|
||||
mod symbol;
|
||||
pub use symbol::*;
|
||||
mod on_enter;
|
||||
pub use on_enter::*;
|
||||
mod prepare_rename;
|
||||
pub use prepare_rename::*;
|
||||
mod references;
|
||||
pub use references::*;
|
||||
|
||||
mod lsp_typst_boundary;
|
||||
pub use lsp_typst_boundary::*;
|
||||
|
||||
mod prelude;
|
||||
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
use typst::syntax::Source;
|
||||
|
||||
/// The physical position in a document.
|
||||
pub type FramePosition = typst::layout::Position;
|
||||
|
||||
pub use typlite::ColorTheme;
|
||||
mod adt;
|
||||
mod lsp_typst_boundary;
|
||||
mod prelude;
|
||||
|
||||
/// A compiled document with an self-incremented logical version.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VersionedDocument {
|
||||
/// The version of the document.
|
||||
pub version: usize,
|
||||
/// The compiled document.
|
||||
pub document: TypstDocument,
|
||||
}
|
||||
mod code_action;
|
||||
mod code_context;
|
||||
mod code_lens;
|
||||
mod color_presentation;
|
||||
mod completion;
|
||||
mod diagnostics;
|
||||
mod document_color;
|
||||
mod document_highlight;
|
||||
mod document_link;
|
||||
mod document_metrics;
|
||||
mod document_symbol;
|
||||
mod folding_range;
|
||||
mod goto_declaration;
|
||||
mod goto_definition;
|
||||
mod hover;
|
||||
mod inlay_hint;
|
||||
mod jump;
|
||||
mod on_enter;
|
||||
mod prepare_rename;
|
||||
mod references;
|
||||
mod rename;
|
||||
mod selection_range;
|
||||
mod semantic_tokens_delta;
|
||||
mod semantic_tokens_full;
|
||||
mod signature_help;
|
||||
mod symbol;
|
||||
mod upstream;
|
||||
mod will_rename_files;
|
||||
mod workspace_label;
|
||||
|
||||
use typst::syntax::Source;
|
||||
|
||||
use tinymist_analysis::log_debug_ct;
|
||||
use tinymist_project::LspComputeGraph;
|
||||
|
||||
/// A request handler with given syntax information.
|
||||
pub trait SyntaxRequest {
|
||||
|
@ -121,22 +112,16 @@ pub trait SemanticRequest {
|
|||
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response>;
|
||||
}
|
||||
|
||||
/// A request handler with given (semantic) analysis context and a versioned
|
||||
/// document.
|
||||
/// A request handler with given (semantic) analysis context and a project
|
||||
/// snapshot.
|
||||
pub trait StatefulRequest {
|
||||
/// The response type of the request.
|
||||
type Response;
|
||||
|
||||
/// Request the information from the given context.
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response>;
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response>;
|
||||
}
|
||||
|
||||
use tinymist_analysis::log_debug_ct;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
mod polymorphic {
|
||||
use completion::CompletionList;
|
||||
|
|
|
@ -17,6 +17,7 @@ pub use lsp_types::{
|
|||
SignatureHelp, SignatureInformation, SymbolInformation, TextEdit, Url, WorkspaceEdit,
|
||||
};
|
||||
pub use serde_json::Value as JsonValue;
|
||||
pub use tinymist_project::LspComputeGraph;
|
||||
pub use tinymist_std::DefId;
|
||||
pub use typst::diag::{EcoString, Tracepoint};
|
||||
pub use typst::foundations::Value;
|
||||
|
@ -35,4 +36,4 @@ pub use crate::lsp_typst_boundary::{
|
|||
};
|
||||
pub use crate::syntax::{classify_syntax, Decl, DefKind};
|
||||
pub(crate) use crate::ty::PathPreference;
|
||||
pub use crate::{SemanticRequest, StatefulRequest, VersionedDocument};
|
||||
pub use crate::{SemanticRequest, StatefulRequest};
|
||||
|
|
|
@ -34,11 +34,8 @@ pub struct PrepareRenameRequest {
|
|||
impl StatefulRequest for PrepareRenameRequest {
|
||||
type Response = PrepareRenameResponse;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.as_ref();
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let syntax = ctx.classify_for_decl(&source, self.position)?;
|
||||
if matches!(syntax.node().kind(), SyntaxKind::FieldAccess) {
|
||||
|
@ -48,7 +45,7 @@ impl StatefulRequest for PrepareRenameRequest {
|
|||
}
|
||||
|
||||
let origin_selection_range = ctx.to_lsp_range(syntax.node().range(), &source);
|
||||
let def = ctx.def_of_syntax(&source, doc.as_ref(), syntax.clone())?;
|
||||
let def = ctx.def_of_syntax(&source, doc, syntax.clone())?;
|
||||
|
||||
let (name, range) = prepare_renaming(ctx, &syntax, &def)?;
|
||||
|
||||
|
@ -136,15 +133,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn prepare() {
|
||||
snapshot_testing("rename", &|world, path| {
|
||||
let source = world.source_by_path(&path).unwrap();
|
||||
snapshot_testing("rename", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let request = PrepareRenameRequest {
|
||||
path: path.clone(),
|
||||
position: find_test_position(&source),
|
||||
};
|
||||
let snap = WorldComputeGraph::from_world(ctx.world.clone());
|
||||
|
||||
let result = request.request(world, None);
|
||||
let result = request.request(ctx, snap);
|
||||
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::sync::OnceLock;
|
||||
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
use typst::syntax::Span;
|
||||
|
||||
use crate::{
|
||||
|
@ -25,15 +26,12 @@ pub struct ReferencesRequest {
|
|||
impl StatefulRequest for ReferencesRequest {
|
||||
type Response = Vec<LspLocation>;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.as_ref();
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let syntax = ctx.classify_for_decl(&source, self.position)?;
|
||||
|
||||
let locations = find_references(ctx, &source, doc.as_ref(), syntax)?;
|
||||
let locations = find_references(ctx, &source, doc, syntax)?;
|
||||
|
||||
crate::log_debug_ct!("references: {locations:?}");
|
||||
Some(locations)
|
||||
|
@ -43,7 +41,7 @@ impl StatefulRequest for ReferencesRequest {
|
|||
pub(crate) fn find_references(
|
||||
ctx: &mut LocalContext,
|
||||
source: &Source,
|
||||
doc: Option<&VersionedDocument>,
|
||||
doc: Option<&TypstDocument>,
|
||||
syntax: SyntaxClass<'_>,
|
||||
) -> Option<Vec<LspLocation>> {
|
||||
let finding_label = match syntax {
|
||||
|
|
|
@ -36,15 +36,13 @@ pub struct RenameRequest {
|
|||
impl StatefulRequest for RenameRequest {
|
||||
type Response = WorkspaceEdit;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let doc = graph.snap.success_doc.as_ref();
|
||||
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let syntax = ctx.classify_for_decl(&source, self.position)?;
|
||||
|
||||
let def = ctx.def_of_syntax(&source, doc.as_ref(), syntax.clone())?;
|
||||
let def = ctx.def_of_syntax(&source, doc, syntax.clone())?;
|
||||
|
||||
prepare_renaming(ctx, &syntax, &def)?;
|
||||
|
||||
|
@ -95,7 +93,7 @@ impl StatefulRequest for RenameRequest {
|
|||
})
|
||||
}
|
||||
_ => {
|
||||
let references = find_references(ctx, &source, doc.as_ref(), syntax)?;
|
||||
let references = find_references(ctx, &source, doc, syntax)?;
|
||||
|
||||
let mut edits = HashMap::new();
|
||||
|
||||
|
@ -321,16 +319,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("rename", &|world, path| {
|
||||
let source = world.source_by_path(&path).unwrap();
|
||||
snapshot_testing("rename", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let request = RenameRequest {
|
||||
path: path.clone(),
|
||||
position: find_test_position(&source),
|
||||
new_name: "new_name".to_string(),
|
||||
};
|
||||
let snap = WorldComputeGraph::from_world(ctx.world.clone());
|
||||
|
||||
let mut result = request.request(world, None);
|
||||
let mut result = request.request(ctx, snap);
|
||||
// sort the edits to make the snapshot stable
|
||||
if let Some(r) = result.as_mut().and_then(|r| r.changes.as_mut()) {
|
||||
for edits in r.values_mut() {
|
||||
|
|
|
@ -10,14 +10,12 @@ use std::{
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
use serde_json::{ser::PrettyFormatter, Serializer, Value};
|
||||
use tinymist_project::{CompileFontArgs, ExportTarget};
|
||||
use tinymist_project::{CompileFontArgs, ExportTarget, LspCompileSnapshot, LspComputeGraph};
|
||||
use tinymist_std::debug_loc::LspRange;
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
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 tinymist_world::{EntryManager, EntryReader, EntryState, ShadowApi, TaskInputs};
|
||||
use typst::foundations::Bytes;
|
||||
use typst::syntax::ast::{self, AstNode};
|
||||
use typst::syntax::{LinkedNode, Source, SyntaxKind, VirtualPath};
|
||||
|
@ -26,12 +24,11 @@ pub use insta::assert_snapshot;
|
|||
pub use serde::Serialize;
|
||||
pub use serde_json::json;
|
||||
pub use tinymist_project::{LspUniverse, LspUniverseBuilder};
|
||||
pub use tinymist_world::WorldComputeGraph;
|
||||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
use crate::syntax::find_module_level_docs;
|
||||
use crate::{
|
||||
analysis::Analysis, prelude::LocalContext, LspPosition, PositionEncoding, VersionedDocument,
|
||||
};
|
||||
use crate::{analysis::Analysis, prelude::LocalContext, LspPosition, PositionEncoding};
|
||||
use crate::{to_lsp_position, CompletionFeat, LspWorldExt};
|
||||
|
||||
pub fn snapshot_testing(name: &str, f: &impl Fn(&mut LocalContext, PathBuf)) {
|
||||
|
@ -122,13 +119,13 @@ pub fn get_test_properties(s: &str) -> HashMap<&'_ str, &'_ str> {
|
|||
pub fn compile_doc_for_test(
|
||||
ctx: &mut LocalContext,
|
||||
properties: &HashMap<&str, &str>,
|
||||
) -> Option<VersionedDocument> {
|
||||
) -> LspComputeGraph {
|
||||
let prev = ctx.world.entry_state();
|
||||
let next = match properties.get("compile")?.trim() {
|
||||
"true" => prev.clone(),
|
||||
"false" => return None,
|
||||
path if path.ends_with(".typ") => prev.select_in_workspace(Path::new(path)),
|
||||
v => panic!("invalid value for 'compile' property: {v}"),
|
||||
let next = match properties.get("compile").map(|s| s.trim()) {
|
||||
Some("true") => prev.clone(),
|
||||
None | Some("false") => return WorldComputeGraph::from_world(ctx.world.clone()),
|
||||
Some(path) if path.ends_with(".typ") => prev.select_in_workspace(Path::new(path)),
|
||||
v => panic!("invalid value for 'compile' property: {v:?}"),
|
||||
};
|
||||
|
||||
let mut world = Cow::Borrowed(&ctx.world);
|
||||
|
@ -138,14 +135,12 @@ pub fn compile_doc_for_test(
|
|||
..Default::default()
|
||||
}));
|
||||
}
|
||||
let mut world = world.into_owned();
|
||||
world.set_is_compiling(true);
|
||||
let mut snap = LspCompileSnapshot::from_world(world.into_owned());
|
||||
snap.world.set_is_compiling(true);
|
||||
|
||||
let doc = typst::compile(&world).output.unwrap();
|
||||
Some(VersionedDocument {
|
||||
version: 0,
|
||||
document: TypstDocument::Paged(Arc::new(doc)),
|
||||
})
|
||||
let doc = typst::compile(&snap.world).output.unwrap();
|
||||
snap.success_doc = Some(TypstDocument::Paged(Arc::new(doc)));
|
||||
WorldComputeGraph::new(snap)
|
||||
}
|
||||
|
||||
pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut LspUniverse, PathBuf) -> T) -> T {
|
||||
|
|
|
@ -15,11 +15,7 @@ pub struct WillRenameFilesRequest {
|
|||
impl StatefulRequest for WillRenameFilesRequest {
|
||||
type Response = WorkspaceEdit;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
ctx: &mut LocalContext,
|
||||
_doc: Option<VersionedDocument>,
|
||||
) -> Option<Self::Response> {
|
||||
fn request(self, ctx: &mut LocalContext, _graph: LspComputeGraph) -> Option<Self::Response> {
|
||||
let mut edits: HashMap<Url, Vec<TextEdit>> = HashMap::new();
|
||||
|
||||
self.paths
|
||||
|
|
|
@ -9,7 +9,7 @@ use core::fmt;
|
|||
|
||||
use base64::Engine;
|
||||
use reflexo_vec2svg::{ExportFeature, SvgExporter, SvgText};
|
||||
use tinymist_query::{FramePosition, LocalContext, VersionedDocument};
|
||||
use tinymist_query::{FramePosition, LocalContext};
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
|
||||
struct PeriscopeExportFeature {}
|
||||
|
@ -74,7 +74,7 @@ impl PeriscopeRenderer {
|
|||
pub fn render_marked(
|
||||
&self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: VersionedDocument,
|
||||
doc: &TypstDocument,
|
||||
pos: FramePosition,
|
||||
) -> Option<String> {
|
||||
let (svg_payload, w, h) = self.render(ctx, doc, pos)?;
|
||||
|
@ -95,10 +95,10 @@ impl PeriscopeRenderer {
|
|||
pub fn render(
|
||||
&self,
|
||||
_ctx: &mut LocalContext,
|
||||
doc: VersionedDocument,
|
||||
doc: &TypstDocument,
|
||||
pos: FramePosition,
|
||||
) -> Option<(String, f32, f32)> {
|
||||
match &doc.document {
|
||||
match doc {
|
||||
TypstDocument::Paged(paged_doc) => {
|
||||
// todo: svg viewer compatibility
|
||||
type UsingExporter = SvgExporter<PeriscopeExportFeature>;
|
||||
|
|
|
@ -12,7 +12,7 @@ use typst::ecow::EcoVec;
|
|||
use typst::syntax::Span;
|
||||
|
||||
use crate::snapshot::CompileSnapshot;
|
||||
use crate::{CompilerFeat, CompilerWorld, EntryReader};
|
||||
use crate::{CompilerFeat, CompilerWorld, EntryReader, TaskInputs};
|
||||
|
||||
type AnyArc = Arc<dyn std::any::Any + Send + Sync>;
|
||||
|
||||
|
@ -89,14 +89,33 @@ impl<F: CompilerFeat> WorldComputeGraph<F> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a graph from the world.
|
||||
pub fn from_world(world: CompilerWorld<F>) -> Arc<Self> {
|
||||
Self::new(CompileSnapshot::from_world(world))
|
||||
}
|
||||
|
||||
/// Clones the graph with the same snapshot.
|
||||
pub fn snapshot(&self) -> Arc<Self> {
|
||||
self.snapshot_unsafe(self.snap.clone())
|
||||
}
|
||||
|
||||
/// Clones the graph with the same snapshot. Take care of the consistency by
|
||||
/// your self.
|
||||
pub fn snapshot_unsafe(&self, snap: CompileSnapshot<F>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
snap: self.snap.clone(),
|
||||
snap,
|
||||
entries: Mutex::new(self.entries.lock().clone()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Forks a new snapshot that compiles a different document.
|
||||
// todo: share cache if task doesn't change.
|
||||
pub fn task(&self, inputs: TaskInputs) -> Arc<Self> {
|
||||
let mut snap = self.snap.clone();
|
||||
snap = snap.task(inputs);
|
||||
Self::new(snap)
|
||||
}
|
||||
|
||||
/// Gets a world computed.
|
||||
pub fn must_get<T: WorldComputable<F>>(&self) -> Result<Arc<T::Output>> {
|
||||
let res = self.get::<T>().transpose()?;
|
||||
|
@ -148,6 +167,18 @@ impl<F: CompilerFeat> WorldComputeGraph<F> {
|
|||
entry
|
||||
}
|
||||
}
|
||||
|
||||
pub fn world(&self) -> &CompilerWorld<F> {
|
||||
&self.snap.world
|
||||
}
|
||||
|
||||
pub fn registry(&self) -> &Arc<F::Registry> {
|
||||
&self.snap.world.registry
|
||||
}
|
||||
|
||||
pub fn library(&self) -> &typst::Library {
|
||||
&self.snap.world.library
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExportDetection<F: CompilerFeat, D> {
|
||||
|
@ -219,7 +250,21 @@ pub type HtmlCompilationTask = CompilationTask<TypstHtmlDocument>;
|
|||
pub struct CompilationTask<D>(std::marker::PhantomData<D>);
|
||||
|
||||
impl<D: typst::Document + Send + Sync + 'static> CompilationTask<D> {
|
||||
pub fn ensure_main<F: CompilerFeat>(world: &CompilerWorld<F>) -> SourceResult<()> {
|
||||
let main_id = world.main_id();
|
||||
let checked = main_id.ok_or_else(|| typst::diag::eco_format!("entry file is not set"));
|
||||
checked.at(Span::detached()).map(|_| ())
|
||||
}
|
||||
|
||||
pub fn execute<F: CompilerFeat>(world: &CompilerWorld<F>) -> Warned<SourceResult<Arc<D>>> {
|
||||
let res = Self::ensure_main(world);
|
||||
if let Err(err) = res {
|
||||
return Warned {
|
||||
output: Err(err),
|
||||
warnings: EcoVec::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let is_paged_compilation = TypeId::of::<D>() == TypeId::of::<TypstPagedDocument>();
|
||||
let is_html_compilation = TypeId::of::<D>() == TypeId::of::<TypstHtmlDocument>();
|
||||
|
||||
|
@ -233,7 +278,7 @@ impl<D: typst::Document + Send + Sync + 'static> CompilationTask<D> {
|
|||
};
|
||||
|
||||
world.to_mut().set_is_compiling(true);
|
||||
let compiled = typst::compile::<D>(world.as_ref());
|
||||
let compiled = ::typst::compile::<D>(world.as_ref());
|
||||
world.to_mut().set_is_compiling(false);
|
||||
|
||||
let exclude_html_warnings = if !is_html_compilation {
|
||||
|
@ -346,83 +391,16 @@ impl DiagnosticsTask {
|
|||
}
|
||||
}
|
||||
|
||||
// pub type ErasedVecExportTask<E> = ErasedExportTask<SourceResult<Bytes>, E>;
|
||||
// pub type ErasedStrExportTask<E> = ErasedExportTask<SourceResult<String>, E>;
|
||||
|
||||
// pub struct ErasedExportTask<T, E> {
|
||||
// _phantom: std::marker::PhantomData<(T, E)>,
|
||||
// }
|
||||
|
||||
// #[allow(clippy::type_complexity)]
|
||||
// struct ErasedExportImpl<F: CompilerFeat, T, E> {
|
||||
// f: Arc<dyn Fn(&Arc<WorldComputeGraph<F>>) -> Result<Option<T>> + Send +
|
||||
// Sync>, _phantom: std::marker::PhantomData<E>,
|
||||
// }
|
||||
|
||||
// impl<T: Send + Sync + 'static, E: Send + Sync + 'static> ErasedExportTask<T,
|
||||
// E> { #[must_use = "the result must be checked"]
|
||||
// pub fn provide_raw<F: CompilerFeat>(
|
||||
// graph: &Arc<WorldComputeGraph<F>>,
|
||||
// f: impl Fn(&Arc<WorldComputeGraph<F>>) -> Result<Option<T>> + Send +
|
||||
// Sync + 'static, ) -> Result<()> {
|
||||
// let provided = graph.provide::<ConfigTask<ErasedExportImpl<F, T,
|
||||
// E>>>(Ok(Arc::new({ ErasedExportImpl {
|
||||
// f: Arc::new(f),
|
||||
// _phantom: std::marker::PhantomData,
|
||||
// }
|
||||
// })));
|
||||
|
||||
// if provided.is_err() {
|
||||
// tinymist_std::bail!("already provided")
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// #[must_use = "the result must be checked"]
|
||||
// pub fn provide<F: CompilerFeat, D, C>(graph: &Arc<WorldComputeGraph<F>>)
|
||||
// -> Result<()> where
|
||||
// D: typst::Document + Send + Sync + 'static,
|
||||
// C: WorldComputable<F> + ExportComputation<F, D, Output = T>,
|
||||
// {
|
||||
// Self::provide_raw(graph, OptionDocumentTask::run_export::<F, C>)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<F: CompilerFeat, T: Send + Sync + 'static, E: Send + Sync + 'static>
|
||||
// WorldComputable<F> for ErasedExportTask<T, E>
|
||||
// {
|
||||
// type Output = Option<T>;
|
||||
|
||||
// fn compute(graph: &Arc<WorldComputeGraph<F>>) -> Result<Self::Output> {
|
||||
// let conf = graph.must_get::<ConfigTask<ErasedExportImpl<F, T,
|
||||
// E>>>()?; (conf.f)(graph)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<F: CompilerFeat> WorldComputeGraph<F> {
|
||||
pub fn ensure_main(&self) -> SourceResult<()> {
|
||||
let main_id = self.snap.world.main_id();
|
||||
let checked = main_id.ok_or_else(|| typst::diag::eco_format!("entry file is not set"));
|
||||
checked.at(Span::detached()).map(|_| ())
|
||||
CompilationTask::<TypstPagedDocument>::ensure_main(&self.snap.world)
|
||||
}
|
||||
|
||||
/// Compile once from scratch.
|
||||
pub fn pure_compile<D: ::typst::Document>(&self) -> Warned<SourceResult<Arc<D>>> {
|
||||
let res = self.ensure_main();
|
||||
if let Err(err) = res {
|
||||
return Warned {
|
||||
output: Err(err),
|
||||
warnings: EcoVec::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let res = ::typst::compile::<D>(&self.snap.world);
|
||||
// compile document
|
||||
Warned {
|
||||
output: res.output.map(Arc::new),
|
||||
warnings: res.warnings,
|
||||
}
|
||||
pub fn pure_compile<D: ::typst::Document + Send + Sync + 'static>(
|
||||
&self,
|
||||
) -> Warned<SourceResult<Arc<D>>> {
|
||||
CompilationTask::<D>::execute(&self.snap.world)
|
||||
}
|
||||
|
||||
/// Compile once from scratch.
|
||||
|
@ -431,12 +409,24 @@ impl<F: CompilerFeat> WorldComputeGraph<F> {
|
|||
}
|
||||
|
||||
/// Compile to html once from scratch.
|
||||
pub fn compile_html(&self) -> Warned<SourceResult<Arc<::typst::html::HtmlDocument>>> {
|
||||
pub fn compile_html(&self) -> Warned<SourceResult<Arc<TypstHtmlDocument>>> {
|
||||
self.pure_compile()
|
||||
}
|
||||
|
||||
// With **the compilation state**, query the matches for the selector.
|
||||
// fn query(&mut self, selector: String, document: &TypstDocument) ->
|
||||
// SourceResult<Vec<Content>> { self.pure_query(world, selector,
|
||||
// document) }
|
||||
/// Compile paged document with cache
|
||||
pub fn shared_compile(self: &Arc<Self>) -> Result<Option<Arc<TypstPagedDocument>>> {
|
||||
let doc = self.compute::<OptionDocumentTask<TypstPagedDocument>>()?;
|
||||
Ok(doc.as_ref().clone())
|
||||
}
|
||||
|
||||
/// Compile HTML document with cache
|
||||
pub fn shared_compile_html(self: &Arc<Self>) -> Result<Option<Arc<TypstHtmlDocument>>> {
|
||||
let doc = self.compute::<OptionDocumentTask<TypstHtmlDocument>>()?;
|
||||
Ok(doc.as_ref().clone())
|
||||
}
|
||||
|
||||
/// Gets the diagnostics from shared compilation.
|
||||
pub fn shared_diagnostics(self: &Arc<Self>) -> Result<Arc<DiagnosticsTask>> {
|
||||
self.compute::<DiagnosticsTask>()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -369,7 +369,7 @@ impl ServerState {
|
|||
// Try to parse without version, but prefer the error message of the
|
||||
// normal package spec parsing if it fails.
|
||||
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
||||
let version = snap.world.registry.determine_latest_version(&spec)?;
|
||||
let version = snap.registry().determine_latest_version(&spec)?;
|
||||
StrResult::Ok(spec.at(version))
|
||||
})
|
||||
.map_err(map_string_err("failed to parse package spec"))
|
||||
|
@ -378,7 +378,7 @@ impl ServerState {
|
|||
let from_source = TemplateSource::Package(spec);
|
||||
|
||||
let entry_path = package::init(
|
||||
&snap.world,
|
||||
snap.world(),
|
||||
InitTask {
|
||||
tmpl: from_source.clone(),
|
||||
dir: to_path.clone(),
|
||||
|
@ -412,7 +412,7 @@ impl ServerState {
|
|||
// Try to parse without version, but prefer the error message of the
|
||||
// normal package spec parsing if it fails.
|
||||
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
||||
let version = snap.world.registry.determine_latest_version(&spec)?;
|
||||
let version = snap.registry().determine_latest_version(&spec)?;
|
||||
StrResult::Ok(spec.at(version))
|
||||
})
|
||||
.map_err(map_string_err("failed to parse package spec"))
|
||||
|
@ -420,7 +420,7 @@ impl ServerState {
|
|||
|
||||
let from_source = TemplateSource::Package(spec);
|
||||
|
||||
let entry = package::get_entry(&snap.world, from_source)
|
||||
let entry = package::get_entry(snap.world(), from_source)
|
||||
.map_err(map_string_err("failed to get template entry"))
|
||||
.map_err(internal_error)?;
|
||||
|
||||
|
@ -490,8 +490,8 @@ impl ServerState {
|
|||
compiler_program: self_path,
|
||||
root: root.as_ref().to_owned(),
|
||||
main,
|
||||
inputs: snap.world.inputs().as_ref().deref().clone(),
|
||||
font_paths: snap.world.font_resolver.font_paths().to_owned(),
|
||||
inputs: snap.world().inputs().as_ref().deref().clone(),
|
||||
font_paths: snap.world().font_resolver.font_paths().to_owned(),
|
||||
rpc_kind: "http".into(),
|
||||
})?;
|
||||
|
||||
|
@ -558,7 +558,7 @@ impl ServerState {
|
|||
pub fn resource_package_dirs(&mut self, _arguments: Vec<JsonValue>) -> AnySchedulableResponse {
|
||||
let snap = self.snapshot().map_err(internal_error)?;
|
||||
just_future(async move {
|
||||
let paths = snap.world.registry.paths();
|
||||
let paths = snap.registry().paths();
|
||||
let paths = paths.iter().map(|p| p.as_ref()).collect::<Vec<_>>();
|
||||
serde_json::to_value(paths).map_err(|e| internal_error(e.to_string()))
|
||||
})
|
||||
|
@ -571,7 +571,7 @@ impl ServerState {
|
|||
) -> AnySchedulableResponse {
|
||||
let snap = self.snapshot().map_err(internal_error)?;
|
||||
just_future(async move {
|
||||
let paths = snap.world.registry.local_path();
|
||||
let paths = snap.registry().local_path();
|
||||
let paths = paths.as_deref().into_iter().collect::<Vec<_>>();
|
||||
serde_json::to_value(paths).map_err(|e| internal_error(e.to_string()))
|
||||
})
|
||||
|
@ -586,11 +586,10 @@ impl ServerState {
|
|||
|
||||
let snap = self.snapshot().map_err(internal_error)?;
|
||||
just_future(async move {
|
||||
let packages =
|
||||
tinymist_query::package::list_package_by_namespace(&snap.world.registry, ns)
|
||||
.into_iter()
|
||||
.map(PackageInfo::from)
|
||||
.collect::<Vec<_>>();
|
||||
let packages = tinymist_query::package::list_package_by_namespace(snap.registry(), ns)
|
||||
.into_iter()
|
||||
.map(PackageInfo::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
serde_json::to_value(packages).map_err(|e| internal_error(e.to_string()))
|
||||
})
|
||||
|
@ -662,7 +661,7 @@ impl ServerState {
|
|||
let snap = self.query_snapshot().map_err(internal_error)?;
|
||||
|
||||
Ok(async move {
|
||||
let world = &snap.world;
|
||||
let world = snap.world();
|
||||
|
||||
let entry: StrResult<EntryState> = Ok(()).and_then(|_| {
|
||||
let toml_id = tinymist_query::package::get_manifest_id(&info)?;
|
||||
|
|
|
@ -108,7 +108,7 @@ impl ServerState {
|
|||
inputs: input.inputs,
|
||||
};
|
||||
|
||||
let snapshot = self.project.snapshot().unwrap().task(input);
|
||||
let snapshot = self.project.snapshot().unwrap().snap.clone().task(input);
|
||||
let world = &snapshot.world;
|
||||
|
||||
let main = world
|
||||
|
|
|
@ -356,8 +356,8 @@ impl ServerState {
|
|||
|
||||
if matches!(query, Completion(..)) {
|
||||
// Prefetch the package index for completion.
|
||||
if snap.world.registry.cached_index().is_none() {
|
||||
let registry = snap.world.registry.clone();
|
||||
if snap.registry().cached_index().is_none() {
|
||||
let registry = snap.registry().clone();
|
||||
tokio::spawn(async move {
|
||||
let _ = registry.download_index();
|
||||
});
|
||||
|
|
|
@ -246,7 +246,7 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> {
|
|||
let snap = state.snapshot().unwrap();
|
||||
|
||||
RUNTIMES.tokio_runtime.block_on(async {
|
||||
let w = snap.world.task(TaskInputs {
|
||||
let w = snap.world().clone().task(TaskInputs {
|
||||
entry: Some(entry),
|
||||
inputs,
|
||||
});
|
||||
|
@ -296,9 +296,8 @@ pub fn query_main(cmds: QueryCommands) -> Result<()> {
|
|||
QueryCommands::PackageDocs(args) => {
|
||||
let pkg = PackageSpec::from_str(&args.id).unwrap();
|
||||
let path = args.path.map(PathBuf::from);
|
||||
let path = path.unwrap_or_else(|| {
|
||||
snap.world.registry.resolve(&pkg).unwrap().as_ref().into()
|
||||
});
|
||||
let path = path
|
||||
.unwrap_or_else(|| snap.registry().resolve(&pkg).unwrap().as_ref().into());
|
||||
|
||||
let res = state
|
||||
.resource_package_docs_(PackageInfo {
|
||||
|
@ -315,9 +314,8 @@ pub fn query_main(cmds: QueryCommands) -> Result<()> {
|
|||
QueryCommands::CheckPackage(args) => {
|
||||
let pkg = PackageSpec::from_str(&args.id).unwrap();
|
||||
let path = args.path.map(PathBuf::from);
|
||||
let path = path.unwrap_or_else(|| {
|
||||
snap.world.registry.resolve(&pkg).unwrap().as_ref().into()
|
||||
});
|
||||
let path = path
|
||||
.unwrap_or_else(|| snap.registry().resolve(&pkg).unwrap().as_ref().into());
|
||||
|
||||
state
|
||||
.check_package(PackageInfo {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use reflexo_typst::diag::print_diagnostics;
|
||||
use reflexo_typst::{diag::print_diagnostics, TypstDocument};
|
||||
pub use tinymist_project::*;
|
||||
|
||||
use std::{num::NonZeroUsize, sync::Arc};
|
||||
|
@ -31,7 +31,7 @@ use tinymist_project::vfs::{FileChangeSet, MemoryEvent};
|
|||
use tinymist_query::{
|
||||
analysis::{Analysis, AnalysisRevLock, LocalContextGuard, PeriscopeProvider},
|
||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, LocalContext, SemanticRequest,
|
||||
StatefulRequest, VersionedDocument,
|
||||
StatefulRequest,
|
||||
};
|
||||
use tinymist_render::PeriscopeRenderer;
|
||||
use tinymist_std::{error::prelude::*, ImmutPath};
|
||||
|
@ -57,7 +57,7 @@ impl ServerState {
|
|||
}
|
||||
|
||||
/// Snapshots the project for tasks
|
||||
pub fn snapshot(&mut self) -> Result<LspCompileSnapshot> {
|
||||
pub fn snapshot(&mut self) -> Result<LspComputeGraph> {
|
||||
self.project.snapshot()
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ impl ProjectInsStateExt {
|
|||
handler: &dyn CompileHandler<LspCompilerFeat, ProjectInsStateExt>,
|
||||
compilation: &LspCompiledArtifact,
|
||||
) {
|
||||
let rev = compilation.world.revision().get();
|
||||
let rev = compilation.world().revision().get();
|
||||
if self.notified_revision >= rev {
|
||||
return;
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ impl ProjectInsStateExt {
|
|||
return false;
|
||||
};
|
||||
|
||||
let last_rev = last_compilation.world.revision();
|
||||
let last_rev = last_compilation.world().revision();
|
||||
if last_rev != *revision {
|
||||
return false;
|
||||
}
|
||||
|
@ -272,8 +272,7 @@ impl ProjectInsStateExt {
|
|||
return false;
|
||||
}
|
||||
self.emitted_reasons.see(self.pending_reasons);
|
||||
let mut last_compilation = last_compilation.clone();
|
||||
last_compilation.snap.signal = pending_reasons.into();
|
||||
let last_compilation = last_compilation.clone().with_signal(pending_reasons.into());
|
||||
|
||||
handler.notify_compile(&last_compilation);
|
||||
self.pending_reasons = CompileReasons::default();
|
||||
|
@ -297,7 +296,7 @@ impl ProjectState {
|
|||
}
|
||||
|
||||
/// Snapshot the compiler thread for tasks
|
||||
pub fn snapshot(&mut self) -> Result<LspCompileSnapshot> {
|
||||
pub fn snapshot(&mut self) -> Result<LspComputeGraph> {
|
||||
Ok(self.compiler.snapshot())
|
||||
}
|
||||
|
||||
|
@ -316,7 +315,7 @@ impl ProjectState {
|
|||
|
||||
pub fn do_interrupt(compiler: &mut LspProjectCompiler, intr: Interrupt<LspCompilerFeat>) {
|
||||
if let Interrupt::Compiled(compiled) = &intr {
|
||||
let proj = compiler.projects().find(|p| p.id == compiled.id);
|
||||
let proj = compiler.projects().find(|p| &p.id == compiled.id());
|
||||
if let Some(proj) = proj {
|
||||
proj.ext
|
||||
.compiled(&proj.verse.revision, proj.handler.as_ref(), compiled);
|
||||
|
@ -351,7 +350,7 @@ impl PeriscopeProvider for TypstPeriscopeProvider {
|
|||
fn periscope_at(
|
||||
&self,
|
||||
ctx: &mut LocalContext,
|
||||
doc: VersionedDocument,
|
||||
doc: &TypstDocument,
|
||||
pos: TypstPosition,
|
||||
) -> Option<String> {
|
||||
self.0.render_marked(ctx, doc, pos)
|
||||
|
@ -440,9 +439,9 @@ impl CompileHandlerImpl {
|
|||
}
|
||||
|
||||
fn notify_diagnostics(&self, snap: &LspCompiledArtifact) {
|
||||
let world = &snap.world;
|
||||
let world = snap.world();
|
||||
let dv = ProjVersion {
|
||||
id: snap.id.clone(),
|
||||
id: snap.id().clone(),
|
||||
revision: world.revision().get(),
|
||||
};
|
||||
|
||||
|
@ -450,11 +449,9 @@ impl CompileHandlerImpl {
|
|||
// todo: check all errors in this file
|
||||
let valid = !world.entry_state().is_inactive();
|
||||
let diagnostics = valid.then(|| {
|
||||
let errors = snap.doc.as_ref().err().into_iter().flatten();
|
||||
let warnings = snap.warnings.as_ref();
|
||||
let diagnostics = tinymist_query::convert_diagnostics(
|
||||
world,
|
||||
errors.chain(warnings),
|
||||
snap.diagnostics(),
|
||||
self.analysis.position_encoding,
|
||||
);
|
||||
|
||||
|
@ -491,7 +488,7 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
break 'vfs_is_clean false;
|
||||
};
|
||||
|
||||
let last_rev = compilation.world.vfs().revision();
|
||||
let last_rev = compilation.world().vfs().revision();
|
||||
let deps = compilation.depended_files().clone();
|
||||
s.verse.vfs().is_clean_compile(last_rev.get(), &deps)
|
||||
}
|
||||
|
@ -601,29 +598,23 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
fn notify_compile(&self, snap: &LspCompiledArtifact) {
|
||||
{
|
||||
let mut n_revs = self.notified_revision.lock();
|
||||
let n_rev = n_revs.entry(snap.id.clone()).or_default();
|
||||
if *n_rev >= snap.world.revision().get() {
|
||||
let n_rev = n_revs.entry(snap.id().clone()).or_default();
|
||||
if *n_rev >= snap.world().revision().get() {
|
||||
log::info!(
|
||||
"Project: already notified for revision {} <= {n_rev}",
|
||||
snap.world.revision(),
|
||||
snap.world().revision(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
*n_rev = snap.world.revision().get();
|
||||
*n_rev = snap.world().revision().get();
|
||||
}
|
||||
|
||||
// Prints the diagnostics when we are running the compilation in standalone
|
||||
// CLI.
|
||||
if self.is_standalone {
|
||||
print_diagnostics(
|
||||
&snap.world,
|
||||
snap.doc
|
||||
.as_ref()
|
||||
.err()
|
||||
.cloned()
|
||||
.iter()
|
||||
.flatten()
|
||||
.chain(snap.warnings.iter()),
|
||||
snap.world(),
|
||||
snap.diagnostics(),
|
||||
reflexo_typst::DiagnosticFormat::Human,
|
||||
)
|
||||
.log_error("failed to print diagnostics");
|
||||
|
@ -635,11 +626,11 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
self.export.signal(snap);
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
if let Some(inner) = self.preview.get(&snap.id) {
|
||||
if let Some(inner) = self.preview.get(snap.id()) {
|
||||
let snap = snap.clone();
|
||||
inner.notify_compile(Arc::new(crate::tool::preview::PreviewCompileView { snap }));
|
||||
} else {
|
||||
log::info!("Project: no preview for {:?}", snap.id);
|
||||
log::info!("Project: no preview for {:?}", snap.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -647,13 +638,13 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
pub type QuerySnapWithStat = (LspQuerySnapshot, QueryStatGuard);
|
||||
|
||||
pub struct LspQuerySnapshot {
|
||||
pub snap: LspCompileSnapshot,
|
||||
pub snap: LspComputeGraph,
|
||||
analysis: Arc<Analysis>,
|
||||
rev_lock: AnalysisRevLock,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for LspQuerySnapshot {
|
||||
type Target = LspCompileSnapshot;
|
||||
type Target = LspComputeGraph;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.snap
|
||||
|
@ -671,11 +662,8 @@ impl LspQuerySnapshot {
|
|||
query: T,
|
||||
wrapper: fn(Option<T::Response>) -> CompilerQueryResponse,
|
||||
) -> Result<CompilerQueryResponse> {
|
||||
let doc = self.snap.success_doc.as_ref().map(|doc| VersionedDocument {
|
||||
version: self.world.revision().get(),
|
||||
document: doc.clone(),
|
||||
});
|
||||
self.run_analysis(|ctx| query.request(ctx, doc))
|
||||
let graph = self.snap.clone();
|
||||
self.run_analysis(|ctx| query.request(ctx, graph))
|
||||
.map(wrapper)
|
||||
}
|
||||
|
||||
|
@ -688,7 +676,7 @@ impl LspQuerySnapshot {
|
|||
}
|
||||
|
||||
pub fn run_analysis<T>(self, f: impl FnOnce(&mut LocalContextGuard) -> T) -> Result<T> {
|
||||
let world = self.snap.world;
|
||||
let world = self.snap.world().clone();
|
||||
let Some(..) = world.main_id() else {
|
||||
log::error!("Project: main file is not set");
|
||||
bail!("main file is not set");
|
||||
|
|
|
@ -5,7 +5,7 @@ use tinymist_std::debug_loc::DataSource;
|
|||
use typst::text::{FontStretch, FontStyle, FontWeight};
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::project::LspCompileSnapshot;
|
||||
use crate::project::LspComputeGraph;
|
||||
use crate::world::font::FontResolver;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -43,9 +43,9 @@ struct FontResourceResult {
|
|||
|
||||
impl ServerState {
|
||||
/// Get the all valid fonts
|
||||
pub async fn get_font_resources(snap: LspCompileSnapshot) -> LspResult<JsonValue> {
|
||||
pub async fn get_font_resources(snap: LspComputeGraph) -> LspResult<JsonValue> {
|
||||
// fonts
|
||||
let resolver = &snap.world.font_resolver;
|
||||
let resolver = &snap.world().font_resolver;
|
||||
let font_book = resolver.font_book();
|
||||
let mut source_map: HashMap<Arc<DataSource>, u32> = HashMap::new();
|
||||
let mut sources: Vec<DataSource> = Vec::new();
|
||||
|
|
|
@ -8,7 +8,7 @@ use typst::foundations::Bytes;
|
|||
use typst::{syntax::VirtualPath, World};
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::project::LspCompileSnapshot;
|
||||
use crate::project::LspComputeGraph;
|
||||
use crate::world::{base::ShadowApi, EntryState, TaskInputs};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -948,12 +948,11 @@ static CAT_MAP: Lazy<HashMap<&str, SymCategory>> = Lazy::new(|| {
|
|||
|
||||
impl ServerState {
|
||||
/// Get the all valid symbols
|
||||
pub async fn get_symbol_resources(snap: LspCompileSnapshot) -> LspResult<JsonValue> {
|
||||
pub async fn get_symbol_resources(snap: LspComputeGraph) -> LspResult<JsonValue> {
|
||||
let mut symbols = ResourceSymbolMap::new();
|
||||
|
||||
let std = snap
|
||||
.world
|
||||
.library
|
||||
.library()
|
||||
.std
|
||||
.read()
|
||||
.scope()
|
||||
|
@ -993,7 +992,7 @@ impl ServerState {
|
|||
|
||||
let new_entry = EntryState::new_rootless(VirtualPath::new(&entry_path));
|
||||
|
||||
let mut forked = snap.world.task(TaskInputs {
|
||||
let mut forked = snap.world().task(TaskInputs {
|
||||
entry: Some(new_entry),
|
||||
..TaskInputs::default()
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ use typst::syntax::Source;
|
|||
use crate::actor::editor::{EditorActor, EditorRequest};
|
||||
use crate::lsp::query::OnEnter;
|
||||
use crate::project::{
|
||||
update_lock, CompiledArtifact, EntryResolver, LspCompileSnapshot, LspInterrupt, ProjectInsId,
|
||||
update_lock, CompiledArtifact, EntryResolver, LspComputeGraph, LspInterrupt, ProjectInsId,
|
||||
ProjectState, PROJECT_ROUTE_USER_ACTION_PRIORITY,
|
||||
};
|
||||
use crate::route::ProjectRouteState;
|
||||
|
@ -396,7 +396,7 @@ impl ServerState {
|
|||
|
||||
let snap = self.snapshot()?;
|
||||
just_future(async move {
|
||||
let w = &snap.world;
|
||||
let w = snap.world();
|
||||
|
||||
let info = ServerInfoResponse {
|
||||
root: w.entry_state().root().map(|e| e.as_ref().to_owned()),
|
||||
|
@ -421,12 +421,12 @@ impl ServerState {
|
|||
let lock_dir = self.entry_resolver().resolve_lock(&entry);
|
||||
|
||||
let update_dep = lock_dir.clone().map(|lock_dir| {
|
||||
|snap: LspCompileSnapshot| async move {
|
||||
|snap: LspComputeGraph| async move {
|
||||
let mut updater = update_lock(lock_dir);
|
||||
let world = snap.world.clone();
|
||||
let doc_id = updater.compiled(&world)?;
|
||||
let world = snap.world();
|
||||
let doc_id = updater.compiled(world)?;
|
||||
|
||||
updater.update_materials(doc_id.clone(), snap.world.depended_fs_paths());
|
||||
updater.update_materials(doc_id.clone(), world.depended_fs_paths());
|
||||
updater.route(doc_id, PROJECT_ROUTE_USER_ACTION_PRIORITY);
|
||||
|
||||
updater.commit();
|
||||
|
@ -442,7 +442,7 @@ impl ServerState {
|
|||
..TaskInputs::default()
|
||||
});
|
||||
|
||||
let artifact = CompiledArtifact::from_snapshot(snap.clone());
|
||||
let artifact = CompiledArtifact::from_graph(snap.clone());
|
||||
let res = ExportTask::do_export(task, artifact, lock_dir).await?;
|
||||
if let Some(update_dep) = update_dep {
|
||||
tokio::spawn(update_dep(snap));
|
||||
|
|
|
@ -66,8 +66,8 @@ impl ExportTask {
|
|||
artifact: &LspCompiledArtifact,
|
||||
config: &Arc<ExportUserConfig>,
|
||||
) -> Option<()> {
|
||||
let doc = artifact.doc.as_ref().ok()?;
|
||||
let s = artifact.signal;
|
||||
let doc = artifact.doc.as_ref()?;
|
||||
let s = artifact.snap.signal;
|
||||
|
||||
let when = config.task.when().unwrap_or_default();
|
||||
let need_export = (!matches!(when, TaskWhen::Never) && s.by_entry_update)
|
||||
|
@ -82,7 +82,7 @@ impl ExportTask {
|
|||
return None;
|
||||
}
|
||||
|
||||
let rev = artifact.world.revision().get();
|
||||
let rev = artifact.world().revision().get();
|
||||
let fut = self.export_folder.spawn(rev, || {
|
||||
let task = config.task.clone();
|
||||
let artifact = artifact.clone();
|
||||
|
@ -107,12 +107,12 @@ impl ExportTask {
|
|||
}
|
||||
|
||||
let editor_tx = self.editor_tx.clone()?;
|
||||
let rev = artifact.world.revision().get();
|
||||
let rev = artifact.world().revision().get();
|
||||
let fut = self.count_word_folder.spawn(rev, || {
|
||||
let artifact = artifact.clone();
|
||||
Box::pin(async move {
|
||||
let id = artifact.snap.id;
|
||||
let doc = artifact.doc.ok()?;
|
||||
let id = artifact.id().clone();
|
||||
let doc = artifact.doc?;
|
||||
let wc =
|
||||
log_err(FutureFolder::compute(move |_| word_count::word_count(&doc)).await);
|
||||
log::debug!("WordCount({id:?}:{rev}): {wc:?}");
|
||||
|
@ -138,10 +138,10 @@ impl ExportTask {
|
|||
use reflexo_vec2svg::DefaultExportFeature;
|
||||
use ProjectTask::*;
|
||||
|
||||
let CompiledArtifact { snap, doc, .. } = artifact;
|
||||
let CompiledArtifact { graph, doc, .. } = artifact;
|
||||
|
||||
// Prepare the output path.
|
||||
let entry = snap.world.entry_state();
|
||||
let entry = graph.snap.world.entry_state();
|
||||
let config = task.as_export().unwrap();
|
||||
let output = config.output.clone().unwrap_or_default();
|
||||
let Some(to) = output.substitute(&entry) else {
|
||||
|
@ -168,7 +168,7 @@ impl ExportTask {
|
|||
let _: Option<()> = lock_dir.and_then(|lock_dir| {
|
||||
let mut updater = crate::project::update_lock(lock_dir);
|
||||
|
||||
let doc_id = updater.compiled(&snap.world)?;
|
||||
let doc_id = updater.compiled(graph.world())?;
|
||||
|
||||
updater.task(ApplyProjectTask {
|
||||
id: doc_id.clone(),
|
||||
|
@ -181,7 +181,7 @@ impl ExportTask {
|
|||
});
|
||||
|
||||
// Prepare the document.
|
||||
let doc = doc.ok().context("cannot export with compilation errors")?;
|
||||
let doc = doc.context("cannot export with compilation errors")?;
|
||||
|
||||
// Prepare data.
|
||||
let kind2 = task.clone();
|
||||
|
@ -196,7 +196,7 @@ impl ExportTask {
|
|||
.get_or_init(|| -> Result<_> {
|
||||
Ok(match &doc {
|
||||
TypstDocument::Html(html_doc) => html_doc.clone(),
|
||||
TypstDocument::Paged(_) => extra_compile_for_export(&snap.world)?,
|
||||
TypstDocument::Paged(_) => extra_compile_for_export(graph.world())?,
|
||||
})
|
||||
})
|
||||
.as_ref()
|
||||
|
@ -208,7 +208,7 @@ impl ExportTask {
|
|||
.get_or_init(|| -> Result<_> {
|
||||
Ok(match &doc {
|
||||
TypstDocument::Paged(paged_doc) => paged_doc.clone(),
|
||||
TypstDocument::Html(_) => extra_compile_for_export(&snap.world)?,
|
||||
TypstDocument::Html(_) => extra_compile_for_export(graph.world())?,
|
||||
})
|
||||
})
|
||||
.as_ref()
|
||||
|
@ -252,7 +252,7 @@ impl ExportTask {
|
|||
one,
|
||||
}) => {
|
||||
let pretty = false;
|
||||
let elements = reflexo_typst::query::retrieve(&snap.world, &selector, doc)
|
||||
let elements = reflexo_typst::query::retrieve(&graph.world(), &selector, doc)
|
||||
.map_err(|e| anyhow::anyhow!("failed to retrieve: {e}"))?;
|
||||
if one && elements.len() != 1 {
|
||||
bail!("expected exactly one element, found {}", elements.len());
|
||||
|
@ -287,7 +287,7 @@ impl ExportTask {
|
|||
TextExport::run_on_doc(doc)?.into_bytes()
|
||||
}
|
||||
ExportMd(ExportMarkdownTask { export: _ }) => {
|
||||
let conv = Typlite::new(Arc::new(snap.world))
|
||||
let conv = Typlite::new(Arc::new(graph.world().clone()))
|
||||
.convert()
|
||||
.map_err(|e| anyhow::anyhow!("failed to convert to markdown: {e}"))?;
|
||||
|
||||
|
|
|
@ -183,26 +183,26 @@ pub struct PreviewCompileView {
|
|||
|
||||
impl typst_preview::CompileView for PreviewCompileView {
|
||||
fn doc(&self) -> Option<TypstDocument> {
|
||||
self.snap.doc.clone().ok()
|
||||
self.snap.doc.clone()
|
||||
}
|
||||
|
||||
fn status(&self) -> CompileStatus {
|
||||
match self.snap.doc {
|
||||
Ok(_) => CompileStatus::CompileSuccess,
|
||||
Err(_) => CompileStatus::CompileError,
|
||||
Some(_) => CompileStatus::CompileSuccess,
|
||||
None => CompileStatus::CompileError,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_on_saved(&self) -> bool {
|
||||
self.snap.signal.by_fs_events
|
||||
self.snap.snap.signal.by_fs_events
|
||||
}
|
||||
|
||||
fn is_by_entry_update(&self) -> bool {
|
||||
self.snap.signal.by_entry_update
|
||||
self.snap.snap.signal.by_entry_update
|
||||
}
|
||||
|
||||
fn resolve_source_span(&self, loc: Location) -> Option<SourceSpanOffset> {
|
||||
let world = &self.snap.world;
|
||||
let world = self.snap.world();
|
||||
let Location::Src(loc) = loc;
|
||||
|
||||
let source_id = world.id_for_path(Path::new(&loc.filepath))?;
|
||||
|
@ -230,7 +230,7 @@ impl typst_preview::CompileView for PreviewCompileView {
|
|||
let TypstDocument::Paged(doc) = self.doc()? else {
|
||||
return None;
|
||||
};
|
||||
let world = &self.snap.world;
|
||||
let world = self.snap.world();
|
||||
|
||||
let page = pos.page_no.checked_sub(1)?;
|
||||
let page = doc.pages.get(page)?;
|
||||
|
@ -240,7 +240,7 @@ impl typst_preview::CompileView for PreviewCompileView {
|
|||
}
|
||||
|
||||
fn resolve_document_position(&self, loc: Location) -> Vec<Position> {
|
||||
let world = &self.snap.world;
|
||||
let world = self.snap.world();
|
||||
let Location::Src(src_loc) = loc;
|
||||
|
||||
let line = src_loc.pos.line as usize;
|
||||
|
@ -265,7 +265,7 @@ impl typst_preview::CompileView for PreviewCompileView {
|
|||
}
|
||||
|
||||
fn resolve_span(&self, span: Span, offset: Option<usize>) -> Option<DocToSrcJumpInfo> {
|
||||
let world = &self.snap.world;
|
||||
let world = self.snap.world();
|
||||
let resolve_off =
|
||||
|src: &Source, off: usize| src.byte_to_line(off).zip(src.byte_to_column(off));
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::{
|
|||
use clap_complete::Shell;
|
||||
use parking_lot::Mutex;
|
||||
use reflexo::{path::unix_slash, ImmutPath};
|
||||
use reflexo_typst::WorldComputeGraph;
|
||||
use tinymist_query::analysis::Analysis;
|
||||
use tinymist_std::{bail, error::prelude::*};
|
||||
use tokio::sync::mpsc;
|
||||
|
@ -137,13 +138,13 @@ pub async fn compile_main(args: CompileArgs) -> Result<()> {
|
|||
// Prepares for the compilation
|
||||
let universe = (input, lock_dir.clone()).resolve()?;
|
||||
let world = universe.snapshot();
|
||||
let snap = CompileSnapshot::from_world(world);
|
||||
let graph = WorldComputeGraph::from_world(world);
|
||||
|
||||
// Compiles the project
|
||||
let compiled = CompiledArtifact::from_snapshot(snap);
|
||||
let compiled = CompiledArtifact::from_graph(graph);
|
||||
|
||||
let diag = compiled.diagnostics();
|
||||
print_diagnostics(&compiled.world, diag, DiagnosticFormat::Human)
|
||||
print_diagnostics(compiled.world(), diag, DiagnosticFormat::Human)
|
||||
.context_ut("print diagnostics")?;
|
||||
|
||||
if compiled.has_errors() {
|
||||
|
|
|
@ -189,9 +189,10 @@ pub async fn test_main(args: TestArgs) -> Result<()> {
|
|||
log_info!("Runs testing again...");
|
||||
}
|
||||
// Sets is_compiling to track dependencies
|
||||
artifact.snap.world.set_is_compiling(true);
|
||||
let res = test_once(&artifact.world, &config);
|
||||
artifact.snap.world.set_is_compiling(false);
|
||||
let mut world = artifact.snap.world.clone();
|
||||
world.set_is_compiling(true);
|
||||
let res = test_once(&world, &config);
|
||||
world.set_is_compiling(false);
|
||||
|
||||
if let Err(err) = res {
|
||||
test_error!("Fatal:", "{err}");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue