Skip to content

Commit 0f3fc0f

Browse files
committed
Move db updating into Session
1 parent bb484df commit 0f3fc0f

File tree

14 files changed

+202
-189
lines changed

14 files changed

+202
-189
lines changed

crates/ty_server/src/document/notebook.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ impl NotebookDocument {
141141
new_cells.into_iter().map(NotebookCell::new),
142142
);
143143

144-
// Re-build the cell-index if new cells were added, deleted or removed
144+
// Re-build the cell-index if new cells were added or deleted
145145
if !deleted_range.is_empty() || added > 0 {
146146
self.cell_index.clear();
147147
self.cell_index.extend(
148148
self.cells
149149
.iter()
150150
.enumerate()
151-
.map(|(i, cell)| (cell.url.clone(), i)),
151+
.map(|(cell_index, cell)| (cell.url.clone(), cell_index)),
152152
);
153153
}
154154

crates/ty_server/src/document/range.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl LspPosition {
7878
}
7979

8080
pub(crate) trait RangeExt {
81-
/// Convert an LSP Range to internal [`TextRange`].
81+
/// Convert an LSP Range to a [`TextRange`].
8282
///
8383
/// Returns `None` if `file` is a notebook and the
8484
/// cell identified by `url` can't be looked up or if the notebook
@@ -148,21 +148,22 @@ impl PositionExt for lsp_types::Position {
148148
index.source_location(cell_start_offset, source.as_str(), encoding.into());
149149
assert_eq!(cell_start_location.character_offset, OneIndexed::MIN);
150150

151-
let absolute_start = SourceLocation {
151+
// Absolute position into the concatenated notebook source text.
152+
let absolute_position = SourceLocation {
152153
line: cell_start_location
153154
.line
154155
.saturating_add(cell_relative_line.to_zero_indexed()),
155156
character_offset: OneIndexed::from_zero_indexed(u32_index_to_usize(self.character)),
156157
};
157-
return Some(index.offset(absolute_start, &source, encoding.into()));
158+
return Some(index.offset(absolute_position, &source, encoding.into()));
158159
}
159160

160161
Some(lsp_position_to_text_size(*self, &source, &index, encoding))
161162
}
162163
}
163164

164165
pub(crate) trait TextSizeExt {
165-
/// Converts self into a position into an LSP text document (can be a cell or regular document).
166+
/// Converts `self` into a position in an LSP text document (can be a cell or regular document).
166167
///
167168
/// Returns `None` if the position can't be converted:
168169
///
@@ -297,12 +298,8 @@ impl ToRangeExt for TextRange {
297298
let start_in_concatenated =
298299
index.source_location(self.start(), &source, encoding.into());
299300
let cell_index = notebook_index.cell(start_in_concatenated.line)?;
300-
let cell_range = notebook.cell_range(cell_index)?;
301301

302-
// Clamp the end offset to the end of the cell.
303-
let end_offset = self.end().min(cell_range.end());
304-
305-
let end_in_concatenated = index.source_location(end_offset, &source, encoding.into());
302+
let end_in_concatenated = index.source_location(self.end(), &source, encoding.into());
306303

307304
let start_in_cell = source_location_to_position(
308305
&notebook_index.translate_source_location(&start_in_concatenated),

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,16 @@ impl LspDiagnostics {
113113
}
114114

115115
pub(super) fn clear_diagnostics_if_needed(
116+
document: &DocumentHandle,
116117
session: &Session,
117-
uri: &lsp_types::Url,
118118
client: &Client,
119119
) {
120-
if session.client_capabilities().supports_pull_diagnostics() {
120+
if session.client_capabilities().supports_pull_diagnostics() && !document.is_cell_or_notebook()
121+
{
121122
return;
122123
}
123124

124-
clear_diagnostics(uri, client);
125+
clear_diagnostics(document.url(), client);
125126
}
126127

127128
/// Clears the diagnostics for the document identified by `uri`.
@@ -138,21 +139,29 @@ pub(super) fn clear_diagnostics(uri: &lsp_types::Url, client: &Client) {
138139
}
139140

140141
/// Publishes the diagnostics for the given document snapshot using the [publish diagnostics
141-
/// notification].
142+
/// notification] .
142143
///
143-
/// This function publishes the diagnostics for the entire notebook if `url` points to a notebook or a cell,
144-
/// even if the client supports pull diagnostics. This is because VS Code
144+
/// Unlike [`publish_diagnostics`], this function only publishes diagnostics if a client doesn't support
145+
/// pull diagnostics and `document` is not a notebook or cell (VS Code
145146
/// does not support pull diagnostics for notebooks or cells (as of 2025-11-12).
146147
///
147-
/// This function is a no-op for simple text files when the client supports pull diagnostics.
148-
///
149148
/// [publish diagnostics notification]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics
150-
pub(super) fn publish_diagnostics(session: &Session, document: &DocumentHandle, client: &Client) {
149+
pub(super) fn publish_diagnostics_if_needed(
150+
document: &DocumentHandle,
151+
session: &Session,
152+
client: &Client,
153+
) {
151154
if !document.is_cell_or_notebook() && session.client_capabilities().supports_pull_diagnostics()
152155
{
153156
return;
154157
}
155158

159+
publish_diagnostics(document, session, client);
160+
}
161+
162+
/// Publishes the diagnostics for the given document snapshot using the [publish diagnostics
163+
/// notification].
164+
pub(super) fn publish_diagnostics(document: &DocumentHandle, session: &Session, client: &Client) {
156165
let db = session.project_db(document.notebook_or_file_path());
157166

158167
let Some(diagnostics) = compute_diagnostics(db, document, session.position_encoding()) else {

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

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ use lsp_types::{DidChangeTextDocumentParams, VersionedTextDocumentIdentifier};
44

55
use crate::server::Result;
66
use crate::server::api::LSPResult;
7-
use crate::server::api::diagnostics::publish_diagnostics;
7+
use crate::server::api::diagnostics::publish_diagnostics_if_needed;
88
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
9+
use crate::session::Session;
910
use crate::session::client::Client;
10-
use crate::session::{DocumentHandle, Session};
11-
use crate::system::AnySystemPath;
12-
use ty_project::watch::ChangeEvent;
1311

1412
pub(crate) struct DidChangeTextDocumentHandler;
1513

@@ -36,24 +34,8 @@ impl SyncNotificationHandler for DidChangeTextDocumentHandler {
3634
.update_text_document(session, content_changes, version)
3735
.with_failure_code(ErrorCode::InternalError)?;
3836

39-
file_changed(&document, session, client);
37+
publish_diagnostics_if_needed(&document, session, client);
4038

4139
Ok(())
4240
}
4341
}
44-
45-
pub(super) fn file_changed(document: &DocumentHandle, session: &mut Session, client: &Client) {
46-
let path = document.notebook_or_file_path();
47-
let changes = match path {
48-
AnySystemPath::System(system_path) => {
49-
vec![ChangeEvent::file_content_changed(system_path.clone())]
50-
}
51-
AnySystemPath::SystemVirtual(virtual_path) => {
52-
vec![ChangeEvent::ChangedVirtual(virtual_path.clone())]
53-
}
54-
};
55-
56-
session.apply_changes(path, changes);
57-
58-
publish_diagnostics(session, document, client);
59-
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use lsp_types::notification as notif;
44

55
use crate::server::Result;
66
use crate::server::api::LSPResult;
7-
use crate::server::api::notifications::did_change::file_changed;
7+
use crate::server::api::diagnostics::publish_diagnostics;
88
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
99
use crate::session::Session;
1010
use crate::session::client::Client;
@@ -32,7 +32,7 @@ impl SyncNotificationHandler for DidChangeNotebookHandler {
3232
.update_notebook_document(session, cells, metadata, version)
3333
.with_failure_code(ErrorCode::InternalError)?;
3434

35-
file_changed(&document, session, client);
35+
publish_diagnostics(&document, session, client);
3636

3737
Ok(())
3838
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl SyncNotificationHandler for DidChangeWatchedFiles {
9292
);
9393
} else {
9494
for key in session.text_document_handles() {
95-
publish_diagnostics(session, &key, client);
95+
publish_diagnostics(&key, session, client);
9696
}
9797
}
9898

Lines changed: 5 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
use lsp_server::ErrorCode;
22
use lsp_types::notification::DidCloseTextDocument;
33
use lsp_types::{DidCloseTextDocumentParams, TextDocumentIdentifier};
4-
use ruff_db::Db as _;
5-
use ty_project::Db as _;
64

75
use crate::server::Result;
86
use crate::server::api::LSPResult;
9-
use crate::server::api::diagnostics::{clear_diagnostics, clear_diagnostics_if_needed};
7+
use crate::server::api::diagnostics::clear_diagnostics_if_needed;
108
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
9+
use crate::session::Session;
1110
use crate::session::client::Client;
12-
use crate::session::{DocumentHandle, Session};
13-
use crate::system::AnySystemPath;
1411

1512
pub(crate) struct DidCloseTextDocumentHandler;
1613

@@ -31,68 +28,15 @@ impl SyncNotificationHandler for DidCloseTextDocumentHandler {
3128
let document = session
3229
.document_handle(&uri)
3330
.with_failure_code(ErrorCode::InternalError)?;
34-
let is_cell = document.is_cell();
3531

36-
if !is_cell {
37-
close_document(&document, session, client);
38-
}
39-
40-
let url = document.url().clone();
41-
42-
document
32+
let should_clear_diagnostics = document
4333
.close(session)
4434
.with_failure_code(ErrorCode::InternalError)?;
4535

46-
if is_cell {
47-
// Notebooks only support publish diagnostics.
48-
clear_diagnostics(&url, client);
36+
if should_clear_diagnostics {
37+
clear_diagnostics_if_needed(&document, session, client);
4938
}
5039

5140
Ok(())
5241
}
5342
}
54-
55-
pub(super) fn close_document(document: &DocumentHandle, session: &mut Session, client: &Client) {
56-
let path = document.notebook_or_file_path();
57-
let db = session.project_db_mut(path);
58-
59-
match path {
60-
AnySystemPath::System(system_path) => {
61-
if let Some(file) = db.files().try_system(db, system_path) {
62-
db.project().close_file(db, file);
63-
} else {
64-
// This can only fail when the path is a directory or it doesn't exists but the
65-
// file should exists for this handler in this branch. This is because every
66-
// close call is preceded by an open call, which ensures that the file is
67-
// interned in the lookup table (`Files`).
68-
tracing::warn!("Salsa file does not exists for {}", system_path);
69-
}
70-
71-
// For non-virtual files, we clear diagnostics if:
72-
//
73-
// 1. The file does not belong to any workspace e.g., opening a random file from
74-
// outside the workspace because closing it acts like the file doesn't exists
75-
// 2. The diagnostic mode is set to open-files only
76-
if session.workspaces().for_path(system_path).is_none()
77-
|| session
78-
.global_settings()
79-
.diagnostic_mode()
80-
.is_open_files_only()
81-
{
82-
clear_diagnostics_if_needed(session, document.url(), client);
83-
}
84-
}
85-
AnySystemPath::SystemVirtual(virtual_path) => {
86-
if let Some(virtual_file) = db.files().try_virtual_file(virtual_path) {
87-
db.project().close_file(db, virtual_file.file());
88-
virtual_file.close(db);
89-
} else {
90-
tracing::warn!("Salsa virtual file does not exists for {}", virtual_path);
91-
}
92-
93-
// Always clear diagnostics for virtual files, as they don't really exist on disk
94-
// which means closing them is like deleting the file.
95-
clear_diagnostics_if_needed(session, document.url(), client);
96-
}
97-
}
98-
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use lsp_types::{DidCloseNotebookDocumentParams, NotebookDocumentIdentifier};
33

44
use crate::server::Result;
55
use crate::server::api::LSPResult;
6-
use crate::server::api::notifications::did_close::close_document;
76
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
87
use crate::session::Session;
98
use crate::session::client::Client;
@@ -17,7 +16,7 @@ impl NotificationHandler for DidCloseNotebookHandler {
1716
impl SyncNotificationHandler for DidCloseNotebookHandler {
1817
fn run(
1918
session: &mut Session,
20-
client: &Client,
19+
_client: &Client,
2120
params: DidCloseNotebookDocumentParams,
2221
) -> Result<()> {
2322
let DidCloseNotebookDocumentParams {
@@ -29,9 +28,9 @@ impl SyncNotificationHandler for DidCloseNotebookHandler {
2928
.document_handle(&uri)
3029
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
3130

32-
close_document(&document, session, client);
33-
34-
document
31+
// We don't need to call publish any diagnostics because we clear
32+
// the diagnostics when closing the corresponding cell documents.
33+
let _ = document
3534
.close(session)
3635
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
3736

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
use lsp_types::notification::DidOpenTextDocument;
22
use lsp_types::{DidOpenTextDocumentParams, TextDocumentItem};
3-
use ruff_db::Db as _;
4-
use ruff_db::files::system_path_to_file;
5-
use ty_project::Db as _;
6-
use ty_project::watch::{ChangeEvent, CreatedKind};
73

84
use crate::TextDocument;
95
use crate::server::Result;
10-
use crate::server::api::diagnostics::publish_diagnostics;
6+
use crate::server::api::diagnostics::publish_diagnostics_if_needed;
117
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
8+
use crate::session::Session;
129
use crate::session::client::Client;
13-
use crate::session::{DocumentHandle, Session};
14-
use crate::system::AnySystemPath;
1510

1611
pub(crate) struct DidOpenTextDocumentHandler;
1712

@@ -39,49 +34,8 @@ impl SyncNotificationHandler for DidOpenTextDocumentHandler {
3934
TextDocument::new(uri, text, version).with_language_id(&language_id),
4035
);
4136

42-
opened_document(&document, session, client);
37+
publish_diagnostics_if_needed(&document, session, client);
4338

4439
Ok(())
4540
}
4641
}
47-
48-
pub(super) fn opened_document(document: &DocumentHandle, session: &mut Session, client: &Client) {
49-
let path = document.notebook_or_file_path();
50-
51-
// This is a "maybe" because the `File` might've not been interned yet i.e., the
52-
// `try_system` call will return `None` which doesn't mean that the file is new, it's just
53-
// that the server didn't need the file yet.
54-
let is_maybe_new_system_file = path.as_system().is_some_and(|system_path| {
55-
let db = session.project_db(path);
56-
db.files()
57-
.try_system(db, system_path)
58-
.is_none_or(|file| !file.exists(db))
59-
});
60-
61-
match path {
62-
AnySystemPath::System(system_path) => {
63-
let event = if is_maybe_new_system_file {
64-
ChangeEvent::Created {
65-
path: system_path.clone(),
66-
kind: CreatedKind::File,
67-
}
68-
} else {
69-
ChangeEvent::Opened(system_path.clone())
70-
};
71-
session.apply_changes(path, vec![event]);
72-
73-
let db = session.project_db_mut(path);
74-
match system_path_to_file(db, system_path) {
75-
Ok(file) => db.project().open_file(db, file),
76-
Err(err) => tracing::warn!("Failed to open file {system_path}: {err}"),
77-
}
78-
}
79-
AnySystemPath::SystemVirtual(virtual_path) => {
80-
let db = session.project_db_mut(path);
81-
let virtual_file = db.files().virtual_file(db, virtual_path);
82-
db.project().open_file(db, virtual_file.file());
83-
}
84-
}
85-
86-
publish_diagnostics(session, document, client);
87-
}

0 commit comments

Comments
 (0)