vue_language_server_ext.rs

  1use anyhow::Context as _;
  2use gpui::{AppContext, WeakEntity};
  3use lsp::{LanguageServer, LanguageServerName};
  4use serde_json::Value;
  5
  6use crate::{LspStore, ProjectSettings};
  7use settings::Settings;
  8
  9struct VueServerRequest;
 10struct TypescriptServerResponse;
 11
 12impl lsp::notification::Notification for VueServerRequest {
 13    type Params = Vec<(u64, String, serde_json::Value)>;
 14
 15    const METHOD: &'static str = "tsserver/request";
 16}
 17
 18impl lsp::notification::Notification for TypescriptServerResponse {
 19    type Params = Vec<(u64, serde_json::Value)>;
 20
 21    const METHOD: &'static str = "tsserver/response";
 22}
 23
 24const VUE_SERVER_NAME: LanguageServerName = LanguageServerName::new_static("vue-language-server");
 25const VTSLS: LanguageServerName = LanguageServerName::new_static("vtsls");
 26const TS_LS: LanguageServerName = LanguageServerName::new_static("typescript-language-server");
 27
 28pub fn register_requests(lsp_store: WeakEntity<LspStore>, language_server: &LanguageServer) {
 29    let language_server_name = language_server.name();
 30    if language_server_name != VUE_SERVER_NAME {
 31        return;
 32    }
 33
 34    let vue_server_id = language_server.server_id();
 35    language_server
 36        .on_notification::<VueServerRequest, _>({
 37            move |params, cx| {
 38                let lsp_store = lsp_store.clone();
 39                let Ok(Some(vue_server)) = lsp_store.read_with(cx, |this, _| {
 40                    this.language_server_for_id(vue_server_id)
 41                }) else {
 42                    return;
 43                };
 44
 45                let requests = params;
 46                let target_server = match lsp_store.read_with(cx, |this, _| {
 47                    let language_server_id = this
 48                        .as_local()
 49                        .and_then(|local| {
 50                            local.language_server_ids.iter().find_map(|(seed, v)| {
 51                                [VTSLS, TS_LS].contains(&seed.name).then_some(v.id)
 52                            })
 53                        })
 54                        .context("Could not find language server")?;
 55
 56                    this.language_server_for_id(language_server_id)
 57                        .context("language server not found")
 58                }) {
 59                    Ok(Ok(server)) => server,
 60                    other => {
 61                        log::warn!(
 62                            "vue-language-server forwarding skipped: {other:?}. \
 63                                Returning null tsserver responses"
 64                        );
 65                        if !requests.is_empty() {
 66                            let null_responses = requests
 67                                .into_iter()
 68                                .map(|(id, _, _)| (id, Value::Null))
 69                                .collect::<Vec<_>>();
 70                            let _ = vue_server
 71                                .notify::<TypescriptServerResponse>(null_responses);
 72                        }
 73                        return;
 74                    }
 75                };
 76
 77                let cx = cx.clone();
 78                let request_timeout = cx.update(|app|
 79                    ProjectSettings::get_global(app)
 80                    .global_lsp_settings
 81                    .get_request_timeout()
 82                );
 83
 84                for (request_id, command, payload) in requests.into_iter() {
 85                    let target_server = target_server.clone();
 86                    let vue_server = vue_server.clone();
 87                    cx.background_spawn(async move {
 88                        let response = target_server
 89                            .request::<lsp::request::ExecuteCommand>(
 90                                lsp::ExecuteCommandParams {
 91                                    command: "typescript.tsserverRequest".to_owned(),
 92                                    arguments: vec![Value::String(command), payload],
 93                                    ..Default::default()
 94                                }, request_timeout
 95                            )
 96                            .await;
 97
 98                        let response_body = match response {
 99                            util::ConnectionResult::Result(Ok(result)) => match result {
100                                Some(Value::Object(mut map)) => map
101                                    .remove("body")
102                                    .unwrap_or(Value::Object(map)),
103                                Some(other) => other,
104                                None => Value::Null,
105                            },
106                            util::ConnectionResult::Result(Err(error)) => {
107                                log::warn!(
108                                    "typescript.tsserverRequest failed: {error:?} for request {request_id}"
109                                );
110                                Value::Null
111                            }
112                            other => {
113                                log::warn!(
114                                    "typescript.tsserverRequest did not return a response: {other:?} for request {request_id}"
115                                );
116                                Value::Null
117                            }
118                        };
119
120                        if let Err(err) = vue_server
121                            .notify::<TypescriptServerResponse>(vec![(request_id, response_body)])
122                        {
123                            log::warn!(
124                                "Failed to notify vue-language-server of tsserver response: {err:?}"
125                            );
126                        }
127                    })
128                    .detach();
129                }
130            }
131        })
132        .detach();
133}