changeset 6785:f1e0f64160d7

Add the `syn_loader` to `Document` This type also exists on `Editor`. This change brings it to the `Document` as well because the replacement for `Syntax` in the child commits will eliminate `Syntax`'s copy of `syn_loader`. `Syntax` will also be responsible for returning the highlighter and query iterators (which will borrow the loader), so the loader must be separated from that type. In the long run, when we make a larger refactor to have `Document::apply` be a function of the `Editor` instead of the `Document`, we will be able to drop this field on `Document` - it is currently only necessary for `Document::apply`. Once we make that refactor, we will be able to eliminate the surrounding `Arc` in `Arc<ArcSwap<syntax::Loader>>` and use the `ArcSwap` directly instead.
author Michael Davis <mcarsondavis@gmail.com>
date Thu, 20 Feb 2025 17:45:19 -0500
parents 9900b670afa2
children 6a72d595f89b
files helix-core/src/syntax.rs helix-term/src/application.rs helix-term/src/commands/typed.rs helix-term/src/ui/picker.rs helix-view/src/document.rs helix-view/src/editor.rs helix-view/src/gutter.rs helix-view/src/view.rs
diffstat 8 files changed, 94 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/helix-core/src/syntax.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-core/src/syntax.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -274,6 +274,15 @@
     file_types: Vec<FileTypeGlob>,
 }
 
+impl Default for FileTypeGlobMatcher {
+    fn default() -> Self {
+        Self {
+            matcher: globset::GlobSet::empty(),
+            file_types: Default::default(),
+        }
+    }
+}
+
 impl FileTypeGlobMatcher {
     fn new(file_types: Vec<FileTypeGlob>) -> Result<Self, globset::Error> {
         let mut builder = globset::GlobSetBuilder::new();
@@ -299,7 +308,7 @@
 
 // Expose loader as Lazy<> global since it's always static?
 
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct Loader {
     // highlight_names ?
     language_configs: Vec<Arc<LanguageConfiguration>>,
--- a/helix-term/src/application.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-term/src/application.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -389,8 +389,9 @@
         let lang_loader = helix_core::config::user_lang_loader()?;
 
         self.editor.syn_loader.store(Arc::new(lang_loader));
+        let loader = self.editor.syn_loader.load();
         for document in self.editor.documents.values_mut() {
-            document.detect_language(self.editor.syn_loader.clone());
+            document.detect_language(&loader);
             let diagnostics = Editor::doc_diagnostics(
                 &self.editor.language_servers,
                 &self.editor.diagnostics,
--- a/helix-term/src/commands/typed.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-term/src/commands/typed.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -2090,10 +2090,11 @@
 
     let doc = doc_mut!(cx.editor);
 
+    let loader = cx.editor.syn_loader.load();
     if &args[0] == DEFAULT_LANGUAGE_NAME {
-        doc.set_language(None, None)
+        doc.set_language(None, &loader)
     } else {
-        doc.set_language_by_language_id(&args[0], cx.editor.syn_loader.clone())?;
+        doc.set_language_by_language_id(&args[0], &loader)?;
     }
     doc.detect_indent_and_line_ending();
 
--- a/helix-term/src/ui/picker.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-term/src/ui/picker.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -624,7 +624,14 @@
                             if content_type.is_binary() {
                                 return Ok(CachedPreview::Binary);
                             }
-                            Document::open(&path, None, None, editor.config.clone()).map_or(
+                            Document::open(
+                                &path,
+                                None,
+                                false,
+                                editor.config.clone(),
+                                editor.syn_loader.clone(),
+                            )
+                            .map_or(
                                 Err(std::io::Error::new(
                                     std::io::ErrorKind::NotFound,
                                     "Cannot open document",
--- a/helix-view/src/document.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-view/src/document.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -209,6 +209,11 @@
     // NOTE: ideally this would live on the handler for color swatches. This is blocked on a
     // large refactor that would make `&mut Editor` available on the `DocumentDidChange` event.
     pub color_swatch_controller: TaskController,
+
+    // NOTE: this field should eventually go away - we should use the Editor's syn_loader instead
+    // of storing a copy on every doc. Then we can remove the surrounding `Arc` and use the
+    // `ArcSwap` directly.
+    syn_loader: Arc<ArcSwap<syntax::Loader>>,
 }
 
 #[derive(Debug, Clone, Default)]
@@ -679,6 +684,7 @@
         text: Rope,
         encoding_with_bom_info: Option<(&'static Encoding, bool)>,
         config: Arc<dyn DynAccess<Config>>,
+        syn_loader: Arc<ArcSwap<syntax::Loader>>,
     ) -> Self {
         let (encoding, has_bom) = encoding_with_bom_info.unwrap_or((encoding::UTF_8, false));
         let line_ending = config.load().default_line_ending.into();
@@ -721,13 +727,17 @@
             jump_labels: HashMap::new(),
             color_swatches: None,
             color_swatch_controller: TaskController::new(),
+            syn_loader,
         }
     }
 
-    pub fn default(config: Arc<dyn DynAccess<Config>>) -> Self {
+    pub fn default(
+        config: Arc<dyn DynAccess<Config>>,
+        syn_loader: Arc<ArcSwap<syntax::Loader>>,
+    ) -> Self {
         let line_ending: LineEnding = config.load().default_line_ending.into();
         let text = Rope::from(line_ending.as_str());
-        Self::from(text, None, config)
+        Self::from(text, None, config, syn_loader)
     }
 
     // TODO: async fn?
@@ -736,8 +746,9 @@
     pub fn open(
         path: &Path,
         mut encoding: Option<&'static Encoding>,
-        config_loader: Option<Arc<ArcSwap<syntax::Loader>>>,
+        detect_language: bool,
         config: Arc<dyn DynAccess<Config>>,
+        syn_loader: Arc<ArcSwap<syntax::Loader>>,
     ) -> Result<Self, DocumentOpenError> {
         // If the path is not a regular file (e.g.: /dev/random) it should not be opened.
         if path.metadata().is_ok_and(|metadata| !metadata.is_file()) {
@@ -763,12 +774,13 @@
             (Rope::from(line_ending.as_str()), encoding, false)
         };
 
-        let mut doc = Self::from(rope, Some((encoding, has_bom)), config);
+        let loader = syn_loader.load();
+        let mut doc = Self::from(rope, Some((encoding, has_bom)), config, syn_loader);
 
         // set the path and try detecting the language
         doc.set_path(Some(path));
-        if let Some(loader) = config_loader {
-            doc.detect_language(loader);
+        if detect_language {
+            doc.detect_language(&loader);
         }
 
         doc.editor_config = editor_config;
@@ -1122,12 +1134,8 @@
     }
 
     /// Detect the programming language based on the file type.
-    pub fn detect_language(&mut self, config_loader: Arc<ArcSwap<syntax::Loader>>) {
-        let loader = config_loader.load();
-        self.set_language(
-            self.detect_language_config(&loader),
-            Some(Arc::clone(&config_loader)),
-        );
+    pub fn detect_language(&mut self, loader: &syntax::Loader) {
+        self.set_language(self.detect_language_config(loader), loader);
     }
 
     /// Detect the programming language based on the file type.
@@ -1277,20 +1285,20 @@
     pub fn set_language(
         &mut self,
         language_config: Option<Arc<syntax::config::LanguageConfiguration>>,
-        loader: Option<Arc<ArcSwap<syntax::Loader>>>,
+        loader: &syntax::Loader,
     ) {
-        if let (Some(language_config), Some(loader)) = (language_config, loader) {
-            if let Some(highlight_config) =
-                language_config.highlight_config(&(*loader).load().scopes())
-            {
-                self.syntax = Syntax::new(self.text.slice(..), highlight_config, loader);
-            }
-
-            self.language = Some(language_config);
-        } else {
-            self.syntax = None;
-            self.language = None;
-        };
+        self.language = language_config;
+        self.syntax = self
+            .language
+            .as_ref()
+            .and_then(|config| config.highlight_config(&loader.scopes()))
+            .and_then(|highlight_config| {
+                Syntax::new(
+                    self.text.slice(..),
+                    highlight_config,
+                    self.syn_loader.clone(),
+                )
+            });
     }
 
     /// Set the programming language for the file if you know the language but don't have the
@@ -1298,13 +1306,12 @@
     pub fn set_language_by_language_id(
         &mut self,
         language_id: &str,
-        config_loader: Arc<ArcSwap<syntax::Loader>>,
+        loader: &syntax::Loader,
     ) -> anyhow::Result<()> {
-        let language_config = (*config_loader)
-            .load()
+        let language_config = loader
             .language_config_for_language_id(language_id)
             .ok_or_else(|| anyhow!("invalid language id: {}", language_id))?;
-        self.set_language(Some(language_config), Some(config_loader));
+        self.set_language(Some(language_config), loader);
         Ok(())
     }
 
@@ -2319,6 +2326,7 @@
             text,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         let view = ViewId::default();
         doc.set_selection(view, Selection::single(0, 0));
@@ -2357,6 +2365,7 @@
             text,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         let view = ViewId::default();
         doc.set_selection(view, Selection::single(5, 5));
@@ -2470,9 +2479,12 @@
     #[test]
     fn test_line_ending() {
         assert_eq!(
-            Document::default(Arc::new(ArcSwap::new(Arc::new(Config::default()))))
-                .text()
-                .to_string(),
+            Document::default(
+                Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+                Arc::new(ArcSwap::from_pointee(syntax::Loader::default()))
+            )
+            .text()
+            .to_string(),
             helix_core::NATIVE_LINE_ENDING.as_str()
         );
     }
--- a/helix-view/src/editor.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-view/src/editor.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -1478,9 +1478,9 @@
     }
 
     pub fn refresh_doc_language(&mut self, doc_id: DocumentId) {
-        let loader = self.syn_loader.clone();
+        let loader = self.syn_loader.load();
         let doc = doc_mut!(self, &doc_id);
-        doc.detect_language(loader);
+        doc.detect_language(&loader);
         doc.detect_editor_config();
         doc.detect_indent_and_line_ending();
         self.refresh_language_servers(doc_id);
@@ -1740,7 +1740,10 @@
     }
 
     pub fn new_file(&mut self, action: Action) -> DocumentId {
-        self.new_file_from_document(action, Document::default(self.config.clone()))
+        self.new_file_from_document(
+            action,
+            Document::default(self.config.clone(), self.syn_loader.clone()),
+        )
     }
 
     pub fn new_file_from_stdin(&mut self, action: Action) -> Result<DocumentId, Error> {
@@ -1749,6 +1752,7 @@
             helix_core::Rope::default(),
             Some((encoding, has_bom)),
             self.config.clone(),
+            self.syn_loader.clone(),
         );
         let doc_id = self.new_file_from_document(action, doc);
         let doc = doc_mut!(self, &doc_id);
@@ -1777,8 +1781,9 @@
             let mut doc = Document::open(
                 &path,
                 None,
-                Some(self.syn_loader.clone()),
+                true,
                 self.config.clone(),
+                self.syn_loader.clone(),
             )?;
 
             let diagnostics =
@@ -1874,7 +1879,12 @@
                 .iter()
                 .map(|(&doc_id, _)| doc_id)
                 .next()
-                .unwrap_or_else(|| self.new_document(Document::default(self.config.clone())));
+                .unwrap_or_else(|| {
+                    self.new_document(Document::default(
+                        self.config.clone(),
+                        self.syn_loader.clone(),
+                    ))
+                });
             let view = View::new(doc_id, self.config().gutters.clone());
             let view_id = self.tree.insert(view);
             let doc = doc_mut!(self, &doc_id);
--- a/helix-view/src/gutter.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-view/src/gutter.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -334,7 +334,7 @@
     use crate::graphics::Rect;
     use crate::DocumentId;
     use arc_swap::ArcSwap;
-    use helix_core::Rope;
+    use helix_core::{syntax, Rope};
 
     #[test]
     fn test_default_gutter_widths() {
@@ -346,6 +346,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
 
         assert_eq!(view.gutters.layout.len(), 5);
@@ -371,6 +372,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
 
         assert_eq!(view.gutters.layout.len(), 1);
@@ -389,6 +391,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
 
         assert_eq!(view.gutters.layout.len(), 2);
@@ -411,6 +414,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
 
         let rope = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np");
@@ -418,6 +422,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
 
         assert_eq!(view.gutters.layout.len(), 2);
--- a/helix-view/src/view.rs	Thu Feb 20 17:08:47 2025 -0500
+++ b/helix-view/src/view.rs	Thu Feb 20 17:45:19 2025 -0500
@@ -699,7 +699,7 @@
 
     use super::*;
     use arc_swap::ArcSwap;
-    use helix_core::Rope;
+    use helix_core::{syntax, Rope};
 
     // 1 diagnostic + 1 spacer + 3 linenr (< 1000 lines) + 1 spacer + 1 diff
     const DEFAULT_GUTTER_OFFSET: u16 = 7;
@@ -719,6 +719,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         doc.ensure_view_init(view.id);
 
@@ -894,6 +895,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         doc.ensure_view_init(view.id);
         assert_eq!(
@@ -924,6 +926,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         doc.ensure_view_init(view.id);
         assert_eq!(
@@ -948,6 +951,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         doc.ensure_view_init(view.id);
 
@@ -1032,6 +1036,7 @@
             rope,
             None,
             Arc::new(ArcSwap::new(Arc::new(Config::default()))),
+            Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
         );
         doc.ensure_view_init(view.id);