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:
parent
f2a4dbaf7f
commit
7b03e977e4
6 changed files with 111 additions and 57 deletions
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue