Move LanguageRegistry into buffer

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Antonio Scandurra
2021-10-04 19:59:03 +02:00
co-authored by Nathan Sobo Max Brunsfeld
parent becae9feee
commit 154620233b
10 changed files with 141 additions and 131 deletions
+96 -1
View File
@@ -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);
}
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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},
+9 -5
View File
@@ -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;
+23 -115
View File
@@ -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()
}
+3 -1
View File
@@ -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>,
+1 -1
View File
@@ -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 -1
View File
@@ -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::{
+5 -3
View File
@@ -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,
+1 -2
View File
@@ -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::{