From a4799afd24361376272644c7b28ea6881cdbcea3 Mon Sep 17 00:00:00 2001 From: thesiti92 Date: Tue, 29 Sep 2026 10:39:33 -0400 Subject: [PATCH] Make Fortran, Verilog, and F# parsers opt-in Exclude the three grammars from default builds and preserve text diffs when they are disabled. Add individual language features, an all-languages feature, and CLI regression coverage. AI assistance: OpenAI Codex. --- Cargo.toml | 10 ++++-- src/parse/guess_language.rs | 12 ++++++++ src/parse/tree_sitter_parser.rs | 3 ++ tests/cli.rs | 54 +++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9b6f4655a..63d18b22b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,8 +90,8 @@ tree-sitter-elixir = "0.3.4" tree-sitter-elm = "5.8.0" tree-sitter-erlang = "0.20.0" tree-sitter-fish = "3.6.0" -tree-sitter-fortran = "0.6.0" -tree-sitter-fsharp = "0.3.0" +tree-sitter-fortran = { version = "0.6.0", optional = true } +tree-sitter-fsharp = { version = "0.3.0", optional = true } tree-sitter-gleam = "1.0.0" tree-sitter-go = "0.25.0" tree-sitter-haskell = "0.23.1" @@ -124,7 +124,7 @@ tree-sitter-solidity = "1.2.13" tree-sitter-swift = "0.7.1" tree-sitter-toml-ng = "0.7.0" tree-sitter-typescript = "0.23.2" -tree-sitter-verilog = "1.0.3" +tree-sitter-verilog = { version = "1.0.3", optional = true } tree-sitter-vhdl = "1.4.0" tree-sitter-xml = "0.7.0" tree-sitter-yaml = "0.7.0" @@ -235,4 +235,8 @@ name = "wasm" required-features = ["wasm-plugin-tests"] [features] +all-languages = ["lang-fortran", "lang-fsharp", "lang-verilog"] +lang-fortran = ["dep:tree-sitter-fortran"] +lang-fsharp = ["dep:tree-sitter-fsharp"] +lang-verilog = ["dep:tree-sitter-verilog"] wasm-plugin-tests = [] diff --git a/src/parse/guess_language.rs b/src/parse/guess_language.rs index 82cbb8b1e..c36ca5fc6 100644 --- a/src/parse/guess_language.rs +++ b/src/parse/guess_language.rs @@ -38,7 +38,9 @@ pub(crate) enum Language { EmacsLisp, Erlang, Fish, + #[cfg(feature = "lang-fsharp")] FSharp, + #[cfg(feature = "lang-fortran")] Fortran, Gleam, Go, @@ -79,6 +81,7 @@ pub(crate) enum Language { Toml, TypeScript, TypeScriptTsx, + #[cfg(feature = "lang-verilog")] Verilog, Vhdl, Xml, @@ -139,7 +142,9 @@ pub(crate) fn language_name(language: Language) -> &'static str { EmacsLisp => "Emacs Lisp", Erlang => "Erlang", Fish => "Fish", + #[cfg(feature = "lang-fsharp")] FSharp => "F#", + #[cfg(feature = "lang-fortran")] Fortran => "Fortran", Gleam => "Gleam", Go => "Go", @@ -180,6 +185,7 @@ pub(crate) fn language_name(language: Language) -> &'static str { Toml => "TOML", TypeScript => "TypeScript", TypeScriptTsx => "TypeScript TSX", + #[cfg(feature = "lang-verilog")] Verilog => "Verilog", Vhdl => "VHDL", Xml => "XML", @@ -289,7 +295,9 @@ pub(crate) fn language_globs(language: Language) -> Vec { "rebar.lock", ], Fish => &["*.fish"], + #[cfg(feature = "lang-fsharp")] FSharp => &["*.fs", "*.fsx", "*.fsi"], + #[cfg(feature = "lang-fortran")] Fortran => &["*.f", "*.for", "*.f90", "*.F", "*.FOR", "*.F90"], Gleam => &["*.gleam"], Go => &["*.go"], @@ -396,6 +404,7 @@ pub(crate) fn language_globs(language: Language) -> Vec { ], TypeScript => &["*.ts", "*.cts", "*.mts"], TypeScriptTsx => &["*.tsx"], + #[cfg(feature = "lang-verilog")] Verilog => &["*.v", "*.sv", "*.vh"], Vhdl => &["*.vhdl", "*.vhd"], Xml => &[ @@ -545,7 +554,9 @@ fn from_emacs_mode_header(src: &str) -> Option { "elm" => Elm, "emacs-lisp" => EmacsLisp, "fish" => Fish, + #[cfg(feature = "lang-fsharp")] "fsharp" => FSharp, + #[cfg(feature = "lang-fortran")] "fortran" => Fortran, "gleam" => Gleam, "go" => Go, @@ -572,6 +583,7 @@ fn from_emacs_mode_header(src: &str) -> Option { "toml" => Toml, "tuareg" => OCaml, "typescript" => TypeScript, + #[cfg(feature = "lang-verilog")] "verilog" => Verilog, "vhdl" => Vhdl, "yaml" => Yaml, diff --git a/src/parse/tree_sitter_parser.rs b/src/parse/tree_sitter_parser.rs index 39eb5f18f..1aef0c877 100644 --- a/src/parse/tree_sitter_parser.rs +++ b/src/parse/tree_sitter_parser.rs @@ -456,6 +456,7 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } + #[cfg(feature = "lang-fsharp")] FSharp => { let language_fn = tree_sitter_fsharp::LANGUAGE_FSHARP; let language = tree_sitter::Language::new(language_fn); @@ -471,6 +472,7 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } + #[cfg(feature = "lang-fortran")] Fortran => { let language_fn = tree_sitter_fortran::LANGUAGE; let language = tree_sitter::Language::new(language_fn); @@ -1246,6 +1248,7 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } + #[cfg(feature = "lang-verilog")] Verilog => { let language_fn = tree_sitter_verilog::LANGUAGE; let language = tree_sitter::Language::new(language_fn); diff --git a/tests/cli.rs b/tests/cli.rs index ffaf29c66..7eff6f7ac 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -25,6 +25,60 @@ fn list_languages() { cmd.assert().stdout(predicate_fn); } +#[test] +fn optional_languages_follow_build_features() { + let listed = debug_command() + .arg("--list-languages") + .assert() + .success() + .get_output() + .stdout + .clone(); + let listed = String::from_utf8(listed).unwrap(); + let dir = tempfile::tempdir().unwrap(); + let config = dir.path().join("config.toml"); + std::fs::write(&config, "[plugins]\norder = []\n").unwrap(); + + for (name, fixture, extension, enabled) in [ + ("Fortran", "fortran", "f90", cfg!(feature = "lang-fortran")), + ("Verilog", "verilog", "sv", cfg!(feature = "lang-verilog")), + ("F#", "f_sharp", "fs", cfg!(feature = "lang-fsharp")), + ] { + assert_eq!(listed.contains(name), enabled, "{name}"); + let output = get_base_command() + .arg("--config") + .arg(&config) + .args([ + "--format", + "ndjson", + "--no-index", + &format!("sample_files/{fixture}_1.{extension}"), + &format!("sample_files/{fixture}_2.{extension}"), + ]) + .assert() + .success() + .get_output() + .stdout + .clone(); + let events: Vec = std::str::from_utf8(&output) + .unwrap() + .lines() + .map(|line| serde_json::from_str(line).unwrap()) + .collect(); + let file = events.iter().find(|event| event["type"] == "file").unwrap(); + assert_eq!(file["diff"]["type"], "text", "{name}: {file}"); + assert_eq!( + file["diff"]["stats"]["fallback"]["code"].as_str(), + if enabled { + None + } else { + Some("unsupported_language") + }, + "{name}", + ); + } +} + #[test] fn dump_tree_sitter() { let mut cmd = debug_command();