feat: allow to disable lint or lint on save (#1658)

* feat: allow to disable lint or lint on save

* fix: description
This commit is contained in:
Myriad-Dreamin 2025-04-16 03:15:56 +08:00 committed by GitHub
parent 39d13c83f6
commit 2709aaf429
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 140 additions and 33 deletions

View file

@ -12,7 +12,7 @@ use tinymist_analysis::stats::AllocStats;
use tinymist_analysis::ty::term_value;
use tinymist_analysis::{analyze_expr_, analyze_import_};
use tinymist_lint::LintInfo;
use tinymist_project::{LspComputeGraph, LspWorld};
use tinymist_project::{LspComputeGraph, LspWorld, TaskWhen};
use tinymist_std::hash::{hash128, FxDashMap};
use tinymist_std::typst::TypstDocument;
use tinymist_world::debug_loc::DataSource;
@ -69,6 +69,8 @@ pub struct Analysis {
pub completion_feat: CompletionFeat,
/// The editor's color theme.
pub color_theme: ColorTheme,
/// When to trigger the lint.
pub lint: TaskWhen,
/// The periscope provider.
pub periscope: Option<Arc<dyn PeriscopeProvider + Send + Sync>>,
/// The global worker resources for analysis.

View file

@ -42,21 +42,8 @@ impl ExportTimings {
timing: Option<TaskWhen>,
docs: Option<&D>,
) -> Option<bool> {
let s = snap.signal;
let when = timing.unwrap_or(TaskWhen::Never);
if !matches!(when, TaskWhen::Never) && s.by_entry_update {
return Some(true);
}
match when {
TaskWhen::Never => Some(false),
TaskWhen::OnType => Some(s.by_mem_events),
TaskWhen::OnSave => Some(s.by_fs_events),
TaskWhen::OnDocumentHasTitle if s.by_fs_events => {
docs.map(|doc| doc.info().title.is_some())
}
TaskWhen::OnDocumentHasTitle => Some(false),
}
snap.signal
.should_run_task(timing.unwrap_or_default(), docs)
}
}

View file

@ -2,7 +2,7 @@
use core::fmt;
use crate::{CompilerFeat, CompilerWorld, EntryReader, TaskInputs};
use crate::{args::TaskWhen, CompilerFeat, CompilerWorld, EntryReader, TaskInputs};
use ecow::EcoString;
use tinymist_std::typst::TypstDocument;
@ -43,6 +43,38 @@ impl ExportSignal {
self.by_fs_events |= other.by_fs_events;
self.by_entry_update |= other.by_entry_update;
}
pub fn should_run_task_dyn(
&self,
when: TaskWhen,
docs: Option<&TypstDocument>,
) -> Option<bool> {
match docs {
Some(TypstDocument::Paged(doc)) => self.should_run_task(when, Some(doc.as_ref())),
Some(TypstDocument::Html(doc)) => self.should_run_task(when, Some(doc.as_ref())),
None => self.should_run_task::<typst::layout::PagedDocument>(when, None),
}
}
pub fn should_run_task<D: typst::Document>(
&self,
when: TaskWhen,
docs: Option<&D>,
) -> Option<bool> {
if !matches!(when, TaskWhen::Never) && self.by_entry_update {
return Some(true);
}
match when {
TaskWhen::Never => Some(false),
TaskWhen::OnType => Some(self.by_mem_events),
TaskWhen::OnSave => Some(self.by_fs_events),
TaskWhen::OnDocumentHasTitle if self.by_fs_events => {
docs.map(|doc| doc.info().title.is_some())
}
TaskWhen::OnDocumentHasTitle => Some(false),
}
}
}
/// A snapshot of the project and compilation state.

View file

@ -90,6 +90,8 @@ pub struct Config {
pub completion: CompletionFeat,
/// Tinymist's preview features.
pub preview: PreviewFeat,
/// When to trigger the lint checks.
pub lint: LintFeat,
/// Specifies the cli font options
pub font_opts: CompileFontArgs,
@ -318,6 +320,7 @@ impl Config {
assign_config!(formatter_indent_size := "formatterIndentSize"?: Option<u32>);
assign_config!(output_path := "outputPath"?: PathPattern);
assign_config!(preview := "preview"?: PreviewFeat);
assign_config!(lint := "preview"?: LintFeat);
assign_config!(semantic_tokens := "semanticTokens"?: SemanticTokensMode);
assign_config!(support_html_in_markdown := "supportHtmlInMarkdown"?: bool);
assign_config!(system_fonts := "systemFonts"?: Option<bool>);
@ -788,6 +791,26 @@ pub struct PreviewFeat {
pub background: BackgroundPreviewOpts,
}
/// The lint features.
#[derive(Debug, Default, Clone, Deserialize)]
pub struct LintFeat {
/// Whether to enable linting.
pub enabled: Option<bool>,
/// When to trigger the lint checks.
pub when: Option<TaskWhen>,
}
impl LintFeat {
/// When to trigger the lint checks.
pub fn when(&self) -> TaskWhen {
if matches!(self.enabled, Some(false)) {
return TaskWhen::Never;
}
self.when.unwrap_or(TaskWhen::OnSave)
}
}
/// Options for browsing preview.
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]

View file

@ -161,6 +161,7 @@ impl ServerState {
Some("dark") => tinymist_query::ColorTheme::Dark,
_ => tinymist_query::ColorTheme::Light,
},
lint: config.lint.when(),
periscope: periscope_args.map(|args| {
let r = TypstPeriscopeProvider(PeriscopeRenderer::new(args));
Arc::new(r) as Arc<dyn PeriscopeProvider + Send + Sync>
@ -429,36 +430,54 @@ impl CompileHandlerImpl {
.log_error("failed to send diagnostics");
}
fn notify_diagnostics(&self, snap: &LspCompiledArtifact) {
fn notify_diagnostics(&self, art: &LspCompiledArtifact) {
let dv = ProjVersion {
id: snap.id().clone(),
revision: snap.world().revision().get(),
id: art.id().clone(),
revision: art.world().revision().get(),
};
// todo: better way to remove diagnostics
let valid = !snap.world().entry_state().is_inactive();
let valid = !art.world().entry_state().is_inactive();
if !valid {
self.push_diagnostics(dv, None);
return;
}
let snap = snap.clone();
let editor_tx = self.editor_tx.clone();
let analysis = self.analysis.clone();
rayon::spawn(move || {
let world = snap.world().clone();
let mut ctx = analysis.enter(world);
let should_lint = art
.snap
.signal
.should_run_task_dyn(self.analysis.lint, art.doc.as_ref())
.unwrap_or_default();
// todo: check all errors in this file
let Some(diagnostics) = CheckRequest { snap }.request(&mut ctx) else {
return;
};
if !should_lint {
let enc = self.analysis.position_encoding;
let diagnostics =
tinymist_query::convert_diagnostics(art.world(), art.diagnostics(), enc);
log::trace!("notify diagnostics({dv:?}): {diagnostics:#?}");
editor_tx
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();
rayon::spawn(move || {
let world = snap.world().clone();
let mut ctx = analysis.enter(world);
// 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");
});
}
}
}

View file

@ -367,6 +367,26 @@
"%extension.tinymist.config.tinymist.semanticTokens.string.enum.disable%"
]
},
"tinymist.lint.enabled": {
"title": "%extension.tinymist.config.tinymist.lint.enabled.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.enabled.desc%",
"type": "boolean",
"default": true
},
"tinymist.lint.when": {
"title": "%extension.tinymist.config.tinymist.lint.when.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.when.desc%",
"type": "string",
"enum": [
"onSave",
"onType"
],
"default": "onSave",
"enumDescriptions": [
"%extension.tinymist.config.tinymist.lint.when.string.enum.onSave%",
"%extension.tinymist.config.tinymist.lint.when.string.enum.onType%"
]
},
"tinymist.typingContinueCommentsOnNewline": {
"title": "%extension.tinymist.config.tinymist.typingContinueCommentsOnNewline.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.typingContinueCommentsOnNewline.desc%",

View file

@ -1538,3 +1538,27 @@ zh = "将当前预览文件声明为 typst-lsp 或 tinymist 的入口点。这
[extension.tinymist.config.tinymist.preview.pinPreviewFile.deprecation]
en = "This setting is deprecated and no longer needed. The extension will always pin the previewed file as the entrypoint for the language server."
zh = "此设置已弃用,不再需要。扩展程序将始终将预览的文件固定为语言服务器的入口点。"
[extension.tinymist.config.tinymist.lint.enabled.title]
en = "Enable Lint Checks"
zh = "启用代码检查"
[extension.tinymist.config.tinymist.lint.enabled.desc]
en = "Enable or disable lint checks. Note: restarting the editor is required to change this setting."
zh = "启用或禁用代码检查。注意:更改此设置需要重新启动编辑器。"
[extension.tinymist.config.tinymist.lint.when.title]
en = "When to perform lint checks"
zh = "何时执行代码检查"
[extension.tinymist.config.tinymist.lint.when.desc]
en = "Configure when to perform lint checks. Note: restarting the editor is required to change this setting."
zh = "配置何时执行代码检查。注意:更改此设置需要重新启动编辑器。"
[extension.tinymist.config.tinymist.lint.when.string.enum.onSave]
en = "Perform lint checks on save"
zh = "保存文件时执行代码检查"
[extension.tinymist.config.tinymist.lint.when.string.enum.onType]
en = "Perform lint checks on type"
zh = "标记文件时执行代码检查"