ts_bridge/protocol/text_document/
implementation.rs

1//! =============================================================================
2//! textDocument/implementation
3//! =============================================================================
4//!
5//! Mirrors the definition/typeDefinition handlers but targets tsserver’s
6//! `implementation` command so users can jump to interface/class/abstract
7//! implementations directly from Neovim.
8
9use anyhow::{Context, Result};
10use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse};
11use serde_json::{Value, json};
12
13use crate::protocol::{AdapterResult, RequestSpec};
14use crate::rpc::{Priority, Route};
15use crate::utils::{tsserver_span_to_location_link, uri_to_file_path};
16
17const CMD_IMPLEMENTATION: &str = "implementation";
18
19pub fn handle(params: GotoDefinitionParams) -> RequestSpec {
20    let text_document = params.text_document_position_params.text_document;
21    let uri_string = text_document.uri.to_string();
22    let file_name = uri_to_file_path(text_document.uri.as_str()).unwrap_or(uri_string);
23    let position = params.text_document_position_params.position;
24
25    let request = json!({
26        "command": CMD_IMPLEMENTATION,
27        "arguments": {
28            "file": file_name,
29            "line": position.line + 1,
30            "offset": position.character + 1,
31        }
32    });
33
34    RequestSpec {
35        route: Route::Syntax,
36        payload: request,
37        priority: Priority::Normal,
38        on_response: Some(adapt_implementation),
39        response_context: None,
40    }
41}
42
43fn adapt_implementation(payload: &Value, _context: Option<&Value>) -> Result<AdapterResult> {
44    let body = payload
45        .get("body")
46        .context("tsserver implementation missing body")?;
47    let locations = body
48        .as_array()
49        .context("tsserver implementation body must be array")?;
50
51    let mut links = Vec::new();
52    for span in locations {
53        if let Some(link) = tsserver_span_to_location_link(span, None) {
54            links.push(link);
55        }
56    }
57
58    let response = GotoDefinitionResponse::Link(links);
59    Ok(AdapterResult::ready(serde_json::to_value(response)?))
60}