Skip to content

Commit

Permalink
Auto merge of rust-lang#12251 - matklad:lsp-server, r=matklad
Browse files Browse the repository at this point in the history
internal: vendor lsp-server
  • Loading branch information
bors committed May 14, 2022
2 parents 50ed1a5 + 57cb65b commit 1ff5b2c
Show file tree
Hide file tree
Showing 18 changed files with 946 additions and 9 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/hir-def/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ itertools = "0.10.3"
indexmap = "1.8.0"
smallvec = "1.8.0"
arrayvec = "0.7.2"
la-arena = { version = "0.3.0", path = "../../lib/arena" }
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }

stdx = { path = "../stdx", version = "0.0.0" }
base-db = { path = "../base-db", version = "0.0.0" }
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-expand/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cov-mark = "2.0.0-pre.1"
tracing = "0.1.32"
either = "1.6.1"
rustc-hash = "1.1.0"
la-arena = { version = "0.3.0", path = "../../lib/arena" }
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
itertools = "0.10.3"
hashbrown = { version = "0.12.0", features = [
"inline-more",
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ scoped-tls = "1.0.0"
chalk-solve = { version = "0.82.0", default-features = false }
chalk-ir = "0.82.0"
chalk-recursive = { version = "0.82.0", default-features = false }
la-arena = { version = "0.3.0", path = "../../lib/arena" }
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.10.0"
typed-arena = "2.0.1"

Expand Down
2 changes: 1 addition & 1 deletion crates/profile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ doctest = false
once_cell = "1.10.0"
cfg-if = "1.0.0"
libc = "0.2.121"
la-arena = { version = "0.3.0", path = "../../lib/arena" }
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
countme = { version = "3.0.1", features = ["enable"] }
jemalloc-ctl = { version = "0.4.2", package = "tikv-jemalloc-ctl", optional = true }

Expand Down
2 changes: 1 addition & 1 deletion crates/project-model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
anyhow = "1.0.56"
expect-test = "1.2.2"
la-arena = { version = "0.3.0", path = "../../lib/arena" }
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }

cfg = { path = "../cfg", version = "0.0.0" }
base-db = { path = "../base-db", version = "0.0.0" }
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-analyzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ threadpool = "1.8.1"
rayon = "1.5.1"
num_cpus = "1.13.1"
mimalloc = { version = "0.1.28", default-features = false, optional = true }
lsp-server = "0.6.0"
lsp-server = { version = "0.6.0", path = "../../lib/lsp-server" }
tracing = "0.1.32"
tracing-subscriber = { version = "0.3.9", default-features = false, features = [
"env-filter",
Expand Down
2 changes: 1 addition & 1 deletion lib/arena/Cargo.toml → lib/la-arena/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "la-arena"
version = "0.3.0"
description = "Simple index-based arena without deletion."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-analyzer"
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/la-arena"
documentation = "https://docs.rs/la-arena"
categories = ["data-structures", "memory-management", "rust-patterns"]
edition = "2021"
Expand Down
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions lib/lsp-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "lsp-server"
version = "0.6.0"
description = "Generic LSP server scaffold."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-analyzer/rust-analyzer/tree/master/lib/lsp-server"
edition = "2021"

[dependencies]
log = "0.4.3"
serde_json = "1.0.34"
serde = { version = "1.0.83", features = ["derive"] }
crossbeam-channel = "0.5.4"

[dev-dependencies]
lsp-types = "0.93.0"
121 changes: 121 additions & 0 deletions lib/lsp-server/examples/goto_def.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use
//! this example, execute it and then send an `initialize` request.
//!
//! ```no_run
//! Content-Length: 85
//!
//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}}
//! ```
//!
//! This will respond with a server response. Then send it a `initialized` notification which will
//! have no response.
//!
//! ```no_run
//! Content-Length: 59
//!
//! {"jsonrpc": "2.0", "method": "initialized", "params": {}}
//! ```
//!
//! Once these two are sent, then we enter the main loop of the server. The only request this
//! example can handle is `gotoDefinition`:
//!
//! ```no_run
//! Content-Length: 159
//!
//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}}
//! ```
//!
//! To finish up without errors, send a shutdown request:
//!
//! ```no_run
//! Content-Length: 67
//!
//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null}
//! ```
//!
//! The server will exit the main loop and finally we send a `shutdown` notification to stop
//! the server.
//!
//! ```
//! Content-Length: 54
//!
//! {"jsonrpc": "2.0", "method": "exit", "params": null}
//! ```
use std::error::Error;

use lsp_types::OneOf;
use lsp_types::{
request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities,
};

use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};

fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
// Note that we must have our logging only write out to stderr.
eprintln!("starting generic LSP server");

// Create the transport. Includes the stdio (stdin and stdout) versions but this could
// also be implemented to use sockets or HTTP.
let (connection, io_threads) = Connection::stdio();

// Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
let server_capabilities = serde_json::to_value(&ServerCapabilities {
definition_provider: Some(OneOf::Left(true)),
..Default::default()
})
.unwrap();
let initialization_params = connection.initialize(server_capabilities)?;
main_loop(connection, initialization_params)?;
io_threads.join()?;

// Shut down gracefully.
eprintln!("shutting down server");
Ok(())
}

fn main_loop(
connection: Connection,
params: serde_json::Value,
) -> Result<(), Box<dyn Error + Sync + Send>> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();
eprintln!("starting example main loop");
for msg in &connection.receiver {
eprintln!("got msg: {:?}", msg);
match msg {
Message::Request(req) => {
if connection.handle_shutdown(&req)? {
return Ok(());
}
eprintln!("got request: {:?}", req);
match cast::<GotoDefinition>(req) {
Ok((id, params)) => {
eprintln!("got gotoDefinition request #{}: {:?}", id, params);
let result = Some(GotoDefinitionResponse::Array(Vec::new()));
let result = serde_json::to_value(&result).unwrap();
let resp = Response { id, result: Some(result), error: None };
connection.sender.send(Message::Response(resp))?;
continue;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{:?}", err),
Err(ExtractError::MethodMismatch(req)) => req,
};
// ...
}
Message::Response(resp) => {
eprintln!("got response: {:?}", resp);
}
Message::Notification(not) => {
eprintln!("got notification: {:?}", not);
}
}
}
Ok(())
}

fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}
50 changes: 50 additions & 0 deletions lib/lsp-server/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::fmt;

use crate::{Notification, Request};

#[derive(Debug, Clone)]
pub struct ProtocolError(pub(crate) String);

impl std::error::Error for ProtocolError {}

impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}

#[derive(Debug)]
pub enum ExtractError<T> {
/// The extracted message was of a different method than expected.
MethodMismatch(T),
/// Failed to deserialize the message.
JsonError { method: String, error: serde_json::Error },
}

impl std::error::Error for ExtractError<Request> {}
impl fmt::Display for ExtractError<Request> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExtractError::MethodMismatch(req) => {
write!(f, "Method mismatch for request '{}'", req.method)
}
ExtractError::JsonError { method, error } => {
write!(f, "Invalid request\nMethod: {method}\n error: {error}",)
}
}
}
}

impl std::error::Error for ExtractError<Notification> {}
impl fmt::Display for ExtractError<Notification> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExtractError::MethodMismatch(req) => {
write!(f, "Method mismatch for notification '{}'", req.method)
}
ExtractError::JsonError { method, error } => {
write!(f, "Invalid notification\nMethod: {method}\n error: {error}")
}
}
}
}
Loading

0 comments on commit 1ff5b2c

Please sign in to comment.