Add Vue language server v3 support (#40651)
Closes https://github.com/zed-extensions/vue/issues/48 Migration guide: https://github.com/vuejs/language-tools/discussions/5456 PR to remove tdsk: https://github.com/zed-extensions/vue/pull/61 Release Notes: - Added support for Vue language server version 3. Know more [here](https://github.com/vuejs/language-tools/releases/tag/v3.0.0). --------- Co-authored-by: MrSubidubi <dev@bahn.sh> Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
co-authored by
MrSubidubi
Piotr Osiewicz
parent
1d3bf9789e
commit
92ff29fa7d
@@ -1821,7 +1821,7 @@
|
||||
"use_on_type_format": false
|
||||
},
|
||||
"Vue.js": {
|
||||
"language_servers": ["vue-language-server", "..."],
|
||||
"language_servers": ["vue-language-server", "vtsls", "..."],
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod json_language_server_ext;
|
||||
pub mod log_store;
|
||||
pub mod lsp_ext_command;
|
||||
pub mod rust_analyzer_ext;
|
||||
pub mod vue_language_server_ext;
|
||||
|
||||
use crate::{
|
||||
CodeAction, ColorPresentation, Completion, CompletionDisplayOptions, CompletionResponse,
|
||||
@@ -987,6 +988,7 @@ impl LocalLspStore {
|
||||
})
|
||||
.detach();
|
||||
|
||||
vue_language_server_ext::register_requests(this.clone(), language_server);
|
||||
json_language_server_ext::register_requests(this.clone(), language_server);
|
||||
rust_analyzer_ext::register_notifications(this.clone(), language_server);
|
||||
clangd_ext::register_notifications(this, language_server, adapter);
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
use anyhow::Context as _;
|
||||
use gpui::{AppContext, WeakEntity};
|
||||
use lsp::{LanguageServer, LanguageServerName};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::LspStore;
|
||||
|
||||
struct VueServerRequest;
|
||||
struct TypescriptServerResponse;
|
||||
|
||||
impl lsp::notification::Notification for VueServerRequest {
|
||||
type Params = Vec<(u64, String, serde_json::Value)>;
|
||||
|
||||
const METHOD: &'static str = "tsserver/request";
|
||||
}
|
||||
|
||||
impl lsp::notification::Notification for TypescriptServerResponse {
|
||||
type Params = Vec<(u64, serde_json::Value)>;
|
||||
|
||||
const METHOD: &'static str = "tsserver/response";
|
||||
}
|
||||
|
||||
const VUE_SERVER_NAME: LanguageServerName = LanguageServerName::new_static("vue-language-server");
|
||||
const VTSLS: LanguageServerName = LanguageServerName::new_static("vtsls");
|
||||
const TS_LS: LanguageServerName = LanguageServerName::new_static("typescript-language-server");
|
||||
|
||||
pub fn register_requests(lsp_store: WeakEntity<LspStore>, language_server: &LanguageServer) {
|
||||
let language_server_name = language_server.name();
|
||||
if language_server_name == VUE_SERVER_NAME {
|
||||
let vue_server_id = language_server.server_id();
|
||||
language_server
|
||||
.on_notification::<VueServerRequest, _>({
|
||||
move |params, cx| {
|
||||
let lsp_store = lsp_store.clone();
|
||||
let Ok(Some(vue_server)) = lsp_store.read_with(cx, |this, _| {
|
||||
this.language_server_for_id(vue_server_id)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let requests = params;
|
||||
let target_server = match lsp_store.read_with(cx, |this, _| {
|
||||
let language_server_id = this
|
||||
.as_local()
|
||||
.and_then(|local| {
|
||||
local.language_server_ids.iter().find_map(|(seed, v)| {
|
||||
[VTSLS, TS_LS].contains(&seed.name).then_some(v.id)
|
||||
})
|
||||
})
|
||||
.context("Could not find language server")?;
|
||||
|
||||
this.language_server_for_id(language_server_id)
|
||||
.context("language server not found")
|
||||
}) {
|
||||
Ok(Ok(server)) => server,
|
||||
other => {
|
||||
log::warn!(
|
||||
"vue-language-server forwarding skipped: {other:?}. \
|
||||
Returning null tsserver responses"
|
||||
);
|
||||
if !requests.is_empty() {
|
||||
let null_responses = requests
|
||||
.into_iter()
|
||||
.map(|(id, _, _)| (id, Value::Null))
|
||||
.collect::<Vec<_>>();
|
||||
let _ = vue_server
|
||||
.notify::<TypescriptServerResponse>(null_responses);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let cx = cx.clone();
|
||||
for (request_id, command, payload) in requests.into_iter() {
|
||||
let target_server = target_server.clone();
|
||||
let vue_server = vue_server.clone();
|
||||
cx.background_spawn(async move {
|
||||
let response = target_server
|
||||
.request::<lsp::request::ExecuteCommand>(
|
||||
lsp::ExecuteCommandParams {
|
||||
command: "typescript.tsserverRequest".to_owned(),
|
||||
arguments: vec![Value::String(command), payload],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let response_body = match response {
|
||||
util::ConnectionResult::Result(Ok(result)) => match result {
|
||||
Some(Value::Object(mut map)) => map
|
||||
.remove("body")
|
||||
.unwrap_or(Value::Object(map)),
|
||||
Some(other) => other,
|
||||
None => Value::Null,
|
||||
},
|
||||
util::ConnectionResult::Result(Err(error)) => {
|
||||
log::warn!(
|
||||
"typescript.tsserverRequest failed: {error:?} for request {request_id}"
|
||||
);
|
||||
Value::Null
|
||||
}
|
||||
other => {
|
||||
log::warn!(
|
||||
"typescript.tsserverRequest did not return a response: {other:?} for request {request_id}"
|
||||
);
|
||||
Value::Null
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = vue_server
|
||||
.notify::<TypescriptServerResponse>(vec![(request_id, response_body)])
|
||||
{
|
||||
log::warn!(
|
||||
"Failed to notify vue-language-server of tsserver response: {err:?}"
|
||||
);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user