WIP

MrSubidubi created

Change summary

Cargo.lock                                  |  1 
crates/extension_host/src/extension_host.rs | 32 +++--------
crates/gpui_util/src/lib.rs                 | 14 +++++
crates/language/Cargo.toml                  |  1 
crates/language/src/language.rs             |  4 
crates/language/src/language_registry.rs    | 63 +++++++++++++++++-----
crates/languages/src/lib.rs                 | 29 +++++-----
crates/util/src/util.rs                     |  8 ++
8 files changed, 96 insertions(+), 56 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -9390,6 +9390,7 @@ dependencies = [
  "smol",
  "streaming-iterator",
  "strsim",
+ "strum 0.27.2",
  "sum_tree",
  "task",
  "text",

crates/extension_host/src/extension_host.rs 🔗

@@ -37,8 +37,8 @@ use gpui::{
 };
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use language::{
-    LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
-    QUERY_FILENAME_PREFIXES, Rope,
+    LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LanguageQuery, LoadedLanguage,
+    Rope,
 };
 use node_runtime::NodeRuntime;
 use project::ContextProviderWithTasks;
@@ -1877,26 +1877,14 @@ impl ExtensionStore {
 
 fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
     let mut result = LanguageQueries::default();
-    if let Some(entries) = std::fs::read_dir(root_path).log_err() {
-        for entry in entries {
-            let Some(entry) = entry.log_err() else {
-                continue;
-            };
-            let path = entry.path();
-            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
-                if !remainder.ends_with(".scm") {
-                    continue;
-                }
-                for (name, query) in QUERY_FILENAME_PREFIXES {
-                    if remainder.starts_with(name) {
-                        if let Some(contents) = std::fs::read_to_string(&path).log_err() {
-                            match query(&mut result) {
-                                None => *query(&mut result) = Some(contents.into()),
-                                Some(r) => r.to_mut().push_str(contents.as_ref()),
-                            }
-                        }
-                        break;
-                    }
+    if std::fs::metadata(root_path).is_ok_and(|metadata| metadata.is_dir()) {
+        for query in LanguageQuery::variants() {
+            let query_path = query.query_path(&root_path);
+            if let Some(contents) = std::fs::read_to_string(&query_path).log_err() {
+                let query = query.query_mut(&mut result);
+                match query {
+                    None => *query = Some(contents.into()),
+                    Some(r) => r.to_mut().push_str(contents.as_ref()),
                 }
             }
         }

crates/gpui_util/src/lib.rs 🔗

@@ -82,6 +82,10 @@ pub trait ResultExt<E> {
     /// Assert that this result should never be an error in development or tests.
     fn debug_assert_ok(self, reason: &str) -> Self;
     fn warn_on_err(self) -> Option<Self::Ok>;
+    fn is_err_or<F>(self, f: F) -> bool
+    where
+        F: FnOnce(Self::Ok) -> bool;
+
     fn log_with_level(self, level: log::Level) -> Option<Self::Ok>;
     fn anyhow(self) -> anyhow::Result<Self::Ok>
     where
@@ -112,6 +116,16 @@ where
         self.log_with_level(log::Level::Warn)
     }
 
+    fn is_err_or<F>(self, f: F) -> bool
+    where
+        F: FnOnce(T) -> bool,
+    {
+        match self {
+            Ok(value) => f(value),
+            Err(_) => true,
+        }
+    }
+
     #[track_caller]
     fn log_with_level(self, level: log::Level) -> Option<T> {
         match self {

crates/language/Cargo.toml 🔗

@@ -58,6 +58,7 @@ smallvec.workspace = true
 smol.workspace = true
 streaming-iterator.workspace = true
 strsim.workspace = true
+strum.workspace = true
 sum_tree.workspace = true
 task.workspace = true
 text.workspace = true

crates/language/src/language.rs 🔗

@@ -83,8 +83,8 @@ pub use buffer::Operation;
 pub use buffer::*;
 pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup};
 pub use language_registry::{
-    AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry,
-    QUERY_FILENAME_PREFIXES,
+    AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageQuery,
+    LanguageRegistry,
 };
 pub use lsp::{LanguageServerId, LanguageServerName};
 pub use outline::*;

crates/language/src/language_registry.rs 🔗

@@ -232,22 +232,53 @@ impl std::fmt::Display for LanguageNotFound {
     }
 }
 
-pub const QUERY_FILENAME_PREFIXES: &[(
-    &str,
-    fn(&mut LanguageQueries) -> &mut Option<Cow<'static, str>>,
-)] = &[
-    ("highlights", |q| &mut q.highlights),
-    ("brackets", |q| &mut q.brackets),
-    ("outline", |q| &mut q.outline),
-    ("indents", |q| &mut q.indents),
-    ("injections", |q| &mut q.injections),
-    ("overrides", |q| &mut q.overrides),
-    ("redactions", |q| &mut q.redactions),
-    ("runnables", |q| &mut q.runnables),
-    ("debugger", |q| &mut q.debugger),
-    ("textobjects", |q| &mut q.text_objects),
-    ("imports", |q| &mut q.imports),
-];
+#[derive(Copy, Clone, PartialEq, Eq, strum::Display, strum::EnumIter)]
+#[strum(serialize_all = "snake_case")]
+pub enum LanguageQuery {
+    Highlights,
+    Brackets,
+    Outline,
+    Indents,
+    Injections,
+    Overrides,
+    Runnables,
+    Debugger,
+    Textobjects,
+    Imports,
+}
+
+impl LanguageQuery {
+    pub fn variants() -> impl Iterator<Item = Self> {
+        use strum::IntoEnumIterator;
+        Self::iter()
+    }
+
+    pub fn query_mut<'a>(
+        &self,
+        queries: &'a mut LanguageQueries,
+    ) -> &'a mut Option<Cow<'static, str>> {
+        match self {
+            LanguageQuery::Highlights => &mut queries.highlights,
+            LanguageQuery::Brackets => &mut queries.brackets,
+            LanguageQuery::Outline => &mut queries.outline,
+            LanguageQuery::Indents => &mut queries.indents,
+            LanguageQuery::Injections => &mut queries.injections,
+            LanguageQuery::Overrides => &mut queries.overrides,
+            LanguageQuery::Runnables => &mut queries.runnables,
+            LanguageQuery::Debugger => &mut queries.debugger,
+            LanguageQuery::Textobjects => &mut queries.text_objects,
+            LanguageQuery::Imports => &mut queries.imports,
+        }
+    }
+
+    pub fn query_path(&self, folder_path: &Path) -> PathBuf {
+        folder_path.join(self.file_name())
+    }
+
+    pub fn file_name(&self) -> String {
+        format!("{self}.scm")
+    }
+}
 
 /// Tree-sitter language queries for a given language.
 #[derive(Debug, Default)]

crates/languages/src/lib.rs 🔗

@@ -8,7 +8,7 @@ use rust_embed::RustEmbed;
 use settings::{SemanticTokenRules, SettingsStore};
 use smol::stream::StreamExt;
 use std::{str, sync::Arc};
-use util::{ResultExt, asset_str};
+use util::{ResultExt, embedded_file_to_str};
 
 pub use language::*;
 
@@ -436,20 +436,19 @@ fn load_config(name: &str) -> LanguageConfig {
 
 fn load_queries(name: &str) -> LanguageQueries {
     let mut result = LanguageQueries::default();
-    for path in LanguageDir::iter() {
-        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
-            if !remainder.ends_with(".scm") {
-                continue;
-            }
-            for (name, query) in QUERY_FILENAME_PREFIXES {
-                if remainder.starts_with(name) {
-                    let contents = asset_str::<LanguageDir>(path.as_ref());
-                    match query(&mut result) {
-                        None => *query(&mut result) = Some(contents),
-                        Some(r) => r.to_mut().push_str(contents.as_ref()),
-                    }
-                }
-            }
+    for query in LanguageQuery::variants() {
+        let Some(contents) = LanguageDir::get(&format!(
+            "{name}/{query_filename}",
+            query_filename = query.file_name()
+        ))
+        .map(embedded_file_to_str) else {
+            continue;
+        };
+
+        let query = query.query_mut(&mut result);
+        match query {
+            None => *query = Some(contents),
+            Some(r) => r.to_mut().push_str(contents.as_ref()),
         }
     }
     result

crates/util/src/util.rs 🔗

@@ -20,6 +20,7 @@ pub mod time;
 use anyhow::Result;
 use itertools::Either;
 use regex::Regex;
+use rust_embed::EmbeddedFile;
 use std::path::{Path, PathBuf};
 use std::sync::LazyLock;
 use std::{
@@ -594,7 +595,12 @@ pub use rng::RandomCharIter;
 
 /// Get an embedded file as a string.
 pub fn asset_str<A: rust_embed::RustEmbed>(path: &str) -> Cow<'static, str> {
-    match A::get(path).expect(path).data {
+    let asset = A::get(path).expect(path);
+    embedded_file_to_str(asset)
+}
+
+pub fn embedded_file_to_str(file: EmbeddedFile) -> Cow<'static, str> {
+    match file.data {
         Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).unwrap()),
         Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).unwrap()),
     }