tower-lsp-0.20.0/.cargo_vcs_info.json0000644000000001360000000000100130470ustar { "git": { "sha1": "7b580128faa39b907db8f40bc53924181887acde" }, "path_in_vcs": "" }tower-lsp-0.20.0/.github/dependabot.yml000064400000000000000000000005101046102023000160230ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily time: "21:00" open-pull-requests-limit: 10 ignore: - dependency-name: tokio versions: - 1.1.0 - 1.2.0 - 1.3.0 - 1.4.0 - dependency-name: lsp-types versions: - 0.86.0 - 0.87.0 - 0.88.0 tower-lsp-0.20.0/.github/workflows/ci.yml000064400000000000000000000037141046102023000163570ustar 00000000000000name: ci on: push: branches: - master - release/* pull_request: branches: - master - release/* jobs: cargo-test: name: cargo test (${{ matrix.os }} / ${{ matrix.rust-version }} / ${{ matrix.runtime }}) runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: os: [ubuntu-latest, windows-latest, macOS-latest] rust-version: [1.64.0, beta, nightly] runtime: [runtime-tokio, runtime-agnostic] include: - rust-version: nightly continue-on-error: true steps: - uses: actions/checkout@v3 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust-version }} - name: Run cargo test continue-on-error: ${{ matrix.continue-on-error || false }} run: | cargo +${{ matrix.rust-version }} test --workspace --no-default-features --features ${{ matrix.runtime }} cargo-audit: name: cargo audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} cargo-clippy: name: cargo clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - id: install-rust name: Install Rust toolchain uses: dtolnay/rust-toolchain@1.64.0 with: components: clippy - name: Run cargo clippy uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} toolchain: ${{ steps.install-rust.outputs.name }} args: --all-targets --features proposed -- -D warnings cargo-fmt: name: cargo fmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - id: install-rust name: Install Rust toolchain uses: dtolnay/rust-toolchain@1.64.0 with: components: rustfmt - name: Run cargo fmt run: cargo +${{ steps.install-rust.outputs.name }} fmt --all -- --check tower-lsp-0.20.0/.gitignore000064400000000000000000000000401046102023000136210ustar 00000000000000**/target **/*.rs.bk Cargo.lock tower-lsp-0.20.0/CHANGELOG.md000064400000000000000000000511751046102023000134610ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.20.0] - 2023-08-10 ### Added * Add support for pull-based diagnostics from LSP 3.17.0 (PR #396). * Implement `textDocument/diagnostic` server request. * Implement `workspace/diagnostic` server request. * Implement `workspace/diagnostic/refresh` client request. * Implement `std::str::FromStr` for `jsonrpc::{Request,Response}` (PR #379). * Implement `From` for `i64` (PR #379). * Document supported LSP features in [FEATURES.md](./FEATURES.md) matrix (PR #383). ### Changed * Bump minimum supported Rust version from `1.52.0` to `1.64.0` (PR #377, PR #395). * Update `lsp-types` from `0.94` to `0.94.1` (PR #396). * Update `syn` from `1` to `2` (PR #390). * Update dev-dependency `async-tungstenite` from `0.18` to `0.22` (PR #395). * Update dev-dependency `ws_stream_tungstenite` from `0.9` to `0.10` (PR #395). * Optimize JSON-RPC deserialization types. * Change `jsonrpc::Error::message` field to `Cow<'static, str>` (PR #378). * Mark several methods on `jsonrpc::Error` as `const fn` (PR #378). * Mark all methods on `jsonrpc::ErrorCode` as `const fn` (PR #378). * Avoid heap allocation in `version` field deserialization (PR #379). ### Fixed * Fix broken Markdown in doc comment for `LanguageServer::completion()` (PR #396). ## [0.19.0] - 2023-02-28 ### Added * Add `LspService::inner()` method (PR #344). * Add missing `window/showDocument` client request from LSP 3.16.0 (PR #375). * Add partial support for Language Server Protocol 3.17.0 (PR #375): * Implement `textDocument/prepareTypeHierarchy` server request. * Implement `typeHierarchy/supertypes` server request. * Implement `typeHierarchy/subtypes` server request. * Implement `textDocument/inlineValue` server request. * Implement `textDocument/inlayHint` server request. * Implement `inlayHint/resolve` server request. * Implement `workspaceSymbol/resolve` server request. * Implement `workspace/inlineValue/refresh` client request. * Implement `workspace/inlayHint/refresh` client request. ### Changed * Address Clippy lints (PR #369). * Update `edition` from `2018` to `2021` (PR #370). * Update `lsp-types` from `0.93` to `0.94` (PR #367). * Reorder `LanguageServer` trait methods to match the LSP 3.17.0 spec document (PR #375). * Reorder `Client` inherent methods to better match the LSP 3.17.0 spec document (PR #375). ### Fixed * Fix doc links for `textDocument/colorPresentation` request (PR #371). * Fix doc links for `textDocument/willSaveWaitUntil` request (PR #371). * Fix doc links for `workspace/didChangeWatchedFiles` request (PR #371). * Improve documentation for `LanguageServer` and `Client` methods (PR #375). ## [0.18.0] - 2023-01-14 ### Changed * Switch from `log` facade to `tracing` (PR #332). * Change `$/cancelRequest` log message from `warn` to `debug` (PR #353). * Update `auto_impl` from `0.5` to `1.0` (PR #343). * Update `httparse` from `1.3.5` to `1.8` (PR #363) * Update `memchr` from `2.4.1` to `2.5` (PR #363). * Relax `tower` version requirement from `0.3.11` to `0.3` (PR #363). * Update dev-dependency `async-tungstenite` from `0.16` to `0.18` (PR #363). * Update dev-dependency `ws_stream_tungstenite` from `0.7` to `0.9` (PR #363). ### Fixed * Improve client connection behavior in `tcp` example (PR #336). * Tweak grammar in `initialized` and `textDocument/codeAction` doc comments (PR #361). ## [0.17.0] - 2022-04-15 ### Added * Support proposed LSP features with the `proposed` feature flag (PR #330). ### Changed * Update `lsp-types` from `0.92` to `0.93` (PR #333). ## [0.16.0] - 2022-03-10 ### Added * Support defining custom JSON-RPC requests on `LspService` (PR #313). * Add compatibility with WASM (PR #309). * Support alternative async runtimes other than `tokio` when enabling the `runtime-agnostic` feature (PR #309). * Implement `Service>` for `Client` (PR #313). * Expose `concurrency_level` setting on `Server`, allowing adjustment from the default value of 4. * Add `Request::build()` interface for creating custom requests. * Add convenient `From` implementations for `jsonrpc::Id`. * Add `.result()`/`.error()` and `.is_ok()`/`.is_error()` methods to `jsonrpc::Response`. ### Changed * `LspService` now implements `Service>` . * `LspService::new()` now returns a `ClientSocket` instead of a `MessageStream`. * `Server::new()` now requires a third `ClientSocket` argument instead of using `.interleave()`. * Rename `Client::send_custom_{request,notification}` to `Client::send_{request,notification}`. * Rename `jsonrpc::Response::{ok, error}` to `jsonrpc::Response::{from_ok, from_error}`. ### Fixed * Close `Client` channel properly on `exit` notification (PR #309). * Fix `Server` occasionally stalling by processing client responses separately from client-to-server requests (PR #313). * Return error code `-32600` (invalid request) if incoming data is valid JSON, but isn't a JSON-RPC request or response (PR #313). ### Removed * Remove `.interleave()` method from `Server` (PR #313). * Remove `jsonrpc::{ClientRequest, Incoming, Outgoing, ServerRequest}` (PR #313). * Remove `MessageStream` (PR #313). ## [0.15.1] - 2022-02-14 ### Fixed * Fix semver incompatibility in release of `tower-lsp-macros` (PR #306). * Re-released `tower-lsp-macros` 0.4.2 -> 0.5.0. * Re-released `tower-lsp` 0.15.0 -> 0.15.1. * Update `tokio-util` from `0.6.5` to `0.7.0` (PR #303). ## [0.15.0] - 2022-02-10 [YANKED] ### Changed * Bump minimum supported Rust version from `1.45.0` to `1.52.0` (PR #300). * Update `lsp-types` from `0.89` to `0.92` (PR #300). * Update `auto_impl` from `0.4` to `0.5` (PR #298). * Update `dashmap` from `4.0.2` to `5.0.0` (PR #298). * Update `nom` from `6.1.2` to `7.1.0` (PR #298). ### Fixed * Support `null` and negative integer values as request IDs (PR #285). ## [0.14.1] - 2021-05-21 ### Fixed * Fix regression in server-side `$/cancelRequest` support (PR #280). ## [0.14.0] - 2021-05-20 ### Added * Add support for Language Server Protocol 3.16.0 (PR #270): * Implement `workspace/willCreateFiles` server request. * Implement `workspace/willRenameFiles` server request. * Implement `workspace/willDeleteFiles` server request. * Implement `workspace/didCreateFiles` server notification. * Implement `workspace/didRenameFiles` server notification. * Implement `workspace/didDeleteFiles` server notification. * Implement call hierarchy server requests. * Implement semantic tokens server requests. * Implement `workspace/codeLens/refresh` client request. * Implement `workspace/semanticTokens/refresh` client request. * Implement `textDocument/linkedEditingRange` server request. * Implement `textDocument/moniker` request. * Implement `codeAction/resolve` request. * Add support for custom server-to-client requests (PR #275). ### Changed * Bump minimum supported Rust version from `1.41.0` to `1.45.0` (PR #264). * Update `lsp-types` from `0.82` to `0.89` (PR #264). * Update `tokio` from `0.2` to `1.6` (PR #264, PR #268). * Update `tokio-util` from `0.3` to `0.6.5` (PR #264). * Update `bytes` from `0.5` to `1.0.1` (PR #264). * Update `dashmap` from `3.5.1` to `4.0.2` (PR #264). * Update `nom` from `5.1` to `6.1.2` (PR #264). * Eliminate looping, message reparsing in codec using SIMD accelerated `take_until` combinator (PR #274). ## Fixed * Fix race when sending requests to the client (PR #245). * Permit `window/showMessageRequest` while server is uninitialized (PR #288). * Fix client request futures hanging by fixing `serde` overlap (PR #269). * Correctly handle incoming zero-length messages (PR #271). * Clean up documentation, fix broken intra-doc and external doc links. ## [0.13.3] - 2020-09-19 ### Changed * Increase `lsp-types` semantic version range to `>=0.79, <0.82`. This is safe because the upstream changes only concern proposed LSP features, which this library does not currently support. ## [0.13.2] - 2020-09-03 ### Changed * Increase `lsp-types` semantic version range to `>=0.79, <0.81`. This is safe because the upstream changes only concern proposed LSP features, which this library does not currently support. ## [0.13.1] - 2020-08-21 ### Changed * Improve `std::fmt::Debug` implementation for `Client` (PR #216). * Several API documentation improvements. ### Fixed * Fix infinite loop upon encountering invalid UTF-8 characters in an incoming message (PR #215). ## [0.13.0] - 2020-08-20 ### Changed * Improve log message quality and reduce noise. * Return responses in corresponding request order to make things easier on the client side (PR #212). * Change remaining `Client` notification methods to `async fn` (PR #213). * Bump minimum supported Rust version to 1.41.0 (PR #213). ### Fixed * Report missing params as "invalid params" instead of "parse error" (PR #211). ## [0.12.1] - 2020-08-11 ### Fixed * Reject multiple `initialize` requests sent in quick succession (PR #208). * Fix bug deserializing `jsonrpc` field from `serde_json::Value` (PR #209). ## [0.12.0] - 2020-08-09 ### Added * Add private subcrate `tower-lsp-macros` for internal use only (PR #202). * Implement cancellation support via `$/cancelRequest` (PR #202). * Officially support serving over TCP (PR #198). ### Changed * Update `lsp-types` crate from 0.74 to 0.79. * Have language servers store `Client` directly as struct field (PR #199). * Replace `jsonrpc-core` with minimal JSON-RPC implementation (PR #202). * Redefine `LspService` as `Service>`. * Implement `FusedStream` for `MessageStream`. ### Fixed * Fix typo which caused `workspace/didChangeConfiguration` to break (PR #195). * Implement proper parse error recovery in LSP codec (PR #201). * Refuse to accept further requests after `shutdown` has been called once. ### Removed * Remove dependency on `jsonrpc-core`, as `tower-lsp` no longer relies on it. * Remove `LspService::with_handler()` constructor (PR #202). ## [0.11.0] - 2020-04-30 ### Changed * Update `lsp-types` crate from 0.73 to 0.74 (PR #178). This introduces breaking changes to the following `LanguageServer` trait method signatures: * `hover()` * `signatureHelp()` * `goto_declaration()` * `goto_definition()` * `goto_type_definition()` * `goto_implementation()` * `document_highlight()` * Make `LanguageServer::initialize()` handler `async fn` (PR #182). * Accept `stdin` and `stdout` handles that are not `Send + 'static`. This permits the use of `std::io::Cursor` or `Vec` as mock stdio sources for tests, and passing in `&mut` handles is now supported as well (PR #184). ### Fixed * Fix broken bidirectional request/response routing (PR #184). The original implementation introduced in [0.9.0](#090---2020-03-04) would deadlock under certain conditions. ## [0.10.1] - 2020-04-29 ### Changed * Implement `Clone` for `Client` so it can be safely passed to functions expecting `'static` values. * Mark `MessageStream` as `#[must_use]`. ## [0.10.0] - 2020-03-19 ### Added * Implement support for the following client-to-server messages: * `textDocument/willSaveWaitUntil` * `textDocument/selectionRange` * Re-export useful `jsonrpc-core` types in a new `jsonrpc` module (PR #169). ### Changed * Update `lsp-types` crate from 0.70 to 0.73 (PR #162). ### Fixed * Fix JSON-RPC delegate for `textDocument/foldingRange` (PR #167). ## [0.9.1] - 2020-03-07 ### Added * Implement support for the following client-to-server messages: * `textDocument/documentColor` * `textDocument/colorPresentation` * `textDocument/rangeFormatting` * `textDocument/onTypeFormatting` * `textDocument/foldingRange` ### Changed * Server will accept the `initialize` request from the client only once and will respond with JSON-RPC error code `-32600` if sent again (PR #160). ### Fixed * Fix broken links and improve documentation (PRs #152 #157 #158). ## [0.9.0] - 2020-03-04 ### Added * Add `info!()` message when server initializes to be consistent with the existing `info!()` message that is emitted when the server exits. * Implement support for the following client-to-server messages: * `textDocument/references` * `textDocument/documentLink` * `documentLink/resolve` * `textDocument/rename` * `textDocument/prepareRename` * Implement support for the following server-to-client messages: * `window/showMessageRequest` * `workspace/workspaceFolders` * `workspace/configuration` ### Changed * Improve LSP message encoding efficiency (PR #126). * Reduce chattiness of `trace!()` logs (PR #130). * Change all notification trait methods to `async fn` (PR #131). * Implement proper server-to-client request routing (PRs #134 #135). * Rename `Printer` to `Client`. * Change `Client::apply_edit()` to return `Result`. * Change `Client::register_capability()` to return `Result<()>`. * Change `Client::unregister_capability()` to return `Result<()>`. ### Removed * Remove redundant serialization steps from `Client` (PR #129). ## [0.8.0] - 2020-02-28 ### Added * Implement support for the following client-to-server messages: * `textDocument/willSave` * `completionItem/resolve` * `textDocument/documentSymbol` * `textDocument/codeAction` * `textDocument/codeLens` * `codeLens/resolve` * `textDocument/formatting` ### Changed * `LspService::call()` stops serving requests after `exit` notification, meaning there is no longer a need for `ExitReceiver::run_until_exit` and the `Server::serve()` async method can now be awaited directly (PR #117). * Return `Option` as service response type (PR #116). * Disable unused `nom` features for a hopefully lighter build (PR #112). * Link to current version of LSP specification in doc comments (PR #122). ### Fixed * Correctly handle backpressure using `Service::poll_ready()` (PR #117). ### Removed * Remove `ExitReceiver` type and `LspService::close_handle()` method (PR #117). ## [0.7.0] - 2020-02-24 ### Added * Add default implementations to all non-required `LanguageServer` methods. * Add support for emitting custom notifications to clients (PR #99). * Implement support for the following client-to-server messages: * `textDocument/signatureHelp` * `textDocument/implementation` ### Changed * Bump minimum supported Rust version to 1.39.0. * Convert to `std::future` and async/await (PR #101). * Update `futures` crate from 0.1.28 to 0.3. * Update `lsp-types` crate from 0.68.0 to 0.70. * Update `tokio` crate from 0.1.12 to 0.2. * Update `tower-service` crate from 0.2.0 to 0.3. ### Fixed * Fix some incorrect links in doc comments. ## [0.6.0] - 2020-01-07 ### Added * Implement support for the following client-to-server messages: * `textDocument/declaration` * `textDocument/definition` * `textDocument/typeDefinition` ### Changed * Update `lsp-types` crate from 0.63.1 to 0.68.0. ## [0.5.0] - 2019-12-12 ### Added * Add support for Language Server Protocol 3.15. ### Changed * Update `lsp-types` crate from 0.61.0 to 0.63.1. ## [0.4.1] - 2019-12-09 ### Changed * Update `jsonrpc-core` crate from 14.0 to 14.0.5. * Update `jsonrpc-derive` crate from 14.0 to 14.0.5. * Update `log` crate from 0.4.7 to 0.4.8. * Update `serde` crate from 1.0.99 to 1.0.103. * Update `tokio-executor` crate from 0.1.8 to 0.1.9. * Update `env_logger` crate from 0.7.0 to 0.7.1. ### Fixed * Correctly handle LSP requests containing incomplete UTF-8 (PR #66). ## [0.4.0] - 2019-10-02 ### Added * Implement support for `textDocument/completion` request. ### Changed * Expose `Printer` in `LanguageServer::initialize()`. * Update `env_logger` crate from 0.6.2 to 0.7.0. * Update `lsp-types` crate from 0.60.0 to 0.61.0. ### Fixed * Allow `window/logMessage`, `window/showMessage`, and `telemetry/event` server-to-client notifications in `initialize` request (PR #48). * Update links to the LSP specification website to point to the new URL. ## [0.3.1] - 2019-09-08 ### Changed * Use more descriptive message in not initialized JSON-RPC error. * Initialize example server with available features so it can be used as a working mock language server. ### Fixed * Allow JSON data for `telemetry/event` notification to be null. ## [0.3.0] - 2019-09-05 ### Added * Add support for decoding the optional `Content-Type` field in messages. * Implement support for the following client-to-server messages: * `workspace/didChangeWorkspaceFolders` * `workspace/didChangeConfiguration` * `workspace/didChangeWatchedFiles` * `workspace/symbol` * `workspace/executeCommand` * Implement support for the following server-to-client messages: * `telemetry/event` * `client/registerCapability` * `client/unregisterCapability` * `workspace/applyEdit` ### Changed * Bump minimum Rust version to 1.34.0. * Rename `highlight()` to `document_highlight()` to better match the specification. * Make all notification methods into provided methods (PR #34). * Change `LspService` request type from `String` to `Incoming` (PR #28). * Update `Server` to spawn services with `Incoming` request type. * Use `env_logger` to print log messages in examples. ### Fixed * Fix broken doc link to `textDocument/didChange` in `LanguageServer` trait. ## [0.2.0] - 2019-09-03 ### Added * Add `ExitedError` for when calling `LspService` after it has already exited. ### Changed * Language server now returns server error code `-32002` if any method is called before `initialize` request is received, [as per the spec][init]. * `LspService` sets `Service::Error` to `ExitedError`. * `Server` can now accept any service where `Service::Error` is convertible to `Box`. This enables compatibility with most Tower middleware. * Retain error or success from future in `ExitReceiver::run_until_exit()`. * Remove `'static` bounds on some `Server` and `ExitReceiver` methods. [init]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#initialize ## [0.1.0] - 2019-09-02 ### Added * Initial crate release. * Implement support for the following message types: * `initialize` * `initialized` * `shutdown` * `exit` * `window/showMessage` * `window/logMessage` * `textDocument/publishDiagnostics` * `textDocument/didOpen` * `textDocument/didChange` * `textDocument/didSave` * `textDocument/didClose` * `textDocument/hover` * `textDocument/documentHighlight` [Unreleased]: https://github.com/ebkalderon/tower-lsp/compare/v0.20.0...HEAD [0.20.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.19.0...v0.20.0 [0.19.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.18.0...v0.19.0 [0.18.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.17.0...v0.18.0 [0.17.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.16.0...v0.17.0 [0.16.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.15.1...v0.16.0 [0.15.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.15.0...v0.15.1 [0.15.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.14.1...v0.15.0 [0.14.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.14.0...v0.14.1 [0.14.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.13.3...v0.14.0 [0.13.3]: https://github.com/ebkalderon/tower-lsp/compare/v0.13.2...v0.13.3 [0.13.2]: https://github.com/ebkalderon/tower-lsp/compare/v0.13.1...v0.13.2 [0.13.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.13.0...v0.13.1 [0.13.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.12.1...v0.13.0 [0.12.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.12.0...v0.12.1 [0.12.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.11.0...v0.12.0 [0.11.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.10.1...v0.11.0 [0.10.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.10.0...v0.10.1 [0.10.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.9.1...v0.10.0 [0.9.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.5.0...v0.6.0 [0.5.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.3.1...v0.4.0 [0.3.1]: https://github.com/ebkalderon/tower-lsp/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/ebkalderon/tower-lsp/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/ebkalderon/tower-lsp/releases/tag/v0.1.0 tower-lsp-0.20.0/CODE_OF_CONDUCT.md000064400000000000000000000064301046102023000144410ustar 00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ebkalderon@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq tower-lsp-0.20.0/CONTRIBUTING.md000064400000000000000000000100061046102023000140650ustar 00000000000000# Contributing to tower-lsp `tower-lsp` is an open-source project that values community contribution. We could always use a helping hand! What would you like to do? 1. [I want to submit issues or request features.](#submitting-issues) 2. [I want to contribute code.](#contributing-code) 3. [I want to write documentation.](#writing-documentation) ## Submitting issues One way you can help `tower-lsp` is to report bugs or request features on our GitHub issue trackers. We can't fix problems we don't know about, so please report early and often! When reporting a bug or asking for help, please include enough details so that the people helping you can reproduce the behavior you are seeing. For some tips on how to approach this, read about how to produce a [Minimal, Complete, and Verifiable example][mcve]. [mcve]: https://stackoverflow.com/help/mcve When making a feature request, please make it clear what problem you intend to solve with the feature, any ideas for how `tower-lsp` could support solving that problem, any possible alternatives, and any disadvantages. ## Contributing code The general workflow for contributing code to the `tower-lsp` repository is as follows: 1. Fork this repository to your own GitHub account. 2. `git clone` the forked repository to your local machine. 3. Check out the branch you wish to make changes against. 4. Make your changes and push them up to the forked repository. 5. [Open a pull request] against this repository. [Open a pull request]: https://github.com/ebkalderon/tower-lsp/compare Before submitting your pull request, please make sure the following conditions are satisfied: 1. The pull request is based on an up-to-date version of your respective branch. 2. If your pull request introduces new functionality, you have written test cases for them. * Unit tests are placed at the bottom of the same `.rs` file in a submodule called `tests`. See [this example] for reference. * Integration tests are placed in a separate `.rs` file in the `tests` subdirectory. 3. The codebase has been processed with `cargo fmt`. 4. All of the following commands completed without errors: * `cargo build` * `cargo test --all` * `cargo run --example server` [this example]: ./src/codec.rs#L129-L157 We encourage you to check that the test suite passes locally before submitting a pull request with your changes. If anything does not pass, typically it will be easier to iterate and fix it locally than waiting for the CI servers to run tests for you. Thank you very much for your contribution! Now `tower-lsp` will be a little faster, more ergonomic, and more efficient. ## Writing documentation Documentation improvements are always welcome! As with most Rust projects, API documentation is generated directly from doc comments, denoted by either `///` or `//!`, using a tool called Rustdoc. See [the official Rust book's chapter on Rustdoc][rd] for more information on how this works. [rd]: https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments Documentation of any kind should adhere to the following standard: 1. Lines must not extend beyond 80 characters in length. 2. To enhance readability in text editors and terminals, use only *reference style* Markdown links, as shown in the example below. However, if the link points to an anchor that exists on the same page, the *inline style* should be used instead. ```markdown Here is some [example text] with a link in it. While we are at it, here is yet yet [another link][al], but shorter. If we are linking to [an anchor](#anchor) on the same page, we can do this inline. [example text]: https://some.url/ [al]: https://another.url/ ``` When submitting your pull requests, please follow the same workflow described in the [Contributing Code](#contributing-code) section above. ## Code of conduct Please note that this project is released with a [Contributor Code of Conduct]. By participating in this project, you agree to abide by its terms. [Contributor Code of Conduct]: ./CODE_OF_CONDUCT.md tower-lsp-0.20.0/Cargo.lock0000644000000704270000000000100110340ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "async-codec-lite" version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2527c30e3972d8ff366b353125dae828c4252a154dbe6063684f6c5e014760a3" dependencies = [ "anyhow", "bytes", "futures-core", "futures-io", "futures-sink", "log", "pin-project-lite", "thiserror", ] [[package]] name = "async-trait" version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "async-tungstenite" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce01ac37fdc85f10a43c43bc582cbd566720357011578a935761075f898baf58" dependencies = [ "futures-io", "futures-util", "log", "pin-project-lite", "tokio", "tungstenite", ] [[package]] name = "async_io_stream" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", "rustc_version", "tokio", ] [[package]] name = "auto_impl" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" dependencies = [ "proc-macro-error", "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "block-buffer" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "dashmap" version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", "hashbrown", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "data-encoding" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-macro" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "futures-sink" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "http" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "itoa" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "lsp-types" version = "0.94.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" dependencies = [ "bitflags 1.3.2", "serde", "serde_json", "serde_repr", "url", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", "windows-sys 0.45.0", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num_cpus" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot_core" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-sys 0.45.0", ] [[package]] name = "percent-encoding" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pharos" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", "rustc_version", ] [[package]] name = "pin-project" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn 1.0.107", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "ryu" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "serde_json" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_repr" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "sha1" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sharded-slab" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] [[package]] name = "slab" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", ] [[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "thread_local" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg", "bytes", "libc", "memchr", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.42.0", ] [[package]] name = "tokio-macros" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "tokio-util" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "pin-project-lite", "tokio", "tracing", ] [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", "pin-project", "pin-project-lite", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-lsp" version = "0.20.0" dependencies = [ "async-codec-lite", "async-trait", "async-tungstenite", "auto_impl", "bytes", "dashmap", "futures", "httparse", "lsp-types", "memchr", "serde", "serde_json", "tokio", "tokio-util", "tower", "tower-lsp-macros", "tracing", "tracing-subscriber", "ws_stream_tungstenite", ] [[package]] name = "tower-lsp-macros" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", "syn 2.0.28", ] [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", "syn 1.0.107", ] [[package]] name = "tracing-core" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ "nu-ansi-term", "sharded-slab", "smallvec", "thread_local", "tracing-core", "tracing-log", ] [[package]] name = "tungstenite" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" dependencies = [ "byteorder", "bytes", "data-encoding", "http", "httparse", "log", "rand", "sha1", "thiserror", "url", "utf-8", ] [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "ws_stream_tungstenite" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b6e7a5ba9436eb3868b052be83377dc685fad6d2f4cddaa2a2251b673472071" dependencies = [ "async-tungstenite", "async_io_stream", "bitflags 2.3.3", "futures-core", "futures-io", "futures-sink", "futures-util", "log", "pharos", "rustc_version", "tokio", "tungstenite", ] tower-lsp-0.20.0/Cargo.toml0000644000000046260000000000100110550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64.0" name = "tower-lsp" version = "0.20.0" authors = ["Eyal Kalderon "] exclude = ["FEATURES.md"] description = "Language Server Protocol implementation based on Tower" homepage = "https://github.com/ebkalderon/tower-lsp" documentation = "https://docs.rs/tower-lsp/" readme = "README.md" keywords = [ "language-server", "lsp", "tower", ] categories = ["asynchronous"] license = "MIT OR Apache-2.0" repository = "https://github.com/ebkalderon/tower-lsp" [dependencies.async-codec-lite] version = "0.0" optional = true [dependencies.async-trait] version = "0.1" [dependencies.auto_impl] version = "1.0" [dependencies.bytes] version = "1.0" [dependencies.dashmap] version = "5.1" [dependencies.futures] version = "0.3" features = [ "std", "async-await", ] default-features = false [dependencies.httparse] version = "1.8" [dependencies.lsp-types] version = "0.94.1" [dependencies.memchr] version = "2.5" [dependencies.serde] version = "1.0" features = ["derive"] [dependencies.serde_json] version = "1.0" [dependencies.tokio] version = "1.17" optional = true [dependencies.tokio-util] version = "0.7" features = ["codec"] optional = true [dependencies.tower] version = "0.4" features = ["util"] default-features = false [dependencies.tower-lsp-macros] version = "0.9" [dependencies.tracing] version = "0.1" [dev-dependencies.async-tungstenite] version = "0.22" features = ["tokio-runtime"] [dev-dependencies.tokio] version = "1.17" features = [ "io-util", "io-std", "macros", "rt-multi-thread", ] [dev-dependencies.tokio-util] version = "0.7" features = ["compat"] [dev-dependencies.tracing-subscriber] version = "0.3" [dev-dependencies.ws_stream_tungstenite] version = "0.10" features = ["tokio_io"] [features] default = ["runtime-tokio"] proposed = ["lsp-types/proposed"] runtime-agnostic = ["async-codec-lite"] runtime-tokio = [ "tokio", "tokio-util", ] tower-lsp-0.20.0/Cargo.toml.orig000064400000000000000000000032621046102023000145310ustar 00000000000000[package] name = "tower-lsp" version = "0.20.0" authors = ["Eyal Kalderon "] edition = "2021" rust-version = "1.64.0" description = "Language Server Protocol implementation based on Tower" license = "MIT OR Apache-2.0" homepage = "https://github.com/ebkalderon/tower-lsp" repository = "https://github.com/ebkalderon/tower-lsp" documentation = "https://docs.rs/tower-lsp/" readme = "README.md" categories = ["asynchronous"] keywords = ["language-server", "lsp", "tower"] exclude = ["FEATURES.md"] [features] default = ["runtime-tokio"] runtime-agnostic = ["async-codec-lite"] runtime-tokio = ["tokio", "tokio-util"] proposed = ["lsp-types/proposed"] [dependencies] async-codec-lite = { version = "0.0", optional = true } async-trait = "0.1" auto_impl = "1.0" bytes = "1.0" dashmap = "5.1" futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } httparse = "1.8" lsp-types = "0.94.1" memchr = "2.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.17", 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"] } tracing-subscriber = "0.3" tokio = { version = "1.17", 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"] } [workspace] members = [".", "./tower-lsp-macros"] default-members = ["."] tower-lsp-0.20.0/LICENSE-APACHE000064400000000000000000000251371046102023000135730ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tower-lsp-0.20.0/LICENSE-MIT000064400000000000000000000020411046102023000132700ustar 00000000000000Copyright (c) 2023 Eyal Kalderon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tower-lsp-0.20.0/README.md000064400000000000000000000073601046102023000131240ustar 00000000000000# tower-lsp [![Build Status][build-badge]][build-url] [![Crates.io][crates-badge]][crates-url] [![Documentation][docs-badge]][docs-url] [build-badge]: https://github.com/ebkalderon/tower-lsp/workflows/rust/badge.svg [build-url]: https://github.com/ebkalderon/tower-lsp/actions [crates-badge]: https://img.shields.io/crates/v/tower-lsp.svg [crates-url]: https://crates.io/crates/tower-lsp [docs-badge]: https://docs.rs/tower-lsp/badge.svg [docs-url]: https://docs.rs/tower-lsp [Language Server Protocol] implementation for Rust based on [Tower]. [language server protocol]: https://microsoft.github.io/language-server-protocol [tower]: https://github.com/tower-rs/tower Tower is a simple and composable framework for implementing asynchronous services in Rust. Central to Tower is the [`Service`] trait, which provides the necessary abstractions for defining request/response clients and servers. Examples of protocols implemented using the `Service` trait include [`hyper`] for HTTP and [`tonic`] for gRPC. [`service`]: https://docs.rs/tower-service/ [`hyper`]: https://docs.rs/hyper/ [`tonic`]: https://docs.rs/tonic/ This library (`tower-lsp`) provides a simple implementation of the Language Server Protocol (LSP) that makes it easy to write your own language server. It consists of three parts: - The `LanguageServer` trait which defines the behavior of your language server. - The asynchronous `LspService` delegate which wraps your language server implementation and defines the behavior of the protocol. - A `Server` which spawns the `LspService` and processes requests and responses over `stdio` or TCP. ## Example ```rust use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult::default()) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "server initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } } #[tokio::main] async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(stdin, stdout, socket).serve(service).await; } ``` ## Using runtimes other than tokio By default, `tower-lsp` is configured for use with `tokio`. Using `tower-lsp` with other runtimes requires disabling `default-features` and enabling the `runtime-agnostic` feature: ```toml [dependencies.tower-lsp] version = "*" default-features = false features = ["runtime-agnostic"] ``` ## Using proposed features You can use enable proposed features in the [LSP Specification version 3.18](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/) by enabling the `proposed` Cargo crate feature. Note that there are no semver guarantees to the `proposed` features so there may be breaking changes between any type of version in the `proposed` features. ## Ecosystem - [tower-lsp-boilerplate](https://github.com/IWANABETHATGUY/tower-lsp-boilerplate) - Useful GitHub project template which makes writing new language servers easier. ## License `tower-lsp` is free and open source software distributed under the terms of either the [MIT](LICENSE-MIT) or the [Apache 2.0](LICENSE-APACHE) license, at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. tower-lsp-0.20.0/examples/custom_notification.rs000064400000000000000000000051261046102023000201070ustar 00000000000000use serde::{Deserialize, Serialize}; use serde_json::Value; use tower_lsp::jsonrpc::{Error, Result}; use tower_lsp::lsp_types::notification::Notification; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; #[derive(Debug, Deserialize, Serialize)] struct CustomNotificationParams { title: String, message: String, } impl CustomNotificationParams { fn new(title: impl Into, message: impl Into) -> Self { CustomNotificationParams { title: title.into(), message: message.into(), } } } enum CustomNotification {} impl Notification for CustomNotification { type Params = CustomNotificationParams; const METHOD: &'static str = "custom/notification"; } #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["custom.notification".to_string()], work_done_progress_options: Default::default(), }), ..ServerCapabilities::default() }, ..Default::default() }) } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn execute_command(&self, params: ExecuteCommandParams) -> Result> { if params.command == "custom.notification" { self.client .send_notification::(CustomNotificationParams::new( "Hello", "Message", )) .await; self.client .log_message( MessageType::INFO, format!("Command executed with params: {params:?}"), ) .await; Ok(None) } else { Err(Error::invalid_request()) } } } #[tokio::main] async fn main() { #[cfg(feature = "runtime-agnostic")] use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; tracing_subscriber::fmt().init(); let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout()); #[cfg(feature = "runtime-agnostic")] let (stdin, stdout) = (stdin.compat(), stdout.compat_write()); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(stdin, stdout, socket).serve(service).await; } tower-lsp-0.20.0/examples/stdio.rs000064400000000000000000000107401046102023000151470ustar 00000000000000use serde_json::Value; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::INCREMENTAL, )), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_string()]), work_done_progress_options: Default::default(), all_commit_characters: None, ..Default::default() }), execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["dummy.do_something".to_string()], work_done_progress_options: Default::default(), }), workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { supported: Some(true), change_notifications: Some(OneOf::Left(true)), }), file_operations: None, }), ..ServerCapabilities::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) { self.client .log_message(MessageType::INFO, "workspace folders changed!") .await; } async fn did_change_configuration(&self, _: DidChangeConfigurationParams) { self.client .log_message(MessageType::INFO, "configuration changed!") .await; } async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) { self.client .log_message(MessageType::INFO, "watched files have changed!") .await; } async fn execute_command(&self, _: ExecuteCommandParams) -> Result> { self.client .log_message(MessageType::INFO, "command executed!") .await; match self.client.apply_edit(WorkspaceEdit::default()).await { Ok(res) if res.applied => self.client.log_message(MessageType::INFO, "applied").await, Ok(_) => self.client.log_message(MessageType::INFO, "rejected").await, Err(err) => self.client.log_message(MessageType::ERROR, err).await, } Ok(None) } async fn did_open(&self, _: DidOpenTextDocumentParams) { self.client .log_message(MessageType::INFO, "file opened!") .await; } async fn did_change(&self, _: DidChangeTextDocumentParams) { self.client .log_message(MessageType::INFO, "file changed!") .await; } async fn did_save(&self, _: DidSaveTextDocumentParams) { self.client .log_message(MessageType::INFO, "file saved!") .await; } async fn did_close(&self, _: DidCloseTextDocumentParams) { self.client .log_message(MessageType::INFO, "file closed!") .await; } async fn completion(&self, _: CompletionParams) -> Result> { Ok(Some(CompletionResponse::Array(vec![ CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()), ]))) } } #[tokio::main] async fn main() { #[cfg(feature = "runtime-agnostic")] use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; tracing_subscriber::fmt().init(); let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout()); #[cfg(feature = "runtime-agnostic")] let (stdin, stdout) = (stdin.compat(), stdout.compat_write()); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(stdin, stdout, socket).serve(service).await; } tower-lsp-0.20.0/examples/tcp.rs000064400000000000000000000127211046102023000146140ustar 00000000000000use serde_json::Value; use tokio::net::{TcpListener, TcpStream}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::INCREMENTAL, )), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_string()]), work_done_progress_options: Default::default(), all_commit_characters: None, ..Default::default() }), execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["dummy.do_something".to_string()], work_done_progress_options: Default::default(), }), workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { supported: Some(true), change_notifications: Some(OneOf::Left(true)), }), file_operations: None, }), ..ServerCapabilities::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) { self.client .log_message(MessageType::INFO, "workspace folders changed!") .await; } async fn did_change_configuration(&self, _: DidChangeConfigurationParams) { self.client .log_message(MessageType::INFO, "configuration changed!") .await; } async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) { self.client .log_message(MessageType::INFO, "watched files have changed!") .await; } async fn execute_command(&self, _: ExecuteCommandParams) -> Result> { self.client .log_message(MessageType::INFO, "command executed!") .await; match self.client.apply_edit(WorkspaceEdit::default()).await { Ok(res) if res.applied => self.client.log_message(MessageType::INFO, "applied").await, Ok(_) => self.client.log_message(MessageType::INFO, "rejected").await, Err(err) => self.client.log_message(MessageType::ERROR, err).await, } Ok(None) } async fn did_open(&self, _: DidOpenTextDocumentParams) { self.client .log_message(MessageType::INFO, "file opened!") .await; } async fn did_change(&self, _: DidChangeTextDocumentParams) { self.client .log_message(MessageType::INFO, "file changed!") .await; } async fn did_save(&self, _: DidSaveTextDocumentParams) { self.client .log_message(MessageType::INFO, "file saved!") .await; } async fn did_close(&self, _: DidCloseTextDocumentParams) { self.client .log_message(MessageType::INFO, "file closed!") .await; } async fn completion(&self, _: CompletionParams) -> Result> { Ok(Some(CompletionResponse::Array(vec![ CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()), ]))) } } #[tokio::main] async fn main() { #[cfg(feature = "runtime-agnostic")] use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; tracing_subscriber::fmt().init(); let mut args = std::env::args(); let stream = match args.nth(1).as_deref() { None => { // If no argument is supplied (args is just the program name), then // we presume that the client has opened the TCP port and is waiting // for us to connect. This is the connection pattern used by clients // built with vscode-langaugeclient. TcpStream::connect("127.0.0.1:9257").await.unwrap() } Some("--listen") => { // If the `--listen` argument is supplied, then the roles are // reversed: we need to start a server and wait for the client to // connect. let listener = TcpListener::bind("127.0.0.1:9257").await.unwrap(); let (stream, _) = listener.accept().await.unwrap(); stream } Some(arg) => panic!( "Unrecognized argument: {}. Use --listen to listen for connections.", arg ), }; let (read, write) = tokio::io::split(stream); #[cfg(feature = "runtime-agnostic")] let (read, write) = (read.compat(), write.compat_write()); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(read, write, socket).serve(service).await; } tower-lsp-0.20.0/examples/wasm/README.md000064400000000000000000000003451046102023000157050ustar 00000000000000# WebAssembly tower-lsp example See [tower-lsp-web-demo](https://github.com/silvanshade/tower-lsp-web-demo) for a complete example that uses `tower-lsp` to build a language server which compiles to Wasm and runs in the browser. tower-lsp-0.20.0/examples/websocket.rs000064400000000000000000000112301046102023000160060ustar 00000000000000use async_tungstenite::tokio::accept_async; use serde_json::Value; use tokio::net::TcpListener; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; use tracing::info; use ws_stream_tungstenite::*; #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::INCREMENTAL, )), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_string()]), ..Default::default() }), execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["dummy.do_something".to_string()], ..Default::default() }), workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { supported: Some(true), change_notifications: Some(OneOf::Left(true)), }), ..Default::default() }), ..ServerCapabilities::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) { self.client .log_message(MessageType::INFO, "workspace folders changed!") .await; } async fn did_change_configuration(&self, _: DidChangeConfigurationParams) { self.client .log_message(MessageType::INFO, "configuration changed!") .await; } async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) { self.client .log_message(MessageType::INFO, "watched files have changed!") .await; } async fn execute_command(&self, _: ExecuteCommandParams) -> Result> { self.client .log_message(MessageType::INFO, "command executed!") .await; match self.client.apply_edit(WorkspaceEdit::default()).await { Ok(res) if res.applied => self.client.log_message(MessageType::INFO, "applied").await, Ok(_) => self.client.log_message(MessageType::INFO, "rejected").await, Err(err) => self.client.log_message(MessageType::ERROR, err).await, } Ok(None) } async fn did_open(&self, _: DidOpenTextDocumentParams) { self.client .log_message(MessageType::INFO, "file opened!") .await; } async fn did_change(&self, _: DidChangeTextDocumentParams) { self.client .log_message(MessageType::INFO, "file changed!") .await; } async fn did_save(&self, _: DidSaveTextDocumentParams) { self.client .log_message(MessageType::INFO, "file saved!") .await; } async fn did_close(&self, _: DidCloseTextDocumentParams) { self.client .log_message(MessageType::INFO, "file closed!") .await; } async fn completion(&self, _: CompletionParams) -> Result> { Ok(Some(CompletionResponse::Array(vec![ CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()), ]))) } } #[tokio::main] async fn main() { #[cfg(feature = "runtime-agnostic")] use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; tracing_subscriber::fmt().init(); let listener = TcpListener::bind("127.0.0.1:9257").await.unwrap(); info!("Listening on {}", listener.local_addr().unwrap()); let (stream, _) = listener.accept().await.unwrap(); let (read, write) = tokio::io::split(WsStream::new(accept_async(stream).await.unwrap())); #[cfg(feature = "runtime-agnostic")] let (read, write) = (read.compat(), write.compat_write()); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(read, write, socket).serve(service).await; } tower-lsp-0.20.0/rust-toolchain000064400000000000000000000000071046102023000145320ustar 000000000000001.64.0 tower-lsp-0.20.0/src/codec.rs000064400000000000000000000330271046102023000140560ustar 00000000000000//! Encoder and decoder for Language Server Protocol messages. use std::error::Error; use std::fmt::{self, Display, Formatter}; use std::io::{Error as IoError, Write}; use std::marker::PhantomData; use std::num::ParseIntError; use std::str::Utf8Error; use bytes::buf::BufMut; use bytes::{Buf, BytesMut}; use memchr::memmem; use serde::{de::DeserializeOwned, Serialize}; use tracing::{trace, warn}; #[cfg(feature = "runtime-agnostic")] use async_codec_lite::{Decoder, Encoder}; #[cfg(feature = "runtime-tokio")] use tokio_util::codec::{Decoder, Encoder}; /// Errors that can occur when processing an LSP message. #[derive(Debug)] pub enum ParseError { /// Failed to parse the JSON body. Body(serde_json::Error), /// Failed to encode the response. Encode(IoError), /// Failed to parse headers. Headers(httparse::Error), /// The media type in the `Content-Type` header is invalid. InvalidContentType, /// The length value in the `Content-Length` header is invalid. InvalidContentLength(ParseIntError), /// Request lacks the required `Content-Length` header. MissingContentLength, /// Request contains invalid UTF8. Utf8(Utf8Error), } impl Display for ParseError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { ParseError::Body(ref e) => write!(f, "unable to parse JSON body: {e}"), ParseError::Encode(ref e) => write!(f, "failed to encode response: {e}"), ParseError::Headers(ref e) => write!(f, "failed to parse headers: {e}"), ParseError::InvalidContentType => write!(f, "unable to parse content type"), ParseError::InvalidContentLength(ref e) => { write!(f, "unable to parse content length: {e}") } ParseError::MissingContentLength => { write!(f, "missing required `Content-Length` header") } ParseError::Utf8(ref e) => write!(f, "request contains invalid UTF8: {e}"), } } } impl Error for ParseError { fn source(&self) -> Option<&(dyn Error + 'static)> { match *self { ParseError::Body(ref e) => Some(e), ParseError::Encode(ref e) => Some(e), ParseError::InvalidContentLength(ref e) => Some(e), ParseError::Utf8(ref e) => Some(e), _ => None, } } } impl From for ParseError { fn from(error: serde_json::Error) -> Self { ParseError::Body(error) } } impl From for ParseError { fn from(error: IoError) -> Self { ParseError::Encode(error) } } impl From for ParseError { fn from(error: httparse::Error) -> Self { ParseError::Headers(error) } } impl From for ParseError { fn from(error: ParseIntError) -> Self { ParseError::InvalidContentLength(error) } } impl From for ParseError { fn from(error: Utf8Error) -> Self { ParseError::Utf8(error) } } /// Encodes and decodes Language Server Protocol messages. pub struct LanguageServerCodec { content_len: Option, _marker: PhantomData, } impl Default for LanguageServerCodec { fn default() -> Self { LanguageServerCodec { content_len: None, _marker: PhantomData, } } } #[cfg(feature = "runtime-agnostic")] impl Encoder for LanguageServerCodec { type Item = T; type Error = ParseError; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { let msg = serde_json::to_string(&item)?; trace!("-> {}", msg); // Reserve just enough space to hold the `Content-Length: ` and `\r\n\r\n` constants, // the length of the message, and the message body. dst.reserve(msg.len() + number_of_digits(msg.len()) + 20); let mut writer = dst.writer(); write!(writer, "Content-Length: {}\r\n\r\n{}", msg.len(), msg)?; writer.flush()?; Ok(()) } } #[cfg(feature = "runtime-tokio")] impl Encoder for LanguageServerCodec { type Error = ParseError; fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> { let msg = serde_json::to_string(&item)?; trace!("-> {}", msg); // Reserve just enough space to hold the `Content-Length: ` and `\r\n\r\n` constants, // the length of the message, and the message body. dst.reserve(msg.len() + number_of_digits(msg.len()) + 20); let mut writer = dst.writer(); write!(writer, "Content-Length: {}\r\n\r\n{}", msg.len(), msg)?; writer.flush()?; Ok(()) } } fn number_of_digits(mut n: usize) -> usize { let mut num_digits = 0; while n > 0 { n /= 10; num_digits += 1; } num_digits } impl Decoder for LanguageServerCodec { type Item = T; type Error = ParseError; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if let Some(content_len) = self.content_len { if src.len() < content_len { return Ok(None); } let bytes = &src[..content_len]; let message = std::str::from_utf8(bytes)?; let result = if message.is_empty() { Ok(None) } else { trace!("<- {}", message); match serde_json::from_str(message) { Ok(parsed) => Ok(Some(parsed)), Err(err) => Err(err.into()), } }; src.advance(content_len); self.content_len = None; // Reset state in preparation for parsing next message. result } else { let mut dst = [httparse::EMPTY_HEADER; 2]; let (headers_len, headers) = match httparse::parse_headers(src, &mut dst)? { httparse::Status::Complete(output) => output, httparse::Status::Partial => return Ok(None), }; match decode_headers(headers) { Ok(content_len) => { src.advance(headers_len); self.content_len = Some(content_len); self.decode(src) // Recurse right back in, now that `Content-Length` is known. } Err(err) => { match err { ParseError::MissingContentLength => {} _ => src.advance(headers_len), } // Skip any garbage bytes by scanning ahead for another potential message. src.advance(memmem::find(src, b"Content-Length").unwrap_or_default()); Err(err) } } } } } fn decode_headers(headers: &[httparse::Header<'_>]) -> Result { let mut content_len = None; for header in headers { match header.name { "Content-Length" => { let string = std::str::from_utf8(header.value)?; let parsed_len = string.parse()?; content_len = Some(parsed_len); } "Content-Type" => { let string = std::str::from_utf8(header.value)?; let charset = string .split(';') .skip(1) .map(|param| param.trim()) .find_map(|param| param.strip_prefix("charset=")); match charset { Some("utf-8") | Some("utf8") => {} _ => return Err(ParseError::InvalidContentType), } } other => warn!("encountered unsupported header: {:?}", other), } } if let Some(content_len) = content_len { Ok(content_len) } else { Err(ParseError::MissingContentLength) } } #[cfg(test)] mod tests { use bytes::BytesMut; use serde_json::Value; use super::*; macro_rules! assert_err { ($expression:expr, $($pattern:tt)+) => { match $expression { $($pattern)+ => (), ref e => panic!("expected `{}` but got `{:?}`", stringify!($($pattern)+), e), } } } fn encode_message(content_type: Option<&str>, message: &str) -> String { let content_type = content_type .map(|ty| format!("\r\nContent-Type: {ty}")) .unwrap_or_default(); format!( "Content-Length: {}{}\r\n\r\n{}", message.len(), content_type, message ) } #[test] fn encode_and_decode() { let decoded = r#"{"jsonrpc":"2.0","method":"exit"}"#; let encoded = encode_message(None, decoded); let mut codec = LanguageServerCodec::default(); let mut buffer = BytesMut::new(); let item: Value = serde_json::from_str(decoded).unwrap(); codec.encode(item, &mut buffer).unwrap(); assert_eq!(buffer, BytesMut::from(encoded.as_str())); let mut buffer = BytesMut::from(encoded.as_str()); let message = codec.decode(&mut buffer).unwrap(); let decoded = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(decoded)); } #[test] fn decodes_optional_content_type() { let decoded = r#"{"jsonrpc":"2.0","method":"exit"}"#; let content_type = "application/vscode-jsonrpc; charset=utf-8"; let encoded = encode_message(Some(content_type), decoded); let mut codec = LanguageServerCodec::default(); let mut buffer = BytesMut::from(encoded.as_str()); let message = codec.decode(&mut buffer).unwrap(); let decoded_: Value = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(decoded_)); let content_type = "application/vscode-jsonrpc; charset=utf8"; let encoded = encode_message(Some(content_type), decoded); let mut buffer = BytesMut::from(encoded.as_str()); let message = codec.decode(&mut buffer).unwrap(); let decoded_: Value = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(decoded_)); let content_type = "application/vscode-jsonrpc; charset=invalid"; let encoded = encode_message(Some(content_type), decoded); let mut buffer = BytesMut::from(encoded.as_str()); assert_err!( codec.decode(&mut buffer), Err(ParseError::InvalidContentType) ); let content_type = "application/vscode-jsonrpc"; let encoded = encode_message(Some(content_type), decoded); let mut buffer = BytesMut::from(encoded.as_str()); assert_err!( codec.decode(&mut buffer), Err(ParseError::InvalidContentType) ); let content_type = "this-mime-should-be-ignored; charset=utf8"; let encoded = encode_message(Some(content_type), decoded); let mut buffer = BytesMut::from(encoded.as_str()); let message = codec.decode(&mut buffer).unwrap(); let decoded_: Value = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(decoded_)); } #[test] fn decodes_zero_length_message() { let content_type = "application/vscode-jsonrpc; charset=utf-8"; let encoded = encode_message(Some(content_type), ""); let mut codec = LanguageServerCodec::default(); let mut buffer = BytesMut::from(encoded.as_str()); let message: Option = codec.decode(&mut buffer).unwrap(); assert_eq!(message, None); } #[test] fn recovers_from_parse_error() { let decoded = r#"{"jsonrpc":"2.0","method":"exit"}"#; let encoded = encode_message(None, decoded); let mixed = format!("foobar{encoded}Content-Length: foobar\r\n\r\n{encoded}"); let mut codec = LanguageServerCodec::default(); let mut buffer = BytesMut::from(mixed.as_str()); assert_err!( codec.decode(&mut buffer), Err(ParseError::MissingContentLength) ); let message: Option = codec.decode(&mut buffer).unwrap(); let first_valid = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(first_valid)); assert_err!( codec.decode(&mut buffer), Err(ParseError::InvalidContentLength(_)) ); let message = codec.decode(&mut buffer).unwrap(); let second_valid = serde_json::from_str(decoded).unwrap(); assert_eq!(message, Some(second_valid)); let message = codec.decode(&mut buffer).unwrap(); assert_eq!(message, None); } #[test] fn decodes_small_chunks() { let decoded = r#"{"jsonrpc":"2.0","method":"exit"}"#; let content_type = "application/vscode-jsonrpc; charset=utf-8"; let encoded = encode_message(Some(content_type), decoded); let mut codec = LanguageServerCodec::default(); let mut buffer = BytesMut::from(encoded.as_str()); let rest = buffer.split_off(40); let message = codec.decode(&mut buffer).unwrap(); assert_eq!(message, None); buffer.unsplit(rest); let rest = buffer.split_off(80); let message = codec.decode(&mut buffer).unwrap(); assert_eq!(message, None); buffer.unsplit(rest); let rest = buffer.split_off(16); let message = codec.decode(&mut buffer).unwrap(); assert_eq!(message, None); buffer.unsplit(rest); let decoded: Value = serde_json::from_str(decoded).unwrap(); let message = codec.decode(&mut buffer).unwrap(); assert_eq!(message, Some(decoded)); } } tower-lsp-0.20.0/src/jsonrpc/error.rs000064400000000000000000000147161046102023000156140ustar 00000000000000//! Error types defined by the JSON-RPC specification. use std::borrow::Cow; use std::fmt::{self, Display, Formatter}; use serde::{Deserialize, Serialize}; use serde_json::Value; /// A specialized [`Result`] error type for JSON-RPC handlers. /// /// [`Result`]: enum@std::result::Result pub type Result = std::result::Result; /// A list of numeric error codes used in JSON-RPC responses. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(into = "i64", from = "i64")] pub enum ErrorCode { /// Invalid JSON was received by the server. ParseError, /// The JSON sent is not a valid Request object. InvalidRequest, /// The method does not exist / is not available. MethodNotFound, /// Invalid method parameter(s). InvalidParams, /// Internal JSON-RPC error. InternalError, /// Reserved for implementation-defined server errors. ServerError(i64), /// The request was cancelled by the client. /// /// # Compatibility /// /// This error code is defined by the Language Server Protocol. RequestCancelled, /// The request was invalidated by another incoming request. /// /// # Compatibility /// /// This error code is specific to the Language Server Protocol. ContentModified, } impl ErrorCode { /// Returns the integer error code value. pub const fn code(&self) -> i64 { match *self { ErrorCode::ParseError => -32700, ErrorCode::InvalidRequest => -32600, ErrorCode::MethodNotFound => -32601, ErrorCode::InvalidParams => -32602, ErrorCode::InternalError => -32603, ErrorCode::RequestCancelled => -32800, ErrorCode::ContentModified => -32801, ErrorCode::ServerError(code) => code, } } /// Returns a human-readable description of the error. pub const fn description(&self) -> &'static str { match *self { ErrorCode::ParseError => "Parse error", ErrorCode::InvalidRequest => "Invalid request", ErrorCode::MethodNotFound => "Method not found", ErrorCode::InvalidParams => "Invalid params", ErrorCode::InternalError => "Internal error", ErrorCode::RequestCancelled => "Canceled", ErrorCode::ContentModified => "Content modified", ErrorCode::ServerError(_) => "Server error", } } } impl From for ErrorCode { fn from(code: i64) -> Self { match code { -32700 => ErrorCode::ParseError, -32600 => ErrorCode::InvalidRequest, -32601 => ErrorCode::MethodNotFound, -32602 => ErrorCode::InvalidParams, -32603 => ErrorCode::InternalError, -32800 => ErrorCode::RequestCancelled, -32801 => ErrorCode::ContentModified, code => ErrorCode::ServerError(code), } } } impl From for i64 { fn from(code: ErrorCode) -> Self { code.code() } } impl Display for ErrorCode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(&self.code(), f) } } /// A JSON-RPC error object. #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct Error { /// A number indicating the error type that occurred. pub code: ErrorCode, /// A short description of the error. pub message: Cow<'static, str>, /// Additional information about the error, if any. #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, } impl Error { /// Creates a new error from the given `ErrorCode`. pub const fn new(code: ErrorCode) -> Self { Error { code, message: Cow::Borrowed(code.description()), data: None, } } /// Creates a new parse error (`-32700`). pub const fn parse_error() -> Self { Error::new(ErrorCode::ParseError) } /// Creates a new "invalid request" error (`-32600`). pub const fn invalid_request() -> Self { Error::new(ErrorCode::InvalidRequest) } /// Creates a new "method not found" error (`-32601`). pub const fn method_not_found() -> Self { Error::new(ErrorCode::MethodNotFound) } /// Creates a new "invalid params" error (`-32602`). pub fn invalid_params(message: M) -> Self where M: Into>, { Error { code: ErrorCode::InvalidParams, message: message.into(), data: None, } } /// Creates a new internal error (`-32603`). pub const fn internal_error() -> Self { Error::new(ErrorCode::InternalError) } /// Creates a new "request cancelled" error (`-32800`). /// /// # Compatibility /// /// This error code is defined by the Language Server Protocol. pub const fn request_cancelled() -> Self { Error::new(ErrorCode::RequestCancelled) } /// Creates a new "content modified" error (`-32801`). /// /// # Compatibility /// /// This error code is defined by the Language Server Protocol. pub const fn content_modified() -> Self { Error::new(ErrorCode::ContentModified) } } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}: {}", self.code.description(), self.message) } } impl std::error::Error for Error {} /// Error response returned for every request received before the server is initialized. /// /// See [here](https://microsoft.github.io/language-server-protocol/specification#initialize) /// for reference. pub(crate) const fn not_initialized_error() -> Error { Error { code: ErrorCode::ServerError(-32002), message: Cow::Borrowed("Server not initialized"), data: None, } } #[cfg(test)] mod tests { use super::*; #[test] fn error_code_serializes_as_i64() { let serialized = serde_json::to_string(&ErrorCode::ParseError).unwrap(); assert_eq!(serialized, "-32700"); let serialized = serde_json::to_string(&ErrorCode::ServerError(-12345)).unwrap(); assert_eq!(serialized, "-12345"); } #[test] fn error_code_deserializes_from_i64() { let deserialized: ErrorCode = serde_json::from_str("-32700").unwrap(); assert_eq!(deserialized, ErrorCode::ParseError); let deserialized: ErrorCode = serde_json::from_str("-12345").unwrap(); assert_eq!(deserialized, ErrorCode::ServerError(-12345)); } } tower-lsp-0.20.0/src/jsonrpc/request.rs000064400000000000000000000125571046102023000161540ustar 00000000000000use std::borrow::Cow; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use super::{Id, Version}; fn deserialize_some<'de, T, D>(deserializer: D) -> Result, D::Error> where T: Deserialize<'de>, D: Deserializer<'de>, { T::deserialize(deserializer).map(Some) } /// A JSON-RPC request or notification. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct Request { jsonrpc: Version, #[serde(default)] method: Cow<'static, str>, #[serde(default, deserialize_with = "deserialize_some")] #[serde(skip_serializing_if = "Option::is_none")] params: Option, #[serde(default, deserialize_with = "deserialize_some")] #[serde(skip_serializing_if = "Option::is_none")] id: Option, } impl Request { /// Starts building a JSON-RPC method call. /// /// Returns a `RequestBuilder`, which allows setting the `params` field or adding a request ID. pub fn build(method: M) -> RequestBuilder where M: Into>, { RequestBuilder { method: method.into(), params: None, id: None, } } /// Constructs a JSON-RPC request from its corresponding LSP type. /// /// # Panics /// /// Panics if `params` could not be serialized into a [`serde_json::Value`]. Since the /// [`lsp_types::request::Request`] trait promises this invariant is upheld, this should never /// happen in practice (unless the trait was implemented incorrectly). pub(crate) fn from_request(id: Id, params: R::Params) -> Self where R: lsp_types::request::Request, { Request { jsonrpc: Version, method: R::METHOD.into(), params: Some(serde_json::to_value(params).unwrap()), id: Some(id), } } /// Constructs a JSON-RPC notification from its corresponding LSP type. /// /// # Panics /// /// Panics if `params` could not be serialized into a [`serde_json::Value`]. Since the /// [`lsp_types::notification::Notification`] trait promises this invariant is upheld, this /// should never happen in practice (unless the trait was implemented incorrectly). pub(crate) fn from_notification(params: N::Params) -> Self where N: lsp_types::notification::Notification, { Request { jsonrpc: Version, method: N::METHOD.into(), params: Some(serde_json::to_value(params).unwrap()), id: None, } } /// Returns the name of the method to be invoked. pub fn method(&self) -> &str { self.method.as_ref() } /// Returns the unique ID of this request, if present. pub fn id(&self) -> Option<&Id> { self.id.as_ref() } /// Returns the `params` field, if present. pub fn params(&self) -> Option<&Value> { self.params.as_ref() } /// Splits this request into the method name, request ID, and the `params` field, if present. pub fn into_parts(self) -> (Cow<'static, str>, Option, Option) { (self.method, self.id, self.params) } } impl Display for Request { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use std::{io, str}; struct WriterFormatter<'a, 'b: 'a> { inner: &'a mut Formatter<'b>, } impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { fn write(&mut self, buf: &[u8]) -> io::Result { fn io_error(_: E) -> io::Error { // Error value does not matter because fmt::Display impl below just // maps it to fmt::Error io::Error::new(io::ErrorKind::Other, "fmt error") } let s = str::from_utf8(buf).map_err(io_error)?; self.inner.write_str(s).map_err(io_error)?; Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } let mut w = WriterFormatter { inner: f }; serde_json::to_writer(&mut w, self).map_err(|_| fmt::Error) } } impl FromStr for Request { type Err = serde_json::Error; fn from_str(s: &str) -> Result { serde_json::from_str(s) } } /// A builder to construct the properties of a `Request`. /// /// To construct a `RequestBuilder`, refer to [`Request::build`]. #[derive(Debug)] pub struct RequestBuilder { method: Cow<'static, str>, params: Option, id: Option, } impl RequestBuilder { /// Sets the `id` member of the request to the given value. /// /// If this method is not called, the resulting `Request` will be assumed to be a notification. pub fn id>(mut self, id: I) -> Self { self.id = Some(id.into()); self } /// Sets the `params` member of the request to the given value. /// /// This member is omitted from the request by default. pub fn params>(mut self, params: V) -> Self { self.params = Some(params.into()); self } /// Constructs the JSON-RPC request and returns it. pub fn finish(self) -> Request { Request { jsonrpc: Version, method: self.method, params: self.params, id: self.id, } } } tower-lsp-0.20.0/src/jsonrpc/response.rs000064400000000000000000000062601046102023000163140ustar 00000000000000use std::fmt::{self, Debug, Formatter}; use std::str::FromStr; use serde::{Deserialize, Serialize}; use serde_json::Value; use super::{Error, Id, Result, Version}; #[derive(Clone, PartialEq, Deserialize, Serialize)] #[serde(untagged)] enum Kind { Ok { result: Value }, Err { error: Error }, } /// A successful or failed JSON-RPC response. #[derive(Clone, PartialEq, Deserialize, Serialize)] pub struct Response { jsonrpc: Version, #[serde(flatten)] kind: Kind, id: Id, } impl Response { /// Creates a new successful response from a request ID and `Error` object. pub const fn from_ok(id: Id, result: Value) -> Self { Response { jsonrpc: Version, kind: Kind::Ok { result }, id, } } /// Creates a new error response from a request ID and `Error` object. pub const fn from_error(id: Id, error: Error) -> Self { Response { jsonrpc: Version, kind: Kind::Err { error }, id, } } /// Creates a new response from a request ID and either an `Ok(Value)` or `Err(Error)` body. pub fn from_parts(id: Id, body: Result) -> Self { match body { Ok(result) => Response::from_ok(id, result), Err(error) => Response::from_error(id, error), } } /// Splits the response into a request ID paired with either an `Ok(Value)` or `Err(Error)` to /// signify whether the response is a success or failure. pub fn into_parts(self) -> (Id, Result) { match self.kind { Kind::Ok { result } => (self.id, Ok(result)), Kind::Err { error } => (self.id, Err(error)), } } /// Returns `true` if the response indicates success. pub const fn is_ok(&self) -> bool { matches!(self.kind, Kind::Ok { .. }) } /// Returns `true` if the response indicates failure. pub const fn is_error(&self) -> bool { !self.is_ok() } /// Returns the `result` value, if it exists. /// /// This member only exists if the response indicates success. pub const fn result(&self) -> Option<&Value> { match &self.kind { Kind::Ok { result } => Some(result), _ => None, } } /// Returns the `error` value, if it exists. /// /// This member only exists if the response indicates failure. pub const fn error(&self) -> Option<&Error> { match &self.kind { Kind::Err { error } => Some(error), _ => None, } } /// Returns the corresponding request ID, if known. pub const fn id(&self) -> &Id { &self.id } } impl Debug for Response { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut d = f.debug_struct("Response"); d.field("jsonrpc", &self.jsonrpc); match &self.kind { Kind::Ok { result } => d.field("result", result), Kind::Err { error } => d.field("error", error), }; d.field("id", &self.id).finish() } } impl FromStr for Response { type Err = serde_json::Error; fn from_str(s: &str) -> std::result::Result { serde_json::from_str(s) } } tower-lsp-0.20.0/src/jsonrpc/router.rs000064400000000000000000000326701046102023000160020ustar 00000000000000//! Lightweight JSON-RPC router service. use std::collections::HashMap; use std::convert::Infallible; use std::fmt::{self, Debug, Formatter}; use std::future::Future; use std::marker::PhantomData; use std::sync::Arc; use std::task::{Context, Poll}; use futures::future::{self, BoxFuture, FutureExt}; use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; use tower::{util::BoxService, Layer, Service}; use crate::jsonrpc::ErrorCode; use super::{Error, Id, Request, Response}; /// A modular JSON-RPC 2.0 request router service. pub struct Router { server: Arc, methods: HashMap<&'static str, BoxService, E>>, } impl Router { /// Creates a new `Router` with the given shared state. pub fn new(server: S) -> Self { Router { server: Arc::new(server), methods: HashMap::new(), } } /// Returns a reference to the inner server. pub fn inner(&self) -> &S { self.server.as_ref() } /// Registers a new RPC method which constructs a response with the given `callback`. /// /// The `layer` argument can be used to inject middleware into the method handler, if desired. pub fn method(&mut self, name: &'static str, callback: F, layer: L) -> &mut Self where P: FromParams, R: IntoResponse, F: for<'a> Method<&'a S, P, R> + Clone + Send + Sync + 'static, L: Layer>, L::Service: Service, Error = E> + Send + 'static, >::Future: Send + 'static, { let server = &self.server; self.methods.entry(name).or_insert_with(|| { let server = server.clone(); let handler = MethodHandler::new(move |params| { let callback = callback.clone(); let server = server.clone(); async move { callback.invoke(&*server, params).await } }); BoxService::new(layer.layer(handler)) }); self } } impl Debug for Router { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("Router") .field("server", &self.server) .field("methods", &self.methods.keys()) .finish() } } impl Service for Router { type Response = Option; type Error = E; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { if let Some(handler) = self.methods.get_mut(req.method()) { handler.call(req) } else { let (method, id, _) = req.into_parts(); future::ok(id.map(|id| { let mut error = Error::method_not_found(); error.data = Some(Value::from(method)); Response::from_error(id, error) })) .boxed() } } } /// Opaque JSON-RPC method handler. pub struct MethodHandler { f: Box BoxFuture<'static, R> + Send>, _marker: PhantomData, } impl MethodHandler { fn new(handler: F) -> Self where F: Fn(P) -> Fut + Send + 'static, Fut: Future + Send + 'static, { MethodHandler { f: Box::new(move |p| handler(p).boxed()), _marker: PhantomData, } } } impl Service for MethodHandler where P: FromParams, R: IntoResponse, E: Send + 'static, { type Response = Option; type Error = E; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { let (_, id, params) = req.into_parts(); match id { Some(_) if R::is_notification() => return future::ok(().into_response(id)).boxed(), None if !R::is_notification() => return future::ok(None).boxed(), _ => {} } let params = match P::from_params(params) { Ok(params) => params, Err(err) => return future::ok(id.map(|id| Response::from_error(id, err))).boxed(), }; (self.f)(params) .map(move |r| Ok(r.into_response(id))) .boxed() } } /// A trait implemented by all valid JSON-RPC method handlers. /// /// This trait abstracts over the following classes of functions and/or closures: /// /// Signature | Description /// -----------------------------------------------------|--------------------------------- /// `async fn f(&self) -> jsonrpc::Result` | Request without parameters /// `async fn f(&self, params: P) -> jsonrpc::Result` | Request with required parameters /// `async fn f(&self)` | Notification without parameters /// `async fn f(&self, params: P)` | Notification with parameters pub trait Method: private::Sealed { /// The future response value. type Future: Future + Send; /// Invokes the method with the given `server` receiver and parameters. fn invoke(&self, server: S, params: P) -> Self::Future; } /// Support parameter-less JSON-RPC methods. impl Method for F where F: Fn(S) -> Fut, Fut: Future + Send, { type Future = Fut; #[inline] fn invoke(&self, server: S, _: ()) -> Self::Future { self(server) } } /// Support JSON-RPC methods with `params`. impl Method for F where F: Fn(S, P) -> Fut, P: DeserializeOwned, Fut: Future + Send, { type Future = Fut; #[inline] fn invoke(&self, server: S, params: (P,)) -> Self::Future { self(server, params.0) } } /// A trait implemented by all JSON-RPC method parameters. pub trait FromParams: private::Sealed + Send + Sized + 'static { /// Attempts to deserialize `Self` from the `params` value extracted from [`Request`]. fn from_params(params: Option) -> super::Result; } /// Deserialize non-existent JSON-RPC parameters. impl FromParams for () { fn from_params(params: Option) -> super::Result { if let Some(p) = params { Err(Error::invalid_params(format!("Unexpected params: {p}"))) } else { Ok(()) } } } /// Deserialize required JSON-RPC parameters. impl FromParams for (P,) { fn from_params(params: Option) -> super::Result { if let Some(p) = params { serde_json::from_value(p) .map(|params| (params,)) .map_err(|e| Error::invalid_params(e.to_string())) } else { Err(Error::invalid_params("Missing params field")) } } } /// A trait implemented by all JSON-RPC response types. pub trait IntoResponse: private::Sealed + Send + 'static { /// Attempts to construct a [`Response`] using `Self` and a corresponding [`Id`]. fn into_response(self, id: Option) -> Option; /// Returns `true` if this is a notification response type. fn is_notification() -> bool; } /// Support JSON-RPC notification methods. impl IntoResponse for () { fn into_response(self, id: Option) -> Option { id.map(|id| Response::from_error(id, Error::invalid_request())) } #[inline] fn is_notification() -> bool { true } } /// Support JSON-RPC request methods. impl IntoResponse for Result { fn into_response(self, id: Option) -> Option { debug_assert!(id.is_some(), "Requests always contain an `id` field"); if let Some(id) = id { let result = self.and_then(|r| { serde_json::to_value(r).map_err(|e| Error { code: ErrorCode::InternalError, message: e.to_string().into(), data: None, }) }); Some(Response::from_parts(id, result)) } else { None } } #[inline] fn is_notification() -> bool { false } } mod private { pub trait Sealed {} impl Sealed for T {} } #[cfg(test)] mod tests { use serde::{Deserialize, Serialize}; use serde_json::json; use tower::layer::layer_fn; use tower::ServiceExt; use super::*; #[derive(Deserialize, Serialize)] struct Params { foo: i32, bar: String, } struct Mock; impl Mock { async fn request(&self) -> Result { Ok(Value::Null) } async fn request_params(&self, params: Params) -> Result { Ok(params) } async fn notification(&self) {} async fn notification_params(&self, _params: Params) {} } #[tokio::test(flavor = "current_thread")] async fn routes_requests() { let mut router: Router = Router::new(Mock); router .method("first", Mock::request, layer_fn(|s| s)) .method("second", Mock::request_params, layer_fn(|s| s)); let request = Request::build("first").id(0).finish(); let response = router.ready().await.unwrap().call(request).await; assert_eq!(response, Ok(Some(Response::from_ok(0.into(), Value::Null)))); let params = json!({"foo": -123i32, "bar": "hello world"}); let with_params = Request::build("second") .params(params.clone()) .id(1) .finish(); let response = router.ready().await.unwrap().call(with_params).await; assert_eq!(response, Ok(Some(Response::from_ok(1.into(), params)))); } #[tokio::test(flavor = "current_thread")] async fn routes_notifications() { let mut router: Router = Router::new(Mock); router .method("first", Mock::notification, layer_fn(|s| s)) .method("second", Mock::notification_params, layer_fn(|s| s)); let request = Request::build("first").finish(); let response = router.ready().await.unwrap().call(request).await; assert_eq!(response, Ok(None)); let params = json!({"foo": -123i32, "bar": "hello world"}); let with_params = Request::build("second").params(params).finish(); let response = router.ready().await.unwrap().call(with_params).await; assert_eq!(response, Ok(None)); } #[tokio::test(flavor = "current_thread")] async fn rejects_request_with_invalid_params() { let mut router: Router = Router::new(Mock); router.method("request", Mock::request_params, layer_fn(|s| s)); let invalid_params = Request::build("request") .params(json!("wrong")) .id(0) .finish(); let response = router.ready().await.unwrap().call(invalid_params).await; assert_eq!( response, Ok(Some(Response::from_error( 0.into(), Error::invalid_params("invalid type: string \"wrong\", expected struct Params"), ))) ); } #[tokio::test(flavor = "current_thread")] async fn ignores_notification_with_invalid_params() { let mut router: Router = Router::new(Mock); router.method("notification", Mock::request_params, layer_fn(|s| s)); let invalid_params = Request::build("notification") .params(json!("wrong")) .finish(); let response = router.ready().await.unwrap().call(invalid_params).await; assert_eq!(response, Ok(None)); } #[tokio::test(flavor = "current_thread")] async fn handles_incorrect_request_types() { let mut router: Router = Router::new(Mock); router .method("first", Mock::request, layer_fn(|s| s)) .method("second", Mock::notification, layer_fn(|s| s)); let request = Request::build("first").finish(); let response = router.ready().await.unwrap().call(request).await; assert_eq!(response, Ok(None)); let request = Request::build("second").id(0).finish(); let response = router.ready().await.unwrap().call(request).await; assert_eq!( response, Ok(Some(Response::from_error( 0.into(), Error::invalid_request(), ))) ); } #[tokio::test(flavor = "current_thread")] async fn responds_to_nonexistent_request() { let mut router: Router = Router::new(Mock); let request = Request::build("foo").id(0).finish(); let response = router.ready().await.unwrap().call(request).await; let mut error = Error::method_not_found(); error.data = Some("foo".into()); assert_eq!(response, Ok(Some(Response::from_error(0.into(), error)))); } #[tokio::test(flavor = "current_thread")] async fn ignores_nonexistent_notification() { let mut router: Router = Router::new(Mock); let request = Request::build("foo").finish(); let response = router.ready().await.unwrap().call(request).await; assert_eq!(response, Ok(None)); } } tower-lsp-0.20.0/src/jsonrpc.rs000064400000000000000000000145511046102023000144600ustar 00000000000000//! A subset of JSON-RPC types used by the Language Server Protocol. pub(crate) use self::error::not_initialized_error; pub use self::error::{Error, ErrorCode, Result}; pub use self::request::{Request, RequestBuilder}; pub use self::response::Response; pub(crate) use self::router::Router; pub use self::router::{FromParams, IntoResponse, Method}; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter}; use lsp_types::NumberOrString; use serde::de::{self, Deserializer}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; mod error; mod request; mod response; mod router; /// A unique ID used to correlate requests and responses together. #[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum Id { /// Numeric ID. Number(i64), /// String ID. String(String), /// Null 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. Null, } impl Default for Id { fn default() -> Self { Id::Null } } impl Display for Id { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Id::Number(id) => Display::fmt(id, f), Id::String(id) => Debug::fmt(id, f), Id::Null => f.write_str("null"), } } } impl From for Id { fn from(n: i64) -> Self { Id::Number(n) } } impl From<&'_ str> for Id { fn from(s: &'_ str) -> Self { Id::String(s.to_string()) } } impl From for Id { fn from(s: String) -> Self { Id::String(s) } } impl From for Id { fn from(num_or_str: NumberOrString) -> Self { match num_or_str { NumberOrString::Number(num) => Id::Number(num as i64), NumberOrString::String(s) => Id::String(s), } } } #[derive(Clone, Debug, PartialEq)] struct Version; impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, { #[derive(Deserialize)] struct Inner<'a>(#[serde(borrow)] Cow<'a, str>); let Inner(ver) = Inner::deserialize(deserializer)?; match ver.as_ref() { "2.0" => Ok(Version), _ => Err(de::Error::custom("expected JSON-RPC version \"2.0\"")), } } } impl Serialize for Version { fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, { serializer.serialize_str("2.0") } } /// An incoming or outgoing JSON-RPC message. #[derive(Deserialize, Serialize)] #[cfg_attr(test, derive(Debug, PartialEq))] #[serde(untagged)] pub(crate) enum Message { /// A response message. Response(Response), /// A request or notification message. Request(Request), } #[cfg(test)] mod tests { use serde_json::json; use super::*; #[test] fn incoming_from_str_or_value() { let v = json!({"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":0}); let from_str: Message = serde_json::from_str(&v.to_string()).unwrap(); let from_value: Message = serde_json::from_value(v).unwrap(); assert_eq!(from_str, from_value); } #[test] fn outgoing_from_str_or_value() { let v = json!({"jsonrpc":"2.0","result":{},"id":1}); let from_str: Message = serde_json::from_str(&v.to_string()).unwrap(); let from_value: Message = serde_json::from_value(v).unwrap(); assert_eq!(from_str, from_value); } #[test] fn parses_incoming_message() { let server_request = json!({"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":0}); let incoming = serde_json::from_value(server_request).unwrap(); assert!(matches!(incoming, Message::Request(_))); let server_notif = json!({"jsonrpc":"2.0","method":"initialized","params":{}}); let incoming = serde_json::from_value(server_notif).unwrap(); assert!(matches!(incoming, Message::Request(_))); let client_request = json!({"jsonrpc":"2.0","id":0,"result":[null]}); let incoming = serde_json::from_value(client_request).unwrap(); assert!(matches!(incoming, Message::Response(_))); } #[test] fn parses_outgoing_message() { let client_request = json!({"jsonrpc":"2.0","method":"workspace/configuration","params":{"scopeUri":null,"section":"foo"},"id":0}); let outgoing = serde_json::from_value(client_request).unwrap(); assert!(matches!(outgoing, Message::Request(_))); let client_notif = json!({"jsonrpc":"2.0","method":"window/logMessage","params":{"message":"foo","type":0}}); let outgoing = serde_json::from_value(client_notif).unwrap(); assert!(matches!(outgoing, Message::Request(_))); let server_response = json!({"jsonrpc":"2.0","id":0,"result":[null]}); let outgoing = serde_json::from_value(server_response).unwrap(); assert!(matches!(outgoing, Message::Response(_))); } #[test] fn parses_invalid_server_request() { let unknown_method = json!({"jsonrpc":"2.0","method":"foo"}); let incoming = serde_json::from_value(unknown_method).unwrap(); assert!(matches!(incoming, Message::Request(_))); let unknown_method_with_id = json!({"jsonrpc":"2.0","method":"foo","id":0}); let incoming = serde_json::from_value(unknown_method_with_id).unwrap(); assert!(matches!(incoming, Message::Request(_))); let missing_method = json!({"jsonrpc":"2.0"}); let incoming = serde_json::from_value(missing_method).unwrap(); assert!(matches!(incoming, Message::Request(_))); let missing_method_with_id = json!({"jsonrpc":"2.0","id":0}); let incoming = serde_json::from_value(missing_method_with_id).unwrap(); assert!(matches!(incoming, Message::Request(_))); } #[test] fn accepts_null_request_id() { let request_id: Id = serde_json::from_value(json!(null)).unwrap(); assert_eq!(request_id, Id::Null); } #[test] fn accepts_negative_integer_request_id() { let request_id: Id = serde_json::from_value(json!(-1)).unwrap(); assert_eq!(request_id, Id::Number(-1)); } } tower-lsp-0.20.0/src/lib.rs000064400000000000000000001744711046102023000135600ustar 00000000000000//! Language Server Protocol (LSP) server abstraction for [Tower]. //! //! [Tower]: https://github.com/tower-rs/tower //! //! # Example //! //! ```rust //! use tower_lsp::jsonrpc::Result; //! use tower_lsp::lsp_types::*; //! use tower_lsp::{Client, LanguageServer, LspService, Server}; //! //! #[derive(Debug)] //! struct Backend { //! client: Client, //! } //! //! #[tower_lsp::async_trait] //! impl LanguageServer for Backend { //! async fn initialize(&self, _: InitializeParams) -> Result { //! Ok(InitializeResult { //! capabilities: ServerCapabilities { //! hover_provider: Some(HoverProviderCapability::Simple(true)), //! completion_provider: Some(CompletionOptions::default()), //! ..Default::default() //! }, //! ..Default::default() //! }) //! } //! //! async fn initialized(&self, _: InitializedParams) { //! self.client //! .log_message(MessageType::INFO, "server initialized!") //! .await; //! } //! //! async fn shutdown(&self) -> Result<()> { //! Ok(()) //! } //! //! async fn completion(&self, _: CompletionParams) -> Result> { //! Ok(Some(CompletionResponse::Array(vec![ //! CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), //! CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()) //! ]))) //! } //! //! async fn hover(&self, _: HoverParams) -> Result> { //! Ok(Some(Hover { //! contents: HoverContents::Scalar( //! MarkedString::String("You're hovering!".to_string()) //! ), //! range: None //! })) //! } //! } //! //! #[tokio::main] //! async fn main() { //! # tracing_subscriber::fmt().init(); //! # //! # #[cfg(feature = "runtime-agnostic")] //! # use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; //! # use std::io::Cursor; //! let stdin = tokio::io::stdin(); //! let stdout = tokio::io::stdout(); //! # let message = r#"{"jsonrpc":"2.0","method":"exit"}"#; //! # let (stdin, stdout) = (Cursor::new(format!("Content-Length: {}\r\n\r\n{}", message.len(), message).into_bytes()), Cursor::new(Vec::new())); //! # #[cfg(feature = "runtime-agnostic")] //! # let (stdin, stdout) = (stdin.compat(), stdout.compat_write()); //! //! let (service, socket) = LspService::new(|client| Backend { client }); //! Server::new(stdin, stdout, socket).serve(service).await; //! } //! ``` #![deny(missing_debug_implementations)] #![deny(missing_docs)] #![forbid(unsafe_code)] pub extern crate lsp_types; /// A re-export of [`async-trait`](https://docs.rs/async-trait) for convenience. pub use async_trait::async_trait; pub use self::service::{Client, ClientSocket, ExitedError, LspService, LspServiceBuilder}; pub use self::transport::{Loopback, Server}; use auto_impl::auto_impl; use lsp_types::request::{ GotoDeclarationParams, GotoDeclarationResponse, GotoImplementationParams, GotoImplementationResponse, GotoTypeDefinitionParams, GotoTypeDefinitionResponse, }; use lsp_types::*; use serde_json::Value; use tower_lsp_macros::rpc; use tracing::{error, warn}; use self::jsonrpc::{Error, Result}; pub mod jsonrpc; mod codec; mod service; mod transport; /// Trait implemented by language server backends. /// /// This interface allows servers adhering to the [Language Server Protocol] to be implemented in a /// safe and easily testable way without exposing the low-level implementation details. /// /// [Language Server Protocol]: https://microsoft.github.io/language-server-protocol/ #[rpc] #[async_trait] #[auto_impl(Arc, Box)] pub trait LanguageServer: Send + Sync + 'static { /// The [`initialize`] request is the first request sent from the client to the server. /// /// [`initialize`]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// This method is guaranteed to only execute once. If the client sends this request to the /// server again, the server will respond with JSON-RPC error code `-32600` (invalid request). #[rpc(name = "initialize")] async fn initialize(&self, params: InitializeParams) -> Result; /// The [`initialized`] notification is sent from the client to the server after the client /// received the result of the initialize request but before the client sends anything else. /// /// [`initialized`]: https://microsoft.github.io/language-server-protocol/specification#initialized /// /// The server can use the `initialized` notification, for example, to dynamically register /// capabilities with the client. #[rpc(name = "initialized")] async fn initialized(&self, params: InitializedParams) { let _ = params; } /// The [`shutdown`] request asks the server to gracefully shut down, but to not exit. /// /// [`shutdown`]: https://microsoft.github.io/language-server-protocol/specification#shutdown /// /// This request is often later followed by an [`exit`] notification, which will cause the /// server to exit immediately. /// /// [`exit`]: https://microsoft.github.io/language-server-protocol/specification#exit /// /// This method is guaranteed to only execute once. If the client sends this request to the /// server again, the server will respond with JSON-RPC error code `-32600` (invalid request). #[rpc(name = "shutdown")] async fn shutdown(&self) -> Result<()>; // Document Synchronization /// The [`textDocument/didOpen`] notification is sent from the client to the server to signal /// that a new text document has been opened by the client. /// /// [`textDocument/didOpen`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_didOpen /// /// The document's truth is now managed by the client and the server must not try to read the /// document’s truth using the document's URI. "Open" in this sense means it is managed by the /// client. It doesn't necessarily mean that its content is presented in an editor. #[rpc(name = "textDocument/didOpen")] async fn did_open(&self, params: DidOpenTextDocumentParams) { let _ = params; warn!("Got a textDocument/didOpen notification, but it is not implemented"); } /// The [`textDocument/didChange`] notification is sent from the client to the server to signal /// changes to a text document. /// /// [`textDocument/didChange`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_didChange /// /// This notification will contain a distinct version tag and a list of edits made to the /// document for the server to interpret. #[rpc(name = "textDocument/didChange")] async fn did_change(&self, params: DidChangeTextDocumentParams) { let _ = params; warn!("Got a textDocument/didChange notification, but it is not implemented"); } /// The [`textDocument/willSave`] notification is sent from the client to the server before the /// document is actually saved. /// /// [`textDocument/willSave`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_willSave #[rpc(name = "textDocument/willSave")] async fn will_save(&self, params: WillSaveTextDocumentParams) { let _ = params; warn!("Got a textDocument/willSave notification, but it is not implemented"); } /// The [`textDocument/willSaveWaitUntil`] request is sent from the client to the server before /// the document is actually saved. /// /// [`textDocument/willSaveWaitUntil`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_willSaveWaitUntil /// /// The request can return an array of `TextEdit`s which will be applied to the text document /// before it is saved. /// /// Please note that clients might drop results if computing the text edits took too long or if /// a server constantly fails on this request. This is done to keep the save fast and reliable. #[rpc(name = "textDocument/willSaveWaitUntil")] async fn will_save_wait_until( &self, params: WillSaveTextDocumentParams, ) -> Result>> { let _ = params; error!("Got a textDocument/willSaveWaitUntil request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/didSave`] notification is sent from the client to the server when the /// document was saved in the client. /// /// [`textDocument/didSave`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_didSave #[rpc(name = "textDocument/didSave")] async fn did_save(&self, params: DidSaveTextDocumentParams) { let _ = params; warn!("Got a textDocument/didSave notification, but it is not implemented"); } /// The [`textDocument/didClose`] notification is sent from the client to the server when the /// document got closed in the client. /// /// [`textDocument/didClose`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_didClose /// /// The document's truth now exists where the document's URI points to (e.g. if the document's /// URI is a file URI, the truth now exists on disk). #[rpc(name = "textDocument/didClose")] async fn did_close(&self, params: DidCloseTextDocumentParams) { let _ = params; warn!("Got a textDocument/didClose notification, but it is not implemented"); } // Language Features /// The [`textDocument/declaration`] request asks the server for the declaration location of a /// symbol at a given text document position. /// /// [`textDocument/declaration`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration /// /// # Compatibility /// /// This request was introduced in specification version 3.14.0. /// /// The [`GotoDeclarationResponse::Link`](lsp_types::GotoDefinitionResponse::Link) return value /// was introduced in specification version 3.14.0 and requires client-side support in order to /// be used. It can be returned if the client set the following field to `true` in the /// [`initialize`](Self::initialize) method: /// /// ```text /// InitializeParams::capabilities::text_document::declaration::link_support /// ``` #[rpc(name = "textDocument/declaration")] async fn goto_declaration( &self, params: GotoDeclarationParams, ) -> Result> { let _ = params; error!("Got a textDocument/declaration request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/definition`] request asks the server for the definition location of a /// symbol at a given text document position. /// /// [`textDocument/definition`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_definition /// /// # Compatibility /// /// The [`GotoDefinitionResponse::Link`](lsp_types::GotoDefinitionResponse::Link) return value /// was introduced in specification version 3.14.0 and requires client-side support in order to /// be used. It can be returned if the client set the following field to `true` in the /// [`initialize`](Self::initialize) method: /// /// ```text /// InitializeParams::capabilities::text_document::definition::link_support /// ``` #[rpc(name = "textDocument/definition")] async fn goto_definition( &self, params: GotoDefinitionParams, ) -> Result> { let _ = params; error!("Got a textDocument/definition request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/typeDefinition`] request asks the server for the type definition location of /// a symbol at a given text document position. /// /// [`textDocument/typeDefinition`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. /// /// The [`GotoTypeDefinitionResponse::Link`](lsp_types::GotoDefinitionResponse::Link) return /// value was introduced in specification version 3.14.0 and requires client-side support in /// order to be used. It can be returned if the client set the following field to `true` in the /// [`initialize`](Self::initialize) method: /// /// ```text /// InitializeParams::capabilities::text_document::type_definition::link_support /// ``` #[rpc(name = "textDocument/typeDefinition")] async fn goto_type_definition( &self, params: GotoTypeDefinitionParams, ) -> Result> { let _ = params; error!("Got a textDocument/typeDefinition request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/implementation`] request is sent from the client to the server to resolve /// the implementation location of a symbol at a given text document position. /// /// [`textDocument/implementation`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_implementation /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. /// /// The [`GotoImplementationResponse::Link`](lsp_types::GotoDefinitionResponse::Link) /// return value was introduced in specification version 3.14.0 and requires client-side /// support in order to be used. It can be returned if the client set the following field to /// `true` in the [`initialize`](Self::initialize) method: /// /// ```text /// InitializeParams::capabilities::text_document::implementation::link_support /// ``` #[rpc(name = "textDocument/implementation")] async fn goto_implementation( &self, params: GotoImplementationParams, ) -> Result> { let _ = params; error!("Got a textDocument/implementation request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/references`] request is sent from the client to the server to resolve /// project-wide references for the symbol denoted by the given text document position. /// /// [`textDocument/references`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_references #[rpc(name = "textDocument/references")] async fn references(&self, params: ReferenceParams) -> Result>> { let _ = params; error!("Got a textDocument/references request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/prepareCallHierarchy`] request is sent from the client to the server to /// return a call hierarchy for the language element of given text document positions. /// /// [`textDocument/prepareCallHierarchy`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareCallHierarchy /// /// The call hierarchy requests are executed in two steps: /// /// 1. First, a call hierarchy item is resolved for the given text document position (this /// method). /// 2. For a call hierarchy item, the incoming or outgoing call hierarchy items are resolved /// inside [`incoming_calls`] and [`outgoing_calls`], respectively. /// /// [`incoming_calls`]: Self::incoming_calls /// [`outgoing_calls`]: Self::outgoing_calls /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/prepareCallHierarchy")] async fn prepare_call_hierarchy( &self, params: CallHierarchyPrepareParams, ) -> Result>> { let _ = params; error!("Got a textDocument/prepareCallHierarchy request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`callHierarchy/incomingCalls`] request is sent from the client to the server to /// resolve **incoming** calls for a given call hierarchy item. /// /// The request doesn't define its own client and server capabilities. It is only issued if a /// server registers for the [`textDocument/prepareCallHierarchy`] request. /// /// [`callHierarchy/incomingCalls`]: https://microsoft.github.io/language-server-protocol/specification#callHierarchy_incomingCalls /// [`textDocument/prepareCallHierarchy`]: Self::prepare_call_hierarchy /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "callHierarchy/incomingCalls")] async fn incoming_calls( &self, params: CallHierarchyIncomingCallsParams, ) -> Result>> { let _ = params; error!("Got a callHierarchy/incomingCalls request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`callHierarchy/outgoingCalls`] request is sent from the client to the server to /// resolve **outgoing** calls for a given call hierarchy item. /// /// The request doesn't define its own client and server capabilities. It is only issued if a /// server registers for the [`textDocument/prepareCallHierarchy`] request. /// /// [`callHierarchy/outgoingCalls`]: https://microsoft.github.io/language-server-protocol/specification#callHierarchy_outgoingCalls /// [`textDocument/prepareCallHierarchy`]: Self::prepare_call_hierarchy /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "callHierarchy/outgoingCalls")] async fn outgoing_calls( &self, params: CallHierarchyOutgoingCallsParams, ) -> Result>> { let _ = params; error!("Got a callHierarchy/outgoingCalls request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/prepareTypeHierarchy`] request is sent from the client to the server to /// return a type hierarchy for the language element of given text document positions. /// /// [`textDocument/prepareTypeHierarchy`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareTypeHierarchy /// /// Returns `Ok(None)` if the server couldn’t infer a valid type from the position. /// /// The type hierarchy requests are executed in two steps: /// /// 1. First, a type hierarchy item is prepared for the given text document position. /// 2. For a type hierarchy item, the supertype or subtype type hierarchy items are resolved in /// [`supertypes`](Self::supertypes) and [`subtypes`](Self::subtypes), respectively. /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "textDocument/prepareTypeHierarchy")] async fn prepare_type_hierarchy( &self, params: TypeHierarchyPrepareParams, ) -> Result>> { let _ = params; error!("Got a textDocument/prepareTypeHierarchy request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`typeHierarchy/supertypes`] request is sent from the client to the server to resolve /// the **supertypes** for a given type hierarchy item. /// /// Returns `Ok(None)` if the server couldn’t infer a valid type from item in `params`. /// /// The request doesn’t define its own client and server capabilities. It is only issued if a /// server registers for the `textDocument/prepareTypeHierarchy` request. /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "typeHierarchy/supertypes")] async fn supertypes( &self, params: TypeHierarchySupertypesParams, ) -> Result>> { let _ = params; error!("Got a typeHierarchy/supertypes request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`typeHierarchy/subtypes`] request is sent from the client to the server to resolve /// the **subtypes** for a given type hierarchy item. /// /// Returns `Ok(None)` if the server couldn’t infer a valid type from item in `params`. /// /// The request doesn’t define its own client and server capabilities. It is only issued if a /// server registers for the `textDocument/prepareTypeHierarchy` request. /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "typeHierarchy/subtypes")] async fn subtypes( &self, params: TypeHierarchySubtypesParams, ) -> Result>> { let _ = params; error!("Got a typeHierarchy/subtypes request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/documentHighlight`] request is sent from the client to the server to /// resolve appropriate highlights for a given text document position. /// /// [`textDocument/documentHighlight`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight /// /// For programming languages, this usually highlights all textual references to the symbol /// scoped to this file. /// /// This request differs slightly from `textDocument/references` in that this one is allowed to /// be more fuzzy. #[rpc(name = "textDocument/documentHighlight")] async fn document_highlight( &self, params: DocumentHighlightParams, ) -> Result>> { let _ = params; error!("Got a textDocument/documentHighlight request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/documentLink`] request is sent from the client to the server to request /// the location of links in a document. /// /// A document link is a range in a text document that links to an internal or external /// resource, like another text document or a web site. /// /// [`textDocument/documentLink`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink /// /// # Compatibility /// /// The [`DocumentLink::tooltip`] field was introduced in specification version 3.15.0 and /// requires client-side support in order to be used. It can be returned if the client set the /// following field to `true` in the [`initialize`](Self::initialize) method: /// /// ```text /// InitializeParams::capabilities::text_document::document_link::tooltip_support /// ``` #[rpc(name = "textDocument/documentLink")] async fn document_link(&self, params: DocumentLinkParams) -> Result>> { let _ = params; error!("Got a textDocument/documentLink request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`documentLink/resolve`] request is sent from the client to the server to resolve the /// target of a given document link. /// /// [`documentLink/resolve`]: https://microsoft.github.io/language-server-protocol/specification#documentLink_resolve /// /// A document link is a range in a text document that links to an internal or external /// resource, like another text document or a web site. #[rpc(name = "documentLink/resolve")] async fn document_link_resolve(&self, params: DocumentLink) -> Result { let _ = params; error!("Got a documentLink/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/hover`] request asks the server for hover information at a given text /// document position. /// /// [`textDocument/hover`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_hover /// /// Such hover information typically includes type signature information and inline /// documentation for the symbol at the given text document position. #[rpc(name = "textDocument/hover")] async fn hover(&self, params: HoverParams) -> Result> { let _ = params; error!("Got a textDocument/hover request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/codeLens`] request is sent from the client to the server to compute code /// lenses for a given text document. /// /// [`textDocument/codeLens`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens #[rpc(name = "textDocument/codeLens")] async fn code_lens(&self, params: CodeLensParams) -> Result>> { let _ = params; error!("Got a textDocument/codeLens request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`codeLens/resolve`] request is sent from the client to the server to resolve the /// command for a given code lens item. /// /// [`codeLens/resolve`]: https://microsoft.github.io/language-server-protocol/specification#codeLens_resolve #[rpc(name = "codeLens/resolve")] async fn code_lens_resolve(&self, params: CodeLens) -> Result { let _ = params; error!("Got a codeLens/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/foldingRange`] request is sent from the client to the server to return /// all folding ranges found in a given text document. /// /// [`textDocument/foldingRange`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange /// /// # Compatibility /// /// This request was introduced in specification version 3.10.0. #[rpc(name = "textDocument/foldingRange")] async fn folding_range(&self, params: FoldingRangeParams) -> Result>> { let _ = params; error!("Got a textDocument/foldingRange request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/selectionRange`] request is sent from the client to the server to return /// suggested selection ranges at an array of given positions. A selection range is a range /// around the cursor position which the user might be interested in selecting. /// /// [`textDocument/selectionRange`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange /// /// A selection range in the return array is for the position in the provided parameters at the /// same index. Therefore `params.positions[i]` must be contained in `result[i].range`. /// /// # Compatibility /// /// This request was introduced in specification version 3.15.0. #[rpc(name = "textDocument/selectionRange")] async fn selection_range( &self, params: SelectionRangeParams, ) -> Result>> { let _ = params; error!("Got a textDocument/selectionRange request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/documentSymbol`] request is sent from the client to the server to /// retrieve all symbols found in a given text document. /// /// [`textDocument/documentSymbol`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol /// /// The returned result is either: /// /// * [`DocumentSymbolResponse::Flat`] which is a flat list of all symbols found in a given /// text document. Then neither the symbol’s location range nor the symbol’s container name /// should be used to infer a hierarchy. /// * [`DocumentSymbolResponse::Nested`] which is a hierarchy of symbols found in a given text /// document. #[rpc(name = "textDocument/documentSymbol")] async fn document_symbol( &self, params: DocumentSymbolParams, ) -> Result> { let _ = params; error!("Got a textDocument/documentSymbol request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/semanticTokens/full`] request is sent from the client to the server to /// resolve the semantic tokens of a given file. /// /// [`textDocument/semanticTokens/full`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens /// /// Semantic tokens are used to add additional color information to a file that depends on /// language specific symbol information. A semantic token request usually produces a large /// result. The protocol therefore supports encoding tokens with numbers. In addition, optional /// support for deltas is available, i.e. [`semantic_tokens_full_delta`]. /// /// [`semantic_tokens_full_delta`]: Self::semantic_tokens_full_delta /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/semanticTokens/full")] async fn semantic_tokens_full( &self, params: SemanticTokensParams, ) -> Result> { let _ = params; error!("Got a textDocument/semanticTokens/full request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/semanticTokens/full/delta`] request is sent from the client to the server to /// resolve the semantic tokens of a given file, **returning only the delta**. /// /// [`textDocument/semanticTokens/full/delta`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens /// /// Similar to [`semantic_tokens_full`](Self::semantic_tokens_full), except it returns a /// sequence of [`SemanticTokensEdit`] to transform a previous result into a new result. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/semanticTokens/full/delta")] async fn semantic_tokens_full_delta( &self, params: SemanticTokensDeltaParams, ) -> Result> { let _ = params; error!("Got a textDocument/semanticTokens/full/delta request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/semanticTokens/range`] request is sent from the client to the server to /// resolve the semantic tokens **for the visible range** of a given file. /// /// [`textDocument/semanticTokens/range`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens /// /// When a user opens a file, it can be beneficial to only compute the semantic tokens for the /// visible range (faster rendering of the tokens in the user interface). If a server can /// compute these tokens faster than for the whole file, it can implement this method to handle /// this special case. /// /// See the [`semantic_tokens_full`](Self::semantic_tokens_full) documentation for more /// details. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/semanticTokens/range")] async fn semantic_tokens_range( &self, params: SemanticTokensRangeParams, ) -> Result> { let _ = params; error!("Got a textDocument/semanticTokens/range request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/inlineValue`] request is sent from the client to the server to compute /// inline values for a given text document that may be rendered in the editor at the end of /// lines. /// /// [`textDocument/inlineValue`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_inlineValue /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "textDocument/inlineValue")] async fn inline_value(&self, params: InlineValueParams) -> Result>> { let _ = params; error!("Got a textDocument/inlineValue request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/inlayHint`] request is sent from the client to the server to compute /// inlay hints for a given `(text document, range)` tuple that may be rendered in the editor /// in place with other text. /// /// [`textDocument/inlayHint`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0 #[rpc(name = "textDocument/inlayHint")] async fn inlay_hint(&self, params: InlayHintParams) -> Result>> { let _ = params; error!("Got a textDocument/inlayHint request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`inlayHint/resolve`] request is sent from the client to the server to resolve /// additional information for a given inlay hint. /// /// [`inlayHint/resolve`]: https://microsoft.github.io/language-server-protocol/specification#inlayHint_resolve /// /// This is usually used to compute the tooltip, location or command properties of an inlay /// hint’s label part to avoid its unnecessary computation during the `textDocument/inlayHint` /// request. /// /// Consider a client announces the `label.location` property as a property that can be /// resolved lazily using the client capability: /// /// ```js /// textDocument.inlayHint.resolveSupport = { properties: ['label.location'] }; /// ``` /// /// then an inlay hint with a label part, but without a location, must be resolved using the /// `inlayHint/resolve` request before it can be used. /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0 #[rpc(name = "inlayHint/resolve")] async fn inlay_hint_resolve(&self, params: InlayHint) -> Result { let _ = params; error!("Got a inlayHint/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/moniker`] request is sent from the client to the server to get the /// symbol monikers for a given text document position. /// /// [`textDocument/moniker`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_moniker /// /// An array of `Moniker` types is returned as response to indicate possible monikers at the /// given location. If no monikers can be calculated, `Some(vec![])` or `None` should be /// returned. /// /// # Concept /// /// The Language Server Index Format (LSIF) introduced the concept of _symbol monikers_ to help /// associate symbols across different indexes. This request adds capability for LSP server /// implementations to provide the same symbol moniker information given a text document /// position. /// /// Clients can utilize this method to get the moniker at the current location in a file the /// user is editing and do further code navigation queries in other services that rely on LSIF /// indexes and link symbols together. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/moniker")] async fn moniker(&self, params: MonikerParams) -> Result>> { let _ = params; error!("Got a textDocument/moniker request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/completion`] request is sent from the client to the server to compute /// completion items at a given cursor position. /// /// If computing full completion items is expensive, servers can additionally provide a handler /// for the completion item resolve request (`completionItem/resolve`). This request is sent /// when a completion item is selected in the user interface. /// /// [`textDocument/completion`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_completion /// /// # Compatibility /// /// Since 3.16.0, the client can signal that it can resolve more properties lazily. This is /// done using the `completion_item.resolve_support` client capability which lists all /// properties that can be filled in during a `completionItem/resolve` request. /// /// All other properties (usually `sort_text`, `filter_text`, `insert_text`, and `text_edit`) /// must be provided in the `textDocument/completion` response and must not be changed during /// resolve. #[rpc(name = "textDocument/completion")] async fn completion(&self, params: CompletionParams) -> Result> { let _ = params; error!("Got a textDocument/completion request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`completionItem/resolve`] request is sent from the client to the server to resolve /// additional information for a given completion item. /// /// [`completionItem/resolve`]: https://microsoft.github.io/language-server-protocol/specification#completionItem_resolve #[rpc(name = "completionItem/resolve")] async fn completion_resolve(&self, params: CompletionItem) -> Result { let _ = params; error!("Got a completionItem/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/diagnostic`] request is sent from the client to the server to ask the /// server to compute the diagnostics for a given document. /// /// As with other pull requests, the server is asked to compute the diagnostics for the /// currently synced version of the document. /// /// The request doesn't define its own client and server capabilities. It is only issued if a /// server registers for the [`textDocument/diagnostic`] request. /// /// [`textDocument/diagnostic`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_diagnostic /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "textDocument/diagnostic")] async fn diagnostic( &self, params: DocumentDiagnosticParams, ) -> Result { let _ = params; error!("Got a textDocument/diagnostic request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspace/diagnostic`] request is sent from the client to the server to ask the /// server to compute workspace wide diagnostics which previously where pushed from the server /// to the client. /// /// In contrast to the [`textDocument/diagnostic`] request, the workspace request can be /// long-running and is not bound to a specific workspace or document state. If the client /// supports streaming for the workspace diagnostic pull, it is legal to provide a /// `textDocument/diagnostic` report multiple times for the same document URI. The last one /// reported will win over previous reports. /// /// [`textDocument/diagnostic`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_diagnostic /// /// If a client receives a diagnostic report for a document in a workspace diagnostic request /// for which the client also issues individual document diagnostic pull requests, the client /// needs to decide which diagnostics win and should be presented. In general: /// /// * Diagnostics for a higher document version should win over those from a lower document /// version (e.g. note that document versions are steadily increasing). /// * Diagnostics from a document pull should win over diagnostics from a workspace pull. /// /// The request doesn't define its own client and server capabilities. It is only issued if a /// server registers for the [`workspace/diagnostic`] request. /// /// [`workspace/diagnostic`]: https://microsoft.github.io/language-server-protocol/specification#workspace_diagnostic /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "workspace/diagnostic")] async fn workspace_diagnostic( &self, params: WorkspaceDiagnosticParams, ) -> Result { let _ = params; error!("Got a workspace/diagnostic request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/signatureHelp`] request is sent from the client to the server to request /// signature information at a given cursor position. /// /// [`textDocument/signatureHelp`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp #[rpc(name = "textDocument/signatureHelp")] async fn signature_help(&self, params: SignatureHelpParams) -> Result> { let _ = params; error!("Got a textDocument/signatureHelp request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/codeAction`] request is sent from the client to the server to compute /// commands for a given text document and range. These commands are typically code fixes to /// either fix problems or to beautify/refactor code. /// /// The result of a [`textDocument/codeAction`] request is an array of `Command` literals which /// are typically presented in the user interface. /// /// [`textDocument/codeAction`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction /// /// To ensure that a server is useful in many clients, the commands specified in a code actions /// should be handled by the server and not by the client (see [`workspace/executeCommand`] and /// `ServerCapabilities::execute_command_provider`). If the client supports providing edits /// with a code action, then the mode should be used. /// /// When the command is selected the server should be contacted again (via the /// [`workspace/executeCommand`] request) to execute the command. /// /// [`workspace/executeCommand`]: https://microsoft.github.io/language-server-protocol/specification#workspace_executeCommand /// /// # Compatibility /// /// ## Since version 3.16.0 /// /// A client can offer a server to delay the computation of code action properties during a /// `textDocument/codeAction` request. This is useful for cases where it is expensive to /// compute the value of a property (for example, the `edit` property). /// /// Clients signal this through the `code_action.resolve_support` client capability which lists /// all properties a client can resolve lazily. The server capability /// `code_action_provider.resolve_provider` signals that a server will offer a /// `codeAction/resolve` route. /// /// To help servers uniquely identify a code action in the resolve request, a code action /// literal may optionally carry a `data` property. This is also guarded by an additional /// client capability `code_action.data_support`. In general, a client should offer data /// support if it offers resolve support. /// /// It should also be noted that servers shouldn’t alter existing attributes of a code action /// in a `codeAction/resolve` request. /// /// ## Since version 3.8.0 /// /// Support for [`CodeAction`] literals to enable the following scenarios: /// /// * The ability to directly return a workspace edit from the code action request. /// This avoids having another server roundtrip to execute an actual code action. /// However server providers should be aware that if the code action is expensive to compute /// or the edits are huge it might still be beneficial if the result is simply a command and /// the actual edit is only computed when needed. /// /// * The ability to group code actions using a kind. Clients are allowed to ignore that /// information. However it allows them to better group code action, for example, into /// corresponding menus (e.g. all refactor code actions into a refactor menu). #[rpc(name = "textDocument/codeAction")] async fn code_action(&self, params: CodeActionParams) -> Result> { let _ = params; error!("Got a textDocument/codeAction request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`codeAction/resolve`] request is sent from the client to the server to resolve /// additional information for a given code action. /// /// [`codeAction/resolve`]: https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve /// /// This is usually used to compute the edit property of a [`CodeAction`] to avoid its /// unnecessary computation during the [`textDocument/codeAction`](Self::code_action) request. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "codeAction/resolve")] async fn code_action_resolve(&self, params: CodeAction) -> Result { let _ = params; error!("Got a codeAction/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/documentColor`] request is sent from the client to the server to list /// all color references found in a given text document. Along with the range, a color value in /// RGB is returned. /// /// [`textDocument/documentColor`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor /// /// Clients can use the result to decorate color references in an editor. For example: /// /// * Color boxes showing the actual color next to the reference /// * Show a color picker when a color reference is edited /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. #[rpc(name = "textDocument/documentColor")] async fn document_color(&self, params: DocumentColorParams) -> Result> { let _ = params; error!("Got a textDocument/documentColor request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/colorPresentation`] request is sent from the client to the server to /// obtain a list of presentations for a color value at a given location. /// /// [`textDocument/colorPresentation`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation /// /// Clients can use the result to: /// /// * Modify a color reference /// * Show in a color picker and let users pick one of the presentations /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. /// /// This request has no special capabilities and registration options since it is sent as a /// resolve request for the [`textDocument/documentColor`](Self::document_color) request. #[rpc(name = "textDocument/colorPresentation")] async fn color_presentation( &self, params: ColorPresentationParams, ) -> Result> { let _ = params; error!("Got a textDocument/colorPresentation request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/formatting`] request is sent from the client to the server to format a /// whole document. /// /// [`textDocument/formatting`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting #[rpc(name = "textDocument/formatting")] async fn formatting(&self, params: DocumentFormattingParams) -> Result>> { let _ = params; error!("Got a textDocument/formatting request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/rangeFormatting`] request is sent from the client to the server to /// format a given range in a document. /// /// [`textDocument/rangeFormatting`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting #[rpc(name = "textDocument/rangeFormatting")] async fn range_formatting( &self, params: DocumentRangeFormattingParams, ) -> Result>> { let _ = params; error!("Got a textDocument/rangeFormatting request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/onTypeFormatting`] request is sent from the client to the server to /// format parts of the document during typing. /// /// [`textDocument/onTypeFormatting`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting #[rpc(name = "textDocument/onTypeFormatting")] async fn on_type_formatting( &self, params: DocumentOnTypeFormattingParams, ) -> Result>> { let _ = params; error!("Got a textDocument/onTypeFormatting request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/rename`] request is sent from the client to the server to ask the server /// to compute a workspace change so that the client can perform a workspace-wide rename of a /// symbol. /// /// [`textDocument/rename`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_rename #[rpc(name = "textDocument/rename")] async fn rename(&self, params: RenameParams) -> Result> { let _ = params; error!("Got a textDocument/rename request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/prepareRename`] request is sent from the client to the server to setup /// and test the validity of a rename operation at a given location. /// /// [`textDocument/prepareRename`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename /// /// # Compatibility /// /// This request was introduced in specification version 3.12.0. #[rpc(name = "textDocument/prepareRename")] async fn prepare_rename( &self, params: TextDocumentPositionParams, ) -> Result> { let _ = params; error!("Got a textDocument/prepareRename request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`textDocument/linkedEditingRange`] request is sent from the client to the server to /// return for a given position in a document the range of the symbol at the position and all /// ranges that have the same content. /// /// [`textDocument/linkedEditingRange`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_linkedEditingRange /// /// Optionally a word pattern can be returned to describe valid contents. /// /// A rename to one of the ranges can be applied to all other ranges if the new content is /// valid. If no result-specific word pattern is provided, the word pattern from the client's /// language configuration is used. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "textDocument/linkedEditingRange")] async fn linked_editing_range( &self, params: LinkedEditingRangeParams, ) -> Result> { let _ = params; error!("Got a textDocument/linkedEditingRange request, but it is not implemented"); Err(Error::method_not_found()) } // Workspace Features /// The [`workspace/symbol`] request is sent from the client to the server to list project-wide /// symbols matching the given query string. /// /// [`workspace/symbol`]: https://microsoft.github.io/language-server-protocol/specification#workspace_symbol /// /// # Compatibility /// /// Since 3.17.0, servers can also provider a handler for [`workspaceSymbol/resolve`] requests. /// This allows servers to return workspace symbols without a range for a `workspace/symbol` /// request. Clients then need to resolve the range when necessary using the /// `workspaceSymbol/resolve` request. /// /// [`workspaceSymbol/resolve`]: Self::symbol_resolve /// /// Servers can only use this new model if clients advertise support for it via the /// `workspace.symbol.resolve_support` capability. #[rpc(name = "workspace/symbol")] async fn symbol( &self, params: WorkspaceSymbolParams, ) -> Result>> { let _ = params; error!("Got a workspace/symbol request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspaceSymbol/resolve`] request is sent from the client to the server to resolve /// additional information for a given workspace symbol. /// /// [`workspaceSymbol/resolve`]: https://microsoft.github.io/language-server-protocol/specification#workspace_symbolResolve /// /// See the [`symbol`](Self::symbol) documentation for more details. /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. #[rpc(name = "workspaceSymbol/resolve")] async fn symbol_resolve(&self, params: WorkspaceSymbol) -> Result { let _ = params; error!("Got a workspaceSymbol/resolve request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspace/didChangeConfiguration`] notification is sent from the client to the server /// to signal the change of configuration settings. /// /// [`workspace/didChangeConfiguration`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeConfiguration #[rpc(name = "workspace/didChangeConfiguration")] async fn did_change_configuration(&self, params: DidChangeConfigurationParams) { let _ = params; warn!("Got a workspace/didChangeConfiguration notification, but it is not implemented"); } /// The [`workspace/didChangeWorkspaceFolders`] notification is sent from the client to the /// server to inform about workspace folder configuration changes. /// /// [`workspace/didChangeWorkspaceFolders`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWorkspaceFolders /// /// The notification is sent by default if both of these boolean fields were set to `true` in /// the [`initialize`](Self::initialize) method: /// /// * `InitializeParams::capabilities::workspace::workspace_folders` /// * `InitializeResult::capabilities::workspace::workspace_folders::supported` /// /// This notification is also sent if the server has registered itself to receive this /// notification. #[rpc(name = "workspace/didChangeWorkspaceFolders")] async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) { let _ = params; warn!("Got a workspace/didChangeWorkspaceFolders notification, but it is not implemented"); } /// The [`workspace/willCreateFiles`] request is sent from the client to the server before /// files are actually created as long as the creation is triggered from within the client. /// /// [`workspace/willCreateFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_willCreateFiles /// /// The request can return a [`WorkspaceEdit`] which will be applied to workspace before the /// files are created. Please note that clients might drop results if computing the edit took /// too long or if a server constantly fails on this request. This is done to keep creates fast /// and reliable. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "workspace/willCreateFiles")] async fn will_create_files(&self, params: CreateFilesParams) -> Result> { let _ = params; error!("Got a workspace/willCreateFiles request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspace/didCreateFiles`] request is sent from the client to the server when files /// were created from within the client. /// /// [`workspace/didCreateFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didCreateFiles #[rpc(name = "workspace/didCreateFiles")] async fn did_create_files(&self, params: CreateFilesParams) { let _ = params; warn!("Got a workspace/didCreateFiles notification, but it is not implemented"); } /// The [`workspace/willRenameFiles`] request is sent from the client to the server before /// files are actually renamed as long as the rename is triggered from within the client. /// /// [`workspace/willRenameFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_willRenameFiles /// /// The request can return a [`WorkspaceEdit`] which will be applied to workspace before the /// files are renamed. Please note that clients might drop results if computing the edit took /// too long or if a server constantly fails on this request. This is done to keep creates fast /// and reliable. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "workspace/willRenameFiles")] async fn will_rename_files(&self, params: RenameFilesParams) -> Result> { let _ = params; error!("Got a workspace/willRenameFiles request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspace/didRenameFiles`] notification is sent from the client to the server when /// files were renamed from within the client. /// /// [`workspace/didRenameFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didRenameFiles #[rpc(name = "workspace/didRenameFiles")] async fn did_rename_files(&self, params: RenameFilesParams) { let _ = params; warn!("Got a workspace/didRenameFiles notification, but it is not implemented"); } /// The [`workspace/willDeleteFiles`] request is sent from the client to the server before /// files are actually deleted as long as the deletion is triggered from within the client /// either by a user action or by applying a workspace edit. /// /// [`workspace/willDeleteFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_willDeleteFiles /// /// The request can return a [`WorkspaceEdit`] which will be applied to workspace before the /// files are deleted. Please note that clients might drop results if computing the edit took /// too long or if a server constantly fails on this request. This is done to keep deletions /// fast and reliable. /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. #[rpc(name = "workspace/willDeleteFiles")] async fn will_delete_files(&self, params: DeleteFilesParams) -> Result> { let _ = params; error!("Got a workspace/willDeleteFiles request, but it is not implemented"); Err(Error::method_not_found()) } /// The [`workspace/didDeleteFiles`] notification is sent from the client to the server when /// files were deleted from within the client. /// /// [`workspace/didDeleteFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didDeleteFiles #[rpc(name = "workspace/didDeleteFiles")] async fn did_delete_files(&self, params: DeleteFilesParams) { let _ = params; warn!("Got a workspace/didDeleteFiles notification, but it is not implemented"); } /// The [`workspace/didChangeWatchedFiles`] notification is sent from the client to the server /// when the client detects changes to files watched by the language client. /// /// [`workspace/didChangeWatchedFiles`]: https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWatchedFiles /// /// It is recommended that servers register for these file events using the registration /// mechanism. This can be done here or in the [`initialized`](Self::initialized) method using /// [`Client::register_capability`](crate::Client::register_capability). #[rpc(name = "workspace/didChangeWatchedFiles")] async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { let _ = params; warn!("Got a workspace/didChangeWatchedFiles notification, but it is not implemented"); } /// The [`workspace/executeCommand`] request is sent from the client to the server to trigger /// command execution on the server. /// /// [`workspace/executeCommand`]: https://microsoft.github.io/language-server-protocol/specification#workspace_executeCommand /// /// In most cases, the server creates a [`WorkspaceEdit`] structure and applies the changes to /// the workspace using `Client::apply_edit()` before returning from this function. #[rpc(name = "workspace/executeCommand")] async fn execute_command(&self, params: ExecuteCommandParams) -> Result> { let _ = params; error!("Got a workspace/executeCommand request, but it is not implemented"); Err(Error::method_not_found()) } // TODO: Add `work_done_progress_cancel()` here (since 3.15.0) when supported by `tower-lsp`. // https://github.com/ebkalderon/tower-lsp/issues/176 } fn _assert_object_safe() { fn assert_impl() {} assert_impl::>(); } tower-lsp-0.20.0/src/service/client/pending.rs000064400000000000000000000067641046102023000173530ustar 00000000000000//! Types for tracking server-to-client JSON-RPC requests. use std::fmt::{self, Debug, Formatter}; use std::future::Future; use dashmap::{mapref::entry::Entry, DashMap}; use futures::channel::oneshot; use tracing::warn; use crate::jsonrpc::{Id, Response}; /// A hashmap containing pending client requests, keyed by request ID. pub struct Pending(DashMap>>); impl Pending { /// Creates a new pending client requests map. pub fn new() -> Self { Pending(DashMap::new()) } /// Inserts the given response into the map. /// /// The corresponding `.wait()` future will then resolve to the given value. pub fn insert(&self, r: Response) { match r.id() { Id::Null => warn!("received response with request ID of `null`, ignoring"), id => match self.0.entry(id.clone()) { Entry::Vacant(_) => warn!("received response with unknown request ID: {}", id), Entry::Occupied(mut entry) => { let tx = match entry.get().len() { 1 => entry.remove().remove(0), _ => entry.get_mut().remove(0), }; tx.send(r).expect("receiver already dropped"); } }, } } /// Marks the given request ID as pending and waits for its corresponding response to arrive. /// /// If the same request ID is being waited upon in multiple locations, then the incoming /// response will be routed to one of the callers in a first come, first served basis. To /// ensure correct routing of JSON-RPC requests, each identifier value used _must_ be unique. pub fn wait(&self, id: Id) -> impl Future + Send + 'static { let (tx, rx) = oneshot::channel(); match self.0.entry(id) { Entry::Vacant(entry) => { entry.insert(vec![tx]); } Entry::Occupied(mut entry) => { let txs = entry.get_mut(); txs.reserve(1); // We assume concurrent waits are rare, so reserve one by one. txs.push(tx); } } async { rx.await.expect("sender already dropped") } } } impl Debug for Pending { fn fmt(&self, f: &mut Formatter) -> fmt::Result { #[derive(Debug)] struct Waiters(usize); let iter = self .0 .iter() .map(|e| (e.key().clone(), Waiters(e.value().len()))); f.debug_map().entries(iter).finish() } } #[cfg(test)] mod tests { use serde_json::json; use super::*; #[tokio::test(flavor = "current_thread")] async fn waits_for_client_response() { let pending = Pending::new(); let id = Id::Number(1); let wait_fut = pending.wait(id.clone()); let response = Response::from_ok(id, json!({})); pending.insert(response.clone()); assert_eq!(wait_fut.await, response); } #[tokio::test(flavor = "current_thread")] async fn routes_responses_in_fifo_order() { let pending = Pending::new(); let id = Id::Number(1); let wait_fut1 = pending.wait(id.clone()); let wait_fut2 = pending.wait(id.clone()); let foo = Response::from_ok(id.clone(), json!("foo")); let bar = Response::from_ok(id, json!("bar")); pending.insert(bar.clone()); pending.insert(foo.clone()); assert_eq!(wait_fut1.await, bar); assert_eq!(wait_fut2.await, foo); } } tower-lsp-0.20.0/src/service/client/socket.rs000064400000000000000000000100771046102023000172070ustar 00000000000000//! Loopback connection to the language client. use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use futures::channel::mpsc::Receiver; use futures::sink::Sink; use futures::stream::{FusedStream, Stream, StreamExt}; use super::{ExitedError, Pending, ServerState, State}; use crate::jsonrpc::{Request, Response}; /// A loopback channel for server-to-client communication. #[derive(Debug)] pub struct ClientSocket { pub(super) rx: Receiver, pub(super) pending: Arc, pub(super) state: Arc, } impl ClientSocket { /// Splits this `ClientSocket` into two halves capable of operating independently. /// /// The two halves returned implement the [`Stream`] and [`Sink`] traits, respectively. /// /// [`Stream`]: futures::Stream /// [`Sink`]: futures::Sink pub fn split(self) -> (RequestStream, ResponseSink) { let ClientSocket { rx, pending, state } = self; let state_ = state.clone(); ( RequestStream { rx, state: state_ }, ResponseSink { pending, state }, ) } } /// Yields a stream of pending server-to-client requests. impl Stream for ClientSocket { type Item = Request; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.state.get() == State::Exited || self.rx.is_terminated() { Poll::Ready(None) } else { self.rx.poll_next_unpin(cx) } } fn size_hint(&self) -> (usize, Option) { self.rx.size_hint() } } impl FusedStream for ClientSocket { fn is_terminated(&self) -> bool { self.rx.is_terminated() } } /// Routes client-to-server responses back to the server. impl Sink for ClientSocket { type Error = ExitedError; fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { if self.state.get() == State::Exited || self.rx.is_terminated() { Poll::Ready(Err(ExitedError(()))) } else { Poll::Ready(Ok(())) } } fn start_send(self: Pin<&mut Self>, item: Response) -> Result<(), Self::Error> { self.pending.insert(item); Ok(()) } fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } /// Yields a stream of pending server-to-client requests. #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct RequestStream { rx: Receiver, state: Arc, } impl Stream for RequestStream { type Item = Request; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.state.get() == State::Exited || self.rx.is_terminated() { Poll::Ready(None) } else { self.rx.poll_next_unpin(cx) } } fn size_hint(&self) -> (usize, Option) { self.rx.size_hint() } } impl FusedStream for RequestStream { fn is_terminated(&self) -> bool { self.rx.is_terminated() } } /// Routes client-to-server responses back to the server. #[derive(Debug)] pub struct ResponseSink { pending: Arc, state: Arc, } impl Sink for ResponseSink { type Error = ExitedError; fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { if self.state.get() == State::Exited { Poll::Ready(Err(ExitedError(()))) } else { Poll::Ready(Ok(())) } } fn start_send(self: Pin<&mut Self>, item: Response) -> Result<(), Self::Error> { self.pending.insert(item); Ok(()) } fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } tower-lsp-0.20.0/src/service/client.rs000064400000000000000000000612001046102023000157110ustar 00000000000000//! Types for sending data to and from the language client. pub use self::socket::{ClientSocket, RequestStream, ResponseSink}; use std::fmt::{self, Debug, Display, Formatter}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; use futures::channel::mpsc::{self, Sender}; use futures::future::BoxFuture; use futures::sink::SinkExt; use lsp_types::notification::*; use lsp_types::request::*; use lsp_types::*; use serde::Serialize; use serde_json::Value; use tower::Service; use tracing::{error, trace}; use self::pending::Pending; use super::state::{ServerState, State}; use super::ExitedError; use crate::jsonrpc::{self, Error, ErrorCode, Id, Request, Response}; mod pending; mod socket; struct ClientInner { tx: Sender, request_id: AtomicU32, pending: Arc, state: Arc, } /// Handle for communicating with the language client. /// /// This type provides a very cheap implementation of [`Clone`] so API consumers can cheaply clone /// and pass it around as needed. /// /// It also implements [`tower::Service`] in order to remain independent from the underlying /// transport and to facilitate further abstraction with middleware. #[derive(Clone)] pub struct Client { inner: Arc, } impl Client { pub(super) fn new(state: Arc) -> (Self, ClientSocket) { let (tx, rx) = mpsc::channel(1); let pending = Arc::new(Pending::new()); let client = Client { inner: Arc::new(ClientInner { tx, request_id: AtomicU32::new(0), pending: pending.clone(), state: state.clone(), }), }; (client, ClientSocket { rx, pending, state }) } /// Disconnects the `Client` from its corresponding `LspService`. /// /// Closing the client is not required, but doing so will ensure that no more messages can be /// produced. The receiver of the messages will be able to consume any in-flight messages and /// then will observe the end of the stream. /// /// If the client is never closed and never dropped, the receiver of the messages will never /// observe the end of the stream. pub(crate) fn close(&self) { self.inner.tx.clone().close_channel(); } } impl Client { // Lifecycle Messages /// Registers a new capability with the client. /// /// This corresponds to the [`client/registerCapability`] request. /// /// [`client/registerCapability`]: https://microsoft.github.io/language-server-protocol/specification#client_registerCapability /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize pub async fn register_capability( &self, registrations: Vec, ) -> jsonrpc::Result<()> { self.send_request::(RegistrationParams { registrations }) .await } /// Unregisters a capability with the client. /// /// This corresponds to the [`client/unregisterCapability`] request. /// /// [`client/unregisterCapability`]: https://microsoft.github.io/language-server-protocol/specification#client_unregisterCapability /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize pub async fn unregister_capability( &self, unregisterations: Vec, ) -> jsonrpc::Result<()> { self.send_request::(UnregistrationParams { unregisterations }) .await } // Window Features /// Notifies the client to display a particular message in the user interface. /// /// This corresponds to the [`window/showMessage`] notification. /// /// [`window/showMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_showMessage pub async fn show_message(&self, typ: MessageType, message: M) { self.send_notification_unchecked::(ShowMessageParams { typ, message: message.to_string(), }) .await; } /// Requests the client to display a particular message in the user interface. /// /// Unlike the `show_message` notification, this request can also pass a list of actions and /// wait for an answer from the client. /// /// This corresponds to the [`window/showMessageRequest`] request. /// /// [`window/showMessageRequest`]: https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest pub async fn show_message_request( &self, typ: MessageType, message: M, actions: Option>, ) -> jsonrpc::Result> { self.send_request_unchecked::(ShowMessageRequestParams { typ, message: message.to_string(), actions, }) .await } /// Notifies the client to log a particular message. /// /// This corresponds to the [`window/logMessage`] notification. /// /// [`window/logMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_logMessage pub async fn log_message(&self, typ: MessageType, message: M) { self.send_notification_unchecked::(LogMessageParams { typ, message: message.to_string(), }) .await; } /// Asks the client to display a particular resource referenced by a URI in the user interface. /// /// Returns `Ok(true)` if the document was successfully shown, or `Ok(false)` otherwise. /// /// This corresponds to the [`window/showDocument`] request. /// /// [`window/showDocument`]: https://microsoft.github.io/language-server-protocol/specification#window_showDocument /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. pub async fn show_document(&self, params: ShowDocumentParams) -> jsonrpc::Result { let response = self.send_request::(params).await?; Ok(response.success) } // TODO: Add `work_done_progress_create()` here (since 3.15.0) when supported by `tower-lsp`. // https://github.com/ebkalderon/tower-lsp/issues/176 /// Notifies the client to log a telemetry event. /// /// This corresponds to the [`telemetry/event`] notification. /// /// [`telemetry/event`]: https://microsoft.github.io/language-server-protocol/specification#telemetry_event pub async fn telemetry_event(&self, data: S) { 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]); } self.send_notification_unchecked::(value) .await; } } } /// Asks the client to refresh the code lenses currently shown in editors. As a result, the /// client should ask the server to recompute the code lenses for these editors. /// /// This is useful if a server detects a configuration change which requires a re-calculation /// of all code lenses. /// /// Note that the client still has the freedom to delay the re-calculation of the code lenses /// if for example an editor is currently not visible. /// /// This corresponds to the [`workspace/codeLens/refresh`] request. /// /// [`workspace/codeLens/refresh`]: https://microsoft.github.io/language-server-protocol/specification#codeLens_refresh /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. pub async fn code_lens_refresh(&self) -> jsonrpc::Result<()> { self.send_request::(()).await } /// Asks the client to refresh the editors for which this server provides semantic tokens. As a /// result, the client should ask the server to recompute the semantic tokens for these /// editors. /// /// This is useful if a server detects a project-wide configuration change which requires a /// re-calculation of all semantic tokens. Note that the client still has the freedom to delay /// the re-calculation of the semantic tokens if for example an editor is currently not visible. /// /// This corresponds to the [`workspace/semanticTokens/refresh`] request. /// /// [`workspace/semanticTokens/refresh`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.16.0. pub async fn semantic_tokens_refresh(&self) -> jsonrpc::Result<()> { self.send_request::(()).await } /// Asks the client to refresh the inline values currently shown in editors. As a result, the /// client should ask the server to recompute the inline values for these editors. /// /// This is useful if a server detects a configuration change which requires a re-calculation /// of all inline values. Note that the client still has the freedom to delay the /// re-calculation of the inline values if for example an editor is currently not visible. /// /// This corresponds to the [`workspace/inlineValue/refresh`] request. /// /// [`workspace/inlineValue/refresh`]: https://microsoft.github.io/language-server-protocol/specification#workspace_inlineValue_refresh /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. pub async fn inline_value_refresh(&self) -> jsonrpc::Result<()> { self.send_request::(()).await } /// Asks the client to refresh the inlay hints currently shown in editors. As a result, the /// client should ask the server to recompute the inlay hints for these editors. /// /// This is useful if a server detects a configuration change which requires a re-calculation /// of all inlay hints. Note that the client still has the freedom to delay the re-calculation /// of the inlay hints if for example an editor is currently not visible. /// /// This corresponds to the [`workspace/inlayHint/refresh`] request. /// /// [`workspace/inlayHint/refresh`]: https://microsoft.github.io/language-server-protocol/specification#workspace_inlayHint_refresh /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. pub async fn inlay_hint_refresh(&self) -> jsonrpc::Result<()> { self.send_request::(()).await } /// Asks the client to refresh all needed document and workspace diagnostics. /// /// This is useful if a server detects a project wide configuration change which requires a /// re-calculation of all diagnostics. /// /// This corresponds to the [`workspace/diagnostic/refresh`] request. /// /// [`workspace/diagnostic/refresh`]: https://microsoft.github.io/language-server-protocol/specification#diagnostic_refresh /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.17.0. pub async fn workspace_diagnostic_refresh(&self) -> jsonrpc::Result<()> { self.send_request::(()).await } /// Submits validation diagnostics for an open file with the given URI. /// /// This corresponds to the [`textDocument/publishDiagnostics`] notification. /// /// [`textDocument/publishDiagnostics`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics /// /// # Initialization /// /// This notification will only be sent if the server is initialized. pub async fn publish_diagnostics( &self, uri: Url, diags: Vec, version: Option, ) { self.send_notification::(PublishDiagnosticsParams::new( uri, diags, version, )) .await; } // Workspace Features /// Fetches configuration settings from the client. /// /// The request can fetch several configuration settings in one roundtrip. The order of the /// returned configuration settings correspond to the order of the passed /// [`ConfigurationItem`]s (e.g. the first item in the response is the result for the first /// configuration item in the params). /// /// This corresponds to the [`workspace/configuration`] request. /// /// [`workspace/configuration`]: https://microsoft.github.io/language-server-protocol/specification#workspace_configuration /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. pub async fn configuration( &self, items: Vec, ) -> jsonrpc::Result> { self.send_request::(ConfigurationParams { items }) .await } /// Fetches the current open list of workspace folders. /// /// Returns `None` if only a single file is open in the tool. Returns an empty `Vec` if a /// workspace is open but no folders are configured. /// /// This corresponds to the [`workspace/workspaceFolders`] request. /// /// [`workspace/workspaceFolders`]: https://microsoft.github.io/language-server-protocol/specification#workspace_workspaceFolders /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize /// /// # Compatibility /// /// This request was introduced in specification version 3.6.0. pub async fn workspace_folders(&self) -> jsonrpc::Result>> { self.send_request::(()).await } /// Requests a workspace resource be edited on the client side and returns whether the edit was /// applied. /// /// This corresponds to the [`workspace/applyEdit`] request. /// /// [`workspace/applyEdit`]: https://microsoft.github.io/language-server-protocol/specification#workspace_applyEdit /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize pub async fn apply_edit( &self, edit: WorkspaceEdit, ) -> jsonrpc::Result { self.send_request::(ApplyWorkspaceEditParams { edit, label: None }) .await } /// Sends a custom notification to the client. /// /// # Initialization /// /// This notification will only be sent if the server is initialized. pub async fn send_notification(&self, params: N::Params) where N: lsp_types::notification::Notification, { if let State::Initialized | State::ShutDown = self.inner.state.get() { self.send_notification_unchecked::(params).await; } else { let msg = Request::from_notification::(params); trace!("server not initialized, supressing message: {}", msg); } } async fn send_notification_unchecked(&self, params: N::Params) where N: lsp_types::notification::Notification, { let request = Request::from_notification::(params); if self.clone().call(request).await.is_err() { error!("failed to send notification"); } } /// Sends a custom request to the client. /// /// # Initialization /// /// If the request is sent to the client before the server has been initialized, this will /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]). /// /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize pub async fn send_request(&self, params: R::Params) -> jsonrpc::Result where R: lsp_types::request::Request, { if let State::Initialized | State::ShutDown = self.inner.state.get() { self.send_request_unchecked::(params).await } else { let id = self.inner.request_id.load(Ordering::SeqCst) as i64 + 1; let msg = Request::from_request::(id.into(), params); trace!("server not initialized, supressing message: {}", msg); Err(jsonrpc::not_initialized_error()) } } async fn send_request_unchecked(&self, params: R::Params) -> jsonrpc::Result where R: lsp_types::request::Request, { let id = self.next_request_id(); let request = Request::from_request::(id, params); let response = match self.clone().call(request).await { Ok(Some(response)) => response, Ok(None) | Err(_) => return Err(Error::internal_error()), }; let (_, result) = response.into_parts(); result.and_then(|v| { serde_json::from_value(v).map_err(|e| Error { code: ErrorCode::ParseError, message: e.to_string().into(), data: None, }) }) } } impl Client { /// Increments the internal request ID counter and returns the previous value. /// /// This method can be used to build custom [`Request`] objects with numeric IDs that are /// guaranteed to be unique every time. pub fn next_request_id(&self) -> Id { let num = self.inner.request_id.fetch_add(1, Ordering::Relaxed); Id::Number(num as i64) } } impl Debug for Client { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("Client") .field("tx", &self.inner.tx) .field("pending", &self.inner.pending) .field("request_id", &self.inner.request_id) .field("state", &self.inner.state) .finish() } } impl Service for Client { type Response = Option; type Error = ExitedError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner .tx .clone() .poll_ready(cx) .map_err(|_| ExitedError(())) } fn call(&mut self, req: Request) -> Self::Future { let mut tx = self.inner.tx.clone(); let response_waiter = req.id().cloned().map(|id| self.inner.pending.wait(id)); Box::pin(async move { if tx.send(req).await.is_err() { return Err(ExitedError(())); } match response_waiter { Some(fut) => Ok(Some(fut.await)), None => Ok(None), } }) } } #[cfg(test)] mod tests { use std::future::Future; use futures::stream::StreamExt; use serde_json::json; use super::*; async fn assert_client_message(f: F, expected: Request) where F: FnOnce(Client) -> Fut, Fut: Future, { let state = Arc::new(ServerState::new()); state.set(State::Initialized); let (client, socket) = Client::new(state); f(client).await; let messages: Vec<_> = socket.collect().await; assert_eq!(messages, vec![expected]); } #[tokio::test(flavor = "current_thread")] async fn log_message() { let (typ, msg) = (MessageType::LOG, "foo bar".to_owned()); let expected = Request::from_notification::(LogMessageParams { typ, message: msg.clone(), }); assert_client_message(|p| async move { p.log_message(typ, msg).await }, expected).await; } #[tokio::test(flavor = "current_thread")] async fn show_message() { let (typ, msg) = (MessageType::LOG, "foo bar".to_owned()); let expected = Request::from_notification::(ShowMessageParams { typ, message: msg.clone(), }); assert_client_message(|p| async move { p.show_message(typ, msg).await }, expected).await; } #[tokio::test(flavor = "current_thread")] async fn telemetry_event() { let null = json!(null); let expected = Request::from_notification::(null.clone()); 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()); assert_client_message(|p| async move { p.telemetry_event(array).await }, expected).await; let object = json!({}); let expected = Request::from_notification::(object.clone()); 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); 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 diagnostics = vec![Diagnostic::new_simple(Default::default(), "example".into())]; let params = PublishDiagnosticsParams::new(uri.clone(), diagnostics.clone(), None); let expected = Request::from_notification::(params); assert_client_message( |p| async move { p.publish_diagnostics(uri, diagnostics, None).await }, expected, ) .await; } } tower-lsp-0.20.0/src/service/layers.rs000064400000000000000000000211011046102023000157260ustar 00000000000000//! Assorted middleware that implements LSP server semantics. use std::marker::PhantomData; use std::sync::Arc; use std::task::{Context, Poll}; use futures::future::{self, BoxFuture, FutureExt}; use tower::{Layer, Service}; use tracing::{info, warn}; use super::ExitedError; use crate::jsonrpc::{not_initialized_error, Error, Id, Request, Response}; use super::client::Client; use super::pending::Pending; use super::state::{ServerState, State}; /// Middleware which implements `initialize` request semantics. /// /// # Specification /// /// https://microsoft.github.io/language-server-protocol/specification#initialize pub struct Initialize { state: Arc, pending: Arc, } impl Initialize { pub fn new(state: Arc, pending: Arc) -> Self { Initialize { state, pending } } } impl Layer for Initialize { type Service = InitializeService; fn layer(&self, inner: S) -> Self::Service { InitializeService { inner: Cancellable::new(inner, self.pending.clone()), state: self.state.clone(), } } } /// Service created from [`Initialize`] layer. pub struct InitializeService { inner: Cancellable, state: Arc, } impl Service for InitializeService where S: Service, Error = ExitedError>, S::Future: Send + 'static, { type Response = S::Response; type Error = S::Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { if self.state.get() == State::Uninitialized { let state = self.state.clone(); let fut = self.inner.call(req); Box::pin(async move { let response = fut.await?; match &response { Some(res) if res.is_ok() => state.set(State::Initialized), _ => state.set(State::Uninitialized), } Ok(response) }) } else { warn!("received duplicate `initialize` request, ignoring"); let (_, id, _) = req.into_parts(); future::ok(id.map(|id| Response::from_error(id, Error::invalid_request()))).boxed() } } } /// Middleware which implements `shutdown` request semantics. /// /// # Specification /// /// https://microsoft.github.io/language-server-protocol/specification#shutdown pub struct Shutdown { state: Arc, pending: Arc, } impl Shutdown { pub fn new(state: Arc, pending: Arc) -> Self { Shutdown { state, pending } } } impl Layer for Shutdown { type Service = ShutdownService; fn layer(&self, inner: S) -> Self::Service { ShutdownService { inner: Cancellable::new(inner, self.pending.clone()), state: self.state.clone(), } } } /// Service created from [`Shutdown`] layer. pub struct ShutdownService { inner: Cancellable, state: Arc, } impl Service for ShutdownService where S: Service, Error = ExitedError>, S::Future: Into, S::Error>>> + Send + 'static, { type Response = S::Response; type Error = S::Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { match self.state.get() { State::Initialized => { info!("shutdown request received, shutting down"); self.state.set(State::ShutDown); self.inner.call(req) } cur_state => { let (_, id, _) = req.into_parts(); future::ok(not_initialized_response(id, cur_state)).boxed() } } } } /// Middleware which implements `exit` notification semantics. /// /// # Specification /// /// https://microsoft.github.io/language-server-protocol/specification#exit pub struct Exit { state: Arc, pending: Arc, client: Client, } impl Exit { pub fn new(state: Arc, pending: Arc, client: Client) -> Self { Exit { state, pending, client, } } } impl Layer for Exit { type Service = ExitService; fn layer(&self, _: S) -> Self::Service { ExitService { state: self.state.clone(), pending: self.pending.clone(), client: self.client.clone(), _marker: PhantomData, } } } /// Service created from [`Exit`] layer. pub struct ExitService { state: Arc, pending: Arc, client: Client, _marker: PhantomData, } impl Service for ExitService { type Response = Option; type Error = ExitedError; type Future = future::Ready>; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { if self.state.get() == State::Exited { Poll::Ready(Err(ExitedError(()))) } else { Poll::Ready(Ok(())) } } fn call(&mut self, _: Request) -> Self::Future { info!("exit notification received, stopping"); self.state.set(State::Exited); self.pending.cancel_all(); self.client.close(); future::ok(None) } } /// Middleware which implements LSP semantics for all other kinds of requests. pub struct Normal { state: Arc, pending: Arc, } impl Normal { pub fn new(state: Arc, pending: Arc) -> Self { Normal { state, pending } } } impl Layer for Normal { type Service = NormalService; fn layer(&self, inner: S) -> Self::Service { NormalService { inner: Cancellable::new(inner, self.pending.clone()), state: self.state.clone(), } } } /// Service created from [`Normal`] layer. pub struct NormalService { inner: Cancellable, state: Arc, } impl Service for NormalService where S: Service, Error = ExitedError>, S::Future: Into, S::Error>>> + Send + 'static, { type Response = S::Response; type Error = S::Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { match self.state.get() { State::Initialized => self.inner.call(req), cur_state => { let (_, id, _) = req.into_parts(); future::ok(not_initialized_response(id, cur_state)).boxed() } } } } /// Wraps an inner service `S` and implements `$/cancelRequest` semantics for all requests. /// /// # Specification /// /// https://microsoft.github.io/language-server-protocol/specification#cancelRequest struct Cancellable { inner: S, pending: Arc, } impl Cancellable { fn new(inner: S, pending: Arc) -> Self { Cancellable { inner, pending } } } impl Service for Cancellable where S: Service, Error = ExitedError>, S::Future: Send + 'static, { type Response = S::Response; type Error = S::Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { match req.id().cloned() { Some(id) => self.pending.execute(id, self.inner.call(req)).boxed(), None => self.inner.call(req).boxed(), } } } fn not_initialized_response(id: Option, server_state: State) -> Option { let id = id?; let error = match server_state { State::Uninitialized | State::Initializing => not_initialized_error(), _ => Error::invalid_request(), }; Some(Response::from_error(id, error)) } // TODO: Add some `tower-test` middleware tests for each middleware. tower-lsp-0.20.0/src/service/pending.rs000064400000000000000000000075041046102023000160660ustar 00000000000000//! Types for tracking cancelable client-to-server JSON-RPC requests. use std::fmt::{self, Debug, Formatter}; use std::future::Future; use std::sync::Arc; use dashmap::{mapref::entry::Entry, DashMap}; use futures::future::{self, Either}; use tracing::{debug, info}; use super::ExitedError; use crate::jsonrpc::{Error, Id, Response}; /// A hashmap containing pending server requests, keyed by request ID. pub struct Pending(Arc>); impl Pending { /// Creates a new pending server requests map. pub fn new() -> Self { Pending(Arc::new(DashMap::new())) } /// Executes the given async request handler, keyed by the given request ID. /// /// If a cancel request is issued before the future is finished resolving, this will resolve to /// a "canceled" error response, and the pending request handler future will be dropped. pub fn execute( &self, id: Id, fut: F, ) -> impl Future, ExitedError>> + Send + 'static where F: Future, ExitedError>> + Send + 'static, { if let Entry::Vacant(entry) = self.0.entry(id.clone()) { let (handler_fut, abort_handle) = future::abortable(fut); entry.insert(abort_handle); let requests = self.0.clone(); Either::Left(async move { let abort_result = handler_fut.await; requests.remove(&id); // Remove abort handle now to avoid double cancellation. if let Ok(handler_result) = abort_result { handler_result } else { Ok(Some(Response::from_error(id, Error::request_cancelled()))) } }) } else { Either::Right(async { Ok(Some(Response::from_error(id, Error::invalid_request()))) }) } } /// Attempts to cancel the running request handler corresponding to this ID. /// /// This will force the future to resolve to a "canceled" error response. If the future has /// already completed, this method call will do nothing. pub fn cancel(&self, id: &Id) { if let Some((_, handle)) = self.0.remove(id) { handle.abort(); info!("successfully cancelled request with ID: {}", id); } else { debug!( "client asked to cancel request {}, but no such pending request exists, ignoring", id ); } } /// Cancels all pending request handlers, if any. pub fn cancel_all(&self) { self.0.retain(|_, handle| { handle.abort(); false }); } } impl Debug for Pending { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_set() .entries(self.0.iter().map(|entry| entry.key().clone())) .finish() } } #[cfg(test)] mod tests { use serde_json::json; use super::*; #[tokio::test(flavor = "current_thread")] async fn executes_server_request() { let pending = Pending::new(); let id = Id::Number(1); let id2 = id.clone(); let response = pending .execute(id.clone(), async { Ok(Some(Response::from_ok(id2, json!({})))) }) .await; assert_eq!(response, Ok(Some(Response::from_ok(id, json!({}))))); } #[tokio::test(flavor = "current_thread")] async fn cancels_server_request() { let pending = Pending::new(); let id = Id::Number(1); let handler_fut = tokio::spawn(pending.execute(id.clone(), future::pending())); pending.cancel(&id); let res = handler_fut.await.expect("task panicked"); assert_eq!( res, Ok(Some(Response::from_error(id, Error::request_cancelled()))) ); } } tower-lsp-0.20.0/src/service/state.rs000064400000000000000000000026411046102023000155570ustar 00000000000000//! Types representing the current state of the language server. use std::fmt::{self, Debug, Formatter}; use std::sync::atomic::{AtomicU8, Ordering}; /// A list of possible states the language server can be in. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] pub enum State { /// Server has not received an `initialize` request. Uninitialized = 0, /// Server received an `initialize` request, but has not yet responded. Initializing = 1, /// Server received and responded success to an `initialize` request. Initialized = 2, /// Server received a `shutdown` request. ShutDown = 3, /// Server received an `exit` notification. Exited = 4, } /// Atomic value which represents the current state of the server. pub struct ServerState(AtomicU8); impl ServerState { pub const fn new() -> Self { ServerState(AtomicU8::new(State::Uninitialized as u8)) } pub fn set(&self, state: State) { self.0.store(state as u8, Ordering::SeqCst); } pub fn get(&self) -> State { match self.0.load(Ordering::SeqCst) { 0 => State::Uninitialized, 1 => State::Initializing, 2 => State::Initialized, 3 => State::ShutDown, 4 => State::Exited, _ => unreachable!(), } } } impl Debug for ServerState { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.get().fmt(f) } } tower-lsp-0.20.0/src/service.rs000064400000000000000000000320151046102023000144350ustar 00000000000000//! Service abstraction for language servers. pub use self::client::{Client, ClientSocket, RequestStream, ResponseSink}; pub(crate) use self::pending::Pending; pub(crate) use self::state::{ServerState, State}; use std::fmt::{self, Debug, Display, Formatter}; use std::sync::Arc; use std::task::{Context, Poll}; use futures::future::{self, BoxFuture, FutureExt}; use serde_json::Value; use tower::Service; use crate::jsonrpc::{ Error, ErrorCode, FromParams, IntoResponse, Method, Request, Response, Router, }; use crate::LanguageServer; pub(crate) mod layers; mod client; mod pending; mod state; /// Error that occurs when attempting to call the language server after it has already exited. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExitedError(()); impl std::error::Error for ExitedError {} impl Display for ExitedError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("language server has exited") } } /// Service abstraction for the Language Server Protocol. /// /// This service takes an incoming JSON-RPC message as input and produces an outgoing message as /// output. If the incoming message is a server notification or a client response, then the /// corresponding response will be `None`. /// /// This implements [`tower::Service`] in order to remain independent from the underlying transport /// and to facilitate further abstraction with middleware. /// /// Pending requests can be canceled by issuing a [`$/cancelRequest`] notification. /// /// [`$/cancelRequest`]: https://microsoft.github.io/language-server-protocol/specification#cancelRequest /// /// The service shuts down and stops serving requests after the [`exit`] notification is received. /// /// [`exit`]: https://microsoft.github.io/language-server-protocol/specification#exit #[derive(Debug)] pub struct LspService { inner: Router, state: Arc, } impl LspService { /// Creates a new `LspService` with the given server backend, also returning a channel for /// server-to-client communication. pub fn new(init: F) -> (Self, ClientSocket) where F: FnOnce(Client) -> S, { LspService::build(init).finish() } /// Starts building a new `LspService`. /// /// Returns an `LspServiceBuilder`, which allows adding custom JSON-RPC methods to the server. pub fn build(init: F) -> LspServiceBuilder where F: FnOnce(Client) -> S, { let state = Arc::new(ServerState::new()); let (client, socket) = Client::new(state.clone()); let inner = Router::new(init(client.clone())); let pending = Arc::new(Pending::new()); LspServiceBuilder { inner: crate::generated::register_lsp_methods( inner, state.clone(), pending.clone(), client, ), state, pending, socket, } } /// Returns a reference to the inner server. pub fn inner(&self) -> &S { self.inner.inner() } } impl Service for LspService { type Response = Option; type Error = ExitedError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self.state.get() { State::Initializing => Poll::Pending, State::Exited => Poll::Ready(Err(ExitedError(()))), _ => self.inner.poll_ready(cx), } } fn call(&mut self, req: Request) -> Self::Future { if self.state.get() == State::Exited { return future::err(ExitedError(())).boxed(); } let fut = self.inner.call(req); Box::pin(async move { let response = fut.await?; match response.as_ref().and_then(|res| res.error()) { Some(Error { code: ErrorCode::MethodNotFound, data: Some(Value::String(m)), .. }) if m.starts_with("$/") => Ok(None), _ => Ok(response), } }) } } /// A builder to customize the properties of an `LspService`. /// /// To construct an `LspServiceBuilder`, refer to [`LspService::build`]. pub struct LspServiceBuilder { inner: Router, state: Arc, pending: Arc, socket: ClientSocket, } impl LspServiceBuilder { /// Defines a custom JSON-RPC request or notification with the given method `name` and handler. /// /// # Handler varieties /// /// Fundamentally, any inherent `async fn(&self)` method defined directly on the language /// server backend could be considered a valid method handler. /// /// Handlers may optionally include a single `params` argument. This argument may be of any /// type that implements [`Serialize`](serde::Serialize). /// /// Handlers which return `()` are treated as **notifications**, while those which return /// [`jsonrpc::Result`](crate::jsonrpc::Result) are treated as **requests**. /// /// Similar to the `params` argument, the `T` in the `Result` return values may be of any /// type which implements [`DeserializeOwned`](serde::de::DeserializeOwned). Additionally, this /// type _must_ be convertible into a [`serde_json::Value`] using [`serde_json::to_value`]. If /// this latter constraint is not met, the client will receive a JSON-RPC error response with /// code `-32603` (Internal Error) instead of the expected response. /// /// # Examples /// /// ```rust /// use serde_json::{json, Value}; /// use tower_lsp::jsonrpc::Result; /// use tower_lsp::lsp_types::*; /// use tower_lsp::{LanguageServer, LspService}; /// /// struct Mock; /// /// // Implementation of `LanguageServer` omitted... /// # #[tower_lsp::async_trait] /// # impl LanguageServer for Mock { /// # async fn initialize(&self, _: InitializeParams) -> Result { /// # Ok(InitializeResult::default()) /// # } /// # /// # async fn shutdown(&self) -> Result<()> { /// # Ok(()) /// # } /// # } /// /// impl Mock { /// async fn request(&self) -> Result { /// Ok(123) /// } /// /// async fn request_params(&self, params: Vec) -> Result { /// Ok(json!({"num_elems":params.len()})) /// } /// /// async fn notification(&self) { /// // ... /// } /// /// async fn notification_params(&self, params: Value) { /// // ... /// # let _ = params; /// } /// } /// /// let (service, socket) = LspService::build(|_| Mock) /// .custom_method("custom/request", Mock::request) /// .custom_method("custom/requestParams", Mock::request_params) /// .custom_method("custom/notification", Mock::notification) /// .custom_method("custom/notificationParams", Mock::notification_params) /// .finish(); /// ``` pub fn custom_method(mut self, name: &'static str, callback: F) -> Self where P: FromParams, R: IntoResponse, F: for<'a> Method<&'a S, P, R> + Clone + Send + Sync + 'static, { let layer = layers::Normal::new(self.state.clone(), self.pending.clone()); self.inner.method(name, callback, layer); self } /// Constructs the `LspService` and returns it, along with a channel for server-to-client /// communication. pub fn finish(self) -> (LspService, ClientSocket) { let LspServiceBuilder { inner, state, socket, .. } = self; (LspService { inner, state }, socket) } } impl Debug for LspServiceBuilder { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("LspServiceBuilder") .field("inner", &self.inner) .finish_non_exhaustive() } } #[cfg(test)] mod tests { use async_trait::async_trait; use lsp_types::*; use serde_json::json; use tower::ServiceExt; use super::*; use crate::jsonrpc::Result; #[derive(Debug)] struct Mock; #[async_trait] impl LanguageServer for Mock { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult::default()) } async fn shutdown(&self) -> Result<()> { Ok(()) } // This handler should never resolve... async fn code_action_resolve(&self, _: CodeAction) -> Result { future::pending().await } } impl Mock { async fn custom_request(&self, params: i32) -> Result { Ok(params) } } fn initialize_request(id: i64) -> Request { Request::build("initialize") .params(json!({"capabilities":{}})) .id(id) .finish() } #[tokio::test(flavor = "current_thread")] async fn initializes_only_once() { let (mut service, _) = LspService::new(|_| Mock); let request = initialize_request(1); let response = service.ready().await.unwrap().call(request.clone()).await; let ok = Response::from_ok(1.into(), json!({"capabilities":{}})); assert_eq!(response, Ok(Some(ok))); let response = service.ready().await.unwrap().call(request).await; let err = Response::from_error(1.into(), Error::invalid_request()); assert_eq!(response, Ok(Some(err))); } #[tokio::test(flavor = "current_thread")] async fn refuses_requests_after_shutdown() { let (mut service, _) = LspService::new(|_| Mock); let initialize = initialize_request(1); let response = service.ready().await.unwrap().call(initialize).await; let ok = Response::from_ok(1.into(), json!({"capabilities":{}})); assert_eq!(response, Ok(Some(ok))); let shutdown = Request::build("shutdown").id(1).finish(); let response = service.ready().await.unwrap().call(shutdown.clone()).await; let ok = Response::from_ok(1.into(), json!(null)); assert_eq!(response, Ok(Some(ok))); let response = service.ready().await.unwrap().call(shutdown).await; let err = Response::from_error(1.into(), Error::invalid_request()); assert_eq!(response, Ok(Some(err))); } #[tokio::test(flavor = "current_thread")] async fn exit_notification() { let (mut service, _) = LspService::new(|_| Mock); let exit = Request::build("exit").finish(); let response = service.ready().await.unwrap().call(exit.clone()).await; assert_eq!(response, Ok(None)); let ready = future::poll_fn(|cx| service.poll_ready(cx)).await; assert_eq!(ready, Err(ExitedError(()))); assert_eq!(service.call(exit).await, Err(ExitedError(()))); } #[tokio::test(flavor = "current_thread")] async fn cancels_pending_requests() { let (mut service, _) = LspService::new(|_| Mock); let initialize = initialize_request(1); let response = service.ready().await.unwrap().call(initialize).await; let ok = Response::from_ok(1.into(), json!({"capabilities":{}})); assert_eq!(response, Ok(Some(ok))); let pending_request = Request::build("codeAction/resolve") .params(json!({"title":""})) .id(1) .finish(); let cancel_request = Request::build("$/cancelRequest") .params(json!({"id":1i32})) .finish(); let pending_fut = service.ready().await.unwrap().call(pending_request); let cancel_fut = service.ready().await.unwrap().call(cancel_request); let (pending_response, cancel_response) = futures::join!(pending_fut, cancel_fut); let canceled = Response::from_error(1.into(), Error::request_cancelled()); assert_eq!(pending_response, Ok(Some(canceled))); assert_eq!(cancel_response, Ok(None)); } #[tokio::test(flavor = "current_thread")] async fn serves_custom_requests() { let (mut service, _) = LspService::build(|_| Mock) .custom_method("custom", Mock::custom_request) .finish(); let initialize = initialize_request(1); let response = service.ready().await.unwrap().call(initialize).await; let ok = Response::from_ok(1.into(), json!({"capabilities":{}})); assert_eq!(response, Ok(Some(ok))); let custom = Request::build("custom").params(123i32).id(1).finish(); let response = service.ready().await.unwrap().call(custom).await; let ok = Response::from_ok(1.into(), json!(123i32)); assert_eq!(response, Ok(Some(ok))); } #[tokio::test(flavor = "current_thread")] async fn get_inner() { let (service, _) = LspService::build(|_| Mock).finish(); service .inner() .initialize(InitializeParams::default()) .await .unwrap(); } } tower-lsp-0.20.0/src/transport.rs000064400000000000000000000244731046102023000150420ustar 00000000000000//! Generic server for multiplexing bidirectional streams through a transport. #[cfg(feature = "runtime-agnostic")] use async_codec_lite::{FramedRead, FramedWrite}; #[cfg(feature = "runtime-agnostic")] use futures::io::{AsyncRead, AsyncWrite}; #[cfg(feature = "runtime-tokio")] use tokio::io::{AsyncRead, AsyncWrite}; #[cfg(feature = "runtime-tokio")] use tokio_util::codec::{FramedRead, FramedWrite}; use futures::channel::mpsc; use futures::{future, join, stream, FutureExt, Sink, SinkExt, Stream, StreamExt, TryFutureExt}; use tower::Service; use tracing::error; use crate::codec::{LanguageServerCodec, ParseError}; use crate::jsonrpc::{Error, Id, Message, Request, Response}; use crate::service::{ClientSocket, RequestStream, ResponseSink}; const DEFAULT_MAX_CONCURRENCY: usize = 4; const MESSAGE_QUEUE_SIZE: usize = 100; /// Trait implemented by client loopback sockets. /// /// This socket handles the server-to-client half of the bidirectional communication stream. pub trait Loopback { /// Yields a stream of pending server-to-client requests. type RequestStream: Stream; /// Routes client-to-server responses back to the server. type ResponseSink: Sink + Unpin; /// Splits this socket into two halves capable of operating independently. /// /// The two halves returned implement the [`Stream`] and [`Sink`] traits, respectively. fn split(self) -> (Self::RequestStream, Self::ResponseSink); } impl Loopback for ClientSocket { type RequestStream = RequestStream; type ResponseSink = ResponseSink; #[inline] fn split(self) -> (Self::RequestStream, Self::ResponseSink) { self.split() } } /// Server for processing requests and responses on standard I/O or TCP. #[derive(Debug)] pub struct Server { stdin: I, stdout: O, loopback: L, max_concurrency: usize, } impl Server where I: AsyncRead + Unpin, O: AsyncWrite, L: Loopback, >::Error: std::error::Error, { /// Creates a new `Server` with the given `stdin` and `stdout` handles. pub fn new(stdin: I, stdout: O, socket: L) -> Self { Server { stdin, stdout, loopback: socket, max_concurrency: DEFAULT_MAX_CONCURRENCY, } } /// Sets the server concurrency limit to `max`. /// /// This setting specifies how many incoming requests may be processed concurrently. Setting /// this value to `1` forces all requests to be processed sequentially, thereby implicitly /// disabling support for the [`$/cancelRequest`] notification. /// /// [`$/cancelRequest`]: https://microsoft.github.io/language-server-protocol/specification#cancelRequest /// /// If not explicitly specified, `max` defaults to 4. /// /// # Preference over standard `tower` middleware /// /// The [`ConcurrencyLimit`] and [`Buffer`] middlewares provided by `tower` rely on /// [`tokio::spawn`] in common usage, while this library aims to be executor agnostic and to /// support exotic targets currently incompatible with `tokio`, such as WASM. As such, `Server` /// includes its own concurrency facilities that don't require a global executor to be present. /// /// [`ConcurrencyLimit`]: https://docs.rs/tower/latest/tower/limit/concurrency/struct.ConcurrencyLimit.html /// [`Buffer`]: https://docs.rs/tower/latest/tower/buffer/index.html /// [`tokio::spawn`]: https://docs.rs/tokio/latest/tokio/fn.spawn.html pub fn concurrency_level(mut self, max: usize) -> Self { self.max_concurrency = max; self } /// Spawns the service with messages read through `stdin` and responses written to `stdout`. pub async fn serve(self, mut service: T) where T: Service> + Send + 'static, T::Error: Into>, T::Future: Send, { let (client_requests, mut client_responses) = self.loopback.split(); let (client_requests, client_abort) = stream::abortable(client_requests); let (mut responses_tx, responses_rx) = mpsc::channel(0); let (mut server_tasks_tx, server_tasks_rx) = mpsc::channel(MESSAGE_QUEUE_SIZE); let mut framed_stdin = FramedRead::new(self.stdin, LanguageServerCodec::default()); let framed_stdout = FramedWrite::new(self.stdout, LanguageServerCodec::default()); let process_server_tasks = server_tasks_rx .buffer_unordered(self.max_concurrency) .filter_map(future::ready) .map(|res| Ok(Message::Response(res))) .forward(responses_tx.clone().sink_map_err(|_| unreachable!())) .map(|_| ()); let print_output = stream::select(responses_rx, client_requests.map(Message::Request)) .map(Ok) .forward(framed_stdout.sink_map_err(|e| error!("failed to encode message: {}", e))) .map(|_| ()); let read_input = async { while let Some(msg) = framed_stdin.next().await { match msg { Ok(Message::Request(req)) => { if let Err(err) = future::poll_fn(|cx| service.poll_ready(cx)).await { error!("{}", display_sources(err.into().as_ref())); return; } let fut = service.call(req).unwrap_or_else(|err| { error!("{}", display_sources(err.into().as_ref())); None }); server_tasks_tx.send(fut).await.unwrap(); } Ok(Message::Response(res)) => { if let Err(err) = client_responses.send(res).await { error!("{}", display_sources(&err)); return; } } Err(err) => { error!("failed to decode message: {}", err); let res = Response::from_error(Id::Null, to_jsonrpc_error(err)); responses_tx.send(Message::Response(res)).await.unwrap(); } } } server_tasks_tx.disconnect(); responses_tx.disconnect(); client_abort.abort(); }; join!(print_output, read_input, process_server_tasks); } } fn display_sources(error: &dyn std::error::Error) -> String { if let Some(source) = error.source() { format!("{}: {}", error, display_sources(source)) } else { error.to_string() } } #[cfg(feature = "runtime-tokio")] fn to_jsonrpc_error(err: ParseError) -> Error { match err { ParseError::Body(err) if err.is_data() => Error::invalid_request(), _ => Error::parse_error(), } } #[cfg(feature = "runtime-agnostic")] fn to_jsonrpc_error(err: impl std::error::Error) -> Error { match err.source().and_then(|e| e.downcast_ref()) { Some(ParseError::Body(err)) if err.is_data() => Error::invalid_request(), _ => Error::parse_error(), } } #[cfg(test)] mod tests { use std::task::{Context, Poll}; #[cfg(feature = "runtime-agnostic")] use futures::io::Cursor; #[cfg(feature = "runtime-tokio")] use std::io::Cursor; use futures::future::Ready; use futures::{future, sink, stream}; use super::*; const REQUEST: &str = r#"{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}"#; const RESPONSE: &str = r#"{"jsonrpc":"2.0","result":{"capabilities":{}},"id":1}"#; #[derive(Debug)] struct MockService; impl Service for MockService { type Response = Option; type Error = String; type Future = Ready>; fn poll_ready(&mut self, _: &mut Context) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, _: Request) -> Self::Future { let response = serde_json::from_str(RESPONSE).unwrap(); future::ok(Some(response)) } } struct MockLoopback(Vec); impl Loopback for MockLoopback { type RequestStream = stream::Iter>; type ResponseSink = sink::Drain; fn split(self) -> (Self::RequestStream, Self::ResponseSink) { (stream::iter(self.0), sink::drain()) } } fn mock_request() -> Vec { format!("Content-Length: {}\r\n\r\n{}", REQUEST.len(), REQUEST).into_bytes() } fn mock_response() -> Vec { format!("Content-Length: {}\r\n\r\n{}", RESPONSE.len(), RESPONSE).into_bytes() } fn mock_stdio() -> (Cursor>, Vec) { (Cursor::new(mock_request()), Vec::new()) } #[tokio::test(flavor = "current_thread")] async fn serves_on_stdio() { let (mut stdin, mut stdout) = mock_stdio(); Server::new(&mut stdin, &mut stdout, MockLoopback(vec![])) .serve(MockService) .await; assert_eq!(stdin.position(), 80); assert_eq!(stdout, mock_response()); } #[tokio::test(flavor = "current_thread")] async fn interleaves_messages() { let socket = MockLoopback(vec![serde_json::from_str(REQUEST).unwrap()]); let (mut stdin, mut stdout) = mock_stdio(); Server::new(&mut stdin, &mut stdout, socket) .serve(MockService) .await; assert_eq!(stdin.position(), 80); let output: Vec<_> = mock_request().into_iter().chain(mock_response()).collect(); assert_eq!(stdout, output); } #[tokio::test(flavor = "current_thread")] async fn handles_invalid_json() { let invalid = r#"{"jsonrpc":"2.0","method":"#; let message = format!("Content-Length: {}\r\n\r\n{}", invalid.len(), invalid).into_bytes(); let (mut stdin, mut stdout) = (Cursor::new(message), Vec::new()); Server::new(&mut stdin, &mut stdout, MockLoopback(vec![])) .serve(MockService) .await; assert_eq!(stdin.position(), 48); let err = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#; let output = format!("Content-Length: {}\r\n\r\n{}", err.len(), err).into_bytes(); assert_eq!(stdout, output); } }