diff --git a/crates/red_knot_server/src/server/api.rs b/crates/red_knot_server/src/server/api.rs index e6a65823c60b07..f1a838cba44e3b 100644 --- a/crates/red_knot_server/src/server/api.rs +++ b/crates/red_knot_server/src/server/api.rs @@ -7,7 +7,6 @@ mod requests; mod traits; use notifications as notification; -use red_knot_workspace::db::RootDatabase; use requests as request; use self::traits::{NotificationHandler, RequestHandler}; @@ -85,9 +84,10 @@ fn background_request_task<'a, R: traits::BackgroundDocumentRequestHandler>( let Ok(path) = url_to_system_path(&url) else { return Box::new(|_, _| {}); }; - let db = session - .workspace_db_for_path(path.as_std_path()) - .map(RootDatabase::snapshot); + let db = match session.workspace_db_for_path(path.as_std_path()) { + Some(db) => db.snapshot(), + None => session.default_workspace_db().snapshot(), + }; let Some(snapshot) = session.take_snapshot(url) else { return Box::new(|_, _| {}); diff --git a/crates/red_knot_server/src/server/api/notifications/did_close.rs b/crates/red_knot_server/src/server/api/notifications/did_close.rs index 3979957b0cf3e2..cccc4f8337481d 100644 --- a/crates/red_knot_server/src/server/api/notifications/did_close.rs +++ b/crates/red_knot_server/src/server/api/notifications/did_close.rs @@ -2,8 +2,6 @@ use lsp_server::ErrorCode; use lsp_types::notification::DidCloseTextDocument; use lsp_types::DidCloseTextDocumentParams; -use ruff_db::files::File; - use crate::server::api::diagnostics::clear_diagnostics; use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler}; use crate::server::api::LSPResult; @@ -25,7 +23,7 @@ impl SyncNotificationHandler for DidCloseTextDocumentHandler { _requester: &mut Requester, params: DidCloseTextDocumentParams, ) -> Result<()> { - let Ok(path) = url_to_system_path(¶ms.text_document.uri) else { + let Ok(_path) = url_to_system_path(¶ms.text_document.uri) else { return Ok(()); }; @@ -34,10 +32,6 @@ impl SyncNotificationHandler for DidCloseTextDocumentHandler { .close_document(&key) .with_failure_code(ErrorCode::InternalError)?; - if let Some(db) = session.workspace_db_for_path_mut(path.as_std_path()) { - File::sync_path(db, &path); - } - clear_diagnostics(key.url(), ¬ifier)?; Ok(()) diff --git a/crates/red_knot_server/src/server/api/notifications/did_close_notebook.rs b/crates/red_knot_server/src/server/api/notifications/did_close_notebook.rs index e0cfeb16f3741b..e136258ea4f925 100644 --- a/crates/red_knot_server/src/server/api/notifications/did_close_notebook.rs +++ b/crates/red_knot_server/src/server/api/notifications/did_close_notebook.rs @@ -1,8 +1,6 @@ use lsp_types::notification::DidCloseNotebookDocument; use lsp_types::DidCloseNotebookDocumentParams; -use ruff_db::files::File; - use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler}; use crate::server::api::LSPResult; use crate::server::client::{Notifier, Requester}; @@ -23,7 +21,7 @@ impl SyncNotificationHandler for DidCloseNotebookHandler { _requester: &mut Requester, params: DidCloseNotebookDocumentParams, ) -> Result<()> { - let Ok(path) = url_to_system_path(¶ms.notebook_document.uri) else { + let Ok(_path) = url_to_system_path(¶ms.notebook_document.uri) else { return Ok(()); }; @@ -32,10 +30,6 @@ impl SyncNotificationHandler for DidCloseNotebookHandler { .close_document(&key) .with_failure_code(lsp_server::ErrorCode::InternalError)?; - if let Some(db) = session.workspace_db_for_path_mut(path.as_std_path()) { - File::sync_path(db, &path); - } - Ok(()) } } diff --git a/crates/red_knot_server/src/server/api/notifications/did_open.rs b/crates/red_knot_server/src/server/api/notifications/did_open.rs index 31312cf1ce7018..ceb022b6f3f6ec 100644 --- a/crates/red_knot_server/src/server/api/notifications/did_open.rs +++ b/crates/red_knot_server/src/server/api/notifications/did_open.rs @@ -1,7 +1,7 @@ use lsp_types::notification::DidOpenTextDocument; use lsp_types::DidOpenTextDocumentParams; -use ruff_db::files::system_path_to_file; +use red_knot_workspace::watch::ChangeEvent; use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler}; use crate::server::client::{Notifier, Requester}; @@ -30,11 +30,11 @@ impl SyncNotificationHandler for DidOpenTextDocumentHandler { let document = TextDocument::new(params.text_document.text, params.text_document.version); session.open_text_document(params.text_document.uri, document); - if let Some(db) = session.workspace_db_for_path_mut(path.as_std_path()) { - // TODO(dhruvmanila): Store the `file` in `DocumentController` - let file = system_path_to_file(db, &path).unwrap(); - file.sync(db); - } + let db = match session.workspace_db_for_path_mut(path.as_std_path()) { + Some(db) => db, + None => session.default_workspace_db_mut(), + }; + db.apply_changes(vec![ChangeEvent::file_created(path)]); // TODO(dhruvmanila): Publish diagnostics if the client doesn't support pull diagnostics diff --git a/crates/red_knot_server/src/server/api/notifications/did_open_notebook.rs b/crates/red_knot_server/src/server/api/notifications/did_open_notebook.rs index b347bb0da201c9..d463d2be6bac66 100644 --- a/crates/red_knot_server/src/server/api/notifications/did_open_notebook.rs +++ b/crates/red_knot_server/src/server/api/notifications/did_open_notebook.rs @@ -2,7 +2,7 @@ use lsp_server::ErrorCode; use lsp_types::notification::DidOpenNotebookDocument; use lsp_types::DidOpenNotebookDocumentParams; -use ruff_db::files::system_path_to_file; +use red_knot_workspace::watch::ChangeEvent; use crate::edit::NotebookDocument; use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler}; @@ -38,11 +38,11 @@ impl SyncNotificationHandler for DidOpenNotebookHandler { .with_failure_code(ErrorCode::InternalError)?; session.open_notebook_document(params.notebook_document.uri.clone(), notebook); - if let Some(db) = session.workspace_db_for_path_mut(path.as_std_path()) { - // TODO(dhruvmanila): Store the `file` in `DocumentController` - let file = system_path_to_file(db, &path).unwrap(); - file.sync(db); - } + let db = match session.workspace_db_for_path_mut(path.as_std_path()) { + Some(db) => db, + None => session.default_workspace_db_mut(), + }; + db.apply_changes(vec![ChangeEvent::file_created(path)]); // TODO(dhruvmanila): Publish diagnostics if the client doesn't support pull diagnostics diff --git a/crates/red_knot_server/src/server/api/requests/diagnostic.rs b/crates/red_knot_server/src/server/api/requests/diagnostic.rs index bae3ec50c6bc61..44998b435e591b 100644 --- a/crates/red_knot_server/src/server/api/requests/diagnostic.rs +++ b/crates/red_knot_server/src/server/api/requests/diagnostic.rs @@ -26,13 +26,11 @@ impl BackgroundDocumentRequestHandler for DocumentDiagnosticRequestHandler { fn run_with_snapshot( snapshot: DocumentSnapshot, - db: Option, + db: RootDatabase, _notifier: Notifier, _params: DocumentDiagnosticParams, ) -> Result { - let diagnostics = db - .map(|db| compute_diagnostics(&snapshot, &db)) - .unwrap_or_default(); + let diagnostics = compute_diagnostics(&snapshot, &db); Ok(DocumentDiagnosticReportResult::Report( DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { @@ -66,11 +64,11 @@ fn to_lsp_diagnostic(message: &str) -> Diagnostic { let (range, message) = match words.as_slice() { [_filename, line, column, message] => { - let line = line.parse::().unwrap_or_default(); + let line = line.parse::().unwrap_or_default().saturating_sub(1); let column = column.parse::().unwrap_or_default(); ( Range::new( - Position::new(line.saturating_sub(1), column.saturating_sub(1)), + Position::new(line, column.saturating_sub(1)), Position::new(line, column), ), message.trim(), diff --git a/crates/red_knot_server/src/server/api/traits.rs b/crates/red_knot_server/src/server/api/traits.rs index be539e6554f129..03006d0abba14a 100644 --- a/crates/red_knot_server/src/server/api/traits.rs +++ b/crates/red_knot_server/src/server/api/traits.rs @@ -34,7 +34,7 @@ pub(super) trait BackgroundDocumentRequestHandler: RequestHandler { fn run_with_snapshot( snapshot: DocumentSnapshot, - db: Option, + db: RootDatabase, notifier: Notifier, params: <::RequestType as Request>::Params, ) -> super::Result<<::RequestType as Request>::Result>; diff --git a/crates/red_knot_server/src/session.rs b/crates/red_knot_server/src/session.rs index 89f813588a401a..6fe1607ff316b2 100644 --- a/crates/red_knot_server/src/session.rs +++ b/crates/red_knot_server/src/session.rs @@ -82,6 +82,8 @@ impl Session { }) } + /// Returns a reference to the workspace [`RootDatabase`] corresponding to the given path, if + /// any. pub(crate) fn workspace_db_for_path(&self, path: impl AsRef) -> Option<&RootDatabase> { self.workspaces .range(..=path.as_ref().to_path_buf()) @@ -89,6 +91,8 @@ impl Session { .map(|(_, db)| db) } + /// Returns a mutable reference to the workspace [`RootDatabase`] corresponding to the given + /// path, if any. pub(crate) fn workspace_db_for_path_mut( &mut self, path: impl AsRef, @@ -99,6 +103,19 @@ impl Session { .map(|(_, db)| db) } + /// Returns a reference to the default workspace [`RootDatabase`]. The default workspace is the + /// minimum root path in the workspace map. + pub(crate) fn default_workspace_db(&self) -> &RootDatabase { + // SAFETY: There is always at least one workspace. + self.workspaces.values().next().unwrap() + } + + /// Returns a mutable reference to the default workspace [`RootDatabase`]. + pub(crate) fn default_workspace_db_mut(&mut self) -> &mut RootDatabase { + // SAFETY: There is always at least one workspace. + self.workspaces.values_mut().next().unwrap() + } + pub fn key_from_url(&self, url: Url) -> DocumentKey { self.index().key_from_url(url) } diff --git a/crates/red_knot_workspace/src/watch.rs b/crates/red_knot_workspace/src/watch.rs index f59c0a81be51d9..c8588b42f53b3a 100644 --- a/crates/red_knot_workspace/src/watch.rs +++ b/crates/red_knot_workspace/src/watch.rs @@ -46,6 +46,16 @@ pub enum ChangeEvent { } impl ChangeEvent { + /// Creates a new [`Created`] event for a file at the given path. + /// + /// [`Created`]: ChangeEvent::Created + pub fn file_created(path: SystemPathBuf) -> ChangeEvent { + ChangeEvent::Created { + path, + kind: CreatedKind::File, + } + } + pub fn file_name(&self) -> Option<&str> { self.path().and_then(|path| path.file_name()) }