diff --git a/book/src/languages.md b/book/src/languages.md index 944ebf097..60be804d5 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -28,8 +28,13 @@ There are three possible locations for a `languages.toml` file: 3. In a `.helix` folder in your project. Language configuration may also be overridden local to a project by creating a `languages.toml` file in a - `.helix` folder. Its settings will be merged with the language configuration - in the configuration directory and the built-in configuration. + `.helix` folder and adding `workspace-config = true` to the top of your + configuration directory `languages.toml`. Its settings will be merged with + the language configuration in the configuration directory and the built-in + configuration. Enabling workspace configs is a potential security risk. + A project from an untrusted sources may e.g. contain a + `.helix/languages.toml` that modifies a language server command to execute + malicious code on your machine. ## Language configuration diff --git a/helix-loader/src/config.rs b/helix-loader/src/config.rs index d092d20f7..1ef6a2d26 100644 --- a/helix-loader/src/config.rs +++ b/helix-loader/src/config.rs @@ -1,4 +1,5 @@ use std::str::from_utf8; +use std::path::PathBuf; /// Default built-in languages.toml. pub fn default_lang_config() -> toml::Value { @@ -7,40 +8,28 @@ pub fn default_lang_config() -> toml::Value { .expect("Could not parse built-in languages.toml to valid toml") } +fn merge_language_config( + left: toml::Value, file: PathBuf, +) -> Result { + let right = std::fs::read_to_string(file).ok() + .map(|c| toml::from_str(&c)).transpose()?; + + let config = match right { + Some(right) => crate::merge_toml_values(left, right, 3), + None => left, + }; + + Ok(config) +} + /// User configured languages.toml file, merged with the default config. pub fn user_lang_config() -> Result { - let config = [ - crate::config_dir(), - crate::find_workspace().0.join(".helix"), - ] - .into_iter() - .map(|path| path.join("languages.toml")) - .filter_map(|file| { - std::fs::read_to_string(file) - .map(|config| toml::from_str(&config)) - .ok() - }) - .collect::, _>>()? - .into_iter() - .fold(default_lang_config(), |a, b| { - // combines for example - // b: - // [[language]] - // name = "toml" - // language-server = { command = "taplo", args = ["lsp", "stdio"] } - // - // a: - // [[language]] - // language-server = { command = "/usr/bin/taplo" } - // - // into: - // [[language]] - // name = "toml" - // language-server = { command = "/usr/bin/taplo" } - // - // thus it overrides the third depth-level of b with values of a if they exist, but otherwise merges their values - crate::merge_toml_values(a, b, 3) - }); + let global = merge_language_config(default_lang_config(), crate::lang_config_file())?; + + let config = match global.get("workspace-config").and_then(|v| v.as_bool()) { + Some(true) => merge_language_config(global, crate::workspace_lang_config_file())?, + _ => global, + }; Ok(config) } diff --git a/languages.toml b/languages.toml index 11afea0ca..7f49b2b3c 100644 --- a/languages.toml +++ b/languages.toml @@ -1,6 +1,8 @@ # Language support configuration. # See the languages documentation: https://docs.helix-editor.com/master/languages.html +workspace-config = false + use-grammars = { except = [ "hare", "wren", "gemini" ] } [language-server]