diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index f01e43a5e4..bceb51e532 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -2,18 +2,19 @@ use anyhow::{anyhow, bail, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use client::ClientSettings; -use collections::{HashMap, HashSet}; +use collections::{BTreeMap, HashSet}; use fs::{Fs, RemoveOptions}; use futures::channel::mpsc::unbounded; use futures::StreamExt as _; use futures::{io::BufReader, AsyncReadExt as _}; -use gpui::{actions, AppContext, Context, Global, Model, ModelContext, SharedString, Task}; +use gpui::{actions, AppContext, Context, Global, Model, ModelContext, Task}; use language::{ LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES, }; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use settings::Settings as _; +use std::cmp::Ordering; use std::{ ffi::OsStr, path::{Path, PathBuf}, @@ -22,6 +23,7 @@ use std::{ }; use theme::{ThemeRegistry, ThemeSettings}; use util::http::AsyncBody; +use util::TryFutureExt; use util::{http::HttpClient, paths::EXTENSIONS_DIR, ResultExt}; #[cfg(test)] @@ -61,6 +63,9 @@ pub struct ExtensionStore { manifest_path: PathBuf, language_registry: Arc, theme_registry: Arc, + extension_changes: ExtensionChanges, + reload_task: Option>>, + needs_reload: bool, _watch_extensions_dir: [Task<()>; 2], } @@ -68,12 +73,12 @@ struct GlobalExtensionStore(Model); impl Global for GlobalExtensionStore {} -#[derive(Deserialize, Serialize, Default)] +#[derive(Debug, Deserialize, Serialize, Default)] pub struct Manifest { - pub extensions: HashMap, Arc>, - pub grammars: HashMap, GrammarManifestEntry>, - pub languages: HashMap, LanguageManifestEntry>, - pub themes: HashMap, + pub extensions: BTreeMap, Arc>, + pub grammars: BTreeMap, GrammarManifestEntry>, + pub languages: BTreeMap, LanguageManifestEntry>, + pub themes: BTreeMap, ThemeManifestEntry>, } #[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Deserialize, Serialize)] @@ -96,6 +101,13 @@ pub struct ThemeManifestEntry { path: PathBuf, } +#[derive(Default)] +struct ExtensionChanges { + languages: HashSet>, + grammars: HashSet>, + themes: HashSet>, +} + actions!(zed, [ReloadExtensions]); pub fn init( @@ -118,9 +130,7 @@ pub fn init( cx.on_action(|_: &ReloadExtensions, cx| { let store = cx.global::().0.clone(); - store - .update(cx, |store, cx| store.reload(cx)) - .detach_and_log_err(cx); + store.update(cx, |store, cx| store.reload(cx)) }); cx.set_global(GlobalExtensionStore(store)); @@ -145,6 +155,9 @@ impl ExtensionStore { manifest_path: extensions_dir.join("manifest.json"), extensions_being_installed: Default::default(), extensions_being_uninstalled: Default::default(), + reload_task: None, + needs_reload: false, + extension_changes: ExtensionChanges::default(), fs, http_client, language_registry, @@ -181,7 +194,7 @@ impl ExtensionStore { }; if should_reload { - self.reload(cx).detach_and_log_err(cx); + self.reload(cx) } } @@ -248,7 +261,7 @@ impl ExtensionStore { extension_id: Arc, version: Arc, cx: &mut ModelContext, - ) -> Task> { + ) { log::info!("installing extension {extension_id} {version}"); let url = format!( "{}/api/extensions/{extension_id}/{version}/download", @@ -271,21 +284,16 @@ impl ExtensionStore { .unpack(extensions_dir.join(extension_id.as_ref())) .await?; - this.update(&mut cx, |store, cx| { - store - .extensions_being_installed + this.update(&mut cx, |this, cx| { + this.extensions_being_installed .remove(extension_id.as_ref()); - store.reload(cx) - })? - .await + this.reload(cx) + }) }) + .detach_and_log_err(cx); } - pub fn uninstall_extension( - &mut self, - extension_id: Arc, - cx: &mut ModelContext, - ) -> Task> { + pub fn uninstall_extension(&mut self, extension_id: Arc, cx: &mut ModelContext) { let extensions_dir = self.extensions_dir(); let fs = self.fs.clone(); @@ -306,34 +314,98 @@ impl ExtensionStore { this.extensions_being_uninstalled .remove(extension_id.as_ref()); this.reload(cx) - })? - .await + }) }) + .detach_and_log_err(cx) } + /// Updates the set of installed extensions. + /// + /// First, this unloads any themes, languages, or grammars that are + /// no longer in the manifest, or whose files have changed on disk. + /// Then it loads any themes, languages, or grammars that are newly + /// added to the manifest, or whose files have changed on disk. fn manifest_updated(&mut self, manifest: Manifest, cx: &mut ModelContext) { + fn diff<'a, T, I1, I2>( + old_keys: I1, + new_keys: I2, + modified_keys: &HashSet>, + ) -> (Vec>, Vec>) + where + T: PartialEq, + I1: Iterator, T)>, + I2: Iterator, T)>, + { + let mut removed_keys = Vec::default(); + let mut added_keys = Vec::default(); + let mut old_keys = old_keys.peekable(); + let mut new_keys = new_keys.peekable(); + loop { + match (old_keys.peek(), new_keys.peek()) { + (None, None) => return (removed_keys, added_keys), + (None, Some(_)) => { + added_keys.push(new_keys.next().unwrap().0.clone()); + } + (Some(_), None) => { + removed_keys.push(old_keys.next().unwrap().0.clone()); + } + (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(&new_key) { + Ordering::Equal => { + let (old_key, old_value) = old_keys.next().unwrap(); + let (new_key, new_value) = new_keys.next().unwrap(); + if old_value != new_value || modified_keys.contains(old_key) { + removed_keys.push(old_key.clone()); + added_keys.push(new_key.clone()); + } + } + Ordering::Less => { + removed_keys.push(old_keys.next().unwrap().0.clone()); + } + Ordering::Greater => { + added_keys.push(new_keys.next().unwrap().0.clone()); + } + }, + } + } + } + let old_manifest = self.manifest.read(); - let language_names = old_manifest.languages.keys().cloned().collect::>(); - let grammar_names = old_manifest.grammars.keys().cloned().collect::>(); - let theme_names = old_manifest - .themes - .keys() - .cloned() - .map(SharedString::from) - .collect::>(); + let (languages_to_remove, languages_to_add) = diff( + old_manifest.languages.iter(), + manifest.languages.iter(), + &self.extension_changes.languages, + ); + let (grammars_to_remove, grammars_to_add) = diff( + old_manifest.grammars.iter(), + manifest.grammars.iter(), + &self.extension_changes.grammars, + ); + let (themes_to_remove, themes_to_add) = diff( + old_manifest.themes.iter(), + manifest.themes.iter(), + &self.extension_changes.themes, + ); + self.extension_changes.clear(); drop(old_manifest); + + let themes_to_remove = &themes_to_remove + .into_iter() + .map(|theme| theme.into()) + .collect::>(); + self.theme_registry.remove_user_themes(&themes_to_remove); self.language_registry - .remove_languages(&language_names, &grammar_names); - self.theme_registry.remove_user_themes(&theme_names); + .remove_languages(&languages_to_remove, &grammars_to_remove); self.language_registry - .register_wasm_grammars(manifest.grammars.iter().map(|(grammar_name, grammar)| { + .register_wasm_grammars(grammars_to_add.iter().map(|grammar_name| { + let grammar = manifest.grammars.get(grammar_name).unwrap(); let mut grammar_path = self.extensions_dir.clone(); grammar_path.extend([grammar.extension.as_ref(), grammar.path.as_path()]); (grammar_name.clone(), grammar_path) })); - for (language_name, language) in &manifest.languages { + for language_name in &languages_to_add { + let language = manifest.languages.get(language_name.as_ref()).unwrap(); let mut language_path = self.extensions_dir.clone(); language_path.extend([language.extension.as_ref(), language.path.as_path()]); self.language_registry.register_language( @@ -354,10 +426,13 @@ impl ExtensionStore { let fs = self.fs.clone(); let root_dir = self.extensions_dir.clone(); let theme_registry = self.theme_registry.clone(); - let themes = manifest.themes.clone(); + let themes = themes_to_add + .iter() + .filter_map(|name| manifest.themes.get(name).cloned()) + .collect::>(); cx.background_executor() .spawn(async move { - for theme in themes.values() { + for theme in &themes { let mut theme_path = root_dir.clone(); theme_path.extend([theme.extension.as_ref(), theme.path.as_path()]); @@ -384,23 +459,22 @@ impl ExtensionStore { .detach(); *self.manifest.write() = manifest; + cx.notify(); } fn watch_extensions_dir(&self, cx: &mut ModelContext) -> [Task<()>; 2] { let manifest = self.manifest.clone(); let fs = self.fs.clone(); - let language_registry = self.language_registry.clone(); - let theme_registry = self.theme_registry.clone(); let extensions_dir = self.extensions_dir.clone(); - let (reload_theme_tx, mut reload_theme_rx) = unbounded(); + let (changes_tx, mut changes_rx) = unbounded(); let events_task = cx.background_executor().spawn(async move { let mut events = fs.watch(&extensions_dir, Duration::from_millis(250)).await; while let Some(events) = events.next().await { - let mut changed_grammars = Vec::default(); - let mut changed_languages = Vec::default(); - let mut changed_themes = Vec::default(); + let mut changed_grammars = HashSet::default(); + let mut changed_languages = HashSet::default(); + let mut changed_themes = HashSet::default(); { let manifest = manifest.read(); @@ -410,7 +484,7 @@ impl ExtensionStore { grammar_path .extend([grammar.extension.as_ref(), grammar.path.as_path()]); if event.path.starts_with(&grammar_path) || event.path == grammar_path { - changed_grammars.push(grammar_name.clone()); + changed_grammars.insert(grammar_name.clone()); } } @@ -420,42 +494,37 @@ impl ExtensionStore { .extend([language.extension.as_ref(), language.path.as_path()]); if event.path.starts_with(&language_path) || event.path == language_path { - changed_languages.push(language_name.clone()); + changed_languages.insert(language_name.clone()); } } - for (_theme_name, theme) in &manifest.themes { + for (theme_name, theme) in &manifest.themes { let mut theme_path = extensions_dir.clone(); theme_path.extend([theme.extension.as_ref(), theme.path.as_path()]); if event.path.starts_with(&theme_path) || event.path == theme_path { - changed_themes.push(theme_path.clone()); + changed_themes.insert(theme_name.clone()); } } } } - language_registry.reload_languages(&changed_languages, &changed_grammars); - - for theme_path in &changed_themes { - if fs.is_file(&theme_path).await { - theme_registry - .load_user_theme(&theme_path, fs.clone()) - .await - .context("failed to load user theme") - .log_err(); - } - } - - if !changed_themes.is_empty() { - reload_theme_tx.unbounded_send(()).ok(); - } + changes_tx + .unbounded_send(ExtensionChanges { + languages: changed_languages, + grammars: changed_grammars, + themes: changed_themes, + }) + .ok(); } }); - let reload_theme_task = cx.spawn(|_, cx| async move { - while let Some(_) = reload_theme_rx.next().await { - if cx - .update(|cx| ThemeSettings::reload_current_theme(cx)) + let reload_task = cx.spawn(|this, mut cx| async move { + while let Some(changes) = changes_rx.next().await { + if this + .update(&mut cx, |this, cx| { + this.extension_changes.merge(changes); + this.reload(cx); + }) .is_err() { break; @@ -463,136 +532,179 @@ impl ExtensionStore { } }); - [events_task, reload_theme_task] + [events_task, reload_task] } - pub fn reload(&mut self, cx: &mut ModelContext) -> Task> { + fn reload(&mut self, cx: &mut ModelContext) { + if self.reload_task.is_some() { + self.needs_reload = true; + return; + } + let fs = self.fs.clone(); let extensions_dir = self.extensions_dir.clone(); let manifest_path = self.manifest_path.clone(); - cx.spawn(|this, mut cx| async move { - let manifest = cx - .background_executor() - .spawn(async move { - let mut manifest = Manifest::default(); + self.needs_reload = false; + self.reload_task = Some(cx.spawn(|this, mut cx| { + async move { + let manifest = cx + .background_executor() + .spawn(async move { + let mut manifest = Manifest::default(); - let mut extension_paths = fs - .read_dir(&extensions_dir) - .await - .context("failed to read extensions directory")?; - while let Some(extension_dir) = extension_paths.next().await { - let extension_dir = extension_dir?; - let Some(extension_name) = - extension_dir.file_name().and_then(OsStr::to_str) - else { - continue; - }; + fs.create_dir(&extensions_dir).await.log_err(); - #[derive(Deserialize)] - struct ExtensionJson { - pub version: String, + let extension_paths = fs.read_dir(&extensions_dir).await; + if let Ok(mut extension_paths) = extension_paths { + while let Some(extension_dir) = extension_paths.next().await { + let Ok(extension_dir) = extension_dir else { + continue; + }; + Self::add_extension_to_manifest( + fs.clone(), + extension_dir, + &mut manifest, + ) + .await + .log_err(); + } } - let extension_json_path = extension_dir.join("extension.json"); - let extension_json: ExtensionJson = - serde_json::from_str(&fs.load(&extension_json_path).await?)?; + if let Ok(manifest_json) = serde_json::to_string_pretty(&manifest) { + fs.save( + &manifest_path, + &manifest_json.as_str().into(), + Default::default(), + ) + .await + .context("failed to save extension manifest") + .log_err(); + } manifest - .extensions - .insert(extension_name.into(), extension_json.version.into()); + }) + .await; - if let Ok(mut grammar_paths) = - fs.read_dir(&extension_dir.join("grammars")).await - { - while let Some(grammar_path) = grammar_paths.next().await { - let grammar_path = grammar_path?; - let Ok(relative_path) = grammar_path.strip_prefix(&extension_dir) - else { - continue; - }; - let Some(grammar_name) = - grammar_path.file_stem().and_then(OsStr::to_str) - else { - continue; - }; - - manifest.grammars.insert( - grammar_name.into(), - GrammarManifestEntry { - extension: extension_name.into(), - path: relative_path.into(), - }, - ); - } - } - - if let Ok(mut language_paths) = - fs.read_dir(&extension_dir.join("languages")).await - { - while let Some(language_path) = language_paths.next().await { - let language_path = language_path?; - let Ok(relative_path) = language_path.strip_prefix(&extension_dir) - else { - continue; - }; - let config = fs.load(&language_path.join("config.toml")).await?; - let config = ::toml::from_str::(&config)?; - - manifest.languages.insert( - config.name.clone(), - LanguageManifestEntry { - extension: extension_name.into(), - path: relative_path.into(), - matcher: config.matcher, - grammar: config.grammar, - }, - ); - } - } - - if let Ok(mut theme_paths) = - fs.read_dir(&extension_dir.join("themes")).await - { - while let Some(theme_path) = theme_paths.next().await { - let theme_path = theme_path?; - let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) - else { - continue; - }; - - let Some(theme_family) = - ThemeRegistry::read_user_theme(&theme_path, fs.clone()) - .await - .log_err() - else { - continue; - }; - - for theme in theme_family.themes { - let location = ThemeManifestEntry { - extension: extension_name.into(), - path: relative_path.into(), - }; - - manifest.themes.insert(theme.name, location); - } - } - } + this.update(&mut cx, |this, cx| { + this.manifest_updated(manifest, cx); + this.reload_task.take(); + if this.needs_reload { + this.reload(cx); } - - fs.save( - &manifest_path, - &serde_json::to_string_pretty(&manifest)?.as_str().into(), - Default::default(), - ) - .await - .context("failed to save extension manifest")?; - - anyhow::Ok(manifest) }) - .await?; - this.update(&mut cx, |this, cx| this.manifest_updated(manifest, cx)) - }) + } + .log_err() + })); + } + + async fn add_extension_to_manifest( + fs: Arc, + extension_dir: PathBuf, + manifest: &mut Manifest, + ) -> Result<()> { + let extension_name = extension_dir + .file_name() + .and_then(OsStr::to_str) + .ok_or_else(|| anyhow!("invalid extension name"))?; + + #[derive(Deserialize)] + struct ExtensionJson { + pub version: String, + } + + let extension_json_path = extension_dir.join("extension.json"); + let extension_json = fs + .load(&extension_json_path) + .await + .context("failed to load extension.json")?; + let extension_json: ExtensionJson = + serde_json::from_str(&extension_json).context("invalid extension.json")?; + + manifest + .extensions + .insert(extension_name.into(), extension_json.version.into()); + + if let Ok(mut grammar_paths) = fs.read_dir(&extension_dir.join("grammars")).await { + while let Some(grammar_path) = grammar_paths.next().await { + let grammar_path = grammar_path?; + let Ok(relative_path) = grammar_path.strip_prefix(&extension_dir) else { + continue; + }; + let Some(grammar_name) = grammar_path.file_stem().and_then(OsStr::to_str) else { + continue; + }; + + manifest.grammars.insert( + grammar_name.into(), + GrammarManifestEntry { + extension: extension_name.into(), + path: relative_path.into(), + }, + ); + } + } + + if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await { + while let Some(language_path) = language_paths.next().await { + let language_path = language_path?; + let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else { + continue; + }; + let config = fs.load(&language_path.join("config.toml")).await?; + let config = ::toml::from_str::(&config)?; + + manifest.languages.insert( + config.name.clone(), + LanguageManifestEntry { + extension: extension_name.into(), + path: relative_path.into(), + matcher: config.matcher, + grammar: config.grammar, + }, + ); + } + } + + if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await { + while let Some(theme_path) = theme_paths.next().await { + let theme_path = theme_path?; + let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else { + continue; + }; + + let Some(theme_family) = ThemeRegistry::read_user_theme(&theme_path, fs.clone()) + .await + .log_err() + else { + continue; + }; + + for theme in theme_family.themes { + let location = ThemeManifestEntry { + extension: extension_name.into(), + path: relative_path.into(), + }; + + manifest.themes.insert(theme.name.into(), location); + } + } + } + + Ok(()) + } +} + +impl ExtensionChanges { + fn clear(&mut self) { + self.grammars.clear(); + self.languages.clear(); + self.themes.clear(); + } + + fn merge(&mut self, other: Self) { + self.grammars.extend(other.grammars); + self.languages.extend(other.languages); + self.themes.extend(other.themes); } } diff --git a/crates/extension/src/extension_store_test.rs b/crates/extension/src/extension_store_test.rs index 2e1f9cbbf8..a9ff4fe443 100644 --- a/crates/extension/src/extension_store_test.rs +++ b/crates/extension/src/extension_store_test.rs @@ -257,10 +257,7 @@ async fn test_extension_store(cx: &mut TestAppContext) { }, ); - store - .update(cx, |store, cx| store.reload(cx)) - .await - .unwrap(); + store.update(cx, |store, cx| store.reload(cx)); cx.executor().run_until_parked(); store.read_with(cx, |store, _| { @@ -331,13 +328,11 @@ async fn test_extension_store(cx: &mut TestAppContext) { assert_eq!(fs.metadata_call_count(), prev_fs_metadata_call_count + 2); }); - store - .update(cx, |store, cx| { - store.uninstall_extension("zed-ruby".into(), cx) - }) - .await - .unwrap(); + store.update(cx, |store, cx| { + store.uninstall_extension("zed-ruby".into(), cx) + }); + cx.executor().run_until_parked(); expected_manifest.extensions.remove("zed-ruby"); expected_manifest.languages.remove("Ruby"); expected_manifest.languages.remove("ERB"); diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index d84df83ac0..2cbe0d433c 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -38,6 +38,7 @@ pub struct ExtensionsPage { extensions_entries: Vec, query_editor: View, query_contains_error: bool, + _subscription: gpui::Subscription, extension_fetch_task: Option>, } @@ -75,6 +76,9 @@ impl Render for ExtensionsPage { impl ExtensionsPage { pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> View { let extensions_panel = cx.new_view(|cx: &mut ViewContext| { + let store = ExtensionStore::global(cx); + let subscription = cx.observe(&store, |_, _, cx| cx.notify()); + let query_editor = cx.new_view(|cx| Editor::single_line(cx)); cx.subscribe(&query_editor, Self::on_query_change).detach(); @@ -86,6 +90,7 @@ impl ExtensionsPage { extensions_entries: Vec::new(), query_contains_error: false, extension_fetch_task: None, + _subscription: subscription, query_editor, }; this.fetch_extensions(None, cx); @@ -100,25 +105,15 @@ impl ExtensionsPage { version: Arc, cx: &mut ViewContext, ) { - let install = ExtensionStore::global(cx).update(cx, |store, cx| { + ExtensionStore::global(cx).update(cx, |store, cx| { store.install_extension(extension_id, version, cx) }); - cx.spawn(move |this, mut cx| async move { - install.await?; - this.update(&mut cx, |_, cx| cx.notify()) - }) - .detach_and_log_err(cx); cx.notify(); } fn uninstall_extension(&self, extension_id: Arc, cx: &mut ViewContext) { - let install = ExtensionStore::global(cx) + ExtensionStore::global(cx) .update(cx, |store, cx| store.uninstall_extension(extension_id, cx)); - cx.spawn(move |this, mut cx| async move { - install.await?; - this.update(&mut cx, |_, cx| cx.notify()) - }) - .detach_and_log_err(cx); cx.notify(); } @@ -404,15 +399,21 @@ impl Item for ExtensionsPage { _workspace_id: WorkspaceId, cx: &mut ViewContext, ) -> Option> { - Some(cx.new_view(|_| ExtensionsPage { - fs: self.fs.clone(), - workspace: self.workspace.clone(), - list: UniformListScrollHandle::new(), - telemetry: self.telemetry.clone(), - extensions_entries: Default::default(), - query_editor: self.query_editor.clone(), - query_contains_error: false, - extension_fetch_task: None, + Some(cx.new_view(|cx| { + let store = ExtensionStore::global(cx); + let subscription = cx.observe(&store, |_, _, cx| cx.notify()); + + ExtensionsPage { + fs: self.fs.clone(), + workspace: self.workspace.clone(), + list: UniformListScrollHandle::new(), + telemetry: self.telemetry.clone(), + extensions_entries: Default::default(), + query_editor: self.query_editor.clone(), + _subscription: subscription, + query_contains_error: false, + extension_fetch_task: None, + } })) } diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index a571b9af05..fbb72a21fb 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -151,11 +151,6 @@ impl LanguageRegistry { self.state.write().reload(); } - /// Clears out the given languages and reload them from scratch. - pub fn reload_languages(&self, languages: &[Arc], grammars: &[Arc]) { - self.state.write().reload_languages(languages, grammars); - } - /// Removes the specified languages and grammars from the registry. pub fn remove_languages( &self, @@ -209,6 +204,9 @@ impl LanguageRegistry { lsp_adapters, loaded: false, }); + state.version += 1; + state.reload_count += 1; + *state.subscription.0.borrow_mut() = (); } /// Adds grammars to the registry. Language configurations reference a grammar by name. The @@ -229,11 +227,15 @@ impl LanguageRegistry { &self, grammars: impl IntoIterator>, PathBuf)>, ) { - self.state.write().grammars.extend( + let mut state = self.state.write(); + state.grammars.extend( grammars .into_iter() .map(|(name, path)| (name.into(), AvailableGrammar::Unloaded(path))), ); + state.version += 1; + state.reload_count += 1; + *state.subscription.0.borrow_mut() = (); } pub fn language_names(&self) -> Vec { @@ -679,6 +681,10 @@ impl LanguageRegistryState { languages_to_remove: &[Arc], grammars_to_remove: &[Arc], ) { + if languages_to_remove.is_empty() && grammars_to_remove.is_empty() { + return; + } + self.languages .retain(|language| !languages_to_remove.contains(&language.name())); self.available_languages @@ -690,45 +696,6 @@ impl LanguageRegistryState { *self.subscription.0.borrow_mut() = (); } - fn reload_languages( - &mut self, - languages_to_reload: &[Arc], - grammars_to_reload: &[Arc], - ) { - for (name, grammar) in self.grammars.iter_mut() { - if grammars_to_reload.contains(name) { - if let AvailableGrammar::Loaded(path, _) = grammar { - *grammar = AvailableGrammar::Unloaded(path.clone()); - } - } - } - - self.languages.retain(|language| { - let should_reload = languages_to_reload.contains(&language.config.name) - || language - .config - .grammar - .as_ref() - .map_or(false, |grammar| grammars_to_reload.contains(&grammar)); - !should_reload - }); - - for language in &mut self.available_languages { - if languages_to_reload.contains(&language.name) - || language - .grammar - .as_ref() - .map_or(false, |grammar| grammars_to_reload.contains(grammar)) - { - language.loaded = false; - } - } - - self.version += 1; - self.reload_count += 1; - *self.subscription.0.borrow_mut() = (); - } - /// Mark the given language as having been loaded, so that the /// language registry won't try to load it again. fn mark_language_loaded(&mut self, id: LanguageId) {