diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f6f2fdc6..d960f6ab5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * CommitId / ChangeId template types now support `.normal_hex()`. +* Fix tools have a new config `fix.tools.NAME.enabled` which can be + used to configure a disabled tool in user config and enable it in a + individual repo's config. + ### Fixed bugs * Fixed panic when parsing invalid conflict markers of a particular form. diff --git a/cli/src/commands/fix.rs b/cli/src/commands/fix.rs index 09accd64bf..2b29371b23 100644 --- a/cli/src/commands/fix.rs +++ b/cli/src/commands/fix.rs @@ -82,6 +82,9 @@ use crate::ui::Ui; /// empty, no files will be affected by the tool. If there are multiple /// patterns, the tool is applied only once to each file in the union of the /// patterns. +/// - `enabled`: Enables or disables the tool. If omitted, the tool is enabled. +/// This is useful for defining disabled tools in user configuration that can +/// be enabled in individual repositories with one config setting. /// /// For example, the following configuration defines how two code formatters /// (`clang-format` and `black`) will apply to three different file extensions @@ -432,6 +435,12 @@ struct ToolsConfig { struct RawToolConfig { command: CommandNameAndArgs, patterns: Vec, + #[serde(default = "default_tool_enabled")] + enabled: bool, +} + +fn default_tool_enabled() -> bool { + true } /// Parses the `fix.tools` config table. @@ -474,8 +483,10 @@ fn get_tools_config(ui: &mut Ui, config: &config::Config) -> Result = tools_table .into_iter() .sorted_by(|a, b| a.0.cmp(&b.0)) - .map(|(_name, value)| -> Result { - let tool: RawToolConfig = value.try_deserialize()?; + .map(|(_name, value): (_, _)| value.try_deserialize::()) + .filter_ok(|tool| tool.enabled) + .map(|tool| -> Result { + let tool = tool?; Ok(ToolConfig { command: tool.command, matcher: FilesetExpression::union_all( diff --git a/cli/src/config-schema.json b/cli/src/config-schema.json index 997d62c829..f10f55b67b 100644 --- a/cli/src/config-schema.json +++ b/cli/src/config-schema.json @@ -554,6 +554,11 @@ "type": "string" }, "description": "Filesets that will be affected by this tool" + }, + "enabled": { + "type": "boolean", + "description": "Disables this tool if set to false", + "default": true } } }, diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index d2a4d709aa..a476c0ab7a 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -881,6 +881,9 @@ the values have the following properties: empty, no files will be affected by the tool. If there are multiple patterns, the tool is applied only once to each file in the union of the patterns. + - `enabled`: Enables or disables the tool. If omitted, the tool is enabled. + This is useful for defining disabled tools in user configuration that can + be enabled in individual repositories with one config setting. For example, the following configuration defines how two code formatters (`clang-format` and `black`) will apply to three different file extensions diff --git a/cli/tests/test_fix_command.rs b/cli/tests/test_fix_command.rs index e74fa3aa97..6cf9d410d9 100644 --- a/cli/tests/test_fix_command.rs +++ b/cli/tests/test_fix_command.rs @@ -192,6 +192,48 @@ fn test_config_multiple_tools_with_same_name() { insta::assert_snapshot!(content, @"Bar\n"); } +#[test] +fn test_config_disabled_tools() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + let formatter_path = assert_cmd::cargo::cargo_bin("fake-formatter"); + assert!(formatter_path.is_file()); + let escaped_formatter_path = formatter_path.to_str().unwrap().replace('\\', r"\\"); + test_env.add_config(&format!( + r###" + [fix.tools.tool-1] + # default is enabled + command = ["{formatter}", "--uppercase"] + patterns = ["foo"] + + [fix.tools.tool-2] + enabled = true + command = ["{formatter}", "--lowercase"] + patterns = ["bar"] + + [fix.tools.tool-3] + enabled = false + command = ["{formatter}", "--lowercase"] + patterns = ["baz"] + "###, + formatter = escaped_formatter_path.as_str() + )); + + std::fs::write(repo_path.join("foo"), "Foo\n").unwrap(); + std::fs::write(repo_path.join("bar"), "Bar\n").unwrap(); + std::fs::write(repo_path.join("baz"), "Baz\n").unwrap(); + + let (_stdout, _stderr) = test_env.jj_cmd_ok(&repo_path, &["fix"]); + + let content = test_env.jj_cmd_success(&repo_path, &["file", "show", "foo", "-r", "@"]); + insta::assert_snapshot!(content, @"FOO\n"); + let content = test_env.jj_cmd_success(&repo_path, &["file", "show", "bar", "-r", "@"]); + insta::assert_snapshot!(content, @"bar\n"); + let content = test_env.jj_cmd_success(&repo_path, &["file", "show", "baz", "-r", "@"]); + insta::assert_snapshot!(content, @"Baz\n"); +} + #[test] fn test_config_tables_overlapping_patterns() { let test_env = TestEnvironment::default(); diff --git a/docs/config.md b/docs/config.md index 1c1ae2aef6..a1b04d5e13 100644 --- a/docs/config.md +++ b/docs/config.md @@ -769,6 +769,27 @@ command = ["head", "-n", "10"] patterns = ["numbers.txt"] ``` +### Disabling and enabling tools + +Tools can be disabled and enabled with the optional `enabled` config. This +allows you to define tools globally but enable them only for specific +repositories. + +In the user configuration, define a disabled tool for running rustfmt: + +```toml +[fix.tools.rustfmt] +enabled = false +command = ["rustfmt", "--emit", "stdout"] +patterns = ["glob:'**/*.rs'"] +``` + +Then to use the tool in a specific repository, set the `enabled` config: + +```shell +$ jj config set --repo fix.tools.rustfmt.enabled true +``` + ## Commit Signing `jj` can be configured to sign and verify the commits it creates using either