From 06d8d3f55fbf02bb4d938ecbc479cd60309a0a5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Fri, 4 Jun 2021 10:50:03 +0900
Subject: [PATCH] Try to detect language when document file path is set

Fixes #91
---
 helix-core/src/indent.rs   | 35 +++++++++++++++++++----------------
 helix-core/src/syntax.rs   |  8 +++++++-
 helix-term/src/main.rs     | 12 ------------
 helix-view/src/document.rs | 33 ++++++++++++++++++++-------------
 helix-view/src/editor.rs   | 14 +++++++++++++-
 5 files changed, 59 insertions(+), 43 deletions(-)

diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs
index fc253f91..7ab810fd 100644
--- a/helix-core/src/indent.rs
+++ b/helix-core/src/indent.rs
@@ -251,22 +251,25 @@ where
             Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader,
         };
         use once_cell::sync::OnceCell;
-        let loader = Loader::new(Configuration {
-            language: vec![LanguageConfiguration {
-                scope: "source.rust".to_string(),
-                file_types: vec!["rs".to_string()],
-                language_id: Lang::Rust,
-                highlight_config: OnceCell::new(),
-                //
-                roots: vec![],
-                language_server: None,
-                indent: Some(IndentationConfiguration {
-                    tab_width: 4,
-                    unit: String::from("    "),
-                }),
-                indent_query: OnceCell::new(),
-            }],
-        });
+        let loader = Loader::new(
+            Configuration {
+                language: vec![LanguageConfiguration {
+                    scope: "source.rust".to_string(),
+                    file_types: vec!["rs".to_string()],
+                    language_id: Lang::Rust,
+                    highlight_config: OnceCell::new(),
+                    //
+                    roots: vec![],
+                    language_server: None,
+                    indent: Some(IndentationConfiguration {
+                        tab_width: 4,
+                        unit: String::from("    "),
+                    }),
+                    indent_query: OnceCell::new(),
+                }],
+            },
+            Vec::new(),
+        );
 
         // set runtime path so we can find the queries
         let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index febbf821..ec95708b 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -166,13 +166,15 @@ pub struct Loader {
     // highlight_names ?
     language_configs: Vec<Arc<LanguageConfiguration>>,
     language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
+    scopes: Vec<String>,
 }
 
 impl Loader {
-    pub fn new(config: Configuration) -> Self {
+    pub fn new(config: Configuration, scopes: Vec<String>) -> Self {
         let mut loader = Self {
             language_configs: Vec::new(),
             language_config_ids_by_file_type: HashMap::new(),
+            scopes,
         };
 
         for config in config.language {
@@ -192,6 +194,10 @@ impl Loader {
         loader
     }
 
+    pub fn scopes(&self) -> &[String] {
+        &self.scopes
+    }
+
     pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
         // Find all the language configurations that match this file name
         // or a suffix of the file name.
diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs
index 3f37c295..0b55921a 100644
--- a/helix-term/src/main.rs
+++ b/helix-term/src/main.rs
@@ -152,18 +152,6 @@ FLAGS:
 
     setup_logging(logpath, args.verbosity).context("failed to initialize logging")?;
 
-    // initialize language registry
-    use helix_core::syntax::{Loader, LOADER};
-
-    // load $HOME/.config/helix/languages.toml, fallback to default config
-    let config = std::fs::read(helix_core::config_dir().join("languages.toml"));
-    let toml = config
-        .as_deref()
-        .unwrap_or(include_bytes!("../../languages.toml"));
-
-    let config = toml::from_slice(toml).context("Could not parse languages.toml")?;
-    LOADER.get_or_init(|| Loader::new(config));
-
     // TODO: use the thread local executor to spawn the application task separately from the work pool
     let mut app = Application::new(args).context("unable to create new appliction")?;
     app.run().await;
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index cf4a6faa..7a7617c0 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -131,9 +131,8 @@ impl Document {
         }
     }
 
-    // TODO: passing scopes here is awkward
     // TODO: async fn?
-    pub fn load(path: PathBuf, scopes: &[String]) -> Result<Self, Error> {
+    pub fn load(path: PathBuf) -> Result<Self, Error> {
         use std::{env, fs::File, io::BufReader};
         let _current_dir = env::current_dir()?;
 
@@ -143,15 +142,8 @@ impl Document {
         // TODO: create if not found
 
         let mut doc = Self::new(doc);
-
-        let language_config = LOADER
-            .get()
-            .unwrap()
-            .language_config_for_file_name(path.as_path());
-        doc.set_language(language_config, scopes);
-
-        // canonicalize path to absolute value
-        doc.path = Some(std::fs::canonicalize(path)?);
+        // set the path and try detecting the language
+        doc.set_path(&path)?;
 
         Ok(doc)
     }
@@ -218,6 +210,15 @@ impl Document {
         }
     }
 
+    fn detect_language(&mut self) {
+        if let Some(path) = self.path() {
+            let loader = LOADER.get().unwrap();
+            let language_config = loader.language_config_for_file_name(path);
+            let scopes = loader.scopes();
+            self.set_language(language_config, scopes);
+        }
+    }
+
     pub fn set_path(&mut self, path: &Path) -> Result<(), std::io::Error> {
         // canonicalize path to absolute value
         let current_dir = std::env::current_dir()?;
@@ -229,6 +230,10 @@ impl Document {
                 self.path = Some(path);
             }
         }
+
+        // try detecting the language based on filepath
+        self.detect_language();
+
         Ok(())
     }
 
@@ -251,8 +256,10 @@ impl Document {
         };
     }
 
-    pub fn set_language2(&mut self, scope: &str, scopes: &[String]) {
-        let language_config = LOADER.get().unwrap().language_config_for_scope(scope);
+    pub fn set_language2(&mut self, scope: &str) {
+        let loader = LOADER.get().unwrap();
+        let language_config = loader.language_config_for_scope(scope);
+        let scopes = loader.scopes();
 
         self.set_language(language_config, scopes);
     }
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index e7b25814..d991f250 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -36,6 +36,18 @@ impl Editor {
             .unwrap_or(include_bytes!("../../theme.toml"));
         let theme: Theme = toml::from_slice(toml).expect("failed to parse theme.toml");
 
+        // initialize language registry
+        use helix_core::syntax::{Loader, LOADER};
+
+        // load $HOME/.config/helix/languages.toml, fallback to default config
+        let config = std::fs::read(helix_core::config_dir().join("languages.toml"));
+        let toml = config
+            .as_deref()
+            .unwrap_or(include_bytes!("../../languages.toml"));
+
+        let config = toml::from_slice(toml).expect("Could not parse languages.toml");
+        LOADER.get_or_init(|| Loader::new(config, theme.scopes().to_vec()));
+
         let language_servers = helix_lsp::Registry::new();
 
         // HAXX: offset the render area height by 1 to account for prompt/commandline
@@ -135,7 +147,7 @@ impl Editor {
         let id = if let Some(id) = id {
             id
         } else {
-            let mut doc = Document::load(path, self.theme.scopes())?;
+            let mut doc = Document::load(path)?;
 
             // try to find a language server based on the language name
             let language_server = doc