Fix issues with extension API that come up when moving Svelte into an extension (#9611)

We're doing it. Svelte support is moving into an extension. This PR
fixes some issues that came up along the way.

Notes

* extensions need to be able to retrieve the path the `node` binary
installed by Zed
* previously we were silently swallowing any errors that occurred while
loading a grammar
* npm commands ran by extensions weren't run in the right directory
* Tree-sitter's WASM stdlib didn't support a C function (`strncmp`)
needed by the Svelte parser's external scanner
* the way that LSP installation status was reported was unnecessarily
complex

Release Notes:

- Removed built-in support for the Svelte and Gleam languages, because
full support for those languages is now available via extensions. These
extensions will be suggested for download when you open a `.svelte` or
`.gleam` file.

---------

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-03-22 17:29:06 -07:00 committed by GitHub
parent 4459eacc98
commit 6ebe599c98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 1278 additions and 1223 deletions

View file

@ -134,11 +134,15 @@ pub struct CachedLspAdapter {
pub language_ids: HashMap<String, String>,
pub adapter: Arc<dyn LspAdapter>,
pub reinstall_attempt_count: AtomicU64,
/// Indicates whether this language server is the primary language server
/// for a given language. Currently, most LSP-backed features only work
/// with one language server, so one server needs to be primary.
pub is_primary: bool,
cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
}
impl CachedLspAdapter {
pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
pub fn new(adapter: Arc<dyn LspAdapter>, is_primary: bool) -> Arc<Self> {
let name = adapter.name();
let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources();
let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token();
@ -150,6 +154,7 @@ impl CachedLspAdapter {
disk_based_diagnostics_progress_token,
language_ids,
adapter,
is_primary,
cached_binary: Default::default(),
reinstall_attempt_count: AtomicU64::new(0),
})
@ -293,7 +298,6 @@ pub trait LspAdapter: 'static + Send + Sync {
.cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
.await
{
delegate.update_status(self.name(), LanguageServerBinaryStatus::Cached);
log::info!(
"failed to fetch newest version of language server {:?}. falling back to using {:?}",
self.name(),
@ -464,7 +468,7 @@ async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>
.fetch_server_binary(latest_version, container_dir, delegate.as_ref())
.await;
delegate.update_status(name.clone(), LanguageServerBinaryStatus::Downloaded);
delegate.update_status(name.clone(), LanguageServerBinaryStatus::None);
binary
}

View file

@ -25,7 +25,7 @@ use sum_tree::Bias;
use text::{Point, Rope};
use theme::Theme;
use unicase::UniCase;
use util::{paths::PathExt, post_inc, ResultExt};
use util::{maybe, paths::PathExt, post_inc, ResultExt};
pub struct LanguageRegistry {
state: RwLock<LanguageRegistryState>,
@ -54,10 +54,9 @@ struct LanguageRegistryState {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LanguageServerBinaryStatus {
None,
CheckingForUpdate,
Downloading,
Downloaded,
Cached,
Failed { error: String },
}
@ -91,9 +90,10 @@ enum AvailableGrammar {
Loaded(#[allow(unused)] PathBuf, tree_sitter::Language),
Loading(
#[allow(unused)] PathBuf,
Vec<oneshot::Sender<Result<tree_sitter::Language>>>,
Vec<oneshot::Sender<Result<tree_sitter::Language, Arc<anyhow::Error>>>>,
),
Unloaded(PathBuf),
LoadFailed(Arc<anyhow::Error>),
}
#[derive(Debug)]
@ -213,7 +213,20 @@ impl LanguageRegistry {
.lsp_adapters
.entry(language_name)
.or_default()
.push(CachedLspAdapter::new(adapter));
.push(CachedLspAdapter::new(adapter, true));
}
pub fn register_secondary_lsp_adapter(
&self,
language_name: Arc<str>,
adapter: Arc<dyn LspAdapter>,
) {
self.state
.write()
.lsp_adapters
.entry(language_name)
.or_default()
.push(CachedLspAdapter::new(adapter, false));
}
#[cfg(any(feature = "test-support", test))]
@ -227,7 +240,7 @@ impl LanguageRegistry {
.lsp_adapters
.entry(language_name.into())
.or_default()
.push(CachedLspAdapter::new(Arc::new(adapter)));
.push(CachedLspAdapter::new(Arc::new(adapter), true));
self.fake_language_servers(language_name)
}
@ -578,6 +591,9 @@ impl LanguageRegistry {
if let Some(grammar) = state.grammars.get_mut(name.as_ref()) {
match grammar {
AvailableGrammar::LoadFailed(error) => {
tx.send(Err(error.clone())).ok();
}
AvailableGrammar::Native(grammar) | AvailableGrammar::Loaded(_, grammar) => {
tx.send(Ok(grammar.clone())).ok();
}
@ -586,46 +602,47 @@ impl LanguageRegistry {
}
AvailableGrammar::Unloaded(wasm_path) => {
let this = self.clone();
let wasm_path = wasm_path.clone();
*grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]);
self.executor
.spawn({
let wasm_path = wasm_path.clone();
async move {
.spawn(async move {
let grammar_result = maybe!({
let wasm_bytes = std::fs::read(&wasm_path)?;
let grammar_name = wasm_path
.file_stem()
.and_then(OsStr::to_str)
.ok_or_else(|| anyhow!("invalid grammar filename"))?;
let grammar = PARSER.with(|parser| {
anyhow::Ok(PARSER.with(|parser| {
let mut parser = parser.borrow_mut();
let mut store = parser.take_wasm_store().unwrap();
let grammar = store.load_language(&grammar_name, &wasm_bytes);
parser.set_wasm_store(store).unwrap();
grammar
})?;
})?)
})
.map_err(Arc::new);
if let Some(AvailableGrammar::Loading(_, txs)) =
this.state.write().grammars.insert(
name,
AvailableGrammar::Loaded(wasm_path, grammar.clone()),
)
{
for tx in txs {
tx.send(Ok(grammar.clone())).ok();
}
let value = match &grammar_result {
Ok(grammar) => AvailableGrammar::Loaded(wasm_path, grammar.clone()),
Err(error) => AvailableGrammar::LoadFailed(error.clone()),
};
let old_value = this.state.write().grammars.insert(name, value);
if let Some(AvailableGrammar::Loading(_, txs)) = old_value {
for tx in txs {
tx.send(grammar_result.clone()).ok();
}
anyhow::Ok(())
}
})
.detach();
*grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]);
}
}
} else {
tx.send(Err(anyhow!("no such grammar {}", name))).ok();
tx.send(Err(Arc::new(anyhow!("no such grammar {}", name))))
.ok();
}
async move { rx.await? }
async move { rx.await?.map_err(|e| anyhow!(e)) }
}
pub fn to_vec(&self) -> Vec<Arc<Language>> {
@ -691,7 +708,7 @@ impl LanguageRegistry {
// the login shell to be set on our process.
login_shell_env_loaded.await;
let binary = adapter
let binary_result = adapter
.clone()
.get_language_server_command(
language.clone(),
@ -699,8 +716,11 @@ impl LanguageRegistry {
delegate.clone(),
&mut cx,
)
.await?;
.await;
delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
let binary = binary_result?;
let options = adapter
.adapter
.clone()