Merge branch 'main' into fix-broken-lsp-installations

This commit is contained in:
Julia 2023-06-20 17:04:58 -04:00
commit f91e95f24a
54 changed files with 1827 additions and 595 deletions

View file

@ -5,12 +5,11 @@ pub use language::*;
use lsp::LanguageServerBinary;
use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
use util::github::GitHubLspBinaryVersion;
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
pub struct CLspAdapter;
@ -22,9 +21,9 @@ impl super::LspAdapter for CLspAdapter {
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("clangd/clangd", false, http).await?;
let release = latest_github_release("clangd/clangd", false, delegate.http_client()).await?;
let asset_name = format!("clangd-mac-{}.zip", release.name);
let asset = release
.assets
@ -41,8 +40,8 @@ impl super::LspAdapter for CLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let zip_path = container_dir.join(format!("clangd_{}.zip", version.name));
@ -50,7 +49,8 @@ impl super::LspAdapter for CLspAdapter {
let binary_path = version_dir.join("bin/clangd");
if fs::metadata(&binary_path).await.is_err() {
let mut response = http
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.context("error downloading release")?;
@ -82,39 +82,19 @@ impl super::LspAdapter for CLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_clangd_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_clangd_dir = Some(entry.path());
}
}
let clangd_dir = last_clangd_dir.ok_or_else(|| anyhow!("no cached binary"))?;
let clangd_bin = clangd_dir.join("bin/clangd");
if clangd_bin.exists() {
Ok(LanguageServerBinary {
path: clangd_bin,
arguments: vec![],
})
} else {
Err(anyhow!(
"missing clangd binary in directory {:?}",
clangd_dir
))
}
})()
.await
.log_err()
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
get_cached_server_binary(container_dir).await
}
async fn installation_test_binary(
&self,
container_dir: PathBuf,
) -> Option<LanguageServerBinary> {
self.cached_server_binary(container_dir)
get_cached_server_binary(container_dir)
.await
.map(|mut binary| {
binary.arguments = vec!["--help".into()];
@ -259,6 +239,34 @@ impl super::LspAdapter for CLspAdapter {
}
}
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_clangd_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
if entry.file_type().await?.is_dir() {
last_clangd_dir = Some(entry.path());
}
}
let clangd_dir = last_clangd_dir.ok_or_else(|| anyhow!("no cached binary"))?;
let clangd_bin = clangd_dir.join("bin/clangd");
if clangd_bin.exists() {
Ok(LanguageServerBinary {
path: clangd_bin,
arguments: vec![],
})
} else {
Err(anyhow!(
"missing clangd binary in directory {:?}",
clangd_dir
))
}
})()
.await
.log_err()
}
#[cfg(test)]
mod tests {
use gpui::TestAppContext;

View file

@ -1,16 +1,23 @@
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use futures::StreamExt;
use gpui::{AsyncAppContext, Task};
pub use language::*;
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
use util::github::GitHubLspBinaryVersion;
use std::{
any::Any,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
};
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
pub struct ElixirLspAdapter;
@ -20,11 +27,43 @@ impl LspAdapter for ElixirLspAdapter {
LanguageServerName("elixir-ls".into())
}
fn will_start_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
let delegate = delegate.clone();
Some(cx.spawn(|mut cx| async move {
let elixir_output = smol::process::Command::new("elixir")
.args(["--version"])
.output()
.await;
if elixir_output.is_err() {
if DID_SHOW_NOTIFICATION
.compare_exchange(false, true, SeqCst, SeqCst)
.is_ok()
{
cx.update(|cx| {
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
})
}
return Err(anyhow!("cannot run elixir-ls"));
}
Ok(())
}))
}
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("elixir-lsp/elixir-ls", false, http).await?;
let release =
latest_github_release("elixir-lsp/elixir-ls", false, delegate.http_client()).await?;
let asset_name = "elixir-ls.zip";
let asset = release
.assets
@ -41,8 +80,8 @@ impl LspAdapter for ElixirLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name));
@ -50,7 +89,8 @@ impl LspAdapter for ElixirLspAdapter {
let binary_path = version_dir.join("language_server.sh");
if fs::metadata(&binary_path).await.is_err() {
let mut response = http
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.context("error downloading release")?;
@ -88,7 +128,11 @@ impl LspAdapter for ElixirLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -36,8 +36,6 @@
(char) @constant
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
(escape_sequence) @string.escape
[
@ -146,3 +144,10 @@
"<<"
">>"
] @punctuation.bracket
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
((sigil
(sigil_name) @_sigil_name
(quoted_content) @embedded)
(#eq? @_sigil_name "H"))

View file

@ -1,17 +1,24 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use gpui::{AsyncAppContext, Task};
pub use language::*;
use lazy_static::lazy_static;
use lsp::LanguageServerBinary;
use regex::Regex;
use smol::{fs, process};
use std::ffi::{OsStr, OsString};
use std::{any::Any, ops::Range, path::PathBuf, str, sync::Arc};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
use std::{
any::Any,
ffi::{OsStr, OsString},
ops::Range,
path::PathBuf,
str,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
};
use util::{fs::remove_matching, github::latest_github_release, ResultExt};
fn server_binary_arguments() -> Vec<OsString> {
vec!["-mode=stdio".into()]
@ -32,9 +39,9 @@ impl super::LspAdapter for GoLspAdapter {
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("golang/tools", false, http).await?;
let release = latest_github_release("golang/tools", false, delegate.http_client()).await?;
let version: Option<String> = release.name.strip_prefix("gopls/v").map(str::to_string);
if version.is_none() {
log::warn!(
@ -45,11 +52,39 @@ impl super::LspAdapter for GoLspAdapter {
Ok(Box::new(version) as Box<_>)
}
fn will_fetch_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
const NOTIFICATION_MESSAGE: &str =
"Could not install the Go language server `gopls`, because `go` was not found.";
let delegate = delegate.clone();
Some(cx.spawn(|mut cx| async move {
let install_output = process::Command::new("go").args(["version"]).output().await;
if install_output.is_err() {
if DID_SHOW_NOTIFICATION
.compare_exchange(false, true, SeqCst, SeqCst)
.is_ok()
{
cx.update(|cx| {
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
})
}
return Err(anyhow!("cannot install gopls"));
}
Ok(())
}))
}
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<Option<String>>().unwrap();
let this = *self;
@ -69,7 +104,10 @@ impl super::LspAdapter for GoLspAdapter {
});
}
}
} else if let Some(path) = this.cached_server_binary(container_dir.clone()).await {
} else if let Some(path) = this
.cached_server_binary(container_dir.clone(), delegate)
.await
{
return Ok(path);
}
@ -106,7 +144,11 @@ impl super::LspAdapter for GoLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_binary_path = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -1,17 +1,11 @@
; HEEx delimiters
[
"%>"
"--%>"
"-->"
"/>"
"<!"
"<!--"
"<"
"<%!--"
"<%"
"<%#"
"<%%="
"<%="
"</"
"</:"
"<:"
@ -20,6 +14,15 @@
"}"
] @punctuation.bracket
[
"<%!--"
"<%"
"<%#"
"<%%="
"<%="
"%>"
] @keyword
; HEEx operators are highlighted as such
"=" @operator

View file

@ -1,11 +1,13 @@
((directive (partial_expression_value) @content)
(#set! language "elixir")
(#set! include-children)
(#set! combined))
; Regular expression_values do not need to be combined
((directive (expression_value) @content)
(#set! language "elixir"))
(
(directive
[
(partial_expression_value)
(expression_value)
(ending_expression_value)
] @content)
(#set! language "elixir")
(#set! combined)
)
; expressions live within HTML tags, and do not need to be combined
; <link href={ Routes.static_path(..) } />

View file

@ -1,15 +1,17 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use language::{LanguageServerName, LspAdapter};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
use smol::fs;
use std::ffi::OsString;
use std::path::Path;
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
use std::{
any::Any,
ffi::OsString,
path::{Path, PathBuf},
sync::Arc,
};
use util::ResultExt;
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@ -37,7 +39,7 @@ impl LspAdapter for HtmlLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(
self.node
@ -49,8 +51,8 @@ impl LspAdapter for HtmlLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(Self::SERVER_PATH);
@ -70,7 +72,11 @@ impl LspAdapter for HtmlLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -3,7 +3,7 @@ use async_trait::async_trait;
use collections::HashMap;
use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext;
use language::{LanguageRegistry, LanguageServerName, LspAdapter};
use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
@ -17,7 +17,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::http::HttpClient;
use util::{paths, ResultExt};
const SERVER_PATH: &'static str =
@ -46,7 +45,7 @@ impl LspAdapter for JsonLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
Ok(Box::new(
self.node
@ -58,8 +57,8 @@ impl LspAdapter for JsonLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(SERVER_PATH);
@ -79,7 +78,11 @@ impl LspAdapter for JsonLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -3,11 +3,10 @@ use async_trait::async_trait;
use collections::HashMap;
use futures::lock::Mutex;
use gpui::executor::Background;
use language::{LanguageServerName, LspAdapter};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
use util::ResultExt;
#[allow(dead_code)]
@ -73,7 +72,7 @@ impl LspAdapter for PluginLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let runtime = self.runtime.clone();
let function = self.fetch_latest_server_version;
@ -93,8 +92,8 @@ impl LspAdapter for PluginLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = *version.downcast::<String>().unwrap();
let runtime = self.runtime.clone();
@ -111,7 +110,11 @@ impl LspAdapter for PluginLspAdapter {
.await
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
let runtime = self.runtime.clone();
let function = self.cached_server_binary;

View file

@ -3,13 +3,15 @@ use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use async_trait::async_trait;
use futures::{io::BufReader, StreamExt};
use language::LanguageServerName;
use language::{LanguageServerName, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use smol::fs;
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt};
use util::github::GitHubLspBinaryVersion;
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf};
use util::{
async_iife,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
#[derive(Copy, Clone)]
pub struct LuaLspAdapter;
@ -29,9 +31,11 @@ impl super::LspAdapter for LuaLspAdapter {
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("LuaLS/lua-language-server", false, http).await?;
let release =
latest_github_release("LuaLS/lua-language-server", false, delegate.http_client())
.await?;
let version = release.name.clone();
let platform = match consts::ARCH {
"x86_64" => "x64",
@ -54,15 +58,16 @@ impl super::LspAdapter for LuaLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let binary_path = container_dir.join("bin/lua-language-server");
if fs::metadata(&binary_path).await.is_err() {
let mut response = http
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@ -82,7 +87,11 @@ impl super::LspAdapter for LuaLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async_iife!({
let mut last_binary_path = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use language::{LanguageServerName, LspAdapter};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use smol::fs;
@ -11,7 +11,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::http::HttpClient;
use util::ResultExt;
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@ -38,7 +37,7 @@ impl LspAdapter for PythonLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(self.node.npm_package_latest_version("pyright").await?) as Box<_>)
}
@ -46,8 +45,8 @@ impl LspAdapter for PythonLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(Self::SERVER_PATH);
@ -64,7 +63,11 @@ impl LspAdapter for PythonLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -1,9 +1,8 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use language::{LanguageServerName, LspAdapter};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
pub struct RubyLanguageServer;
@ -15,7 +14,7 @@ impl LspAdapter for RubyLanguageServer {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(()))
}
@ -23,13 +22,17 @@ impl LspAdapter for RubyLanguageServer {
async fn fetch_server_binary(
&self,
_version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
_container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
Err(anyhow!("solargraph must be installed manually"))
}
async fn cached_server_binary(&self, _container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
_: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
Some(LanguageServerBinary {
path: "solargraph".into(),
arguments: vec!["stdio".into()],

View file

@ -8,10 +8,11 @@ use lsp::LanguageServerBinary;
use regex::Regex;
use smol::fs::{self, File};
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc};
use util::fs::remove_matching;
use util::github::{latest_github_release, GitHubLspBinaryVersion};
use util::http::HttpClient;
use util::ResultExt;
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
pub struct RustLspAdapter;
@ -23,9 +24,11 @@ impl LspAdapter for RustLspAdapter {
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("rust-analyzer/rust-analyzer", false, http).await?;
let release =
latest_github_release("rust-analyzer/rust-analyzer", false, delegate.http_client())
.await?;
let asset_name = format!("rust-analyzer-{}-apple-darwin.gz", consts::ARCH);
let asset = release
.assets
@ -41,14 +44,15 @@ impl LspAdapter for RustLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name));
if fs::metadata(&destination_path).await.is_err() {
let mut response = http
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@ -70,7 +74,11 @@ impl LspAdapter for RustLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -4,7 +4,7 @@ use async_tar::Archive;
use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt};
use gpui::AppContext;
use language::{LanguageServerName, LspAdapter};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime;
use serde_json::{json, Value};
@ -16,7 +16,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::{fs::remove_matching, github::latest_github_release, http::HttpClient};
use util::{fs::remove_matching, github::latest_github_release};
use util::{github::GitHubLspBinaryVersion, ResultExt};
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@ -58,7 +58,7 @@ impl LspAdapter for TypeScriptLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
Ok(Box::new(TypeScriptVersions {
typescript_version: self.node.npm_package_latest_version("typescript").await?,
@ -72,8 +72,8 @@ impl LspAdapter for TypeScriptLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<TypeScriptVersions>().unwrap();
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
@ -99,7 +99,11 @@ impl LspAdapter for TypeScriptLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let old_server_path = container_dir.join(Self::OLD_SERVER_PATH);
let new_server_path = container_dir.join(Self::NEW_SERVER_PATH);
@ -204,12 +208,13 @@ impl LspAdapter for EsLintLspAdapter {
async fn fetch_latest_server_version(
&self,
http: Arc<dyn HttpClient>,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
// At the time of writing the latest vscode-eslint release was released in 2020 and requires
// special custom LSP protocol extensions be handled to fully initialize. Download the latest
// prerelease instead to sidestep this issue
let release = latest_github_release("microsoft/vscode-eslint", true, http).await?;
let release =
latest_github_release("microsoft/vscode-eslint", true, delegate.http_client()).await?;
Ok(Box::new(GitHubLspBinaryVersion {
name: release.name,
url: release.tarball_url,
@ -219,8 +224,8 @@ impl LspAdapter for EsLintLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let destination_path = container_dir.join(format!("vscode-eslint-{}", version.name));
@ -229,7 +234,8 @@ impl LspAdapter for EsLintLspAdapter {
if fs::metadata(&server_path).await.is_err() {
remove_matching(&container_dir, |entry| entry != destination_path).await;
let mut response = http
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@ -257,7 +263,11 @@ impl LspAdapter for EsLintLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
// This is unfortunate but we don't know what the version is to build a path directly
let mut dir = fs::read_dir(&container_dir).await?;

View file

@ -2,7 +2,9 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext;
use language::{language_settings::all_language_settings, LanguageServerName, LspAdapter};
use language::{
language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::Value;
@ -14,7 +16,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::http::HttpClient;
use util::ResultExt;
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@ -41,7 +42,7 @@ impl LspAdapter for YamlLspAdapter {
async fn fetch_latest_server_version(
&self,
_: Arc<dyn HttpClient>,
_: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(
self.node
@ -53,8 +54,8 @@ impl LspAdapter for YamlLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(Self::SERVER_PATH);
@ -71,7 +72,11 @@ impl LspAdapter for YamlLspAdapter {
})
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View file

@ -31,7 +31,6 @@ use std::{
ffi::OsStr,
fs::OpenOptions,
io::Write as _,
ops::Not,
os::unix::prelude::OsStrExt,
panic,
path::{Path, PathBuf},
@ -373,7 +372,6 @@ struct Panic {
os_version: Option<String>,
architecture: String,
panicked_on: u128,
identifying_backtrace: Option<Vec<String>>,
}
#[derive(Serialize)]
@ -401,61 +399,18 @@ fn init_panic_hook(app: &App) {
.unwrap_or_else(|| "Box<Any>".to_string());
let backtrace = Backtrace::new();
let backtrace = backtrace
let mut backtrace = backtrace
.frames()
.iter()
.filter_map(|frame| {
let symbol = frame.symbols().first()?;
let path = symbol.filename()?;
Some((path, symbol.lineno(), format!("{:#}", symbol.name()?)))
})
.filter_map(|frame| Some(format!("{:#}", frame.symbols().first()?.name()?)))
.collect::<Vec<_>>();
let this_file_path = Path::new(file!());
// Find the first frame in the backtrace for this panic hook itself. Exclude
// that frame and all frames before it.
let mut start_frame_ix = 0;
let mut codebase_root_path = None;
for (ix, (path, _, _)) in backtrace.iter().enumerate() {
if path.ends_with(this_file_path) {
start_frame_ix = ix + 1;
codebase_root_path = path.ancestors().nth(this_file_path.components().count());
break;
}
}
// Exclude any subsequent frames inside of rust's panic handling system.
while let Some((path, _, _)) = backtrace.get(start_frame_ix) {
if path.starts_with("/rustc") {
start_frame_ix += 1;
} else {
break;
}
}
// Build two backtraces:
// * one for display, which includes symbol names for all frames, and files
// and line numbers for symbols in this codebase
// * one for identification and de-duplication, which only includes symbol
// names for symbols in this codebase.
let mut display_backtrace = Vec::new();
let mut identifying_backtrace = Vec::new();
for (path, line, symbol) in &backtrace[start_frame_ix..] {
display_backtrace.push(symbol.clone());
if let Some(codebase_root_path) = &codebase_root_path {
if let Ok(suffix) = path.strip_prefix(&codebase_root_path) {
identifying_backtrace.push(symbol.clone());
let display_path = suffix.to_string_lossy();
if let Some(line) = line {
display_backtrace.push(format!(" {display_path}:{line}"));
} else {
display_backtrace.push(format!(" {display_path}"));
}
}
}
// Strip out leading stack frames for rust panic-handling.
if let Some(ix) = backtrace
.iter()
.position(|name| name == "rust_begin_unwind")
{
backtrace.drain(0..=ix);
}
let panic_data = Panic {
@ -477,29 +432,27 @@ fn init_panic_hook(app: &App) {
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
backtrace: display_backtrace,
identifying_backtrace: identifying_backtrace
.is_empty()
.not()
.then_some(identifying_backtrace),
backtrace,
};
if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() {
if is_pty {
if is_pty {
if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() {
eprintln!("{}", panic_data_json);
return;
}
let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string();
let panic_file_path = paths::LOGS_DIR.join(format!("zed-{}.panic", timestamp));
let panic_file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(&panic_file_path)
.log_err();
if let Some(mut panic_file) = panic_file {
write!(&mut panic_file, "{}", panic_data_json).log_err();
panic_file.flush().log_err();
} else {
if let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err() {
let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string();
let panic_file_path = paths::LOGS_DIR.join(format!("zed-{}.panic", timestamp));
let panic_file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(&panic_file_path)
.log_err();
if let Some(mut panic_file) = panic_file {
writeln!(&mut panic_file, "{}", panic_data_json).log_err();
panic_file.flush().log_err();
}
}
}
}));
@ -531,23 +484,45 @@ fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
}
if telemetry_settings.diagnostics {
let panic_data_text = smol::fs::read_to_string(&child_path)
let panic_file_content = smol::fs::read_to_string(&child_path)
.await
.context("error reading panic file")?;
let body = serde_json::to_string(&PanicRequest {
panic: serde_json::from_str(&panic_data_text)?,
token: ZED_SECRET_CLIENT_TOKEN.into(),
})
.unwrap();
let panic = serde_json::from_str(&panic_file_content)
.ok()
.or_else(|| {
panic_file_content
.lines()
.next()
.and_then(|line| serde_json::from_str(line).ok())
})
.unwrap_or_else(|| {
log::error!(
"failed to deserialize panic file {:?}",
panic_file_content
);
None
});
let request = Request::post(&panic_report_url)
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.header("Content-Type", "application/json")
.body(body.into())?;
let response = http.send(request).await.context("error sending panic")?;
if !response.status().is_success() {
log::error!("Error uploading panic to server: {}", response.status());
if let Some(panic) = panic {
let body = serde_json::to_string(&PanicRequest {
panic,
token: ZED_SECRET_CLIENT_TOKEN.into(),
})
.unwrap();
let request = Request::post(&panic_report_url)
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.header("Content-Type", "application/json")
.body(body.into())?;
let response =
http.send(request).await.context("error sending panic")?;
if !response.status().is_success() {
log::error!(
"Error uploading panic to server: {}",
response.status()
);
}
}
}

View file

@ -384,6 +384,8 @@ pub fn initialize_workspace(
workspace.toggle_dock(project_panel_position, cx);
}
cx.focus_self();
workspace.add_panel(terminal_panel, cx);
if let Some(assistant_panel) = assistant_panel {
workspace.add_panel(assistant_panel, cx);