Unload languages when uninstalling their extension (#7743)
Release Notes: - N/A Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
parent
a2144faf9c
commit
e73e93f333
5 changed files with 120 additions and 28 deletions
|
@ -311,6 +311,13 @@ impl ExtensionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manifest_updated(&mut self, manifest: Manifest, cx: &mut ModelContext<Self>) {
|
fn manifest_updated(&mut self, manifest: Manifest, cx: &mut ModelContext<Self>) {
|
||||||
|
let old_manifest = self.manifest.read();
|
||||||
|
let language_names = old_manifest.languages.keys().cloned().collect::<Vec<_>>();
|
||||||
|
let grammar_names = old_manifest.grammars.keys().cloned().collect::<Vec<_>>();
|
||||||
|
drop(old_manifest);
|
||||||
|
self.language_registry
|
||||||
|
.remove_languages(&language_names, &grammar_names);
|
||||||
|
|
||||||
self.language_registry
|
self.language_registry
|
||||||
.register_wasm_grammars(manifest.grammars.iter().map(|(grammar_name, grammar)| {
|
.register_wasm_grammars(manifest.grammars.iter().map(|(grammar_name, grammar)| {
|
||||||
let mut grammar_path = self.extensions_dir.clone();
|
let mut grammar_path = self.extensions_dir.clone();
|
||||||
|
|
|
@ -302,6 +302,10 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||||
language_registry.language_names(),
|
language_registry.language_names(),
|
||||||
["ERB", "Plain Text", "Ruby"]
|
["ERB", "Plain Text", "Ruby"]
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
language_registry.grammar_names(),
|
||||||
|
["embedded_template".into(), "ruby".into()]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
theme_registry.list_names(false),
|
theme_registry.list_names(false),
|
||||||
[
|
[
|
||||||
|
@ -319,4 +323,27 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||||
assert_eq!(fs.read_dir_call_count(), prev_fs_read_dir_call_count);
|
assert_eq!(fs.read_dir_call_count(), prev_fs_read_dir_call_count);
|
||||||
assert_eq!(fs.metadata_call_count(), prev_fs_metadata_call_count + 2);
|
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();
|
||||||
|
|
||||||
|
expected_manifest.extensions.remove("zed-ruby");
|
||||||
|
expected_manifest.languages.remove("Ruby");
|
||||||
|
expected_manifest.languages.remove("ERB");
|
||||||
|
expected_manifest.grammars.remove("ruby");
|
||||||
|
expected_manifest.grammars.remove("embedded_template");
|
||||||
|
|
||||||
|
store.read_with(cx, |store, _| {
|
||||||
|
let manifest = store.manifest.read();
|
||||||
|
assert_eq!(manifest.grammars, expected_manifest.grammars);
|
||||||
|
assert_eq!(manifest.languages, expected_manifest.languages);
|
||||||
|
assert_eq!(manifest.themes, expected_manifest.themes);
|
||||||
|
|
||||||
|
assert_eq!(language_registry.language_names(), ["Plain Text"]);
|
||||||
|
assert_eq!(language_registry.grammar_names(), []);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ thread_local! {
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
static ref NEXT_LANGUAGE_ID: AtomicUsize = Default::default();
|
||||||
static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
|
static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
|
||||||
|
|
||||||
static ref WASM_ENGINE: wasmtime::Engine = wasmtime::Engine::default();
|
static ref WASM_ENGINE: wasmtime::Engine = wasmtime::Engine::default();
|
||||||
|
|
||||||
/// A shared grammar for plain text, exposed for reuse by downstream crates.
|
/// A shared grammar for plain text, exposed for reuse by downstream crates.
|
||||||
|
@ -613,7 +613,17 @@ pub struct BracketPair {
|
||||||
pub newline: bool,
|
pub newline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
pub(crate) struct LanguageId(usize);
|
||||||
|
|
||||||
|
impl LanguageId {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self(NEXT_LANGUAGE_ID.fetch_add(1, SeqCst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Language {
|
pub struct Language {
|
||||||
|
pub(crate) id: LanguageId,
|
||||||
pub(crate) config: LanguageConfig,
|
pub(crate) config: LanguageConfig,
|
||||||
pub(crate) grammar: Option<Arc<Grammar>>,
|
pub(crate) grammar: Option<Arc<Grammar>>,
|
||||||
pub(crate) adapters: Vec<Arc<CachedLspAdapter>>,
|
pub(crate) adapters: Vec<Arc<CachedLspAdapter>>,
|
||||||
|
@ -625,8 +635,17 @@ pub struct Language {
|
||||||
)>,
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
pub struct GrammarId(pub usize);
|
||||||
|
|
||||||
|
impl GrammarId {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self(NEXT_GRAMMAR_ID.fetch_add(1, SeqCst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Grammar {
|
pub struct Grammar {
|
||||||
id: usize,
|
id: GrammarId,
|
||||||
pub ts_language: tree_sitter::Language,
|
pub ts_language: tree_sitter::Language,
|
||||||
pub(crate) error_query: Query,
|
pub(crate) error_query: Query,
|
||||||
pub(crate) highlights_query: Option<Query>,
|
pub(crate) highlights_query: Option<Query>,
|
||||||
|
@ -697,11 +716,24 @@ struct BracketConfig {
|
||||||
|
|
||||||
impl Language {
|
impl Language {
|
||||||
pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
|
pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
|
||||||
|
Self::new_with_id(
|
||||||
|
LanguageId(NEXT_LANGUAGE_ID.fetch_add(1, SeqCst)),
|
||||||
|
config,
|
||||||
|
ts_language,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_id(
|
||||||
|
id: LanguageId,
|
||||||
|
config: LanguageConfig,
|
||||||
|
ts_language: Option<tree_sitter::Language>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id,
|
||||||
config,
|
config,
|
||||||
grammar: ts_language.map(|ts_language| {
|
grammar: ts_language.map(|ts_language| {
|
||||||
Arc::new(Grammar {
|
Arc::new(Grammar {
|
||||||
id: NEXT_GRAMMAR_ID.fetch_add(1, SeqCst),
|
id: GrammarId::new(),
|
||||||
highlights_query: None,
|
highlights_query: None,
|
||||||
brackets_config: None,
|
brackets_config: None,
|
||||||
outline_config: None,
|
outline_config: None,
|
||||||
|
@ -726,10 +758,6 @@ impl Language {
|
||||||
&self.adapters
|
&self.adapters
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> Option<usize> {
|
|
||||||
self.grammar.as_ref().map(|g| g.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
|
pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
|
||||||
if let Some(query) = queries.highlights {
|
if let Some(query) = queries.highlights {
|
||||||
self = self
|
self = self
|
||||||
|
@ -1237,13 +1265,13 @@ impl LanguageScope {
|
||||||
|
|
||||||
impl Hash for Language {
|
impl Hash for Language {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.id().hash(state)
|
self.id.hash(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Language {
|
impl PartialEq for Language {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id().eq(&other.id())
|
self.id.eq(&other.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1258,7 +1286,7 @@ impl Debug for Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grammar {
|
impl Grammar {
|
||||||
pub fn id(&self) -> usize {
|
pub fn id(&self) -> GrammarId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
CachedLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageServerName, LspAdapter,
|
CachedLspAdapter, Language, LanguageConfig, LanguageId, LanguageMatcher, LanguageServerName,
|
||||||
LspAdapterDelegate, PARSER, PLAIN_TEXT,
|
LspAdapter, LspAdapterDelegate, PARSER, PLAIN_TEXT,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use collections::{hash_map, HashMap};
|
use collections::{hash_map, HashMap};
|
||||||
|
@ -43,8 +43,7 @@ struct LanguageRegistryState {
|
||||||
languages: Vec<Arc<Language>>,
|
languages: Vec<Arc<Language>>,
|
||||||
available_languages: Vec<AvailableLanguage>,
|
available_languages: Vec<AvailableLanguage>,
|
||||||
grammars: HashMap<Arc<str>, AvailableGrammar>,
|
grammars: HashMap<Arc<str>, AvailableGrammar>,
|
||||||
next_available_language_id: AvailableLanguageId,
|
loading_languages: HashMap<LanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
||||||
loading_languages: HashMap<AvailableLanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
|
||||||
subscription: (watch::Sender<()>, watch::Receiver<()>),
|
subscription: (watch::Sender<()>, watch::Receiver<()>),
|
||||||
theme: Option<Arc<Theme>>,
|
theme: Option<Arc<Theme>>,
|
||||||
version: usize,
|
version: usize,
|
||||||
|
@ -68,7 +67,7 @@ pub struct PendingLanguageServer {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AvailableLanguage {
|
struct AvailableLanguage {
|
||||||
id: AvailableLanguageId,
|
id: LanguageId,
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
grammar: Option<Arc<str>>,
|
grammar: Option<Arc<str>>,
|
||||||
matcher: LanguageMatcher,
|
matcher: LanguageMatcher,
|
||||||
|
@ -77,8 +76,6 @@ struct AvailableLanguage {
|
||||||
loaded: bool,
|
loaded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type AvailableLanguageId = usize;
|
|
||||||
|
|
||||||
enum AvailableGrammar {
|
enum AvailableGrammar {
|
||||||
Native(tree_sitter::Language),
|
Native(tree_sitter::Language),
|
||||||
Loaded(PathBuf, tree_sitter::Language),
|
Loaded(PathBuf, tree_sitter::Language),
|
||||||
|
@ -126,7 +123,6 @@ impl LanguageRegistry {
|
||||||
languages: vec![PLAIN_TEXT.clone()],
|
languages: vec![PLAIN_TEXT.clone()],
|
||||||
available_languages: Default::default(),
|
available_languages: Default::default(),
|
||||||
grammars: Default::default(),
|
grammars: Default::default(),
|
||||||
next_available_language_id: 0,
|
|
||||||
loading_languages: Default::default(),
|
loading_languages: Default::default(),
|
||||||
subscription: watch::channel(),
|
subscription: watch::channel(),
|
||||||
theme: Default::default(),
|
theme: Default::default(),
|
||||||
|
@ -160,6 +156,17 @@ impl LanguageRegistry {
|
||||||
self.state.write().reload_languages(languages, grammars);
|
self.state.write().reload_languages(languages, grammars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the specified languages and grammars from the registry.
|
||||||
|
pub fn remove_languages(
|
||||||
|
&self,
|
||||||
|
languages_to_remove: &[Arc<str>],
|
||||||
|
grammars_to_remove: &[Arc<str>],
|
||||||
|
) {
|
||||||
|
self.state
|
||||||
|
.write()
|
||||||
|
.remove_languages(languages_to_remove, grammars_to_remove)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "test-support", test))]
|
#[cfg(any(feature = "test-support", test))]
|
||||||
pub fn register_test_language(&self, config: LanguageConfig) {
|
pub fn register_test_language(&self, config: LanguageConfig) {
|
||||||
self.register_language(
|
self.register_language(
|
||||||
|
@ -194,7 +201,7 @@ impl LanguageRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
state.available_languages.push(AvailableLanguage {
|
state.available_languages.push(AvailableLanguage {
|
||||||
id: post_inc(&mut state.next_available_language_id),
|
id: LanguageId::new(),
|
||||||
name,
|
name,
|
||||||
grammar: grammar_name,
|
grammar: grammar_name,
|
||||||
matcher,
|
matcher,
|
||||||
|
@ -241,6 +248,13 @@ impl LanguageRegistry {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn grammar_names(&self) -> Vec<Arc<str>> {
|
||||||
|
let state = self.state.read();
|
||||||
|
let mut result = state.grammars.keys().cloned().collect::<Vec<_>>();
|
||||||
|
result.sort_unstable_by_key(|grammar_name| grammar_name.to_lowercase());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add(&self, language: Arc<Language>) {
|
pub fn add(&self, language: Arc<Language>) {
|
||||||
self.state.write().add(language);
|
self.state.write().add(language);
|
||||||
}
|
}
|
||||||
|
@ -358,7 +372,7 @@ impl LanguageRegistry {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Language::new(config, grammar)
|
Language::new_with_id(id, config, grammar)
|
||||||
.with_lsp_adapters(language.lsp_adapters)
|
.with_lsp_adapters(language.lsp_adapters)
|
||||||
.await
|
.await
|
||||||
.with_queries(queries)
|
.with_queries(queries)
|
||||||
|
@ -660,6 +674,22 @@ impl LanguageRegistryState {
|
||||||
*self.subscription.0.borrow_mut() = ();
|
*self.subscription.0.borrow_mut() = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_languages(
|
||||||
|
&mut self,
|
||||||
|
languages_to_remove: &[Arc<str>],
|
||||||
|
grammars_to_remove: &[Arc<str>],
|
||||||
|
) {
|
||||||
|
self.languages
|
||||||
|
.retain(|language| !languages_to_remove.contains(&language.name()));
|
||||||
|
self.available_languages
|
||||||
|
.retain(|language| !languages_to_remove.contains(&language.name));
|
||||||
|
self.grammars
|
||||||
|
.retain(|name, _| !grammars_to_remove.contains(&name));
|
||||||
|
self.version += 1;
|
||||||
|
self.reload_count += 1;
|
||||||
|
*self.subscription.0.borrow_mut() = ();
|
||||||
|
}
|
||||||
|
|
||||||
fn reload_languages(
|
fn reload_languages(
|
||||||
&mut self,
|
&mut self,
|
||||||
languages_to_reload: &[Arc<str>],
|
languages_to_reload: &[Arc<str>],
|
||||||
|
@ -701,7 +731,7 @@ impl LanguageRegistryState {
|
||||||
|
|
||||||
/// Mark the given language as having been loaded, so that the
|
/// Mark the given language as having been loaded, so that the
|
||||||
/// language registry won't try to load it again.
|
/// language registry won't try to load it again.
|
||||||
fn mark_language_loaded(&mut self, id: AvailableLanguageId) {
|
fn mark_language_loaded(&mut self, id: LanguageId) {
|
||||||
for language in &mut self.available_languages {
|
for language in &mut self.available_languages {
|
||||||
if language.id == id {
|
if language.id == id {
|
||||||
language.loaded = true;
|
language.loaded = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod syntax_map_tests;
|
mod syntax_map_tests;
|
||||||
|
|
||||||
use crate::{Grammar, InjectionConfig, Language, LanguageRegistry};
|
use crate::{Grammar, InjectionConfig, Language, LanguageId, LanguageRegistry};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -102,9 +102,9 @@ enum SyntaxLayerContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxLayerContent {
|
impl SyntaxLayerContent {
|
||||||
fn language_id(&self) -> Option<usize> {
|
fn language_id(&self) -> Option<LanguageId> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxLayerContent::Parsed { language, .. } => language.id(),
|
SyntaxLayerContent::Parsed { language, .. } => Some(language.id),
|
||||||
SyntaxLayerContent::Pending { .. } => None,
|
SyntaxLayerContent::Pending { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ struct SyntaxLayerSummary {
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
range: Range<Anchor>,
|
range: Range<Anchor>,
|
||||||
last_layer_range: Range<Anchor>,
|
last_layer_range: Range<Anchor>,
|
||||||
last_layer_language: Option<usize>,
|
last_layer_language: Option<LanguageId>,
|
||||||
contains_unknown_injections: bool,
|
contains_unknown_injections: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ struct SyntaxLayerSummary {
|
||||||
struct SyntaxLayerPosition {
|
struct SyntaxLayerPosition {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
range: Range<Anchor>,
|
range: Range<Anchor>,
|
||||||
language: Option<usize>,
|
language: Option<LanguageId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -182,9 +182,9 @@ enum ParseStepLanguage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseStepLanguage {
|
impl ParseStepLanguage {
|
||||||
fn id(&self) -> Option<usize> {
|
fn id(&self) -> Option<LanguageId> {
|
||||||
match self {
|
match self {
|
||||||
ParseStepLanguage::Loaded { language } => language.id(),
|
ParseStepLanguage::Loaded { language } => Some(language.id),
|
||||||
ParseStepLanguage::Pending { .. } => None,
|
ParseStepLanguage::Pending { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue