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}