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:
co-authored by
Nathan Sobo
Max Brunsfeld
parent
becae9feee
commit
154620233b
+96
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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::{
|
||||
|
||||
Reference in New Issue
Block a user