Skip to content

Commit afeb65c

Browse files
committed
Implement basic red-knot server
1 parent 733341a commit afeb65c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+4820
-3
lines changed

Cargo.lock

Lines changed: 30 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ruff_workspace = { path = "crates/ruff_workspace" }
3737

3838
red_knot_module_resolver = { path = "crates/red_knot_module_resolver" }
3939
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
40+
red_knot_server = { path = "crates/red_knot_server" }
4041
red_knot_workspace = { path = "crates/red_knot_workspace" }
4142

4243
aho-corasick = { version = "1.1.3" }

crates/red_knot/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "red_knot"
3-
version = "0.0.0"
3+
version = "0.5.5"
44
edition.workspace = true
55
rust-version.workspace = true
66
homepage.workspace = true
@@ -14,6 +14,7 @@ license.workspace = true
1414
[dependencies]
1515
red_knot_module_resolver = { workspace = true }
1616
red_knot_workspace = { workspace = true }
17+
red_knot_server = { workspace = true }
1718

1819
ruff_db = { workspace = true, features = ["os", "cache"] }
1920

crates/red_knot/src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::num::NonZeroUsize;
12
use std::sync::Mutex;
23

34
use clap::Parser;
@@ -29,6 +30,9 @@ mod cli;
2930
)]
3031
#[command(version)]
3132
struct Args {
33+
#[command(subcommand)]
34+
pub(crate) command: Command,
35+
3236
#[arg(
3337
long,
3438
help = "Changes the current working directory.",
@@ -65,6 +69,11 @@ struct Args {
6569
watch: bool,
6670
}
6771

72+
#[derive(Debug, clap::Subcommand)]
73+
pub enum Command {
74+
Server,
75+
}
76+
6877
#[allow(
6978
clippy::print_stdout,
7079
clippy::unnecessary_wraps,
@@ -73,6 +82,7 @@ struct Args {
7382
)]
7483
pub fn main() -> anyhow::Result<()> {
7584
let Args {
85+
command,
7686
current_directory,
7787
custom_typeshed_dir,
7888
extra_search_path: extra_paths,
@@ -83,6 +93,18 @@ pub fn main() -> anyhow::Result<()> {
8393

8494
let verbosity = verbosity.level();
8595
countme::enable(verbosity == Some(VerbosityLevel::Trace));
96+
97+
if matches!(command, Command::Server) {
98+
let four = NonZeroUsize::new(4).unwrap();
99+
100+
// by default, we set the number of worker threads to `num_cpus`, with a maximum of 4.
101+
let worker_threads = std::thread::available_parallelism()
102+
.unwrap_or(four)
103+
.max(four);
104+
105+
return red_knot_server::Server::new(worker_threads, None)?.run();
106+
}
107+
86108
setup_tracing(verbosity);
87109

88110
let cwd = if let Some(cwd) = current_directory {

crates/red_knot_module_resolver/src/db.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ pub(crate) mod tests {
7474
&self.system
7575
}
7676

77+
fn system_mut(&mut self) -> &mut dyn ruff_db::system::System {
78+
&mut self.system
79+
}
80+
7781
fn files(&self) -> &Files {
7882
&self.files
7983
}

crates/red_knot_python_semantic/src/db.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub(crate) mod tests {
7777
&self.system
7878
}
7979

80+
fn system_mut(&mut self) -> &mut dyn System {
81+
&mut self.system
82+
}
83+
8084
fn files(&self) -> &Files {
8185
&self.files
8286
}

crates/red_knot_server/Cargo.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[package]
2+
name = "red_knot_server"
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+
red_knot_workspace = { workspace = true }
15+
ruff_db = { workspace = true }
16+
ruff_linter = { workspace = true }
17+
ruff_notebook = { workspace = true }
18+
ruff_python_ast = { workspace = true }
19+
ruff_source_file = { workspace = true }
20+
ruff_text_size = { workspace = true }
21+
ruff_workspace = { workspace = true }
22+
23+
anyhow = { workspace = true }
24+
crossbeam = { workspace = true }
25+
ignore = { workspace = true }
26+
jod-thread = { workspace = true }
27+
lsp-server = { workspace = true }
28+
lsp-types = { workspace = true }
29+
rustc-hash = { workspace = true }
30+
salsa = { workspace = true }
31+
serde = { workspace = true }
32+
serde_json = { workspace = true }
33+
shellexpand = { workspace = true }
34+
tracing = { workspace = true }
35+
tracing-subscriber = { workspace = true }
36+
37+
[dev-dependencies]
38+
39+
[target.'cfg(target_vendor = "apple")'.dependencies]
40+
libc = { workspace = true }
41+
42+
[lints]
43+
workspace = true

crates/red_knot_server/src/edit.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Types and utilities for working with text, modifying source files, and `Ruff <-> LSP` type conversion.
2+
3+
mod notebook;
4+
mod range;
5+
mod text_document;
6+
7+
use lsp_types::{PositionEncodingKind, Url};
8+
pub use notebook::NotebookDocument;
9+
pub(crate) use range::RangeExt;
10+
pub(crate) use text_document::DocumentVersion;
11+
pub use text_document::TextDocument;
12+
13+
/// A convenient enumeration for supported text encodings. Can be converted to [`lsp_types::PositionEncodingKind`].
14+
// Please maintain the order from least to greatest priority for the derived `Ord` impl.
15+
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
16+
pub enum PositionEncoding {
17+
/// UTF 16 is the encoding supported by all LSP clients.
18+
#[default]
19+
UTF16,
20+
21+
/// Second choice because UTF32 uses a fixed 4 byte encoding for each character (makes conversion relatively easy)
22+
UTF32,
23+
24+
/// Ruff's preferred encoding
25+
UTF8,
26+
}
27+
28+
/// A unique document ID, derived from a URL passed as part of an LSP request.
29+
/// This document ID can point to either be a standalone Python file, a full notebook, or a cell within a notebook.
30+
#[derive(Clone, Debug)]
31+
pub enum DocumentKey {
32+
Notebook(Url),
33+
NotebookCell(Url),
34+
Text(Url),
35+
}
36+
37+
impl DocumentKey {
38+
/// Returns the URL associated with the key.
39+
pub(crate) fn url(&self) -> &Url {
40+
match self {
41+
DocumentKey::NotebookCell(url)
42+
| DocumentKey::Notebook(url)
43+
| DocumentKey::Text(url) => url,
44+
}
45+
}
46+
}
47+
48+
impl std::fmt::Display for DocumentKey {
49+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50+
match self {
51+
Self::NotebookCell(url) | Self::Notebook(url) | Self::Text(url) => url.fmt(f),
52+
}
53+
}
54+
}
55+
56+
impl From<PositionEncoding> for lsp_types::PositionEncodingKind {
57+
fn from(value: PositionEncoding) -> Self {
58+
match value {
59+
PositionEncoding::UTF8 => lsp_types::PositionEncodingKind::UTF8,
60+
PositionEncoding::UTF16 => lsp_types::PositionEncodingKind::UTF16,
61+
PositionEncoding::UTF32 => lsp_types::PositionEncodingKind::UTF32,
62+
}
63+
}
64+
}
65+
66+
impl TryFrom<&lsp_types::PositionEncodingKind> for PositionEncoding {
67+
type Error = ();
68+
69+
fn try_from(value: &PositionEncodingKind) -> Result<Self, Self::Error> {
70+
Ok(if value == &PositionEncodingKind::UTF8 {
71+
PositionEncoding::UTF8
72+
} else if value == &PositionEncodingKind::UTF16 {
73+
PositionEncoding::UTF16
74+
} else if value == &PositionEncodingKind::UTF32 {
75+
PositionEncoding::UTF32
76+
} else {
77+
return Err(());
78+
})
79+
}
80+
}

0 commit comments

Comments
 (0)