Skip to content

Commit 1ff5b2c

Browse files
committed
Auto merge of rust-lang#12251 - matklad:lsp-server, r=matklad
internal: vendor lsp-server
2 parents 50ed1a5 + 57cb65b commit 1ff5b2c

File tree

18 files changed

+946
-9
lines changed

18 files changed

+946
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-def/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ itertools = "0.10.3"
2626
indexmap = "1.8.0"
2727
smallvec = "1.8.0"
2828
arrayvec = "0.7.2"
29-
la-arena = { version = "0.3.0", path = "../../lib/arena" }
29+
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
3030

3131
stdx = { path = "../stdx", version = "0.0.0" }
3232
base-db = { path = "../base-db", version = "0.0.0" }

crates/hir-expand/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ cov-mark = "2.0.0-pre.1"
1414
tracing = "0.1.32"
1515
either = "1.6.1"
1616
rustc-hash = "1.1.0"
17-
la-arena = { version = "0.3.0", path = "../../lib/arena" }
17+
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
1818
itertools = "0.10.3"
1919
hashbrown = { version = "0.12.0", features = [
2020
"inline-more",

crates/hir-ty/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ scoped-tls = "1.0.0"
2121
chalk-solve = { version = "0.82.0", default-features = false }
2222
chalk-ir = "0.82.0"
2323
chalk-recursive = { version = "0.82.0", default-features = false }
24-
la-arena = { version = "0.3.0", path = "../../lib/arena" }
24+
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
2525
once_cell = "1.10.0"
2626
typed-arena = "2.0.1"
2727

crates/profile/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ doctest = false
1313
once_cell = "1.10.0"
1414
cfg-if = "1.0.0"
1515
libc = "0.2.121"
16-
la-arena = { version = "0.3.0", path = "../../lib/arena" }
16+
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
1717
countme = { version = "3.0.1", features = ["enable"] }
1818
jemalloc-ctl = { version = "0.4.2", package = "tikv-jemalloc-ctl", optional = true }
1919

crates/project-model/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ serde = { version = "1.0.136", features = ["derive"] }
1818
serde_json = "1.0.79"
1919
anyhow = "1.0.56"
2020
expect-test = "1.2.2"
21-
la-arena = { version = "0.3.0", path = "../../lib/arena" }
21+
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
2222

2323
cfg = { path = "../cfg", version = "0.0.0" }
2424
base-db = { path = "../base-db", version = "0.0.0" }

crates/rust-analyzer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ threadpool = "1.8.1"
3333
rayon = "1.5.1"
3434
num_cpus = "1.13.1"
3535
mimalloc = { version = "0.1.28", default-features = false, optional = true }
36-
lsp-server = "0.6.0"
36+
lsp-server = { version = "0.6.0", path = "../../lib/lsp-server" }
3737
tracing = "0.1.32"
3838
tracing-subscriber = { version = "0.3.9", default-features = false, features = [
3939
"env-filter",

lib/arena/Cargo.toml renamed to lib/la-arena/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "la-arena"
33
version = "0.3.0"
44
description = "Simple index-based arena without deletion."
55
license = "MIT OR Apache-2.0"
6-
repository = "https://github.com/rust-lang/rust-analyzer"
6+
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/la-arena"
77
documentation = "https://docs.rs/la-arena"
88
categories = ["data-structures", "memory-management", "rust-patterns"]
99
edition = "2021"
File renamed without changes.
File renamed without changes.

lib/lsp-server/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "lsp-server"
3+
version = "0.6.0"
4+
description = "Generic LSP server scaffold."
5+
license = "MIT OR Apache-2.0"
6+
repository = "https://github.com/rust-analyzer/rust-analyzer/tree/master/lib/lsp-server"
7+
edition = "2021"
8+
9+
[dependencies]
10+
log = "0.4.3"
11+
serde_json = "1.0.34"
12+
serde = { version = "1.0.83", features = ["derive"] }
13+
crossbeam-channel = "0.5.4"
14+
15+
[dev-dependencies]
16+
lsp-types = "0.93.0"

lib/lsp-server/examples/goto_def.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use
2+
//! this example, execute it and then send an `initialize` request.
3+
//!
4+
//! ```no_run
5+
//! Content-Length: 85
6+
//!
7+
//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}}
8+
//! ```
9+
//!
10+
//! This will respond with a server response. Then send it a `initialized` notification which will
11+
//! have no response.
12+
//!
13+
//! ```no_run
14+
//! Content-Length: 59
15+
//!
16+
//! {"jsonrpc": "2.0", "method": "initialized", "params": {}}
17+
//! ```
18+
//!
19+
//! Once these two are sent, then we enter the main loop of the server. The only request this
20+
//! example can handle is `gotoDefinition`:
21+
//!
22+
//! ```no_run
23+
//! Content-Length: 159
24+
//!
25+
//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}}
26+
//! ```
27+
//!
28+
//! To finish up without errors, send a shutdown request:
29+
//!
30+
//! ```no_run
31+
//! Content-Length: 67
32+
//!
33+
//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null}
34+
//! ```
35+
//!
36+
//! The server will exit the main loop and finally we send a `shutdown` notification to stop
37+
//! the server.
38+
//!
39+
//! ```
40+
//! Content-Length: 54
41+
//!
42+
//! {"jsonrpc": "2.0", "method": "exit", "params": null}
43+
//! ```
44+
use std::error::Error;
45+
46+
use lsp_types::OneOf;
47+
use lsp_types::{
48+
request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities,
49+
};
50+
51+
use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
52+
53+
fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
54+
// Note that we must have our logging only write out to stderr.
55+
eprintln!("starting generic LSP server");
56+
57+
// Create the transport. Includes the stdio (stdin and stdout) versions but this could
58+
// also be implemented to use sockets or HTTP.
59+
let (connection, io_threads) = Connection::stdio();
60+
61+
// Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
62+
let server_capabilities = serde_json::to_value(&ServerCapabilities {
63+
definition_provider: Some(OneOf::Left(true)),
64+
..Default::default()
65+
})
66+
.unwrap();
67+
let initialization_params = connection.initialize(server_capabilities)?;
68+
main_loop(connection, initialization_params)?;
69+
io_threads.join()?;
70+
71+
// Shut down gracefully.
72+
eprintln!("shutting down server");
73+
Ok(())
74+
}
75+
76+
fn main_loop(
77+
connection: Connection,
78+
params: serde_json::Value,
79+
) -> Result<(), Box<dyn Error + Sync + Send>> {
80+
let _params: InitializeParams = serde_json::from_value(params).unwrap();
81+
eprintln!("starting example main loop");
82+
for msg in &connection.receiver {
83+
eprintln!("got msg: {:?}", msg);
84+
match msg {
85+
Message::Request(req) => {
86+
if connection.handle_shutdown(&req)? {
87+
return Ok(());
88+
}
89+
eprintln!("got request: {:?}", req);
90+
match cast::<GotoDefinition>(req) {
91+
Ok((id, params)) => {
92+
eprintln!("got gotoDefinition request #{}: {:?}", id, params);
93+
let result = Some(GotoDefinitionResponse::Array(Vec::new()));
94+
let result = serde_json::to_value(&result).unwrap();
95+
let resp = Response { id, result: Some(result), error: None };
96+
connection.sender.send(Message::Response(resp))?;
97+
continue;
98+
}
99+
Err(err @ ExtractError::JsonError { .. }) => panic!("{:?}", err),
100+
Err(ExtractError::MethodMismatch(req)) => req,
101+
};
102+
// ...
103+
}
104+
Message::Response(resp) => {
105+
eprintln!("got response: {:?}", resp);
106+
}
107+
Message::Notification(not) => {
108+
eprintln!("got notification: {:?}", not);
109+
}
110+
}
111+
}
112+
Ok(())
113+
}
114+
115+
fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
116+
where
117+
R: lsp_types::request::Request,
118+
R::Params: serde::de::DeserializeOwned,
119+
{
120+
req.extract(R::METHOD)
121+
}

lib/lsp-server/src/error.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use std::fmt;
2+
3+
use crate::{Notification, Request};
4+
5+
#[derive(Debug, Clone)]
6+
pub struct ProtocolError(pub(crate) String);
7+
8+
impl std::error::Error for ProtocolError {}
9+
10+
impl fmt::Display for ProtocolError {
11+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12+
fmt::Display::fmt(&self.0, f)
13+
}
14+
}
15+
16+
#[derive(Debug)]
17+
pub enum ExtractError<T> {
18+
/// The extracted message was of a different method than expected.
19+
MethodMismatch(T),
20+
/// Failed to deserialize the message.
21+
JsonError { method: String, error: serde_json::Error },
22+
}
23+
24+
impl std::error::Error for ExtractError<Request> {}
25+
impl fmt::Display for ExtractError<Request> {
26+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27+
match self {
28+
ExtractError::MethodMismatch(req) => {
29+
write!(f, "Method mismatch for request '{}'", req.method)
30+
}
31+
ExtractError::JsonError { method, error } => {
32+
write!(f, "Invalid request\nMethod: {method}\n error: {error}",)
33+
}
34+
}
35+
}
36+
}
37+
38+
impl std::error::Error for ExtractError<Notification> {}
39+
impl fmt::Display for ExtractError<Notification> {
40+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41+
match self {
42+
ExtractError::MethodMismatch(req) => {
43+
write!(f, "Method mismatch for notification '{}'", req.method)
44+
}
45+
ExtractError::JsonError { method, error } => {
46+
write!(f, "Invalid notification\nMethod: {method}\n error: {error}")
47+
}
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)