diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b30c9dd848..0085761e0b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8772,6 +8772,10 @@ impl Editor { cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() }) } multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed), + multi_buffer::Event::LanguageChanged => { + cx.emit(EditorEvent::Reparsed); + cx.notify(); + } multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged), multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved), multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => { diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index fffaac44e4..ad88c95729 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -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, + pub grammars: HashMap, GrammarManifestEntry>, pub languages: HashMap, LanguageManifestEntry>, pub themes: HashMap, } @@ -52,6 +52,7 @@ pub struct LanguageManifestEntry { extension: String, path: PathBuf, matcher: LanguageMatcher, + grammar: Option>, } #[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) }) diff --git a/crates/extension/src/extension_store_test.rs b/crates/extension/src/extension_store_test.rs index e95496b529..758111b286 100644 --- a/crates/extension/src/extension_store_test.rs +++ b/crates/extension/src/extension_store_test.rs @@ -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, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b42ea1f12d..debf790936 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -758,6 +758,7 @@ impl Buffer { /// Assign a language to the buffer. pub fn set_language(&mut self, language: Option>, cx: &mut ModelContext) { + self.parse_count += 1; self.syntax_map.lock().clear(); self.language = language; self.reparse(cx); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index c3c76b11ba..f811637a24 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -740,14 +740,16 @@ type AvailableLanguageId = usize; struct AvailableLanguage { id: AvailableLanguageId, name: Arc, + grammar: Option>, source: AvailableLanguageSource, lsp_adapters: Vec>, loaded: bool, } enum AvailableGrammar { - Loaded(tree_sitter::Language), - Loading(Vec>>), + Native(tree_sitter::Language), + Loaded(PathBuf, tree_sitter::Language), + Loading(PathBuf, Vec>>), Unloaded(PathBuf), } @@ -781,7 +783,7 @@ struct LanguageRegistryState { next_language_server_id: usize, languages: Vec>, available_languages: Vec, - grammars: HashMap, + grammars: HashMap, AvailableGrammar>, next_available_language_id: AvailableLanguageId, loading_languages: HashMap>>>>, subscription: (watch::Sender<()>, watch::Receiver<()>), @@ -834,8 +836,8 @@ impl LanguageRegistry { } /// Clear out the given languages and reload them from scratch. - pub fn reload_languages(&self, languages: &HashSet>) { - self.state.write().reload_languages(languages); + pub fn reload_languages(&self, languages: &[Arc], grammars: &[Arc]) { + self.state.write().reload_languages(languages, grammars); } pub fn register( @@ -849,6 +851,7 @@ impl LanguageRegistry { state.available_languages.push(AvailableLanguage { id: post_inc(&mut state.next_available_language_id), name: config.name.clone(), + grammar: config.grammar.clone(), source: AvailableLanguageSource::BuiltIn { config, get_queries, @@ -863,6 +866,7 @@ impl LanguageRegistry { &self, path: Arc, name: Arc, + grammar_name: Option>, matcher: LanguageMatcher, get_queries: fn(&Path) -> LanguageQueries, ) { @@ -885,6 +889,7 @@ impl LanguageRegistry { } state.available_languages.push(AvailableLanguage { id: post_inc(&mut state.next_available_language_id), + grammar: grammar_name, name, source, lsp_adapters: Vec::new(), @@ -894,16 +899,16 @@ impl LanguageRegistry { pub fn add_grammars( &self, - grammars: impl IntoIterator, tree_sitter::Language)>, + grammars: impl IntoIterator>, tree_sitter::Language)>, ) { self.state.write().grammars.extend( grammars .into_iter() - .map(|(name, grammar)| (name.into(), AvailableGrammar::Loaded(grammar))), + .map(|(name, grammar)| (name.into(), AvailableGrammar::Native(grammar))), ); } - pub fn register_grammar(&self, name: String, path: PathBuf) { + pub fn register_grammar(&self, name: Arc, path: PathBuf) { self.state .write() .grammars @@ -1124,46 +1129,49 @@ impl LanguageRegistry { if let Some(grammar) = state.grammars.get_mut(name.as_ref()) { match grammar { - AvailableGrammar::Loaded(grammar) => { + AvailableGrammar::Native(grammar) | AvailableGrammar::Loaded(_, grammar) => { tx.send(Ok(grammar.clone())).ok(); } - AvailableGrammar::Loading(txs) => { + AvailableGrammar::Loading(_, txs) => { txs.push(tx); } AvailableGrammar::Unloaded(wasm_path) => { if let Some(executor) = &self.executor { let this = self.clone(); - let wasm_path = wasm_path.clone(); executor - .spawn(async move { - let wasm_bytes = std::fs::read(&wasm_path)?; - let grammar_name = wasm_path - .file_stem() - .and_then(OsStr::to_str) - .ok_or_else(|| anyhow!("invalid grammar filename"))?; - let grammar = PARSER.with(|parser| { - let mut parser = parser.borrow_mut(); - let mut store = parser.take_wasm_store().unwrap(); - let grammar = store.load_language(&grammar_name, &wasm_bytes); - parser.set_wasm_store(store).unwrap(); - grammar - })?; + .spawn({ + let wasm_path = wasm_path.clone(); + async move { + let wasm_bytes = std::fs::read(&wasm_path)?; + let grammar_name = wasm_path + .file_stem() + .and_then(OsStr::to_str) + .ok_or_else(|| anyhow!("invalid grammar filename"))?; + let grammar = PARSER.with(|parser| { + let mut parser = parser.borrow_mut(); + let mut store = parser.take_wasm_store().unwrap(); + let grammar = + store.load_language(&grammar_name, &wasm_bytes); + parser.set_wasm_store(store).unwrap(); + grammar + })?; - if let Some(AvailableGrammar::Loading(txs)) = - this.state.write().grammars.insert( - name.to_string(), - AvailableGrammar::Loaded(grammar.clone()), - ) - { - for tx in txs { - tx.send(Ok(grammar.clone())).ok(); + if let Some(AvailableGrammar::Loading(_, txs)) = + this.state.write().grammars.insert( + name, + AvailableGrammar::Loaded(wasm_path, grammar.clone()), + ) + { + for tx in txs { + tx.send(Ok(grammar.clone())).ok(); + } } - } - anyhow::Ok(()) + anyhow::Ok(()) + } }) .detach(); - *grammar = AvailableGrammar::Loading(vec![tx]); + *grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]); } } } @@ -1357,16 +1365,42 @@ impl LanguageRegistryState { *self.subscription.0.borrow_mut() = (); } - fn reload_languages(&mut self, languages: &HashSet>) { - self.languages - .retain(|language| !languages.contains(&language.config.name)); - self.version += 1; - self.reload_count += 1; + 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.contains(&language.name) { + 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() = (); } diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 45f430c52a..90a2603cd5 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -23,15 +23,10 @@ lazy_static::lazy_static! { CONFIG_DIR.join("support") }; pub static ref EXTENSIONS_DIR: PathBuf = if cfg!(target_os="macos") { - HOME.join("Library/Application Support/Zed") + HOME.join("Library/Application Support/Zed/extensions") } else { CONFIG_DIR.join("extensions") }; - pub static ref PLUGINS_DIR: PathBuf = if cfg!(target_os="macos") { - HOME.join("Library/Application Support/Zed/plugins") - } else { - CONFIG_DIR.join("plugins") - }; pub static ref LANGUAGES_DIR: PathBuf = if cfg!(target_os="macos") { HOME.join("Library/Application Support/Zed/languages") } else {