diff --git a/Cargo.lock b/Cargo.lock index ccc986e17a..f0e54ece63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2503,6 +2503,17 @@ dependencies = [ "wild", ] +[[package]] +name = "red_knot_ide" +version = "0.0.0" +dependencies = [ + "red_knot_python_semantic", + "red_knot_vendored", + "ruff_db", + "salsa", + "tracing", +] + [[package]] name = "red_knot_project" version = "0.0.0" @@ -2514,6 +2525,7 @@ dependencies = [ "notify", "pep440_rs", "rayon", + "red_knot_ide", "red_knot_python_semantic", "red_knot_vendored", "ruff_cache", diff --git a/Cargo.toml b/Cargo.toml index 0efc208c99..3bfcef5067 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,10 +38,11 @@ ruff_text_size = { path = "crates/ruff_text_size" } red_knot_vendored = { path = "crates/red_knot_vendored" } ruff_workspace = { path = "crates/ruff_workspace" } +red_knot_ide = { path = "crates/red_knot_ide" } +red_knot_project = { path = "crates/red_knot_project", default-features = false } red_knot_python_semantic = { path = "crates/red_knot_python_semantic" } red_knot_server = { path = "crates/red_knot_server" } red_knot_test = { path = "crates/red_knot_test" } -red_knot_project = { path = "crates/red_knot_project", default-features = false } aho-corasick = { version = "1.1.3" } anstream = { version = "0.6.18" } diff --git a/crates/red_knot_ide/Cargo.toml b/crates/red_knot_ide/Cargo.toml new file mode 100644 index 0000000000..184154a9b1 --- /dev/null +++ b/crates/red_knot_ide/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "red_knot_ide" +version = "0.0.0" +publish = false +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +ruff_db = { workspace = true } +red_knot_python_semantic = { workspace = true } + +salsa = { workspace = true } +tracing = { workspace = true } + +[dev-dependencies] +red_knot_vendored = { workspace = true } + +[lints] +workspace = true diff --git a/crates/red_knot_ide/src/db.rs b/crates/red_knot_ide/src/db.rs new file mode 100644 index 0000000000..fff5b7d7dc --- /dev/null +++ b/crates/red_knot_ide/src/db.rs @@ -0,0 +1,124 @@ +use red_knot_python_semantic::Db as SemanticDb; +use ruff_db::{Db as SourceDb, Upcast}; + +#[salsa::db] +pub trait Db: SemanticDb + Upcast {} + +#[cfg(test)] +pub(crate) mod tests { + use std::sync::Arc; + + use super::Db; + use red_knot_python_semantic::lint::{LintRegistry, RuleSelection}; + use red_knot_python_semantic::{default_lint_registry, Db as SemanticDb}; + use ruff_db::files::{File, Files}; + use ruff_db::system::{DbWithTestSystem, System, TestSystem}; + use ruff_db::vendored::VendoredFileSystem; + use ruff_db::{Db as SourceDb, Upcast}; + + #[salsa::db] + #[derive(Clone)] + pub(crate) struct TestDb { + storage: salsa::Storage, + files: Files, + system: TestSystem, + vendored: VendoredFileSystem, + events: Arc>>, + rule_selection: Arc, + } + + #[allow(dead_code)] + impl TestDb { + pub(crate) fn new() -> Self { + Self { + storage: salsa::Storage::default(), + system: TestSystem::default(), + vendored: red_knot_vendored::file_system().clone(), + events: Arc::default(), + files: Files::default(), + rule_selection: Arc::new(RuleSelection::from_registry(default_lint_registry())), + } + } + + /// Takes the salsa events. + /// + /// ## Panics + /// If there are any pending salsa snapshots. + pub(crate) fn take_salsa_events(&mut self) -> Vec { + let inner = Arc::get_mut(&mut self.events).expect("no pending salsa snapshots"); + + let events = inner.get_mut().unwrap(); + std::mem::take(&mut *events) + } + + /// Clears the salsa events. + /// + /// ## Panics + /// If there are any pending salsa snapshots. + pub(crate) fn clear_salsa_events(&mut self) { + self.take_salsa_events(); + } + } + + impl DbWithTestSystem for TestDb { + fn test_system(&self) -> &TestSystem { + &self.system + } + + fn test_system_mut(&mut self) -> &mut TestSystem { + &mut self.system + } + } + + #[salsa::db] + impl SourceDb for TestDb { + fn vendored(&self) -> &VendoredFileSystem { + &self.vendored + } + + fn system(&self) -> &dyn System { + &self.system + } + + fn files(&self) -> &Files { + &self.files + } + } + + impl Upcast for TestDb { + fn upcast(&self) -> &(dyn SourceDb + 'static) { + self + } + fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) { + self + } + } + + #[salsa::db] + impl SemanticDb for TestDb { + fn is_file_open(&self, file: File) -> bool { + !file.path(self).is_vendored_path() + } + + fn rule_selection(&self) -> Arc { + self.rule_selection.clone() + } + + fn lint_registry(&self) -> &LintRegistry { + default_lint_registry() + } + } + + #[salsa::db] + impl Db for TestDb {} + + #[salsa::db] + impl salsa::Database for TestDb { + fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) { + let event = event(); + tracing::trace!("event: {event:?}"); + let mut events = self.events.lock().unwrap(); + events.push(event); + } + } +} diff --git a/crates/red_knot_ide/src/lib.rs b/crates/red_knot_ide/src/lib.rs new file mode 100644 index 0000000000..6f53e6094d --- /dev/null +++ b/crates/red_knot_ide/src/lib.rs @@ -0,0 +1,3 @@ +mod db; + +pub use db::Db; diff --git a/crates/red_knot_project/Cargo.toml b/crates/red_knot_project/Cargo.toml index fc2752a1e6..3fef70dc8d 100644 --- a/crates/red_knot_project/Cargo.toml +++ b/crates/red_knot_project/Cargo.toml @@ -17,6 +17,7 @@ ruff_db = { workspace = true, features = ["cache", "serde"] } ruff_macros = { workspace = true } ruff_python_ast = { workspace = true, features = ["serde"] } ruff_text_size = { workspace = true } +red_knot_ide = { workspace = true } red_knot_python_semantic = { workspace = true, features = ["serde"] } red_knot_vendored = { workspace = true } diff --git a/crates/red_knot_project/src/db.rs b/crates/red_knot_project/src/db.rs index 6b941fe71d..1acbee6504 100644 --- a/crates/red_knot_project/src/db.rs +++ b/crates/red_knot_project/src/db.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use crate::DEFAULT_LINT_REGISTRY; use crate::{Project, ProjectMetadata}; +use red_knot_ide::Db as IdeDb; use red_knot_python_semantic::lint::{LintRegistry, RuleSelection}; use red_knot_python_semantic::{Db as SemanticDb, Program}; use ruff_db::diagnostic::OldDiagnosticTrait; @@ -103,6 +104,19 @@ impl Upcast for ProjectDatabase { } } +impl Upcast for ProjectDatabase { + fn upcast(&self) -> &(dyn IdeDb + 'static) { + self + } + + fn upcast_mut(&mut self) -> &mut (dyn IdeDb + 'static) { + self + } +} + +#[salsa::db] +impl IdeDb for ProjectDatabase {} + #[salsa::db] impl SemanticDb for ProjectDatabase { fn is_file_open(&self, file: File) -> bool { diff --git a/crates/red_knot_server/Cargo.toml b/crates/red_knot_server/Cargo.toml index f14aa19350..d38ca95e3d 100644 --- a/crates/red_knot_server/Cargo.toml +++ b/crates/red_knot_server/Cargo.toml @@ -12,6 +12,7 @@ license = { workspace = true } [dependencies] red_knot_project = { workspace = true } + ruff_db = { workspace = true, features = ["os"] } ruff_notebook = { workspace = true } ruff_python_ast = { workspace = true }