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