Skip to content

Commit

Permalink
Convert to std::future::Future and async/await
Browse files Browse the repository at this point in the history
This commit makes judicious use of `compat()` to convert between
`std::future::Future` and the legacy `futures` 0.1 `Future` trait such
that we can have native Rust async/await support without waiting for the
`jsonrpsee` crate to be ready (see #58 for context).

Naturally, this should be considered a breaking change for this library
since it directly affects the public API for this crate, and it also
increases the minimum supported Rust version to 1.39.0. It tentatively
introduces a reliance on [`async-trait`] for the `LanguageServer` trait
and it also relaxes the `'static` requirements for the `stdin` parameter
for `Server`.

[`async-trait`]: https://github.com/dtolnay/async-trait

This does not yet resolve #13, since that would require either
switching to `jsonrpsee`, once it's ready, or building a custom client
solution for the `Printer`, converting it to a `Client` or similar.
  • Loading branch information
ebkalderon committed Feb 22, 2020
1 parent cdad611 commit 76f9a8a
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 416 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
rust-version: [1.36.0, beta, nightly]
rust-version: [1.39.0, beta, nightly]
include:
- rust-version: nightly
continue-on-error: true
Expand Down
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@ categories = ["asynchronous"]
keywords = ["language-server", "lsp", "tower"]

[dependencies]
bytes = "0.4.12"
futures = "0.1.28"
async-trait = "0.1.24"
bytes = "0.5.0"
futures = { version = "0.3.2", features = ["compat"] }
jsonrpc-core = "14.0.5"
jsonrpc-derive = "14.0.5"
log = "0.4.8"
lsp-types = "0.68.0"
nom = "5.0.1"
serde = { version = "1.0.103", features = ["derive"] }
serde_json = "1.0.40"
tokio-codec = "0.1.1"
tokio-executor = "0.1.9"
tokio-io = "0.1.12"
tower-service = "0.2.0"
tokio = { version = "0.2.11", features = ["rt-core"] }
tokio-util = { version = "0.2.0", features = ["codec"] }
tower-service = "0.3.0"

[dev-dependencies]
env_logger = "0.7.1"
tokio = "0.1.22"
tokio = { version = "0.2.11", features = ["io-std", "macros", "test-util"] }
tokio-test = "0.2.0"
tower-test = "0.3.0"
92 changes: 47 additions & 45 deletions examples/custom_notification.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use futures::future;
use jsonrpc_core::{BoxFuture, Result};
use lsp_types::notification::Notification;
use lsp_types::request::GotoImplementationResponse;
use jsonrpc_core::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tower_lsp::lsp_types::request::GotoDefinitionResponse;
use tower_lsp::lsp_types::notification::Notification;
use tower_lsp::lsp_types::request::{GotoDefinitionResponse, GotoImplementationResponse};
use tower_lsp::lsp_types::*;
use tower_lsp::{LanguageServer, LspService, Printer, Server};

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
struct CustomNotificationParams {
title: String,
message: String,
Expand All @@ -24,7 +22,7 @@ impl CustomNotificationParams {
}

#[derive(Debug)]
struct CustomNotification {}
enum CustomNotification {}

impl Notification for CustomNotification {
type Params = CustomNotificationParams;
Expand All @@ -34,19 +32,8 @@ impl Notification for CustomNotification {
#[derive(Debug, Default)]
struct Backend;

#[tower_lsp::async_trait]
impl LanguageServer for Backend {
type ShutdownFuture = BoxFuture<()>;
type SymbolFuture = BoxFuture<Option<Vec<SymbolInformation>>>;
type ExecuteFuture = BoxFuture<Option<Value>>;
type CompletionFuture = BoxFuture<Option<CompletionResponse>>;
type HoverFuture = BoxFuture<Option<Hover>>;
type SignatureHelpFuture = BoxFuture<Option<SignatureHelp>>;
type DeclarationFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type DefinitionFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type TypeDefinitionFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type ImplementationFuture = BoxFuture<Option<GotoImplementationResponse>>;
type HighlightFuture = BoxFuture<Option<Vec<DocumentHighlight>>>;

fn initialize(&self, _: &Printer, _: InitializeParams) -> Result<InitializeResult> {
Ok(InitializeResult {
server_info: None,
Expand Down Expand Up @@ -87,19 +74,19 @@ impl LanguageServer for Backend {
})
}

fn shutdown(&self) -> Self::ShutdownFuture {
Box::new(future::ok(()))
async fn shutdown(&self) -> Result<()> {
Ok(())
}

fn symbol(&self, _: WorkspaceSymbolParams) -> Self::SymbolFuture {
Box::new(future::ok(None))
async fn symbol(&self, _: WorkspaceSymbolParams) -> Result<Option<Vec<SymbolInformation>>> {
Ok(None)
}

fn execute_command(
async fn execute_command(
&self,
printer: &Printer,
params: ExecuteCommandParams,
) -> Self::ExecuteFuture {
) -> Result<Option<Value>> {
if &params.command == "custom.notification" {
printer.send_notification::<CustomNotification>(CustomNotificationParams::new(
"Hello", "Message",
Expand All @@ -109,44 +96,59 @@ impl LanguageServer for Backend {
MessageType::Info,
format!("command executed!: {:?}", params),
);
printer.apply_edit(WorkspaceEdit::default());
Box::new(future::ok(None))
Ok(None)
}

fn completion(&self, _: CompletionParams) -> Self::CompletionFuture {
Box::new(future::ok(None))
async fn completion(&self, _: CompletionParams) -> Result<Option<CompletionResponse>> {
Ok(None)
}

fn hover(&self, _: TextDocumentPositionParams) -> Self::HoverFuture {
Box::new(future::ok(None))
async fn hover(&self, _: TextDocumentPositionParams) -> Result<Option<Hover>> {
Ok(None)
}

fn signature_help(&self, _: TextDocumentPositionParams) -> Self::SignatureHelpFuture {
Box::new(future::ok(None))
async fn signature_help(&self, _: TextDocumentPositionParams) -> Result<Option<SignatureHelp>> {
Ok(None)
}

fn goto_declaration(&self, _: TextDocumentPositionParams) -> Self::DeclarationFuture {
Box::new(future::ok(None))
async fn goto_declaration(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_definition(&self, _: TextDocumentPositionParams) -> Self::DefinitionFuture {
Box::new(future::ok(None))
async fn goto_definition(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_type_definition(&self, _: TextDocumentPositionParams) -> Self::TypeDefinitionFuture {
Box::new(future::ok(None))
async fn goto_type_definition(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_implementation(&self, _: TextDocumentPositionParams) -> Self::ImplementationFuture {
Box::new(future::ok(None))
async fn goto_implementation(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoImplementationResponse>> {
Ok(None)
}

fn document_highlight(&self, _: TextDocumentPositionParams) -> Self::HighlightFuture {
Box::new(future::ok(None))
async fn document_highlight(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<Vec<DocumentHighlight>>> {
Ok(None)
}
}

fn main() {
#[tokio::main]
async fn main() {
env_logger::init();

let stdin = tokio::io::stdin();
Expand All @@ -158,5 +160,5 @@ fn main() {
.interleave(messages)
.serve(service);

tokio::run(handle.run_until_exit(server));
handle.run_until_exit(server).await;
}
84 changes: 46 additions & 38 deletions examples/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use futures::future;
use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_core::Result;
use serde_json::Value;
use tower_lsp::lsp_types::request::{GotoDefinitionResponse, GotoImplementationResponse};
use tower_lsp::lsp_types::*;
Expand All @@ -8,19 +7,8 @@ use tower_lsp::{LanguageServer, LspService, Printer, Server};
#[derive(Debug, Default)]
struct Backend;

#[tower_lsp::async_trait]
impl LanguageServer for Backend {
type ShutdownFuture = BoxFuture<()>;
type SymbolFuture = BoxFuture<Option<Vec<SymbolInformation>>>;
type ExecuteFuture = BoxFuture<Option<Value>>;
type CompletionFuture = BoxFuture<Option<CompletionResponse>>;
type HoverFuture = BoxFuture<Option<Hover>>;
type SignatureHelpFuture = BoxFuture<Option<SignatureHelp>>;
type DeclarationFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type DefinitionFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type TypeDefinitionFuture = BoxFuture<Option<GotoDefinitionResponse>>;
type ImplementationFuture = BoxFuture<Option<GotoImplementationResponse>>;
type HighlightFuture = BoxFuture<Option<Vec<DocumentHighlight>>>;

fn initialize(&self, _: &Printer, _: InitializeParams) -> Result<InitializeResult> {
Ok(InitializeResult {
server_info: None,
Expand Down Expand Up @@ -62,12 +50,12 @@ impl LanguageServer for Backend {
printer.log_message(MessageType::Info, "server initialized!");
}

fn shutdown(&self) -> Self::ShutdownFuture {
Box::new(future::ok(()))
async fn shutdown(&self) -> Result<()> {
Ok(())
}

fn symbol(&self, _: WorkspaceSymbolParams) -> Self::SymbolFuture {
Box::new(future::ok(None))
async fn symbol(&self, _: WorkspaceSymbolParams) -> Result<Option<Vec<SymbolInformation>>> {
Ok(None)
}

fn did_change_workspace_folders(&self, printer: &Printer, _: DidChangeWorkspaceFoldersParams) {
Expand All @@ -82,10 +70,14 @@ impl LanguageServer for Backend {
printer.log_message(MessageType::Info, "watched files have changed!");
}

fn execute_command(&self, printer: &Printer, _: ExecuteCommandParams) -> Self::ExecuteFuture {
async fn execute_command(
&self,
printer: &Printer,
_: ExecuteCommandParams,
) -> Result<Option<Value>> {
printer.log_message(MessageType::Info, "command executed!");
printer.apply_edit(WorkspaceEdit::default());
Box::new(future::ok(None))
Ok(None)
}

fn did_open(&self, printer: &Printer, _: DidOpenTextDocumentParams) {
Expand All @@ -104,40 +96,56 @@ impl LanguageServer for Backend {
printer.log_message(MessageType::Info, "file closed!");
}

fn completion(&self, _: CompletionParams) -> Self::CompletionFuture {
Box::new(future::ok(None))
async fn completion(&self, _: CompletionParams) -> Result<Option<CompletionResponse>> {
Ok(None)
}

fn hover(&self, _: TextDocumentPositionParams) -> Self::HoverFuture {
Box::new(future::ok(None))
async fn hover(&self, _: TextDocumentPositionParams) -> Result<Option<Hover>> {
Ok(None)
}

fn signature_help(&self, _: TextDocumentPositionParams) -> Self::SignatureHelpFuture {
Box::new(future::ok(None))
async fn signature_help(&self, _: TextDocumentPositionParams) -> Result<Option<SignatureHelp>> {
Ok(None)
}

fn goto_declaration(&self, _: TextDocumentPositionParams) -> Self::DeclarationFuture {
Box::new(future::ok(None))
async fn goto_declaration(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_definition(&self, _: TextDocumentPositionParams) -> Self::DefinitionFuture {
Box::new(future::ok(None))
async fn goto_definition(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_type_definition(&self, _: TextDocumentPositionParams) -> Self::TypeDefinitionFuture {
Box::new(future::ok(None))
async fn goto_type_definition(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoDefinitionResponse>> {
Ok(None)
}

fn goto_implementation(&self, _: TextDocumentPositionParams) -> Self::ImplementationFuture {
Box::new(future::ok(None))
async fn goto_implementation(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<GotoImplementationResponse>> {
Ok(None)
}

fn document_highlight(&self, _: TextDocumentPositionParams) -> Self::HighlightFuture {
Box::new(future::ok(None))
async fn document_highlight(
&self,
_: TextDocumentPositionParams,
) -> Result<Option<Vec<DocumentHighlight>>> {
Ok(None)
}
}

fn main() {
#[tokio::main]
async fn main() {
env_logger::init();

let stdin = tokio::io::stdin();
Expand All @@ -149,5 +157,5 @@ fn main() {
.interleave(messages)
.serve(service);

tokio::run(handle.run_until_exit(server));
handle.run_until_exit(server).await;
}
11 changes: 6 additions & 5 deletions src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::{Error as IoError, Write};
use std::str::{self, Utf8Error};

use bytes::{BufMut, BytesMut};
use bytes::buf::ext::BufMutExt;
use bytes::{Buf, BytesMut};
use nom::branch::alt;
use nom::bytes::streaming::{is_not, tag};
use nom::character::streaming::{char, crlf, digit1, space0};
Expand All @@ -14,7 +15,7 @@ use nom::error::ErrorKind;
use nom::multi::length_data;
use nom::sequence::{delimited, terminated, tuple};
use nom::{Err, IResult, Needed};
use tokio_codec::{Decoder, Encoder};
use tokio_util::codec::{Decoder, Encoder};

/// Errors that can occur when processing an LSP request.
#[derive(Debug)]
Expand Down Expand Up @@ -155,9 +156,9 @@ mod tests {
let mut codec = LanguageServerCodec::default();
let mut buffer = BytesMut::new();
codec.encode(decoded.clone(), &mut buffer).unwrap();
assert_eq!(buffer, BytesMut::from(encoded.clone()));
assert_eq!(buffer, BytesMut::from(encoded.as_str()));

let mut buffer = BytesMut::from(encoded);
let mut buffer = BytesMut::from(encoded.as_str());
let message = codec.decode(&mut buffer).unwrap();
assert_eq!(message, Some(decoded));
}
Expand All @@ -178,7 +179,7 @@ mod tests {
let encoded = format!("{}\r\n{}\r\n\r\n{}", content_len, content_type, decoded);

let mut codec = LanguageServerCodec::default();
let mut buffer = BytesMut::from(encoded);
let mut buffer = BytesMut::from(encoded.as_str());
let message = codec.decode(&mut buffer).unwrap();
assert_eq!(message, Some(decoded));
}
Expand Down
Loading

0 comments on commit 76f9a8a

Please sign in to comment.