Detailed changes
@@ -1,7 +1,7 @@
use crate::{HighlightMap, SyntaxTheme};
use parking_lot::Mutex;
use serde::Deserialize;
-use std::str;
+use std::{path::Path, str, sync::Arc};
use tree_sitter::{Language as Grammar, Query};
pub use tree_sitter::{Parser, Tree};
@@ -25,6 +25,41 @@ pub struct Language {
pub highlight_map: Mutex<HighlightMap>,
}
+#[derive(Default)]
+pub struct LanguageRegistry {
+ languages: Vec<Arc<Language>>,
+}
+
+impl LanguageRegistry {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn add(&mut self, language: Arc<Language>) {
+ self.languages.push(language);
+ }
+
+ pub fn set_theme(&self, theme: &SyntaxTheme) {
+ for language in &self.languages {
+ language.set_theme(theme);
+ }
+ }
+
+ pub fn select_language(&self, path: impl AsRef<Path>) -> Option<&Arc<Language>> {
+ let path = path.as_ref();
+ let filename = path.file_name().and_then(|name| name.to_str());
+ let extension = path.extension().and_then(|name| name.to_str());
+ let path_suffixes = [extension, filename];
+ self.languages.iter().find(|language| {
+ language
+ .config
+ .path_suffixes
+ .iter()
+ .any(|suffix| path_suffixes.contains(&Some(suffix.as_str())))
+ })
+ }
+}
+
impl Language {
pub fn name(&self) -> &str {
self.config.name.as_str()
@@ -38,3 +73,63 @@ impl Language {
*self.highlight_map.lock() = HighlightMap::new(self.highlight_query.capture_names(), theme);
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_select_language() {
+ let grammar = tree_sitter_rust::language();
+ let registry = LanguageRegistry {
+ languages: vec![
+ Arc::new(Language {
+ config: LanguageConfig {
+ name: "Rust".to_string(),
+ path_suffixes: vec!["rs".to_string()],
+ ..Default::default()
+ },
+ grammar,
+ highlight_query: Query::new(grammar, "").unwrap(),
+ brackets_query: Query::new(grammar, "").unwrap(),
+ highlight_map: Default::default(),
+ }),
+ Arc::new(Language {
+ config: LanguageConfig {
+ name: "Make".to_string(),
+ path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
+ ..Default::default()
+ },
+ grammar,
+ highlight_query: Query::new(grammar, "").unwrap(),
+ brackets_query: Query::new(grammar, "").unwrap(),
+ highlight_map: Default::default(),
+ }),
+ ],
+ };
+
+ // matching file extension
+ assert_eq!(
+ registry.select_language("zed/lib.rs").map(|l| l.name()),
+ Some("Rust")
+ );
+ assert_eq!(
+ registry.select_language("zed/lib.mk").map(|l| l.name()),
+ Some("Make")
+ );
+
+ // matching filename
+ assert_eq!(
+ registry.select_language("zed/Makefile").map(|l| l.name()),
+ Some("Make")
+ );
+
+ // matching suffix that is not the full file extension or filename
+ assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
+ assert_eq!(
+ registry.select_language("zed/a.cars").map(|l| l.name()),
+ None
+ );
+ assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
+ }
+}
@@ -15,7 +15,7 @@ use clock::ReplicaId;
use gpui::{AppContext, Entity, ModelContext, MutableAppContext, Task};
pub use highlight_map::{HighlightId, HighlightMap};
use language::Tree;
-pub use language::{Language, LanguageConfig};
+pub use language::{Language, LanguageConfig, LanguageRegistry};
use lazy_static::lazy_static;
use operation_queue::OperationQueue;
use parking_lot::Mutex;
@@ -975,10 +975,10 @@ mod tests {
time::Duration,
};
use zed::{
+ buffer::LanguageRegistry,
channel::{Channel, ChannelDetails, ChannelList},
editor::{Editor, EditorStyle, Insert},
fs::{FakeFs, Fs as _},
- language::LanguageRegistry,
people_panel::JoinWorktree,
project::ProjectPath,
rpc::{self, Client, Credentials, EstablishConnectionError},
@@ -2753,7 +2753,11 @@ impl SelectionExt for Selection {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{editor::Point, language::LanguageRegistry, settings, test::sample_text};
+ use crate::{
+ editor::Point,
+ settings,
+ test::{self, sample_text},
+ };
use buffer::History;
use unindent::Unindent;
@@ -4232,9 +4236,9 @@ mod tests {
#[gpui::test]
async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
- let settings = cx.read(settings::test).1;
- let languages = LanguageRegistry::new();
- let lang = languages.select_language("z.rs");
+ let app_state = cx.update(test::test_app_state);
+
+ let lang = app_state.languages.select_language("z.rs");
let text = r#"
use mod1::mod2::{mod3, mod4};
@@ -4247,7 +4251,7 @@ mod tests {
let history = History::new(text.into());
Buffer::from_history(0, history, None, lang.cloned(), cx)
});
- let (_, view) = cx.add_window(|cx| build_editor(buffer, settings.clone(), cx));
+ let (_, view) = cx.add_window(|cx| build_editor(buffer, app_state.settings.clone(), cx));
view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing())
.await;
@@ -1,128 +1,36 @@
-use buffer::{HighlightMap, Language, SyntaxTheme};
+use buffer::{HighlightMap, Language, LanguageRegistry};
use parking_lot::Mutex;
use rust_embed::RustEmbed;
-use std::{path::Path, str, sync::Arc};
+use std::{str, sync::Arc};
use tree_sitter::Query;
-pub use tree_sitter::{Parser, Tree};
#[derive(RustEmbed)]
#[folder = "languages"]
-pub struct LanguageDir;
+struct LanguageDir;
-pub struct LanguageRegistry {
- languages: Vec<Arc<Language>>,
+pub fn build_language_registry() -> LanguageRegistry {
+ let mut languages = LanguageRegistry::default();
+ languages.add(Arc::new(rust()));
+ languages
}
-impl LanguageRegistry {
- pub fn new() -> Self {
- let grammar = tree_sitter_rust::language();
- let rust_config =
- toml::from_slice(&LanguageDir::get("rust/config.toml").unwrap().data).unwrap();
- let rust_language = Language {
- config: rust_config,
- grammar,
- highlight_query: Self::load_query(grammar, "rust/highlights.scm"),
- brackets_query: Self::load_query(grammar, "rust/brackets.scm"),
- highlight_map: Mutex::new(HighlightMap::default()),
- };
-
- Self {
- languages: vec![Arc::new(rust_language)],
- }
- }
-
- pub fn set_theme(&self, theme: &SyntaxTheme) {
- for language in &self.languages {
- language.set_theme(theme);
- }
- }
-
- pub fn select_language(&self, path: impl AsRef<Path>) -> Option<&Arc<Language>> {
- let path = path.as_ref();
- let filename = path.file_name().and_then(|name| name.to_str());
- let extension = path.extension().and_then(|name| name.to_str());
- let path_suffixes = [extension, filename];
- self.languages.iter().find(|language| {
- language
- .config
- .path_suffixes
- .iter()
- .any(|suffix| path_suffixes.contains(&Some(suffix.as_str())))
- })
- }
-
- fn load_query(grammar: tree_sitter::Language, path: &str) -> Query {
- Query::new(
- grammar,
- str::from_utf8(&LanguageDir::get(path).unwrap().data).unwrap(),
- )
- .unwrap()
+pub fn rust() -> Language {
+ let grammar = tree_sitter_rust::language();
+ let rust_config =
+ toml::from_slice(&LanguageDir::get("rust/config.toml").unwrap().data).unwrap();
+ Language {
+ config: rust_config,
+ grammar,
+ highlight_query: load_query(grammar, "rust/highlights.scm"),
+ brackets_query: load_query(grammar, "rust/brackets.scm"),
+ highlight_map: Mutex::new(HighlightMap::default()),
}
}
-impl Default for LanguageRegistry {
- fn default() -> Self {
- Self::new()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use buffer::LanguageConfig;
-
- #[test]
- fn test_select_language() {
- let grammar = tree_sitter_rust::language();
- let registry = LanguageRegistry {
- languages: vec![
- Arc::new(Language {
- config: LanguageConfig {
- name: "Rust".to_string(),
- path_suffixes: vec!["rs".to_string()],
- ..Default::default()
- },
- grammar,
- highlight_query: Query::new(grammar, "").unwrap(),
- brackets_query: Query::new(grammar, "").unwrap(),
- highlight_map: Default::default(),
- }),
- Arc::new(Language {
- config: LanguageConfig {
- name: "Make".to_string(),
- path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
- ..Default::default()
- },
- grammar,
- highlight_query: Query::new(grammar, "").unwrap(),
- brackets_query: Query::new(grammar, "").unwrap(),
- highlight_map: Default::default(),
- }),
- ],
- };
-
- // matching file extension
- assert_eq!(
- registry.select_language("zed/lib.rs").map(|l| l.name()),
- Some("Rust")
- );
- assert_eq!(
- registry.select_language("zed/lib.mk").map(|l| l.name()),
- Some("Make")
- );
-
- // matching filename
- assert_eq!(
- registry.select_language("zed/Makefile").map(|l| l.name()),
- Some("Make")
- );
-
- // matching suffix that is not the full file extension or filename
- assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
- assert_eq!(
- registry.select_language("zed/a.cars").map(|l| l.name()),
- None
- );
- assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
- }
+fn load_query(grammar: tree_sitter::Language, path: &str) -> Query {
+ Query::new(
+ grammar,
+ str::from_utf8(&LanguageDir::get(path).unwrap().data).unwrap(),
+ )
+ .unwrap()
}
@@ -23,6 +23,8 @@ pub mod workspace;
pub mod worktree;
use crate::util::TryFutureExt;
+pub use buffer;
+use buffer::LanguageRegistry;
use channel::ChannelList;
use gpui::{action, keymap::Binding, ModelHandle};
use parking_lot::Mutex;
@@ -41,7 +43,7 @@ const MIN_FONT_SIZE: f32 = 6.0;
pub struct AppState {
pub settings_tx: Arc<Mutex<watch::Sender<Settings>>>,
pub settings: watch::Receiver<Settings>,
- pub languages: Arc<language::LanguageRegistry>,
+ pub languages: Arc<LanguageRegistry>,
pub themes: Arc<settings::ThemeRegistry>,
pub rpc: Arc<rpc::Client>,
pub user_store: ModelHandle<user::UserStore>,
@@ -32,7 +32,7 @@ fn main() {
let themes = settings::ThemeRegistry::new(Assets, app.font_cache());
let (settings_tx, settings) = settings::channel(&app.font_cache(), &themes).unwrap();
- let languages = Arc::new(language::LanguageRegistry::new());
+ let languages = Arc::new(language::build_language_registry());
languages.set_theme(&settings.borrow().theme.syntax);
app.run(move |cx| {
@@ -1,13 +1,13 @@
use crate::{
fs::Fs,
fuzzy::{self, PathMatch},
- language::LanguageRegistry,
rpc::Client,
util::TryFutureExt as _,
worktree::{self, Worktree},
AppState,
};
use anyhow::Result;
+use buffer::LanguageRegistry;
use futures::Future;
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
use std::{
@@ -3,13 +3,14 @@ use crate::{
channel::ChannelList,
fs::FakeFs,
http::{HttpClient, Request, Response, ServerResponse},
- language::LanguageRegistry,
+ language,
rpc::{self, Client, Credentials, EstablishConnectionError},
settings::{self, ThemeRegistry},
user::UserStore,
AppState,
};
use anyhow::{anyhow, Result};
+use buffer::LanguageRegistry;
use futures::{future::BoxFuture, Future};
use gpui::{AsyncAppContext, Entity, ModelHandle, MutableAppContext, TestAppContext};
use parking_lot::Mutex;
@@ -83,7 +84,8 @@ fn write_tree(path: &Path, tree: serde_json::Value) {
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let (settings_tx, settings) = settings::test(cx);
- let languages = Arc::new(LanguageRegistry::new());
+ let mut languages = LanguageRegistry::new();
+ languages.add(Arc::new(language::rust()));
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
let rpc = rpc::Client::new();
let http = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) });
@@ -92,7 +94,7 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
settings_tx: Arc::new(Mutex::new(settings_tx)),
settings,
themes,
- languages: languages.clone(),
+ languages: Arc::new(languages),
channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), rpc.clone(), cx)),
rpc,
user_store,
@@ -4,13 +4,12 @@ use self::ignore::IgnoreStack;
use crate::{
fs::{self, Fs},
fuzzy::CharBag,
- language::LanguageRegistry,
rpc::{self, proto, Status},
util::{Bias, TryFutureExt},
};
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
use anyhow::{anyhow, Result};
-use buffer::{self, Buffer, History, Operation, Rope};
+use buffer::{self, Buffer, History, LanguageRegistry, Operation, Rope};
use clock::ReplicaId;
use futures::{Stream, StreamExt};
use gpui::{