html: Add support for autoclosing of tags (#11761)
Fixes #5267 TODO: - [x] Publish our fork of vscode-langservers-extracted on GH and wire that through as a language server of choice for HTML extension. - [x] Figure out how to prevent edits made by remote participants from moving the cursor of a host. Release Notes: - Added support for autoclosing of HTML tags in local projects.
This commit is contained in:
parent
097032327d
commit
0b8c1680fb
13 changed files with 173 additions and 75 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -6060,8 +6060,8 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lsp-types"
|
name = "lsp-types"
|
||||||
version = "0.94.1"
|
version = "0.95.1"
|
||||||
source = "git+https://github.com/zed-industries/lsp-types?branch=updated-completion-list-item-defaults#90a040a1d195687bd19e1df47463320a44e93d7a"
|
source = "git+https://github.com/zed-industries/lsp-types?branch=apply-snippet-edit#853c7881d200777e20799026651ca36727144646"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -7683,6 +7683,7 @@ dependencies = [
|
||||||
"sha2 0.10.7",
|
"sha2 0.10.7",
|
||||||
"similar",
|
"similar",
|
||||||
"smol",
|
"smol",
|
||||||
|
"snippet",
|
||||||
"task",
|
"task",
|
||||||
"terminal",
|
"terminal",
|
||||||
"text",
|
"text",
|
||||||
|
@ -13200,9 +13201,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_html"
|
name = "zed_html"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4",
|
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1587,7 +1587,21 @@ impl Editor {
|
||||||
project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
|
project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
|
||||||
if let project::Event::RefreshInlayHints = event {
|
if let project::Event::RefreshInlayHints = event {
|
||||||
editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
|
editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
|
||||||
};
|
} else if let project::Event::SnippetEdit(id, snippet_edits) = event {
|
||||||
|
if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
|
||||||
|
let focus_handle = editor.focus_handle(cx);
|
||||||
|
if focus_handle.is_focused(cx) {
|
||||||
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
|
for (range, snippet) in snippet_edits {
|
||||||
|
let editor_range =
|
||||||
|
language::range_from_lsp(*range).to_offset(&snapshot);
|
||||||
|
editor
|
||||||
|
.insert_snippet(&[editor_range], snippet.clone(), cx)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
let task_inventory = project.read(cx).task_inventory().clone();
|
let task_inventory = project.read(cx).task_inventory().clone();
|
||||||
project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
|
project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
|
||||||
|
@ -1601,7 +1615,6 @@ impl Editor {
|
||||||
&buffer.read(cx).snapshot(cx),
|
&buffer.read(cx).snapshot(cx),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
let focus_handle = cx.focus_handle();
|
let focus_handle = cx.focus_handle();
|
||||||
cx.on_focus(&focus_handle, Self::handle_focus).detach();
|
cx.on_focus(&focus_handle, Self::handle_focus).detach();
|
||||||
cx.on_blur(&focus_handle, Self::handle_blur).detach();
|
cx.on_blur(&focus_handle, Self::handle_blur).detach();
|
||||||
|
@ -10728,7 +10741,6 @@ impl Editor {
|
||||||
|
|
||||||
fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
|
fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
cx.emit(EditorEvent::Focused);
|
cx.emit(EditorEvent::Focused);
|
||||||
|
|
||||||
if let Some(rename) = self.pending_rename.as_ref() {
|
if let Some(rename) = self.pending_rename.as_ref() {
|
||||||
let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
|
let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
|
||||||
cx.focus(&rename_editor_focus_handle);
|
cx.focus(&rename_editor_focus_handle);
|
||||||
|
|
|
@ -16,12 +16,12 @@ brackets = [
|
||||||
]
|
]
|
||||||
word_characters = ["$", "#"]
|
word_characters = ["$", "#"]
|
||||||
tab_size = 2
|
tab_size = 2
|
||||||
scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"]
|
scope_opt_in_language_servers = ["tailwindcss-language-server","vscode-html-language-server", "emmet-language-server"]
|
||||||
|
|
||||||
[overrides.element]
|
[overrides.element]
|
||||||
line_comments = { remove = true }
|
line_comments = { remove = true }
|
||||||
block_comment = ["{/* ", " */}"]
|
block_comment = ["{/* ", " */}"]
|
||||||
opt_into_language_servers = ["emmet-language-server"]
|
opt_into_language_servers = ["emmet-language-server", "vscode-html-language-server"]
|
||||||
|
|
||||||
[overrides.string]
|
[overrides.string]
|
||||||
word_characters = ["-"]
|
word_characters = ["-"]
|
||||||
|
|
|
@ -14,13 +14,13 @@ brackets = [
|
||||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
]
|
]
|
||||||
word_characters = ["#", "$"]
|
word_characters = ["#", "$"]
|
||||||
scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"]
|
scope_opt_in_language_servers = ["vscode-html-language-server", "tailwindcss-language-server", "emmet-language-server"]
|
||||||
tab_size = 2
|
tab_size = 2
|
||||||
|
|
||||||
[overrides.element]
|
[overrides.element]
|
||||||
line_comments = { remove = true }
|
line_comments = { remove = true }
|
||||||
block_comment = ["{/* ", " */}"]
|
block_comment = ["{/* ", " */}"]
|
||||||
opt_into_language_servers = ["emmet-language-server"]
|
opt_into_language_servers = ["vscode-html-language-server", "emmet-language-server"]
|
||||||
|
|
||||||
[overrides.string]
|
[overrides.string]
|
||||||
word_characters = ["-"]
|
word_characters = ["-"]
|
||||||
|
|
|
@ -22,7 +22,7 @@ collections.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
lsp-types = { git = "https://github.com/zed-industries/lsp-types", branch = "updated-completion-list-item-defaults" }
|
lsp-types = { git = "https://github.com/zed-industries/lsp-types", branch = "apply-snippet-edit" }
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
|
@ -601,6 +601,7 @@ impl LanguageServer {
|
||||||
ResourceOperationKind::Delete,
|
ResourceOperationKind::Delete,
|
||||||
]),
|
]),
|
||||||
document_changes: Some(true),
|
document_changes: Some(true),
|
||||||
|
snippet_edit_support: Some(true),
|
||||||
..WorkspaceEditClientCapabilities::default()
|
..WorkspaceEditClientCapabilities::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -712,6 +713,7 @@ impl LanguageServer {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
locale: None,
|
locale: None,
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.spawn(|_| async move {
|
cx.spawn(|_| async move {
|
||||||
|
|
|
@ -58,6 +58,7 @@ settings.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
similar = "1.3"
|
similar = "1.3"
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
|
snippet.workspace = true
|
||||||
terminal.workspace = true
|
terminal.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|
|
@ -55,9 +55,9 @@ use language::{
|
||||||
use log::error;
|
use log::error;
|
||||||
use lsp::{
|
use lsp::{
|
||||||
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
|
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
|
||||||
DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId,
|
DocumentHighlightKind, Edit, LanguageServer, LanguageServerBinary, LanguageServerId,
|
||||||
LspRequestFuture, MessageActionItem, OneOf, ServerCapabilities, ServerHealthStatus,
|
LspRequestFuture, MessageActionItem, OneOf, ServerCapabilities, ServerHealthStatus,
|
||||||
ServerStatus,
|
ServerStatus, TextEdit,
|
||||||
};
|
};
|
||||||
use lsp_command::*;
|
use lsp_command::*;
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
|
@ -67,6 +67,7 @@ use prettier_support::{DefaultPrettier, PrettierInstance};
|
||||||
use project_settings::{LspSettings, ProjectSettings};
|
use project_settings::{LspSettings, ProjectSettings};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use search_history::SearchHistory;
|
use search_history::SearchHistory;
|
||||||
|
use snippet::Snippet;
|
||||||
use worktree::LocalSnapshot;
|
use worktree::LocalSnapshot;
|
||||||
|
|
||||||
use http::{HttpClient, Url};
|
use http::{HttpClient, Url};
|
||||||
|
@ -332,6 +333,7 @@ pub enum Event {
|
||||||
CollaboratorLeft(proto::PeerId),
|
CollaboratorLeft(proto::PeerId),
|
||||||
RefreshInlayHints,
|
RefreshInlayHints,
|
||||||
RevealInProjectPanel(ProjectEntryId),
|
RevealInProjectPanel(ProjectEntryId),
|
||||||
|
SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum LanguageServerState {
|
pub enum LanguageServerState {
|
||||||
|
@ -2694,7 +2696,6 @@ impl Project {
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_version = previous_snapshot.version + 1;
|
let next_version = previous_snapshot.version + 1;
|
||||||
|
|
||||||
buffer_snapshots.push(LspBufferSnapshot {
|
buffer_snapshots.push(LspBufferSnapshot {
|
||||||
version: next_version,
|
version: next_version,
|
||||||
snapshot: next_snapshot.clone(),
|
snapshot: next_snapshot.clone(),
|
||||||
|
@ -6209,7 +6210,7 @@ impl Project {
|
||||||
uri,
|
uri,
|
||||||
version: None,
|
version: None,
|
||||||
},
|
},
|
||||||
edits: edits.into_iter().map(OneOf::Left).collect(),
|
edits: edits.into_iter().map(Edit::Plain).collect(),
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -6287,7 +6288,7 @@ impl Project {
|
||||||
let buffer_to_edit = this
|
let buffer_to_edit = this
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.open_local_buffer_via_lsp(
|
this.open_local_buffer_via_lsp(
|
||||||
op.text_document.uri,
|
op.text_document.uri.clone(),
|
||||||
language_server.server_id(),
|
language_server.server_id(),
|
||||||
lsp_adapter.name.clone(),
|
lsp_adapter.name.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -6297,10 +6298,68 @@ impl Project {
|
||||||
|
|
||||||
let edits = this
|
let edits = this
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
let edits = op.edits.into_iter().map(|edit| match edit {
|
let path = buffer_to_edit.read(cx).project_path(cx);
|
||||||
OneOf::Left(edit) => edit,
|
let active_entry = this.active_entry;
|
||||||
OneOf::Right(edit) => edit.text_edit,
|
let is_active_entry = path.clone().map_or(false, |project_path| {
|
||||||
|
this.entry_for_path(&project_path, cx)
|
||||||
|
.map_or(false, |entry| Some(entry.id) == active_entry)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (mut edits, mut snippet_edits) = (vec![], vec![]);
|
||||||
|
for edit in op.edits {
|
||||||
|
match edit {
|
||||||
|
Edit::Plain(edit) => edits.push(edit),
|
||||||
|
Edit::Annotated(edit) => edits.push(edit.text_edit),
|
||||||
|
Edit::Snippet(edit) => {
|
||||||
|
let Ok(snippet) = Snippet::parse(&edit.snippet.value)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_active_entry {
|
||||||
|
snippet_edits.push((edit.range, snippet));
|
||||||
|
} else {
|
||||||
|
// Since this buffer is not focused, apply a normal edit.
|
||||||
|
edits.push(TextEdit {
|
||||||
|
range: edit.range,
|
||||||
|
new_text: snippet.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !snippet_edits.is_empty() {
|
||||||
|
if let Some(buffer_version) = op.text_document.version {
|
||||||
|
let buffer_id = buffer_to_edit.read(cx).remote_id();
|
||||||
|
// Check if the edit that triggered that edit has been made by this participant.
|
||||||
|
let should_apply_edit = this
|
||||||
|
.buffer_snapshots
|
||||||
|
.get(&buffer_id)
|
||||||
|
.and_then(|server_to_snapshots| {
|
||||||
|
let all_snapshots = server_to_snapshots
|
||||||
|
.get(&language_server.server_id())?;
|
||||||
|
all_snapshots
|
||||||
|
.binary_search_by_key(&buffer_version, |snapshot| {
|
||||||
|
snapshot.version
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.and_then(|index| all_snapshots.get(index))
|
||||||
|
})
|
||||||
|
.map_or(false, |lsp_snapshot| {
|
||||||
|
let version = lsp_snapshot.snapshot.version();
|
||||||
|
let most_recent_edit = version
|
||||||
|
.iter()
|
||||||
|
.max_by_key(|timestamp| timestamp.value);
|
||||||
|
most_recent_edit.map_or(false, |edit| {
|
||||||
|
edit.replica_id == this.replica_id()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if should_apply_edit {
|
||||||
|
cx.emit(Event::SnippetEdit(buffer_id, snippet_edits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.edits_from_lsp(
|
this.edits_from_lsp(
|
||||||
&buffer_to_edit,
|
&buffer_to_edit,
|
||||||
edits,
|
edits,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{collections::BTreeMap, ops::Range};
|
use std::{collections::BTreeMap, ops::Range};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Snippet {
|
pub struct Snippet {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub tabstops: Vec<TabStop>,
|
pub tabstops: Vec<TabStop>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "zed_html"
|
name = "zed_html"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
@ -13,4 +13,4 @@ path = "src/html.rs"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zed_extension_api = "0.0.4"
|
zed_extension_api = "0.0.6"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
id = "html"
|
id = "html"
|
||||||
name = "HTML"
|
name = "HTML"
|
||||||
description = "HTML support."
|
description = "HTML support."
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Isaac Clayton <slightknack@gmail.com>"]
|
authors = ["Isaac Clayton <slightknack@gmail.com>"]
|
||||||
repository = "https://github.com/zed-industries/zed"
|
repository = "https://github.com/zed-industries/zed"
|
||||||
|
@ -9,6 +9,15 @@ repository = "https://github.com/zed-industries/zed"
|
||||||
[language_servers.vscode-html-language-server]
|
[language_servers.vscode-html-language-server]
|
||||||
name = "vscode-html-language-server"
|
name = "vscode-html-language-server"
|
||||||
language = "HTML"
|
language = "HTML"
|
||||||
|
languages = ["TypeScript", "HTML", "TSX", "JavaScript", "JSDoc"]
|
||||||
|
|
||||||
|
[language_servers.vscode-html-language-server.language_ids]
|
||||||
|
"HTML" = "html"
|
||||||
|
"PHP" = "php"
|
||||||
|
"ERB" = "eruby"
|
||||||
|
"JavaScript" = "javascriptreact"
|
||||||
|
"TSX" = "typescriptreact"
|
||||||
|
"CSS" = "css"
|
||||||
|
|
||||||
[grammars.html]
|
[grammars.html]
|
||||||
repository = "https://github.com/tree-sitter/tree-sitter-html"
|
repository = "https://github.com/tree-sitter/tree-sitter-html"
|
||||||
|
|
|
@ -8,7 +8,7 @@ brackets = [
|
||||||
{ start = "[", end = "]", close = true, newline = true },
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
{ start = "(", end = ")", close = true, newline = true },
|
{ start = "(", end = ")", close = true, newline = true },
|
||||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
{ start = "<", end = ">", close = true, newline = true, not_in = ["comment", "string"] },
|
{ start = "<", end = ">", close = false, newline = true, not_in = ["comment", "string"] },
|
||||||
{ start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] },
|
{ start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
]
|
]
|
||||||
word_characters = ["-"]
|
word_characters = ["-"]
|
||||||
|
|
|
@ -1,79 +1,93 @@
|
||||||
use std::{env, fs};
|
use std::{env, fs, path::PathBuf};
|
||||||
use zed_extension_api::{self as zed, Result};
|
use zed_extension_api::{self as zed, Result};
|
||||||
|
|
||||||
const SERVER_PATH: &str =
|
const PACKAGE_NAME: &str = "vscode-language-server";
|
||||||
"node_modules/vscode-langservers-extracted/bin/vscode-html-language-server";
|
|
||||||
const PACKAGE_NAME: &str = "vscode-langservers-extracted";
|
|
||||||
|
|
||||||
struct HtmlExtension {
|
struct HtmlExtension {
|
||||||
did_find_server: bool,
|
path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HtmlExtension {
|
impl HtmlExtension {
|
||||||
fn server_exists(&self) -> bool {
|
fn server_script_path(&self, language_server_id: &zed::LanguageServerId) -> Result<PathBuf> {
|
||||||
fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file())
|
if let Some(path) = self.path.as_ref() {
|
||||||
}
|
if fs::metadata(path).map_or(false, |stat| stat.is_dir()) {
|
||||||
|
return Ok(path.clone());
|
||||||
fn server_script_path(&mut self, config: zed::LanguageServerConfig) -> Result<String> {
|
|
||||||
let server_exists = self.server_exists();
|
|
||||||
if self.did_find_server && server_exists {
|
|
||||||
return Ok(SERVER_PATH.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
zed::set_language_server_installation_status(
|
|
||||||
&config.name,
|
|
||||||
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
|
|
||||||
);
|
|
||||||
let version = zed::npm_package_latest_version(PACKAGE_NAME)?;
|
|
||||||
|
|
||||||
if !server_exists
|
|
||||||
|| zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version)
|
|
||||||
{
|
|
||||||
zed::set_language_server_installation_status(
|
|
||||||
&config.name,
|
|
||||||
&zed::LanguageServerInstallationStatus::Downloading,
|
|
||||||
);
|
|
||||||
let result = zed::npm_install_package(PACKAGE_NAME, &version);
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
if !self.server_exists() {
|
|
||||||
Err(format!(
|
|
||||||
"installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'",
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
if !self.server_exists() {
|
|
||||||
Err(error)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.did_find_server = true;
|
zed::set_language_server_installation_status(
|
||||||
Ok(SERVER_PATH.to_string())
|
language_server_id,
|
||||||
|
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
|
||||||
|
);
|
||||||
|
let release = zed::latest_github_release(
|
||||||
|
"zed-industries/vscode-langservers-extracted",
|
||||||
|
zed::GithubReleaseOptions {
|
||||||
|
require_assets: true,
|
||||||
|
pre_release: false,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let asset_name = "vscode-language-server.tar.gz";
|
||||||
|
|
||||||
|
let asset = release
|
||||||
|
.assets
|
||||||
|
.iter()
|
||||||
|
.find(|asset| asset.name == asset_name)
|
||||||
|
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
|
||||||
|
let version_dir = format!("{}-{}", PACKAGE_NAME, release.version);
|
||||||
|
if !fs::metadata(&version_dir).map_or(false, |stat| stat.is_dir()) {
|
||||||
|
zed::set_language_server_installation_status(
|
||||||
|
&language_server_id,
|
||||||
|
&zed::LanguageServerInstallationStatus::Downloading,
|
||||||
|
);
|
||||||
|
|
||||||
|
zed::download_file(
|
||||||
|
&asset.download_url,
|
||||||
|
&version_dir,
|
||||||
|
zed::DownloadedFileType::GzipTar,
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("failed to download file: {e}"))?;
|
||||||
|
|
||||||
|
let entries =
|
||||||
|
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
|
||||||
|
for entry in entries {
|
||||||
|
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
|
||||||
|
if entry.file_name().to_str() != Some(&version_dir) {
|
||||||
|
fs::remove_dir_all(&entry.path()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(PathBuf::from(version_dir)
|
||||||
|
.join("bin")
|
||||||
|
.join("vscode-html-language-server"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl zed::Extension for HtmlExtension {
|
impl zed::Extension for HtmlExtension {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self { path: None }
|
||||||
did_find_server: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn language_server_command(
|
fn language_server_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
config: zed::LanguageServerConfig,
|
language_server_id: &zed::LanguageServerId,
|
||||||
_worktree: &zed::Worktree,
|
_worktree: &zed::Worktree,
|
||||||
) -> Result<zed::Command> {
|
) -> Result<zed::Command> {
|
||||||
let server_path = self.server_script_path(config)?;
|
let path = match &self.path {
|
||||||
|
Some(path) => path,
|
||||||
|
None => {
|
||||||
|
let path = self.server_script_path(language_server_id)?;
|
||||||
|
self.path = Some(path);
|
||||||
|
self.path.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(zed::Command {
|
Ok(zed::Command {
|
||||||
command: zed::node_binary_path()?,
|
command: zed::node_binary_path()?,
|
||||||
args: vec![
|
args: vec![
|
||||||
env::current_dir()
|
env::current_dir()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.join(&server_path)
|
.join(path)
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"--stdio".to_string(),
|
"--stdio".to_string(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue