Skip to content

Commit c8e8013

Browse files
committed
[ty] Initial support for workspace diagnostics
1 parent d2684a0 commit c8e8013

File tree

17 files changed

+316
-34
lines changed

17 files changed

+316
-34
lines changed

crates/ty_project/src/db.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,20 @@ impl ProjectDatabase {
8181

8282
/// Checks all open files in the project and its dependencies.
8383
pub fn check(&self) -> Vec<Diagnostic> {
84-
let mut reporter = DummyReporter;
85-
let reporter = AssertUnwindSafe(&mut reporter as &mut dyn Reporter);
86-
self.project().check(self, reporter)
84+
self.check_with_mode(CheckMode::OpenFiles)
8785
}
8886

8987
/// Checks all open files in the project and its dependencies, using the given reporter.
9088
pub fn check_with_reporter(&self, reporter: &mut dyn Reporter) -> Vec<Diagnostic> {
9189
let reporter = AssertUnwindSafe(reporter);
92-
self.project().check(self, reporter)
90+
self.project().check(self, CheckMode::OpenFiles, reporter)
91+
}
92+
93+
/// Check the project with the given mode.
94+
pub fn check_with_mode(&self, mode: CheckMode) -> Vec<Diagnostic> {
95+
let mut reporter = DummyReporter;
96+
let reporter = AssertUnwindSafe(&mut reporter as &mut dyn Reporter);
97+
self.project().check(self, mode, reporter)
9398
}
9499

95100
#[tracing::instrument(level = "debug", skip(self))]
@@ -110,6 +115,17 @@ impl ProjectDatabase {
110115
}
111116
}
112117

118+
#[derive(Debug, Clone, Copy, PartialEq)]
119+
pub enum CheckMode {
120+
/// Checks only the open files in the project.
121+
OpenFiles,
122+
123+
/// Checks all files in the project, ignoring the open file set.
124+
///
125+
/// This includes virtual files, such as those created by the language server.
126+
AllFiles,
127+
}
128+
113129
impl Upcast<dyn SemanticDb> for ProjectDatabase {
114130
fn upcast(&self) -> &(dyn SemanticDb + 'static) {
115131
self

crates/ty_project/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::glob::{GlobFilterCheckMode, IncludeResult};
22
use crate::metadata::options::{OptionDiagnostic, ToSettingsError};
33
use crate::walk::{ProjectFilesFilter, ProjectFilesWalker};
4-
pub use db::{Db, ProjectDatabase};
4+
pub use db::{CheckMode, Db, ProjectDatabase};
55
use files::{Index, Indexed, IndexedFiles};
66
use metadata::settings::Settings;
77
pub use metadata::{ProjectMetadata, ProjectMetadataError};
@@ -213,6 +213,7 @@ impl Project {
213213
pub(crate) fn check(
214214
self,
215215
db: &ProjectDatabase,
216+
mode: CheckMode,
216217
mut reporter: AssertUnwindSafe<&mut dyn Reporter>,
217218
) -> Vec<Diagnostic> {
218219
let project_span = tracing::debug_span!("Project::check");
@@ -227,7 +228,11 @@ impl Project {
227228
.map(OptionDiagnostic::to_diagnostic),
228229
);
229230

230-
let files = ProjectFiles::new(db, self);
231+
let files = match mode {
232+
CheckMode::OpenFiles => ProjectFiles::new(db, self),
233+
// TODO: Consider open virtual files as well
234+
CheckMode::AllFiles => ProjectFiles::Indexed(self.files(db)),
235+
};
231236
reporter.set_files(files.len());
232237

233238
diagnostics.extend(

crates/ty_server/src/server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ impl Server {
173173
diagnostic_provider: Some(DiagnosticServerCapabilities::Options(DiagnosticOptions {
174174
identifier: Some(crate::DIAGNOSTIC_NAME.into()),
175175
inter_file_dependencies: true,
176+
workspace_diagnostics: true,
176177
..Default::default()
177178
})),
178179
text_document_sync: Some(TextDocumentSyncCapability::Options(

crates/ty_server/src/server/api.rs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,28 @@ pub(super) fn request(req: server::Request) -> Task {
2828
let id = req.id.clone();
2929

3030
match req.method.as_str() {
31-
requests::DocumentDiagnosticRequestHandler::METHOD => background_request_task::<
31+
requests::DocumentDiagnosticRequestHandler::METHOD => background_document_request_task::<
3232
requests::DocumentDiagnosticRequestHandler,
3333
>(
3434
req, BackgroundSchedule::Worker
3535
),
36-
requests::GotoTypeDefinitionRequestHandler::METHOD => background_request_task::<
36+
requests::WorkspaceDiagnosticRequestHandler::METHOD => background_request_task::<
37+
requests::WorkspaceDiagnosticRequestHandler,
38+
>(
39+
req, BackgroundSchedule::Worker
40+
),
41+
requests::GotoTypeDefinitionRequestHandler::METHOD => background_document_request_task::<
3742
requests::GotoTypeDefinitionRequestHandler,
3843
>(
3944
req, BackgroundSchedule::Worker
4045
),
41-
requests::HoverRequestHandler::METHOD => background_request_task::<
46+
requests::HoverRequestHandler::METHOD => background_document_request_task::<
4247
requests::HoverRequestHandler,
4348
>(req, BackgroundSchedule::Worker),
44-
requests::InlayHintRequestHandler::METHOD => background_request_task::<
49+
requests::InlayHintRequestHandler::METHOD => background_document_request_task::<
4550
requests::InlayHintRequestHandler,
4651
>(req, BackgroundSchedule::Worker),
47-
requests::CompletionRequestHandler::METHOD => background_request_task::<
52+
requests::CompletionRequestHandler::METHOD => background_document_request_task::<
4853
requests::CompletionRequestHandler,
4954
>(
5055
req, BackgroundSchedule::LatencySensitive
@@ -135,7 +140,51 @@ where
135140
}))
136141
}
137142

138-
fn background_request_task<R: traits::BackgroundDocumentRequestHandler>(
143+
fn background_request_task<R: traits::BackgroundRequestHandler>(
144+
req: server::Request,
145+
schedule: BackgroundSchedule,
146+
) -> Result<Task>
147+
where
148+
<<R as RequestHandler>::RequestType as Request>::Params: UnwindSafe,
149+
{
150+
let retry = R::RETRY_ON_CANCELLATION.then(|| req.clone());
151+
let (id, params) = cast_request::<R>(req)?;
152+
153+
Ok(Task::background(schedule, move |session: &Session| {
154+
let cancellation_token = session
155+
.request_queue()
156+
.incoming()
157+
.cancellation_token(&id)
158+
.expect("request should have been tested for cancellation before scheduling");
159+
160+
let snapshot = session.take_workspace_snapshot();
161+
162+
Box::new(move |client| {
163+
let _span = tracing::debug_span!("request", %id, method = R::METHOD).entered();
164+
165+
// Test again if the request was cancelled since it was scheduled on the background task
166+
// and, if so, return early
167+
if cancellation_token.is_cancelled() {
168+
tracing::trace!(
169+
"Ignoring request id={id} method={} because it was cancelled",
170+
R::METHOD
171+
);
172+
173+
// We don't need to send a response here because the `cancel` notification
174+
// handler already responded with a message.
175+
return;
176+
}
177+
178+
let result = ruff_db::panic::catch_unwind(|| R::run(snapshot, client, params));
179+
180+
if let Some(response) = request_result_to_response::<R>(&id, client, result, retry) {
181+
respond::<R>(&id, response, client);
182+
}
183+
})
184+
}))
185+
}
186+
187+
fn background_document_request_task<R: traits::BackgroundDocumentRequestHandler>(
139188
req: server::Request,
140189
schedule: BackgroundSchedule,
141190
) -> Result<Task>
@@ -168,7 +217,7 @@ where
168217
};
169218

170219
let Some(snapshot) = session.take_snapshot(url) else {
171-
tracing::warn!("Ignoring request because snapshot for path `{path:?}` doesn't exist.");
220+
tracing::warn!("Ignoring request because snapshot for path `{path:?}` doesn't exist");
172221
return Box::new(|_| {});
173222
};
174223

@@ -209,7 +258,7 @@ fn request_result_to_response<R>(
209258
request: Option<lsp_server::Request>,
210259
) -> Option<Result<<<R as RequestHandler>::RequestType as Request>::Result>>
211260
where
212-
R: traits::BackgroundDocumentRequestHandler,
261+
R: traits::RetriableRequestHandler,
213262
{
214263
match result {
215264
Ok(response) => Some(response),

crates/ty_server/src/server/api/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub(super) fn compute_diagnostics(
172172

173173
/// Converts the tool specific [`Diagnostic`][ruff_db::diagnostic::Diagnostic] to an LSP
174174
/// [`Diagnostic`].
175-
fn to_lsp_diagnostic(
175+
pub(super) fn to_lsp_diagnostic(
176176
db: &dyn Db,
177177
diagnostic: &ruff_db::diagnostic::Diagnostic,
178178
encoding: PositionEncoding,

crates/ty_server/src/server/api/notifications/did_close.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ impl SyncNotificationHandler for DidCloseTextDocumentHandler {
4141
);
4242
}
4343

44-
clear_diagnostics(&key, client)
44+
if !session.global_settings().diagnostic_mode().is_workspace() {
45+
// The server needs to clear the diagnostics regardless of whether the client supports
46+
// pull diagnostics or not. This is because the client only has the capability to fetch
47+
// the diagnostics but does not automatically clear them when a document is closed.
48+
clear_diagnostics(&key, client)?;
49+
}
50+
51+
Ok(())
4552
}
4653
}

crates/ty_server/src/server/api/requests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ mod goto_type_definition;
44
mod hover;
55
mod inlay_hints;
66
mod shutdown;
7+
mod workspace_diagnostic;
78

89
pub(super) use completion::CompletionRequestHandler;
910
pub(super) use diagnostic::DocumentDiagnosticRequestHandler;
1011
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
1112
pub(super) use hover::HoverRequestHandler;
1213
pub(super) use inlay_hints::InlayHintRequestHandler;
1314
pub(super) use shutdown::ShutdownHandler;
15+
pub(super) use workspace_diagnostic::WorkspaceDiagnosticRequestHandler;

crates/ty_server/src/server/api/requests/completion.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use ty_project::ProjectDatabase;
88

99
use crate::DocumentSnapshot;
1010
use crate::document::PositionExt;
11-
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
11+
use crate::server::api::traits::{
12+
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
13+
};
1214
use crate::session::client::Client;
1315

1416
pub(crate) struct CompletionRequestHandler;
@@ -18,8 +20,6 @@ impl RequestHandler for CompletionRequestHandler {
1820
}
1921

2022
impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
21-
const RETRY_ON_CANCELLATION: bool = true;
22-
2323
fn document_url(params: &CompletionParams) -> Cow<Url> {
2424
Cow::Borrowed(&params.text_document_position.text_document.uri)
2525
}
@@ -65,3 +65,7 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
6565
Ok(Some(response))
6666
}
6767
}
68+
69+
impl RetriableRequestHandler for CompletionRequestHandler {
70+
const RETRY_ON_CANCELLATION: bool = true;
71+
}

crates/ty_server/src/server/api/requests/diagnostic.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use lsp_types::{
88

99
use crate::server::Result;
1010
use crate::server::api::diagnostics::{Diagnostics, compute_diagnostics};
11-
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
11+
use crate::server::api::traits::{
12+
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
13+
};
1214
use crate::session::DocumentSnapshot;
1315
use crate::session::client::Client;
1416
use ty_project::ProjectDatabase;
@@ -43,7 +45,9 @@ impl BackgroundDocumentRequestHandler for DocumentDiagnosticRequestHandler {
4345
}),
4446
))
4547
}
48+
}
4649

50+
impl RetriableRequestHandler for DocumentDiagnosticRequestHandler {
4751
fn salsa_cancellation_error() -> lsp_server::ResponseError {
4852
lsp_server::ResponseError {
4953
code: lsp_server::ErrorCode::ServerCancelled as i32,

crates/ty_server/src/server/api/requests/goto_type_definition.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use ty_project::ProjectDatabase;
88

99
use crate::DocumentSnapshot;
1010
use crate::document::{PositionExt, ToLink};
11-
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
11+
use crate::server::api::traits::{
12+
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
13+
};
1214
use crate::session::client::Client;
1315

1416
pub(crate) struct GotoTypeDefinitionRequestHandler;
@@ -70,3 +72,5 @@ impl BackgroundDocumentRequestHandler for GotoTypeDefinitionRequestHandler {
7072
}
7173
}
7274
}
75+
76+
impl RetriableRequestHandler for GotoTypeDefinitionRequestHandler {}

0 commit comments

Comments
 (0)