diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 018a847..4f47417 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust-version: [1.64.0, beta, nightly] + rust-version: [1.70.0, beta, nightly] runtime: [runtime-tokio, runtime-agnostic] include: - rust-version: nightly @@ -50,7 +50,7 @@ jobs: - uses: actions/checkout@v3 - id: install-rust name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.64.0 + uses: dtolnay/rust-toolchain@1.70.0 with: components: clippy - name: Run cargo clippy @@ -67,7 +67,7 @@ jobs: - uses: actions/checkout@v3 - id: install-rust name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.64.0 + uses: dtolnay/rust-toolchain@1.70.0 with: components: rustfmt - name: Run cargo fmt diff --git a/Cargo.toml b/Cargo.toml index b96c85d..c037e89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "tower-lsp" version = "0.20.0" authors = ["Eyal Kalderon "] edition = "2021" -rust-version = "1.64.0" +rust-version = "1.70.0" description = "Language Server Protocol implementation based on Tower" license = "MIT OR Apache-2.0" homepage = "https://github.com/ebkalderon/tower-lsp" @@ -24,26 +24,26 @@ proposed = ["lsp-types/proposed"] async-codec-lite = { version = "0.0", optional = true } async-trait = "0.1" auto_impl = "1.0" -bytes = "1.0" -dashmap = "5.1" +bytes = "1.5" +dashmap = "5.5" futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } httparse = "1.8" -lsp-types = "0.94.1" -memchr = "2.5" +lsp-types = "0.97.0" +memchr = "2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tokio = { version = "1.17", optional = true } +tokio = { version = "1.35", optional = true } tokio-util = { version = "0.7", optional = true, features = ["codec"] } tower-lsp-macros = { version = "0.9", path = "./tower-lsp-macros" } tower = { version = "0.4", default-features = false, features = ["util"] } tracing = "0.1" [dev-dependencies] -async-tungstenite = { version = "0.22", features = ["tokio-runtime"] } +async-tungstenite = { version = "0.23.0", features = ["tokio-runtime"] } tracing-subscriber = "0.3" -tokio = { version = "1.17", features = ["io-util", "io-std", "macros", "rt-multi-thread"] } +tokio = { version = "1.35", features = ["io-util", "io-std", "macros", "rt-multi-thread"] } tokio-util = { version = "0.7", features = ["compat"] } -ws_stream_tungstenite = { version = "0.10", features = ["tokio_io"] } +ws_stream_tungstenite = { version = "0.11.0", features = ["tokio_io"] } [workspace] members = [".", "./tower-lsp-macros"] diff --git a/rust-toolchain b/rust-toolchain index 9405730..832e9af 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.64.0 +1.70.0 diff --git a/src/jsonrpc.rs b/src/jsonrpc.rs index 223d003..b52348b 100644 --- a/src/jsonrpc.rs +++ b/src/jsonrpc.rs @@ -21,7 +21,7 @@ mod response; mod router; /// A unique ID used to correlate requests and responses together. -#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] +#[derive(Default, Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum Id { /// Numeric ID. @@ -33,15 +33,10 @@ pub enum Id { /// While `null` is considered a valid request ID by the JSON-RPC 2.0 specification, its use is /// _strongly_ discouraged because the specification also uses a `null` value to indicate an /// unknown ID in the [`Response`] object. + #[default] Null, } -impl Default for Id { - fn default() -> Self { - Id::Null - } -} - impl Display for Id { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { diff --git a/src/lib.rs b/src/lib.rs index d8d14c5..76fcb0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,6 +233,54 @@ pub trait LanguageServer: Send + Sync + 'static { warn!("Got a textDocument/didClose notification, but it is not implemented"); } + // Notebook Document Synchronization + + /// The [`notebookDocument/didOpen`] notification is sent from the client to the server when a new notebook document is opened. + /// It is only sent for notebooks selected by the `notebookDocumentSync` server capability. + /// + /// [`notebookDocument/didOpen`]: https://microsoft.github.io/language-server-protocol/specification/#notebookDocument_didChange + /// + // TODO: Write more documentation + #[rpc(name = "notebookDocument/didOpen")] + async fn notebook_did_open(&self, params: DidOpenNotebookDocumentParams) { + let _ = params; + warn!("Got a notebookDocument/didOpen notification, but it is not implemented"); + } + + /// The [`notebookDocument/didChange`] notification is sent from the client to the server when a notebook document changes. + /// It is only sent for notebooks selected by the `notebookDocumentSync` server capability. + /// + /// [`notebookDocument/didChange`]: https://microsoft.github.io/language-server-protocol/specification#notebookDocument_didChange + /// + // TODO: Write more documentation + #[rpc(name = "notebookDocument/didChange")] + async fn notebook_did_change(&self, params: DidChangeNotebookDocumentParams) { + let _ = params; + warn!("Got a notebookDocument/didChange notification, but it is not implemented"); + } + + /// The [`notebookDocument/didSave`] notification is sent from the client to the server when a notebook document is saved. + /// It is only sent for notebooks selected by the `notebookDocumentSync` server capability. + /// + /// [`notebookDocument/didSave`]: https://microsoft.github.io/language-server-protocol/specification#notebookDocument_didSave + #[rpc(name = "notebookDocument/didSave")] + async fn notebook_did_save(&self, params: DidSaveNotebookDocumentParams) { + let _ = params; + warn!("Got a notebookDocument/didSave notification, but it is not implemented"); + } + + /// The [`notebookDocument/didClose`] notification is sent from the client to the server when a notebook document is closed. + /// It is only sent for notebooks selected by the `notebookDocumentSync` server capability. + /// + /// [`notebookDocument/didClose`]: https://microsoft.github.io/language-server-protocol/specification#notebookDocument_didClose + /// + // TODO: Write more documentation + #[rpc(name = "notebookDocument/didClose")] + async fn notebook_did_close(&self, params: DidCloseNotebookDocumentParams) { + let _ = params; + warn!("Got a notebookDocument/didClose notification, but it is not implemented"); + } + // Language Features /// The [`textDocument/declaration`] request asks the server for the declaration location of a diff --git a/src/service/client.rs b/src/service/client.rs index dd7e843..9c28ff8 100644 --- a/src/service/client.rs +++ b/src/service/client.rs @@ -10,6 +10,7 @@ use std::task::{Context, Poll}; use futures::channel::mpsc::{self, Sender}; use futures::future::BoxFuture; use futures::sink::SinkExt; +use lsp_types::request::WorkspaceDiagnosticRefresh; use lsp_types::*; use serde::Serialize; use serde_json::Value; @@ -210,10 +211,12 @@ impl Client { use lsp_types::notification::TelemetryEvent; match serde_json::to_value(data) { Err(e) => error!("invalid JSON in `telemetry/event` notification: {}", e), - Ok(mut value) => { - if !value.is_null() && !value.is_array() && !value.is_object() { - value = Value::Array(vec![value]); - } + Ok(value) => { + let value = match value { + Value::Object(value) => OneOf::Left(value), + Value::Array(value) => OneOf::Right(value), + value => OneOf::Right(vec![value]), + }; self.send_notification_unchecked::(value) .await; } @@ -361,7 +364,7 @@ impl Client { /// This notification will only be sent if the server is initialized. pub async fn publish_diagnostics( &self, - uri: Url, + uri: Uri, diags: Vec, version: Option, ) { @@ -672,26 +675,30 @@ mod tests { #[tokio::test(flavor = "current_thread")] async fn telemetry_event() { let null = json!(null); - let expected = Request::from_notification::(null.clone()); + let value = OneOf::Right(vec![null.clone()]); + let expected = Request::from_notification::(value); assert_client_message(|p| async move { p.telemetry_event(null).await }, expected).await; let array = json!([1, 2, 3]); - let expected = Request::from_notification::(array.clone()); + let value = OneOf::Right(array.as_array().unwrap().to_owned()); + let expected = Request::from_notification::(value); assert_client_message(|p| async move { p.telemetry_event(array).await }, expected).await; let object = json!({}); - let expected = Request::from_notification::(object.clone()); + let value = OneOf::Left(object.as_object().unwrap().to_owned()); + let expected = Request::from_notification::(value); assert_client_message(|p| async move { p.telemetry_event(object).await }, expected).await; let other = json!("hello"); let wrapped = Value::Array(vec![other.clone()]); - let expected = Request::from_notification::(wrapped); + let value = OneOf::Right(wrapped.as_array().unwrap().to_owned()); + let expected = Request::from_notification::(value); assert_client_message(|p| async move { p.telemetry_event(other).await }, expected).await; } #[tokio::test(flavor = "current_thread")] async fn publish_diagnostics() { - let uri: Url = "file:///path/to/file".parse().unwrap(); + let uri: Uri = "file:///path/to/file".parse().unwrap(); let diagnostics = vec![Diagnostic::new_simple(Default::default(), "example".into())]; let params = PublishDiagnosticsParams::new(uri.clone(), diagnostics.clone(), None);