mirror of
https://github.com/zed-industries/zed.git
synced 2024-10-26 08:31:04 +00:00
Remove available language only when it has loaded
This also ensures that, if you load the same language more than once, a future that resolves to the language (or an error) is returned at all times. Previously, we would only return it the first time the language was loaded.
This commit is contained in:
parent
a8ac08f5bd
commit
4d52fc0d12
4 changed files with 111 additions and 74 deletions
|
@ -44,7 +44,7 @@ use syntax_map::SyntaxSnapshot;
|
|||
use theme::{SyntaxTheme, Theme};
|
||||
use tree_sitter::{self, Query};
|
||||
use unicase::UniCase;
|
||||
use util::{merge_json_value_into, ResultExt, TryFutureExt as _, UnwrapFuture};
|
||||
use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use futures::channel::mpsc;
|
||||
|
@ -87,11 +87,11 @@ pub struct CachedLspAdapter {
|
|||
pub disk_based_diagnostic_sources: Vec<String>,
|
||||
pub disk_based_diagnostics_progress_token: Option<String>,
|
||||
pub language_ids: HashMap<String, String>,
|
||||
pub adapter: Box<dyn LspAdapter>,
|
||||
pub adapter: Arc<dyn LspAdapter>,
|
||||
}
|
||||
|
||||
impl CachedLspAdapter {
|
||||
pub async fn new(adapter: Box<dyn LspAdapter>) -> Arc<Self> {
|
||||
pub async fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
|
||||
let name = adapter.name().await;
|
||||
let server_args = adapter.server_args().await;
|
||||
let initialization_options = adapter.initialization_options().await;
|
||||
|
@ -242,7 +242,7 @@ pub struct CodeLabel {
|
|||
pub filter_range: Range<usize>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct LanguageConfig {
|
||||
pub name: Arc<str>,
|
||||
pub path_suffixes: Vec<String>,
|
||||
|
@ -279,7 +279,7 @@ pub struct LanguageScope {
|
|||
override_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug)]
|
||||
#[derive(Clone, Deserialize, Default, Debug)]
|
||||
pub struct LanguageConfigOverride {
|
||||
#[serde(default)]
|
||||
pub line_comment: Override<Arc<str>>,
|
||||
|
@ -289,7 +289,7 @@ pub struct LanguageConfigOverride {
|
|||
pub disabled_bracket_ixs: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Clone, Deserialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum Override<T> {
|
||||
Remove { remove: bool },
|
||||
|
@ -466,11 +466,15 @@ pub enum LanguageServerBinaryStatus {
|
|||
Failed { error: String },
|
||||
}
|
||||
|
||||
type AvailableLanguageId = usize;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AvailableLanguage {
|
||||
id: AvailableLanguageId,
|
||||
path: &'static str,
|
||||
config: LanguageConfig,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapter: Option<Box<dyn LspAdapter>>,
|
||||
lsp_adapter: Option<Arc<dyn LspAdapter>>,
|
||||
get_queries: fn(&str) -> LanguageQueries,
|
||||
}
|
||||
|
||||
|
@ -493,6 +497,8 @@ pub struct LanguageRegistry {
|
|||
struct LanguageRegistryState {
|
||||
languages: Vec<Arc<Language>>,
|
||||
available_languages: Vec<AvailableLanguage>,
|
||||
next_available_language_id: AvailableLanguageId,
|
||||
loading_languages: HashMap<AvailableLanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
||||
subscription: (watch::Sender<()>, watch::Receiver<()>),
|
||||
theme: Option<Arc<Theme>>,
|
||||
version: usize,
|
||||
|
@ -505,6 +511,8 @@ impl LanguageRegistry {
|
|||
state: RwLock::new(LanguageRegistryState {
|
||||
languages: vec![PLAIN_TEXT.clone()],
|
||||
available_languages: Default::default(),
|
||||
next_available_language_id: 0,
|
||||
loading_languages: Default::default(),
|
||||
subscription: watch::channel(),
|
||||
theme: Default::default(),
|
||||
version: 0,
|
||||
|
@ -532,19 +540,18 @@ impl LanguageRegistry {
|
|||
path: &'static str,
|
||||
config: LanguageConfig,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapter: Option<Box<dyn LspAdapter>>,
|
||||
lsp_adapter: Option<Arc<dyn LspAdapter>>,
|
||||
get_queries: fn(&str) -> LanguageQueries,
|
||||
) {
|
||||
self.state
|
||||
.write()
|
||||
.available_languages
|
||||
.push(AvailableLanguage {
|
||||
path,
|
||||
config,
|
||||
grammar,
|
||||
lsp_adapter,
|
||||
get_queries,
|
||||
});
|
||||
let state = &mut *self.state.write();
|
||||
state.available_languages.push(AvailableLanguage {
|
||||
id: post_inc(&mut state.next_available_language_id),
|
||||
path,
|
||||
config,
|
||||
grammar,
|
||||
lsp_adapter,
|
||||
get_queries,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn language_names(&self) -> Vec<String> {
|
||||
|
@ -588,13 +595,7 @@ impl LanguageRegistry {
|
|||
}
|
||||
|
||||
pub fn add(&self, language: Arc<Language>) {
|
||||
let mut state = self.state.write();
|
||||
if let Some(theme) = state.theme.as_ref() {
|
||||
language.set_theme(&theme.editor.syntax);
|
||||
}
|
||||
state.languages.push(language);
|
||||
state.version += 1;
|
||||
*state.subscription.0.borrow_mut() = ();
|
||||
self.state.write().add(language);
|
||||
}
|
||||
|
||||
pub fn subscribe(&self) -> watch::Receiver<()> {
|
||||
|
@ -669,37 +670,62 @@ impl LanguageRegistry {
|
|||
{
|
||||
let _ = tx.send(Ok(language.clone()));
|
||||
} else if let Some(executor) = self.executor.clone() {
|
||||
if let Some(ix) = state
|
||||
if let Some(language) = state
|
||||
.available_languages
|
||||
.iter()
|
||||
.position(|l| callback(&l.config))
|
||||
.find(|l| callback(&l.config))
|
||||
.cloned()
|
||||
{
|
||||
let language = state.available_languages.remove(ix);
|
||||
drop(state);
|
||||
let name = language.config.name.clone();
|
||||
let this = self.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let queries = (language.get_queries)(&language.path);
|
||||
let language = Language::new(language.config, Some(language.grammar))
|
||||
.with_lsp_adapter(language.lsp_adapter)
|
||||
.await;
|
||||
match language.with_queries(queries) {
|
||||
Ok(language) => {
|
||||
let language = Arc::new(language);
|
||||
this.add(language.clone());
|
||||
let _ = tx.send(Ok(language));
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = tx.send(Err(anyhow!(
|
||||
"failed to load language {}: {}",
|
||||
name,
|
||||
err
|
||||
)));
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
let txs = state
|
||||
.loading_languages
|
||||
.entry(language.id)
|
||||
.or_insert_with(|| {
|
||||
let this = self.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let id = language.id;
|
||||
let queries = (language.get_queries)(&language.path);
|
||||
let language =
|
||||
Language::new(language.config, Some(language.grammar))
|
||||
.with_lsp_adapter(language.lsp_adapter)
|
||||
.await;
|
||||
let name = language.name();
|
||||
match language.with_queries(queries) {
|
||||
Ok(language) => {
|
||||
let language = Arc::new(language);
|
||||
let mut state = this.state.write();
|
||||
state.add(language.clone());
|
||||
state
|
||||
.available_languages
|
||||
.retain(|language| language.id != id);
|
||||
if let Some(mut txs) = state.loading_languages.remove(&id) {
|
||||
for tx in txs.drain(..) {
|
||||
let _ = tx.send(Ok(language.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let mut state = this.state.write();
|
||||
state
|
||||
.available_languages
|
||||
.retain(|language| language.id != id);
|
||||
if let Some(mut txs) = state.loading_languages.remove(&id) {
|
||||
for tx in txs.drain(..) {
|
||||
let _ = tx.send(Err(anyhow!(
|
||||
"failed to load language {}: {}",
|
||||
name,
|
||||
err
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
Vec::new()
|
||||
});
|
||||
txs.push(tx);
|
||||
} else {
|
||||
let _ = tx.send(Err(anyhow!("language not found")));
|
||||
}
|
||||
|
@ -804,6 +830,17 @@ impl LanguageRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
impl LanguageRegistryState {
|
||||
fn add(&mut self, language: Arc<Language>) {
|
||||
if let Some(theme) = self.theme.as_ref() {
|
||||
language.set_theme(&theme.editor.syntax);
|
||||
}
|
||||
self.languages.push(language);
|
||||
self.version += 1;
|
||||
*self.subscription.0.borrow_mut() = ();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
impl Default for LanguageRegistry {
|
||||
fn default() -> Self {
|
||||
|
@ -1135,7 +1172,7 @@ impl Language {
|
|||
Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap()
|
||||
}
|
||||
|
||||
pub async fn with_lsp_adapter(mut self, lsp_adapter: Option<Box<dyn LspAdapter>>) -> Self {
|
||||
pub async fn with_lsp_adapter(mut self, lsp_adapter: Option<Arc<dyn LspAdapter>>) -> Self {
|
||||
if let Some(adapter) = lsp_adapter {
|
||||
self.adapter = Some(CachedLspAdapter::new(adapter).await);
|
||||
}
|
||||
|
@ -1149,7 +1186,7 @@ impl Language {
|
|||
) -> mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
|
||||
let (servers_tx, servers_rx) = mpsc::unbounded();
|
||||
self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone()));
|
||||
let adapter = CachedLspAdapter::new(Box::new(fake_lsp_adapter)).await;
|
||||
let adapter = CachedLspAdapter::new(Arc::new(fake_lsp_adapter)).await;
|
||||
self.adapter = Some(adapter);
|
||||
servers_rx
|
||||
}
|
||||
|
|
|
@ -37,12 +37,12 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"c",
|
||||
tree_sitter_c::language(),
|
||||
Some(Box::new(c::CLspAdapter) as Box<dyn LspAdapter>),
|
||||
Some(Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>),
|
||||
),
|
||||
(
|
||||
"cpp",
|
||||
tree_sitter_cpp::language(),
|
||||
Some(Box::new(c::CLspAdapter)),
|
||||
Some(Arc::new(c::CLspAdapter)),
|
||||
),
|
||||
(
|
||||
"css",
|
||||
|
@ -52,17 +52,17 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
Some(Box::new(elixir::ElixirLspAdapter)),
|
||||
Some(Arc::new(elixir::ElixirLspAdapter)),
|
||||
),
|
||||
(
|
||||
"go",
|
||||
tree_sitter_go::language(),
|
||||
Some(Box::new(go::GoLspAdapter)),
|
||||
Some(Arc::new(go::GoLspAdapter)),
|
||||
),
|
||||
(
|
||||
"json",
|
||||
tree_sitter_json::language(),
|
||||
Some(Box::new(json::JsonLspAdapter::new(
|
||||
Some(Arc::new(json::JsonLspAdapter::new(
|
||||
languages.clone(),
|
||||
themes.clone(),
|
||||
))),
|
||||
|
@ -75,12 +75,12 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"python",
|
||||
tree_sitter_python::language(),
|
||||
Some(Box::new(python::PythonLspAdapter)),
|
||||
Some(Arc::new(python::PythonLspAdapter)),
|
||||
),
|
||||
(
|
||||
"rust",
|
||||
tree_sitter_rust::language(),
|
||||
Some(Box::new(rust::RustLspAdapter)),
|
||||
Some(Arc::new(rust::RustLspAdapter)),
|
||||
),
|
||||
(
|
||||
"toml",
|
||||
|
@ -90,32 +90,32 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"tsx",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
Some(Box::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
),
|
||||
(
|
||||
"typescript",
|
||||
tree_sitter_typescript::language_typescript(),
|
||||
Some(Box::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
),
|
||||
(
|
||||
"javascript",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
Some(Box::new(typescript::TypeScriptLspAdapter)),
|
||||
Some(Arc::new(typescript::TypeScriptLspAdapter)),
|
||||
),
|
||||
(
|
||||
"html",
|
||||
tree_sitter_html::language(),
|
||||
Some(Box::new(html::HtmlLspAdapter)),
|
||||
Some(Arc::new(html::HtmlLspAdapter)),
|
||||
),
|
||||
(
|
||||
"ruby",
|
||||
tree_sitter_ruby::language(),
|
||||
Some(Box::new(ruby::RubyLanguageServer)),
|
||||
Some(Arc::new(ruby::RubyLanguageServer)),
|
||||
),
|
||||
(
|
||||
"erb",
|
||||
tree_sitter_embedded_template::language(),
|
||||
Some(Box::new(ruby::RubyLanguageServer)),
|
||||
Some(Arc::new(ruby::RubyLanguageServer)),
|
||||
),
|
||||
(
|
||||
"scheme",
|
||||
|
@ -130,12 +130,12 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
(
|
||||
"lua",
|
||||
tree_sitter_lua::language(),
|
||||
Some(Box::new(lua::LuaLspAdapter)),
|
||||
Some(Arc::new(lua::LuaLspAdapter)),
|
||||
),
|
||||
(
|
||||
"yaml",
|
||||
tree_sitter_yaml::language(),
|
||||
Some(Box::new(yaml::YamlLspAdapter)),
|
||||
Some(Arc::new(yaml::YamlLspAdapter)),
|
||||
),
|
||||
] {
|
||||
languages.register(name, load_config(name), grammar, lsp_adapter, load_queries);
|
||||
|
@ -146,7 +146,7 @@ pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
|
|||
pub async fn language(
|
||||
name: &str,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapter: Option<Box<dyn LspAdapter>>,
|
||||
lsp_adapter: Option<Arc<dyn LspAdapter>>,
|
||||
) -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(load_config(name), Some(grammar))
|
||||
|
|
|
@ -314,7 +314,7 @@ mod tests {
|
|||
let language = language(
|
||||
"go",
|
||||
tree_sitter_go::language(),
|
||||
Some(Box::new(GoLspAdapter)),
|
||||
Some(Arc::new(GoLspAdapter)),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ mod tests {
|
|||
let language = language(
|
||||
"rust",
|
||||
tree_sitter_rust::language(),
|
||||
Some(Box::new(RustLspAdapter)),
|
||||
Some(Arc::new(RustLspAdapter)),
|
||||
)
|
||||
.await;
|
||||
let grammar = language.grammar().unwrap();
|
||||
|
@ -392,7 +392,7 @@ mod tests {
|
|||
let language = language(
|
||||
"rust",
|
||||
tree_sitter_rust::language(),
|
||||
Some(Box::new(RustLspAdapter)),
|
||||
Some(Arc::new(RustLspAdapter)),
|
||||
)
|
||||
.await;
|
||||
let grammar = language.grammar().unwrap();
|
||||
|
|
Loading…
Reference in a new issue