Skip to content

Commit b57c62e

Browse files
authored
[red-knot] IDE crate (#17045)
## Summary This PR adds a new but so far empty and unused `red_knot_ide` crate. This new crate's purpose is to implement IDE-specific functionality, such as go to definition, hover, completion, etc., which are used by both the LSP and the playground. The crate itself doesn't depend on `lsptypes`. The idea is that the facade crates (e.g., `red_knot_server`) convert external to internal types. Not only allows this to share the logic between server and playground, it also ensures that the core functionality is easier to test because it can be tested without needing a full LSP. ## Test Plan `cargo build`
1 parent a9dbfeb commit b57c62e

File tree

8 files changed

+181
-1
lines changed

8 files changed

+181
-1
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ ruff_text_size = { path = "crates/ruff_text_size" }
3838
red_knot_vendored = { path = "crates/red_knot_vendored" }
3939
ruff_workspace = { path = "crates/ruff_workspace" }
4040

41+
red_knot_ide = { path = "crates/red_knot_ide" }
42+
red_knot_project = { path = "crates/red_knot_project", default-features = false }
4143
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
4244
red_knot_server = { path = "crates/red_knot_server" }
4345
red_knot_test = { path = "crates/red_knot_test" }
44-
red_knot_project = { path = "crates/red_knot_project", default-features = false }
4546

4647
aho-corasick = { version = "1.1.3" }
4748
anstream = { version = "0.6.18" }

crates/red_knot_ide/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "red_knot_ide"
3+
version = "0.0.0"
4+
publish = false
5+
authors = { workspace = true }
6+
edition = { workspace = true }
7+
rust-version = { workspace = true }
8+
homepage = { workspace = true }
9+
documentation = { workspace = true }
10+
repository = { workspace = true }
11+
license = { workspace = true }
12+
13+
[dependencies]
14+
ruff_db = { workspace = true }
15+
red_knot_python_semantic = { workspace = true }
16+
17+
salsa = { workspace = true }
18+
tracing = { workspace = true }
19+
20+
[dev-dependencies]
21+
red_knot_vendored = { workspace = true }
22+
23+
[lints]
24+
workspace = true

crates/red_knot_ide/src/db.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use red_knot_python_semantic::Db as SemanticDb;
2+
use ruff_db::{Db as SourceDb, Upcast};
3+
4+
#[salsa::db]
5+
pub trait Db: SemanticDb + Upcast<dyn SourceDb> {}
6+
7+
#[cfg(test)]
8+
pub(crate) mod tests {
9+
use std::sync::Arc;
10+
11+
use super::Db;
12+
use red_knot_python_semantic::lint::{LintRegistry, RuleSelection};
13+
use red_knot_python_semantic::{default_lint_registry, Db as SemanticDb};
14+
use ruff_db::files::{File, Files};
15+
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
16+
use ruff_db::vendored::VendoredFileSystem;
17+
use ruff_db::{Db as SourceDb, Upcast};
18+
19+
#[salsa::db]
20+
#[derive(Clone)]
21+
pub(crate) struct TestDb {
22+
storage: salsa::Storage<Self>,
23+
files: Files,
24+
system: TestSystem,
25+
vendored: VendoredFileSystem,
26+
events: Arc<std::sync::Mutex<Vec<salsa::Event>>>,
27+
rule_selection: Arc<RuleSelection>,
28+
}
29+
30+
#[allow(dead_code)]
31+
impl TestDb {
32+
pub(crate) fn new() -> Self {
33+
Self {
34+
storage: salsa::Storage::default(),
35+
system: TestSystem::default(),
36+
vendored: red_knot_vendored::file_system().clone(),
37+
events: Arc::default(),
38+
files: Files::default(),
39+
rule_selection: Arc::new(RuleSelection::from_registry(default_lint_registry())),
40+
}
41+
}
42+
43+
/// Takes the salsa events.
44+
///
45+
/// ## Panics
46+
/// If there are any pending salsa snapshots.
47+
pub(crate) fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
48+
let inner = Arc::get_mut(&mut self.events).expect("no pending salsa snapshots");
49+
50+
let events = inner.get_mut().unwrap();
51+
std::mem::take(&mut *events)
52+
}
53+
54+
/// Clears the salsa events.
55+
///
56+
/// ## Panics
57+
/// If there are any pending salsa snapshots.
58+
pub(crate) fn clear_salsa_events(&mut self) {
59+
self.take_salsa_events();
60+
}
61+
}
62+
63+
impl DbWithTestSystem for TestDb {
64+
fn test_system(&self) -> &TestSystem {
65+
&self.system
66+
}
67+
68+
fn test_system_mut(&mut self) -> &mut TestSystem {
69+
&mut self.system
70+
}
71+
}
72+
73+
#[salsa::db]
74+
impl SourceDb for TestDb {
75+
fn vendored(&self) -> &VendoredFileSystem {
76+
&self.vendored
77+
}
78+
79+
fn system(&self) -> &dyn System {
80+
&self.system
81+
}
82+
83+
fn files(&self) -> &Files {
84+
&self.files
85+
}
86+
}
87+
88+
impl Upcast<dyn SourceDb> for TestDb {
89+
fn upcast(&self) -> &(dyn SourceDb + 'static) {
90+
self
91+
}
92+
fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) {
93+
self
94+
}
95+
}
96+
97+
#[salsa::db]
98+
impl SemanticDb for TestDb {
99+
fn is_file_open(&self, file: File) -> bool {
100+
!file.path(self).is_vendored_path()
101+
}
102+
103+
fn rule_selection(&self) -> Arc<RuleSelection> {
104+
self.rule_selection.clone()
105+
}
106+
107+
fn lint_registry(&self) -> &LintRegistry {
108+
default_lint_registry()
109+
}
110+
}
111+
112+
#[salsa::db]
113+
impl Db for TestDb {}
114+
115+
#[salsa::db]
116+
impl salsa::Database for TestDb {
117+
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
118+
let event = event();
119+
tracing::trace!("event: {event:?}");
120+
let mut events = self.events.lock().unwrap();
121+
events.push(event);
122+
}
123+
}
124+
}

crates/red_knot_ide/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod db;
2+
3+
pub use db::Db;

crates/red_knot_project/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ruff_db = { workspace = true, features = ["cache", "serde"] }
1717
ruff_macros = { workspace = true }
1818
ruff_python_ast = { workspace = true, features = ["serde"] }
1919
ruff_text_size = { workspace = true }
20+
red_knot_ide = { workspace = true }
2021
red_knot_python_semantic = { workspace = true, features = ["serde"] }
2122
red_knot_vendored = { workspace = true }
2223

crates/red_knot_project/src/db.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::sync::Arc;
33

44
use crate::DEFAULT_LINT_REGISTRY;
55
use crate::{Project, ProjectMetadata};
6+
use red_knot_ide::Db as IdeDb;
67
use red_knot_python_semantic::lint::{LintRegistry, RuleSelection};
78
use red_knot_python_semantic::{Db as SemanticDb, Program};
89
use ruff_db::diagnostic::OldDiagnosticTrait;
@@ -103,6 +104,19 @@ impl Upcast<dyn SourceDb> for ProjectDatabase {
103104
}
104105
}
105106

107+
impl Upcast<dyn IdeDb> for ProjectDatabase {
108+
fn upcast(&self) -> &(dyn IdeDb + 'static) {
109+
self
110+
}
111+
112+
fn upcast_mut(&mut self) -> &mut (dyn IdeDb + 'static) {
113+
self
114+
}
115+
}
116+
117+
#[salsa::db]
118+
impl IdeDb for ProjectDatabase {}
119+
106120
#[salsa::db]
107121
impl SemanticDb for ProjectDatabase {
108122
fn is_file_open(&self, file: File) -> bool {

crates/red_knot_server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ license = { workspace = true }
1212

1313
[dependencies]
1414
red_knot_project = { workspace = true }
15+
1516
ruff_db = { workspace = true, features = ["os"] }
1617
ruff_notebook = { workspace = true }
1718
ruff_python_ast = { workspace = true }

0 commit comments

Comments
 (0)