mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-12-23 08:47:50 +00:00
Merge 5dbcd4ebd7 into 0bfe655ada
This commit is contained in:
commit
6d398cdea9
5 changed files with 384 additions and 162 deletions
|
|
@ -10,14 +10,24 @@ pub struct CheckRequest {
|
|||
pub snap: LspCompiledArtifact,
|
||||
}
|
||||
|
||||
/// The diagnostics emitted by a full check run.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DiagnosticsResult {
|
||||
/// Diagnostics reported by the compiler.
|
||||
pub compiler: DiagnosticsMap,
|
||||
/// Diagnostics reported by lint passes.
|
||||
pub lint: DiagnosticsMap,
|
||||
}
|
||||
|
||||
impl SemanticRequest for CheckRequest {
|
||||
type Response = DiagnosticsMap;
|
||||
type Response = DiagnosticsResult;
|
||||
|
||||
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||
let worker = DiagWorker::new(ctx);
|
||||
let compiler_diags = self.snap.diagnostics();
|
||||
let compiler_diags: Vec<_> = self.snap.diagnostics().cloned().collect();
|
||||
let known_issues = KnownIssues::from_compiler_diagnostics(compiler_diags.iter());
|
||||
let lint = DiagWorker::new(ctx).check(&known_issues).results;
|
||||
let compiler = DiagWorker::new(ctx).convert_all(compiler_diags.iter());
|
||||
|
||||
let known_issues = KnownIssues::from_compiler_diagnostics(compiler_diags.clone());
|
||||
Some(worker.check(&known_issues).convert_all(compiler_diags))
|
||||
Some(DiagnosticsResult { compiler, lint })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,34 @@ pub struct EditorActorConfig {
|
|||
pub enum EditorRequest {
|
||||
Config(EditorActorConfig),
|
||||
/// Publishes diagnostics to the editor.
|
||||
Diag(ProjVersion, Option<DiagnosticsMap>),
|
||||
Diag(ProjVersion, DiagKind, Option<DiagnosticsMap>),
|
||||
/// Updates compile status to the editor.
|
||||
Status(CompileReport),
|
||||
/// Updastes words count status to the editor.
|
||||
WordCount(ProjectInsId, WordsCount),
|
||||
}
|
||||
|
||||
/// The kind of diagnostics published to the editor.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DiagKind {
|
||||
/// Diagnostics reported by the Typst compiler.
|
||||
Compiler,
|
||||
/// Diagnostics reported by Tinymist's lint engine.
|
||||
Lint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct DiagKey {
|
||||
pub project: ProjectInsId,
|
||||
pub kind: DiagKind,
|
||||
}
|
||||
|
||||
impl DiagKey {
|
||||
fn new(project: ProjectInsId, kind: DiagKind) -> Self {
|
||||
Self { project, kind }
|
||||
}
|
||||
}
|
||||
|
||||
/// The actor maintaining output to the editor, including diagnostics and
|
||||
/// compile status.
|
||||
pub struct EditorActor {
|
||||
|
|
@ -44,11 +65,11 @@ pub struct EditorActor {
|
|||
|
||||
/// Accumulated diagnostics per file.
|
||||
/// The outer `HashMap` is indexed by the file's URL.
|
||||
/// The inner `HashMap` is indexed by the project ID, allowing multiple
|
||||
/// projects publishing diagnostics to the same file independently.
|
||||
diagnostics: HashMap<Url, HashMap<ProjectInsId, EcoVec<Diagnostic>>>,
|
||||
/// The map from project ID to the affected files.
|
||||
affect_map: HashMap<ProjectInsId, Vec<Url>>,
|
||||
/// The inner `HashMap` is indexed by the `(project, kind)` pair, allowing
|
||||
/// multiple sources publishing diagnostics to the same file independently.
|
||||
diagnostics: HashMap<Url, HashMap<DiagKey, EcoVec<Diagnostic>>>,
|
||||
/// The map from `(project, kind)` to the affected files.
|
||||
affect_map: HashMap<DiagKey, Vec<Url>>,
|
||||
|
||||
/// The local state.
|
||||
status: StatusAll,
|
||||
|
|
@ -100,13 +121,13 @@ impl EditorActor {
|
|||
log::info!("received config request: {config:?}");
|
||||
self.config = config;
|
||||
}
|
||||
EditorRequest::Diag(version, diagnostics) => {
|
||||
EditorRequest::Diag(version, kind, diagnostics) => {
|
||||
log::debug!(
|
||||
"received diagnostics from {version:?}: diag({:?})",
|
||||
diagnostics.as_ref().map(|files| files.len())
|
||||
);
|
||||
|
||||
self.publish(version.id, diagnostics);
|
||||
self.publish(version, kind, diagnostics);
|
||||
}
|
||||
EditorRequest::Status(compile_status) => {
|
||||
log::trace!("received status request: {compile_status:?}");
|
||||
|
|
@ -137,12 +158,18 @@ impl EditorActor {
|
|||
}
|
||||
|
||||
/// Publishes diagnostics of a project to the editor.
|
||||
pub fn publish(&mut self, id: ProjectInsId, next_diag: Option<DiagnosticsMap>) {
|
||||
pub fn publish(
|
||||
&mut self,
|
||||
version: ProjVersion,
|
||||
kind: DiagKind,
|
||||
next_diag: Option<DiagnosticsMap>,
|
||||
) {
|
||||
let key = DiagKey::new(version.id.clone(), kind);
|
||||
let affected = match next_diag.as_ref() {
|
||||
Some(next_diag) => self
|
||||
.affect_map
|
||||
.insert(id.clone(), next_diag.keys().cloned().collect()),
|
||||
None => self.affect_map.remove(&id),
|
||||
.insert(key.clone(), next_diag.keys().cloned().collect()),
|
||||
None => self.affect_map.remove(&key),
|
||||
};
|
||||
|
||||
// Gets sources which had some diagnostic published last time, but not this
|
||||
|
|
@ -155,24 +182,24 @@ impl EditorActor {
|
|||
// Gets sources that affected by this group in last round but not this time
|
||||
for uri in affected.into_iter().flatten() {
|
||||
if !next_diag.as_ref().is_some_and(|e| e.contains_key(&uri)) {
|
||||
self.publish_file(&id, uri, None)
|
||||
self.publish_file(&key, uri, None)
|
||||
}
|
||||
}
|
||||
|
||||
// Gets touched updates
|
||||
for (uri, next) in next_diag.into_iter().flatten() {
|
||||
self.publish_file(&id, uri, Some(next))
|
||||
self.publish_file(&key, uri, Some(next))
|
||||
}
|
||||
}
|
||||
|
||||
/// Publishes diagnostics of a file to the editor.
|
||||
fn publish_file(&mut self, id: &ProjectInsId, uri: Url, next: Option<EcoVec<Diagnostic>>) {
|
||||
fn publish_file(&mut self, key: &DiagKey, uri: Url, next: Option<EcoVec<Diagnostic>>) {
|
||||
let mut diagnostics = EcoVec::new();
|
||||
|
||||
// Gets the diagnostics from other groups
|
||||
let path_diags = self.diagnostics.entry(uri.clone()).or_default();
|
||||
for (existing_id, diags) in path_diags.iter() {
|
||||
if existing_id != id {
|
||||
for (existing_key, diags) in path_diags.iter() {
|
||||
if existing_key != key {
|
||||
diagnostics.push(diags.clone());
|
||||
}
|
||||
}
|
||||
|
|
@ -184,8 +211,8 @@ impl EditorActor {
|
|||
|
||||
// Updates the diagnostics for this group
|
||||
match next {
|
||||
Some(next) => path_diags.insert(id.clone(), next),
|
||||
None => path_diags.remove(id),
|
||||
Some(next) => path_diags.insert(key.clone(), next),
|
||||
None => path_diags.remove(key),
|
||||
};
|
||||
|
||||
// Publishes the diagnostics
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ use tokio::sync::mpsc;
|
|||
use typst::{diag::FileResult, foundations::Bytes, layout::Position as TypstPosition};
|
||||
|
||||
use super::ServerState;
|
||||
use crate::actor::editor::{EditorRequest, ProjVersion};
|
||||
use crate::actor::editor::{DiagKind, EditorRequest, ProjVersion};
|
||||
use crate::stats::{CompilerQueryStats, QueryStatGuard};
|
||||
#[cfg(feature = "export")]
|
||||
use crate::task::ExportUserConfig;
|
||||
|
|
@ -159,45 +159,54 @@ impl ServerState {
|
|||
config.export(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
let preview_state = preview.clone();
|
||||
|
||||
// Create the compile handler for client consuming results.
|
||||
let periscope_args = config.periscope_args.clone();
|
||||
let handle = Arc::new(CompileHandlerImpl {
|
||||
#[cfg(feature = "preview")]
|
||||
preview,
|
||||
is_standalone: false,
|
||||
#[cfg(feature = "export")]
|
||||
export: export.clone(),
|
||||
editor_tx: editor_tx.clone(),
|
||||
client: Arc::new(client.clone().to_untyped()),
|
||||
analysis: Arc::new(Analysis {
|
||||
position_encoding: const_config.position_encoding,
|
||||
allow_overlapping_token: const_config.tokens_overlapping_token_support,
|
||||
allow_multiline_token: const_config.tokens_multiline_token_support,
|
||||
remove_html: !config.support_html_in_markdown,
|
||||
support_client_codelens: true,
|
||||
extended_code_action: config.extended_code_action,
|
||||
completion_feat: config.completion.clone(),
|
||||
color_theme: match config.color_theme.as_deref() {
|
||||
Some("dark") => tinymist_query::ColorTheme::Dark,
|
||||
_ => tinymist_query::ColorTheme::Light,
|
||||
},
|
||||
lint: config.lint.when().clone(),
|
||||
periscope: periscope_args.map(|args| {
|
||||
let r = TypstPeriscopeProvider(PeriscopeRenderer::new(args));
|
||||
Arc::new(r) as Arc<dyn PeriscopeProvider + Send + Sync>
|
||||
}),
|
||||
local_packages: Arc::default(),
|
||||
tokens_caches: Arc::default(),
|
||||
workers: Default::default(),
|
||||
caches: Default::default(),
|
||||
analysis_rev_cache: Arc::default(),
|
||||
stats: Arc::default(),
|
||||
let analysis = Arc::new(Analysis {
|
||||
position_encoding: const_config.position_encoding,
|
||||
allow_overlapping_token: const_config.tokens_overlapping_token_support,
|
||||
allow_multiline_token: const_config.tokens_multiline_token_support,
|
||||
remove_html: !config.support_html_in_markdown,
|
||||
support_client_codelens: true,
|
||||
extended_code_action: config.extended_code_action,
|
||||
completion_feat: config.completion.clone(),
|
||||
color_theme: match config.color_theme.as_deref() {
|
||||
Some("dark") => tinymist_query::ColorTheme::Dark,
|
||||
_ => tinymist_query::ColorTheme::Light,
|
||||
},
|
||||
lint: config.lint.when().clone(),
|
||||
periscope: periscope_args.map(|args| {
|
||||
let r = TypstPeriscopeProvider(PeriscopeRenderer::new(args));
|
||||
Arc::new(r) as Arc<dyn PeriscopeProvider + Send + Sync>
|
||||
}),
|
||||
|
||||
status_revision: Mutex::default(),
|
||||
notified_revision: Mutex::default(),
|
||||
local_packages: Arc::default(),
|
||||
tokens_caches: Arc::default(),
|
||||
workers: Default::default(),
|
||||
caches: Default::default(),
|
||||
analysis_rev_cache: Arc::default(),
|
||||
stats: Arc::default(),
|
||||
});
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut hooks: Vec<Box<dyn CompileHook + Send + Sync>> = vec![
|
||||
Box::new(DiagHook::new(analysis.clone(), editor_tx.clone())),
|
||||
Box::new(LintHook::new(analysis.clone(), editor_tx.clone())),
|
||||
];
|
||||
#[cfg(feature = "preview")]
|
||||
hooks.push(Box::new(PreviewHook::new(preview)));
|
||||
#[cfg(feature = "export")]
|
||||
hooks.push(Box::new(ExportHook::new(export.clone())));
|
||||
|
||||
let handle = CompileHandlerImpl::new(
|
||||
analysis.clone(),
|
||||
editor_tx.clone(),
|
||||
Arc::new(client.clone().to_untyped()),
|
||||
false,
|
||||
hooks,
|
||||
);
|
||||
|
||||
let export_target = config.export_target;
|
||||
let default_path = config.entry_resolver.resolve_default();
|
||||
let entry = config.entry_resolver.resolve(default_path);
|
||||
|
|
@ -245,11 +254,11 @@ impl ServerState {
|
|||
ProjectState {
|
||||
compiler,
|
||||
#[cfg(feature = "preview")]
|
||||
preview: handle.preview.clone(),
|
||||
preview: preview_state,
|
||||
analysis: handle.analysis.clone(),
|
||||
stats: CompilerQueryStats::default(),
|
||||
#[cfg(feature = "export")]
|
||||
export: handle.export.clone(),
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -448,19 +457,234 @@ impl ProjectPreviewState {
|
|||
}
|
||||
}
|
||||
|
||||
fn push_editor_diagnostics(
|
||||
editor_tx: &EditorSender,
|
||||
dv: ProjVersion,
|
||||
kind: DiagKind,
|
||||
diagnostics: Option<DiagnosticsMap>,
|
||||
) {
|
||||
editor_tx
|
||||
.send(EditorRequest::Diag(dv, kind, diagnostics))
|
||||
.log_error("failed to send diagnostics");
|
||||
}
|
||||
|
||||
/// A hook that handles diagnostics.
|
||||
pub struct DiagHook {
|
||||
analysis: Arc<Analysis>,
|
||||
editor_tx: EditorSender,
|
||||
}
|
||||
|
||||
impl DiagHook {
|
||||
/// Creates a new diagnostics hook.
|
||||
pub fn new(analysis: Arc<Analysis>, editor_tx: EditorSender) -> Self {
|
||||
Self {
|
||||
analysis,
|
||||
editor_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&self, dv: ProjVersion, art: &LspCompiledArtifact) {
|
||||
let enc = self.analysis.position_encoding;
|
||||
let diagnostics =
|
||||
tinymist_query::convert_diagnostics(art.graph.clone(), art.diagnostics(), enc);
|
||||
|
||||
log::trace!(
|
||||
"notify compiler diagnostics({:?}): {:#?}",
|
||||
dv.id,
|
||||
diagnostics
|
||||
);
|
||||
|
||||
push_editor_diagnostics(
|
||||
&self.editor_tx,
|
||||
dv.clone(),
|
||||
DiagKind::Compiler,
|
||||
Some(diagnostics),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A hook that handles compilation events.
|
||||
pub trait CompileHook {
|
||||
/// Notifies the hook of a compilation result.
|
||||
fn notify(&self, dv: ProjVersion, art: &LspCompiledArtifact, client: &Arc<dyn ProjectClient>);
|
||||
/// Notifies the hook of a compilation status.
|
||||
fn status(&self, _revision: usize, _rep: &CompileReport) {}
|
||||
}
|
||||
|
||||
impl CompileHook for DiagHook {
|
||||
fn notify(&self, dv: ProjVersion, art: &LspCompiledArtifact, _client: &Arc<dyn ProjectClient>) {
|
||||
if art.world().entry_state().is_inactive() {
|
||||
push_editor_diagnostics(&self.editor_tx, dv.clone(), DiagKind::Compiler, None);
|
||||
return;
|
||||
}
|
||||
|
||||
self.notify(dv, art);
|
||||
}
|
||||
}
|
||||
|
||||
/// A hook that handles linting.
|
||||
pub struct LintHook {
|
||||
analysis: Arc<Analysis>,
|
||||
editor_tx: EditorSender,
|
||||
}
|
||||
|
||||
impl LintHook {
|
||||
/// Creates a new lint hook.
|
||||
pub fn new(analysis: Arc<Analysis>, editor_tx: EditorSender) -> Self {
|
||||
Self {
|
||||
analysis,
|
||||
editor_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&self, dv: ProjVersion, art: &LspCompiledArtifact) {
|
||||
let should_lint = art
|
||||
.snap
|
||||
.signal
|
||||
.should_run_task_dyn(&self.analysis.lint, art.doc.as_ref())
|
||||
.unwrap_or_default();
|
||||
log::debug!(
|
||||
"Project: should_lint: {should_lint:?}, signal: {:?}",
|
||||
art.snap.signal
|
||||
);
|
||||
|
||||
if !should_lint {
|
||||
return;
|
||||
}
|
||||
|
||||
let snap = art.clone();
|
||||
let editor_tx = self.editor_tx.clone();
|
||||
let analysis = self.analysis.clone();
|
||||
spawn_cpu(move || {
|
||||
let mut ctx = analysis.enter(snap.graph.clone());
|
||||
|
||||
// todo: check all errors in this file
|
||||
let Some(diagnostics) = CheckRequest { snap }.request(&mut ctx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
log::trace!(
|
||||
"notify lint diagnostics({:?}): {:#?}",
|
||||
dv.id,
|
||||
diagnostics.lint
|
||||
);
|
||||
|
||||
editor_tx
|
||||
.send(EditorRequest::Diag(
|
||||
dv,
|
||||
DiagKind::Lint,
|
||||
Some(diagnostics.lint),
|
||||
))
|
||||
.log_error("failed to send lint diagnostics");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl CompileHook for LintHook {
|
||||
fn notify(&self, dv: ProjVersion, art: &LspCompiledArtifact, _client: &Arc<dyn ProjectClient>) {
|
||||
if art.world().entry_state().is_inactive() {
|
||||
push_editor_diagnostics(&self.editor_tx, dv.clone(), DiagKind::Lint, None);
|
||||
return;
|
||||
}
|
||||
|
||||
self.notify(dv, art);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
#[derive(Clone)]
|
||||
/// A hook that handles preview.
|
||||
pub struct PreviewHook {
|
||||
state: ProjectPreviewState,
|
||||
}
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
impl PreviewHook {
|
||||
/// Creates a new preview hook.
|
||||
pub fn new(state: ProjectPreviewState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
fn notify(&self, art: &LspCompiledArtifact) {
|
||||
if let Some(inner) = self.state.get(art.id()) {
|
||||
let art = art.clone();
|
||||
inner.notify_compile(Arc::new(crate::tool::preview::PreviewCompileView { art }));
|
||||
} else {
|
||||
log::debug!("Project: no preview for {:?}", art.id());
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self, _revision: usize, rep: &CompileReport) {
|
||||
if let Some(inner) = self.state.get(&rep.id) {
|
||||
use tinymist_preview::CompileStatus;
|
||||
use tinymist_project::CompileStatusEnum::*;
|
||||
|
||||
inner.status(match &rep.status {
|
||||
Compiling => CompileStatus::Compiling,
|
||||
Suspend | CompileSuccess { .. } => CompileStatus::CompileSuccess,
|
||||
ExportError { .. } | CompileError { .. } => CompileStatus::CompileError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn state(&self) -> ProjectPreviewState {
|
||||
self.state.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
impl CompileHook for PreviewHook {
|
||||
fn notify(
|
||||
&self,
|
||||
_dv: ProjVersion,
|
||||
art: &LspCompiledArtifact,
|
||||
_client: &Arc<dyn ProjectClient>,
|
||||
) {
|
||||
self.notify(art);
|
||||
}
|
||||
|
||||
fn status(&self, revision: usize, rep: &CompileReport) {
|
||||
self.status(revision, rep);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "export")]
|
||||
#[derive(Clone)]
|
||||
/// A hook that handles export.
|
||||
pub struct ExportHook {
|
||||
task: crate::task::ExportTask,
|
||||
}
|
||||
|
||||
#[cfg(feature = "export")]
|
||||
impl ExportHook {
|
||||
/// Creates a new export hook.
|
||||
pub fn new(task: crate::task::ExportTask) -> Self {
|
||||
Self { task }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn task(&self) -> crate::task::ExportTask {
|
||||
self.task.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "export")]
|
||||
impl CompileHook for ExportHook {
|
||||
fn notify(&self, _dv: ProjVersion, art: &LspCompiledArtifact, client: &Arc<dyn ProjectClient>) {
|
||||
self.task.signal(art, client);
|
||||
}
|
||||
}
|
||||
|
||||
/// The implementation of the compile handler.
|
||||
pub struct CompileHandlerImpl {
|
||||
/// The analysis data.
|
||||
pub(crate) analysis: Arc<Analysis>,
|
||||
hooks: Vec<Box<dyn CompileHook + Send + Sync>>,
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
pub(crate) preview: ProjectPreviewState,
|
||||
/// Whether the compile server is running in standalone CLI (not as a
|
||||
/// language server).
|
||||
pub is_standalone: bool,
|
||||
/// The export task.
|
||||
#[cfg(feature = "export")]
|
||||
pub(crate) export: crate::task::ExportTask,
|
||||
/// The editor sender, used to send editor requests to the editor.
|
||||
pub(crate) editor_tx: EditorSender,
|
||||
/// The client used to send events back to the server itself or the clients.
|
||||
|
|
@ -519,11 +743,22 @@ impl ProjectClient for mpsc::UnboundedSender<LspInterrupt> {
|
|||
}
|
||||
|
||||
impl CompileHandlerImpl {
|
||||
/// Pushes diagnostics to the editor.
|
||||
fn push_diagnostics(&self, dv: ProjVersion, diagnostics: Option<DiagnosticsMap>) {
|
||||
self.editor_tx
|
||||
.send(EditorRequest::Diag(dv, diagnostics))
|
||||
.log_error("failed to send diagnostics");
|
||||
pub(crate) fn new(
|
||||
analysis: Arc<Analysis>,
|
||||
editor_tx: EditorSender,
|
||||
client: Arc<dyn ProjectClient>,
|
||||
is_standalone: bool,
|
||||
hooks: Vec<Box<dyn CompileHook + Send + Sync>>,
|
||||
) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
analysis,
|
||||
is_standalone,
|
||||
editor_tx,
|
||||
client,
|
||||
status_revision: Mutex::default(),
|
||||
notified_revision: Mutex::default(),
|
||||
hooks,
|
||||
})
|
||||
}
|
||||
|
||||
/// Notifies the diagnostics.
|
||||
|
|
@ -532,51 +767,9 @@ impl CompileHandlerImpl {
|
|||
id: art.id().clone(),
|
||||
revision: art.world().revision().get(),
|
||||
};
|
||||
// todo: better way to remove diagnostics
|
||||
let valid = !art.world().entry_state().is_inactive();
|
||||
if !valid {
|
||||
self.push_diagnostics(dv, None);
|
||||
return;
|
||||
}
|
||||
|
||||
let should_lint = art
|
||||
.snap
|
||||
.signal
|
||||
.should_run_task_dyn(&self.analysis.lint, art.doc.as_ref())
|
||||
.unwrap_or_default();
|
||||
log::debug!(
|
||||
"Project: should_lint: {should_lint:?}, signal: {:?}",
|
||||
art.snap.signal
|
||||
);
|
||||
|
||||
if !should_lint {
|
||||
let enc = self.analysis.position_encoding;
|
||||
let diagnostics =
|
||||
tinymist_query::convert_diagnostics(art.graph.clone(), art.diagnostics(), enc);
|
||||
|
||||
log::trace!("notify diagnostics({dv:?}): {diagnostics:#?}");
|
||||
|
||||
self.editor_tx
|
||||
.send(EditorRequest::Diag(dv, Some(diagnostics)))
|
||||
.log_error("failed to send diagnostics");
|
||||
} else {
|
||||
let snap = art.clone();
|
||||
let editor_tx = self.editor_tx.clone();
|
||||
let analysis = self.analysis.clone();
|
||||
spawn_cpu(move || {
|
||||
let mut ctx = analysis.enter(snap.graph.clone());
|
||||
|
||||
// todo: check all errors in this file
|
||||
let Some(diagnostics) = CheckRequest { snap }.request(&mut ctx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
log::trace!("notify diagnostics({dv:?}): {diagnostics:#?}");
|
||||
|
||||
editor_tx
|
||||
.send(EditorRequest::Diag(dv, Some(diagnostics)))
|
||||
.log_error("failed to send diagnostics");
|
||||
});
|
||||
for hook in &self.hooks {
|
||||
hook.notify(dv.clone(), art, &self.client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -691,19 +884,13 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
id: rep.id.clone(),
|
||||
revision,
|
||||
};
|
||||
self.push_diagnostics(dv, None);
|
||||
push_editor_diagnostics(&self.editor_tx, dv.clone(), DiagKind::Compiler, None);
|
||||
push_editor_diagnostics(&self.editor_tx, dv, DiagKind::Lint, None);
|
||||
}
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
if let Some(inner) = self.preview.get(&rep.id) {
|
||||
use tinymist_preview::CompileStatus;
|
||||
use tinymist_project::CompileStatusEnum::*;
|
||||
|
||||
inner.status(match &rep.status {
|
||||
Compiling => CompileStatus::Compiling,
|
||||
Suspend | CompileSuccess { .. } => CompileStatus::CompileSuccess,
|
||||
ExportError { .. } | CompileError { .. } => CompileStatus::CompileError,
|
||||
});
|
||||
for hook in &self.hooks {
|
||||
hook.status(revision, &rep);
|
||||
}
|
||||
|
||||
self.editor_tx.send(EditorRequest::Status(rep)).unwrap();
|
||||
|
|
@ -720,7 +907,8 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
|
||||
// todo: race condition with notify_compile?
|
||||
// remove diagnostics
|
||||
self.push_diagnostics(dv, None);
|
||||
push_editor_diagnostics(&self.editor_tx, dv.clone(), DiagKind::Compiler, None);
|
||||
push_editor_diagnostics(&self.editor_tx, dv, DiagKind::Lint, None);
|
||||
}
|
||||
|
||||
fn notify_compile(&self, art: &LspCompiledArtifact) {
|
||||
|
|
@ -770,17 +958,6 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
|
|||
.log_error("failed to print diagnostics");
|
||||
}
|
||||
|
||||
#[cfg(feature = "export")]
|
||||
self.export.signal(art, &self.client);
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
if let Some(inner) = self.preview.get(art.id()) {
|
||||
let art = art.clone();
|
||||
inner.notify_compile(Arc::new(crate::tool::preview::PreviewCompileView { art }));
|
||||
} else {
|
||||
log::debug!("Project: no preview for {:?}", art.id());
|
||||
}
|
||||
|
||||
self.notify_diagnostics(art);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use tinymist_std::path::PathClean;
|
|||
use tinymist_std::typst::TypstDocument;
|
||||
use tinymist_task::{
|
||||
output_template, DocumentQuery, ExportMarkdownTask, ExportPngTask, ExportSvgTask, ExportTarget,
|
||||
ImageOutput, PdfExport, PngExport, SvgExport, TextExport,
|
||||
ExportTask as ProjectExportTask, ImageOutput, PdfExport, PngExport, SvgExport, TextExport,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use typlite::{Format, Typlite};
|
||||
|
|
@ -27,16 +27,16 @@ use parking_lot::Mutex;
|
|||
use rayon::Scope;
|
||||
|
||||
use super::SyncTaskFactory;
|
||||
use crate::actor::editor::EditorRequest;
|
||||
use crate::lsp::query::QueryFuture;
|
||||
use crate::project::{
|
||||
update_lock, ApplyProjectTask, CompiledArtifact, DevEvent, DevExportEvent, EntryReader,
|
||||
ExportHtmlTask, ExportPdfTask, ExportTask as ProjectExportTask, ExportTeXTask, ExportTextTask,
|
||||
LspCompiledArtifact, LspComputeGraph, ProjectClient, ProjectTask, TaskWhen,
|
||||
PROJECT_ROUTE_USER_ACTION_PRIORITY,
|
||||
ExportHtmlTask, ExportPdfTask, ExportTeXTask, ExportTextTask, LspCompiledArtifact,
|
||||
LspComputeGraph, ProjectClient, ProjectTask, TaskWhen, PROJECT_ROUTE_USER_ACTION_PRIORITY,
|
||||
};
|
||||
use crate::tool::word_count;
|
||||
use crate::world::TaskInputs;
|
||||
use crate::ServerState;
|
||||
use crate::{actor::editor::EditorRequest, tool::word_count};
|
||||
|
||||
impl ServerState {
|
||||
/// Exports the current document.
|
||||
|
|
@ -159,15 +159,11 @@ impl ExportTask {
|
|||
self.factory.mutate(|data| *data = config);
|
||||
}
|
||||
|
||||
pub(crate) fn signal(
|
||||
&self,
|
||||
snap: &LspCompiledArtifact,
|
||||
client: &std::sync::Arc<dyn ProjectClient + 'static>,
|
||||
) {
|
||||
pub(crate) fn signal(&self, art: &LspCompiledArtifact, client: &Arc<dyn ProjectClient>) {
|
||||
let config = self.factory.task();
|
||||
|
||||
self.signal_export(snap, &config, client);
|
||||
self.signal_count_word(snap, &config);
|
||||
self.signal_export(art, &config, client);
|
||||
self.signal_count_word(art, &config);
|
||||
}
|
||||
|
||||
fn signal_export(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use tinymist_query::analysis::Analysis;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
|
|
@ -76,20 +75,33 @@ where
|
|||
log::warn!("Project: system watcher is not enabled, file changes will not be watched");
|
||||
}
|
||||
|
||||
// Create the actor
|
||||
let compile_handle = Arc::new(CompileHandlerImpl {
|
||||
#[cfg(feature = "preview")]
|
||||
preview: opts.preview,
|
||||
is_standalone: true,
|
||||
#[cfg(feature = "export")]
|
||||
export: crate::task::ExportTask::new(handle, Some(editor_tx.clone()), opts.config.export()),
|
||||
editor_tx,
|
||||
client: Arc::new(intr_tx.clone()),
|
||||
let analysis = opts.analysis.clone();
|
||||
|
||||
analysis: opts.analysis,
|
||||
status_revision: Mutex::default(),
|
||||
notified_revision: Mutex::default(),
|
||||
});
|
||||
#[cfg(feature = "preview")]
|
||||
let preview = opts.preview;
|
||||
|
||||
#[cfg(feature = "export")]
|
||||
let export_task =
|
||||
crate::task::ExportTask::new(handle, Some(editor_tx.clone()), opts.config.export());
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut hooks: Vec<Box<dyn CompileHook + Send + Sync>> = vec![
|
||||
Box::new(DiagHook::new(analysis.clone(), editor_tx.clone())),
|
||||
Box::new(LintHook::new(analysis.clone(), editor_tx.clone())),
|
||||
];
|
||||
#[cfg(feature = "preview")]
|
||||
hooks.push(Box::new(PreviewHook::new(preview)));
|
||||
#[cfg(feature = "export")]
|
||||
hooks.push(Box::new(ExportHook::new(export_task)));
|
||||
|
||||
// Create the actor
|
||||
let compile_handle = CompileHandlerImpl::new(
|
||||
analysis,
|
||||
editor_tx.clone(),
|
||||
Arc::new(intr_tx.clone()),
|
||||
true,
|
||||
hooks,
|
||||
);
|
||||
|
||||
let mut compiler = ProjectCompiler::new(
|
||||
verse,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue