Reload grammars in extensions when they are updated on disk (#7531)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-02-07 16:39:11 -08:00 committed by GitHub
parent f2a4dbaf7f
commit 7b03e977e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 57 deletions

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use collections::{HashMap, HashSet};
use anyhow::{Context as _, Result};
use collections::HashMap;
use fs::Fs;
use futures::StreamExt as _;
use gpui::{actions, AppContext, Context, Global, Model, ModelContext, Task};
@ -36,7 +36,7 @@ impl Global for GlobalExtensionStore {}
#[derive(Deserialize, Serialize, Default)]
pub struct Manifest {
pub grammars: HashMap<String, GrammarManifestEntry>,
pub grammars: HashMap<Arc<str>, GrammarManifestEntry>,
pub languages: HashMap<Arc<str>, LanguageManifestEntry>,
pub themes: HashMap<String, ThemeManifestEntry>,
}
@ -52,6 +52,7 @@ pub struct LanguageManifestEntry {
extension: String,
path: PathBuf,
matcher: LanguageMatcher,
grammar: Option<Arc<str>>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
@ -152,6 +153,7 @@ impl ExtensionStore {
self.language_registry.register_extension(
language_path.into(),
language_name.clone(),
language.grammar.clone(),
language.matcher.clone(),
load_plugin_queries,
);
@ -188,19 +190,29 @@ impl ExtensionStore {
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_languages = HashSet::default();
let mut changed_themes = HashSet::default();
let mut changed_grammars = Vec::default();
let mut changed_languages = Vec::default();
let mut changed_themes = Vec::default();
{
let manifest = manifest.read();
for event in events {
for (grammar_name, grammar) in &manifest.grammars {
let mut grammar_path = extensions_dir.clone();
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());
}
}
for (language_name, language) in &manifest.languages {
let mut language_path = extensions_dir.clone();
language_path
.extend([language.extension.as_ref(), language.path.as_path()]);
if event.path.starts_with(&language_path) || event.path == language_path
{
changed_languages.insert(language_name.clone());
changed_languages.push(language_name.clone());
}
}
@ -208,18 +220,19 @@ impl ExtensionStore {
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.insert(theme_path.clone());
changed_themes.push(theme_path.clone());
}
}
}
}
language_registry.reload_languages(&changed_languages);
language_registry.reload_languages(&changed_languages, &changed_grammars);
for theme_path in &changed_themes {
theme_registry
.load_user_theme(&theme_path, fs.clone())
.await
.context("failed to load user theme")
.log_err();
}
@ -253,7 +266,10 @@ impl ExtensionStore {
.spawn(async move {
let mut manifest = Manifest::default();
let mut extension_paths = fs.read_dir(&extensions_dir).await?;
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) =
@ -305,6 +321,7 @@ impl ExtensionStore {
extension: extension_name.into(),
path: relative_path.into(),
matcher: config.matcher,
grammar: config.grammar,
},
);
}
@ -345,7 +362,8 @@ impl ExtensionStore {
&serde_json::to_string_pretty(&manifest)?.as_str().into(),
Default::default(),
)
.await?;
.await
.context("failed to save extension manifest")?;
anyhow::Ok(manifest)
})

View file

@ -106,6 +106,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
LanguageManifestEntry {
extension: "zed-ruby".into(),
path: "languages/erb".into(),
grammar: Some("embedded_template".into()),
matcher: LanguageMatcher {
path_suffixes: vec!["erb".into()],
first_line_pattern: None,
@ -117,6 +118,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
LanguageManifestEntry {
extension: "zed-ruby".into(),
path: "languages/ruby".into(),
grammar: Some("ruby".into()),
matcher: LanguageMatcher {
path_suffixes: vec!["rb".into()],
first_line_pattern: None,