just playing with tangled

cli completion: complete --tool args for merge tools #6

open opened by ilyagr.bsky.social targeting main from completetool

Includes diff tools, diff editors, and merge editors.

Labels

None yet.

Participants 1
AT URI
at://did:plc:jp6rly3c67o3zlwarw2ttafu/sh.tangled.repo.pull/3lqh3ndpsd322
+266 -13
Diff #0
+6 -1
cli/src/commands/commit.rs
··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 + use clap_complete::ArgValueCandidates; 15 16 use clap_complete::ArgValueCompleter; 16 17 use indoc::writedoc; 17 18 use jj_lib::backend::Signature; ··· 37 38 #[arg(short, long)] 38 39 interactive: bool, 39 40 /// Specify diff editor to be used (implies --interactive) 40 - #[arg(long, value_name = "NAME")] 41 + #[arg( 42 + long, 43 + value_name = "NAME", 44 + add = ArgValueCandidates::new(complete::diff_editors), 45 + )] 41 46 tool: Option<String>, 42 47 /// The change description to use (don't open editor) 43 48 #[arg(long = "message", short, value_name = "MESSAGE")]
+6 -1
cli/src/commands/diffedit.rs
··· 14 14 15 15 use std::io::Write as _; 16 16 17 + use clap_complete::ArgValueCandidates; 17 18 use clap_complete::ArgValueCompleter; 18 19 use itertools::Itertools as _; 19 20 use jj_lib::matchers::EverythingMatcher; ··· 77 78 )] 78 79 to: Option<RevisionArg>, 79 80 /// Specify diff editor to be used 80 - #[arg(long, value_name = "NAME")] 81 + #[arg( 82 + long, 83 + value_name = "NAME", 84 + add = ArgValueCandidates::new(complete::diff_editors), 85 + )] 81 86 tool: Option<String>, 82 87 /// Preserve the content (not the diff) when rebasing descendants 83 88 ///
+7 -1
cli/src/commands/resolve.rs
··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 + use clap_complete::ArgValueCandidates; 15 16 use clap_complete::ArgValueCompleter; 16 17 use itertools::Itertools as _; 17 18 use jj_lib::object_id::ObjectId as _; ··· 61 62 /// 62 63 /// The built-in merge tools `:ours` and `:theirs` can be used to choose 63 64 /// side #1 and side #2 of the conflict respectively. 64 - #[arg(long, conflicts_with = "list", value_name = "NAME")] 65 + #[arg( 66 + long, 67 + conflicts_with = "list", 68 + value_name = "NAME", 69 + add = ArgValueCandidates::new(complete::merge_tools), 70 + )] 65 71 tool: Option<String>, 66 72 /// Only resolve conflicts in these paths. You can use the `--list` argument 67 73 /// to find paths to use here.
+6 -1
cli/src/commands/restore.rs
··· 14 14 15 15 use std::io::Write as _; 16 16 17 + use clap_complete::ArgValueCandidates; 17 18 use clap_complete::ArgValueCompleter; 18 19 use indoc::formatdoc; 19 20 use itertools::Itertools as _; ··· 96 97 #[arg(long, short)] 97 98 interactive: bool, 98 99 /// Specify diff editor to be used (implies --interactive) 99 - #[arg(long, value_name = "NAME")] 100 + #[arg( 101 + long, 102 + value_name = "NAME", 103 + add = ArgValueCandidates::new(complete::diff_editors), 104 + )] 100 105 tool: Option<String>, 101 106 /// Preserve the content (not the diff) when rebasing descendants 102 107 #[arg(long)]
+6 -1
cli/src/commands/split.rs
··· 14 14 use std::collections::HashMap; 15 15 use std::io::Write as _; 16 16 17 + use clap_complete::ArgValueCandidates; 17 18 use clap_complete::ArgValueCompleter; 18 19 use jj_lib::backend::CommitId; 19 20 use jj_lib::commit::Commit; ··· 70 71 #[arg(long, short)] 71 72 interactive: bool, 72 73 /// Specify diff editor to be used (implies --interactive) 73 - #[arg(long, value_name = "NAME")] 74 + #[arg( 75 + long, 76 + value_name = "NAME", 77 + add = ArgValueCandidates::new(complete::diff_editors), 78 + )] 74 79 tool: Option<String>, 75 80 /// The revision to split 76 81 #[arg(
+6 -1
cli/src/commands/squash.rs
··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 + use clap_complete::ArgValueCandidates; 15 16 use clap_complete::ArgValueCompleter; 16 17 use indoc::formatdoc; 17 18 use itertools::Itertools as _; ··· 99 100 #[arg(long, short)] 100 101 interactive: bool, 101 102 /// Specify diff editor to be used (implies --interactive) 102 - #[arg(long, value_name = "NAME")] 103 + #[arg( 104 + long, 105 + value_name = "NAME", 106 + add = ArgValueCandidates::new(complete::diff_editors), 107 + )] 103 108 tool: Option<String>, 104 109 /// Move only changes to these paths (instead of all paths) 105 110 #[arg(
+48
cli/src/complete.rs
··· 34 34 use crate::config::ConfigArgKind; 35 35 use crate::config::ConfigEnv; 36 36 use crate::config::CONFIG_SCHEMA; 37 + use crate::merge_tools::configured_merge_tools; 38 + use crate::merge_tools::MergeEditor; 37 39 use crate::revset_util::load_revset_aliases; 38 40 use crate::ui::Ui; 39 41 ··· 471 473 472 474 473 475 476 + .collect()) 477 + }) 478 + } 479 + pub fn merge_tools() -> Vec<CompletionCandidate> { 480 + with_jj(|_, settings| { 481 + Ok([":builtin", ":ours", ":theirs"] 482 + .into_iter() 483 + .chain( 484 + configured_merge_tools(settings) 485 + .filter(|name| MergeEditor::dummy_with_name(name, settings).is_ok()), 486 + ) 487 + .map(CompletionCandidate::new) 488 + .collect()) 489 + }) 490 + } 491 + 492 + /// Approximate list of known diff editors 493 + /// 494 + /// Diff tools can be used without configuration. Some merge tools that are 495 + /// configured for 3-way merging may not work for diffing/diff editing, and we 496 + /// can't tell which these are. So, this not reliable, but probably good enough 497 + /// for command-line completion. 498 + pub fn diff_editors() -> Vec<CompletionCandidate> { 499 + let builtin_format_kinds: Vec<String> = crate::diff_util::BuiltinFormatKind::ALL_VARIANTS 500 + .iter() 501 + .map(|kind| format!(":{}", kind.to_arg_name())) 502 + .collect(); 503 + with_jj(|_, settings| { 504 + Ok(std::iter::once(":builtin") 505 + .chain(builtin_format_kinds.iter().map(|s| s.as_str())) 506 + .chain(configured_merge_tools(settings)) 507 + .map(CompletionCandidate::new) 508 + .collect()) 509 + }) 510 + } 511 + 512 + /// Approximate list of known diff tools 513 + /// 514 + /// Diff tools can be used without configuration. Some merge tools that are 515 + /// configured for 3-way merging may not work for diffing/diff editing, and we 516 + /// can't tell which these are. So, this not reliable, but probably good enough 517 + /// for command-line completion. 518 + pub fn diff_tools() -> Vec<CompletionCandidate> { 519 + with_jj(|_, settings| { 520 + Ok(configured_merge_tools(settings) 521 + .map(CompletionCandidate::new) 474 522 .collect()) 475 523 }) 476 524 }
+98 -1
cli/src/diff_util.rs
··· 23 23 24 24 use bstr::BStr; 25 25 use bstr::BString; 26 + use clap_complete::ArgValueCandidates; 26 27 use futures::executor::block_on_stream; 27 28 use futures::stream::BoxStream; 28 29 use futures::StreamExt as _; ··· 124 125 /// 125 126 /// A builtin format can also be specified as `:<name>`. For example, 126 127 /// `--tool=:git` is equivalent to `--git`. 127 - #[arg(long)] 128 + #[arg( 129 + long, 130 + add = ArgValueCandidates::new(crate::complete::diff_tools), 131 + )] 128 132 pub tool: Option<String>, 129 133 /// Number of lines of context to show 130 134 #[arg(long)] 135 + 136 + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + } 157 + 158 + #[derive(Clone, Copy, Debug, Eq, PartialEq)] 159 + pub enum BuiltinFormatKind { 160 + Summary, 161 + Stat, 162 + Types, 163 + 164 + 165 + 166 + } 167 + 168 + impl BuiltinFormatKind { 169 + // Alternatively, we could use or vendor one of the crates `strum`, 170 + // `enum-iterator`, or `variant_count` (for a check that the length of the array 171 + // is correct). The latter is very simple and is also a nightly feature. 172 + pub const ALL_VARIANTS: &[BuiltinFormatKind] = &[ 173 + Self::Summary, 174 + Self::Stat, 175 + Self::Types, 176 + Self::NameOnly, 177 + Self::Git, 178 + Self::ColorWords, 179 + ]; 180 + 181 + fn from_name(name: &str) -> Result<Self, String> { 182 + match name { 183 + "summary" => Ok(Self::Summary), 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 203 + 204 + 205 + 206 + 207 + 208 + 209 + 210 + 211 + 212 + 213 + 214 + 215 + 216 + 217 + 218 + 219 + 220 + 221 + } 222 + } 223 + 224 + pub fn to_arg_name(self) -> &'static str { 225 + match self { 226 + Self::Summary => "summary", 227 + Self::Stat => "stat",
+22 -6
cli/src/merge_tools/mod.rs
··· 207 207 } 208 208 } 209 209 210 + /// List configured merge tools (diff editors, diff tools, merge editors) 211 + pub fn configured_merge_tools(settings: &UserSettings) -> impl Iterator<Item = &str> { 212 + settings.table_keys("merge-tools") 213 + } 214 + 210 215 /// Loads external diff/merge tool options from `[merge-tools.<name>]`. 211 216 pub fn get_external_tool_config( 212 217 settings: &UserSettings, ··· 380 385 Self::new_inner(name, tool, path_converter, conflict_marker_style) 381 386 } 382 387 388 + /// For the purposes of testing or checking basic config 389 + pub fn dummy_with_name( 390 + name: &str, 391 + settings: &UserSettings, 392 + ) -> Result<Self, MergeToolConfigError> { 393 + Self::with_name( 394 + name, 395 + settings, 396 + RepoPathUiConverter::Fs { 397 + cwd: "".into(), 398 + base: "".into(), 399 + }, 400 + ConflictMarkerStyle::Diff, 401 + ) 402 + } 403 + 383 404 /// Loads the default 3-way merge editor from the settings. 384 405 pub fn from_settings( 385 406 ui: &Ui, ··· 765 786 let get = |name, config_text| { 766 787 let config = config_from_string(config_text); 767 788 let settings = UserSettings::from_config(config).unwrap(); 768 - let path_converter = RepoPathUiConverter::Fs { 769 - cwd: "".into(), 770 - base: "".into(), 771 - }; 772 - MergeEditor::with_name(name, &settings, path_converter, ConflictMarkerStyle::Diff) 773 - .map(|editor| editor.tool) 789 + MergeEditor::dummy_with_name(name, &settings).map(|editor| editor.tool) 774 790 }; 775 791 776 792 insta::assert_debug_snapshot!(get(":builtin", "").unwrap(), @"Builtin");
+61
cli/tests/test_completion.rs
··· 1089 1089 "); 1090 1090 } 1091 1091 1092 + #[test] 1093 + fn test_merge_tools() { 1094 + let mut test_env = TestEnvironment::default(); 1095 + test_env.add_env_var("COMPLETE", "fish"); 1096 + let dir = test_env.env_root(); 1097 + 1098 + let output = test_env.run_jj_in(dir, ["--", "jj", "diff", "--tool", ""]); 1099 + insta::assert_snapshot!(output, @r" 1100 + diffedit3 1101 + diffedit3-ssh 1102 + difft 1103 + kdiff3 1104 + meld 1105 + meld-3 1106 + mergiraf 1107 + smerge 1108 + vimdiff 1109 + vscode 1110 + vscodium 1111 + [EOF] 1112 + "); 1113 + // Includes :builtin 1114 + let output = test_env.run_jj_in(dir, ["--", "jj", "diffedit", "--tool", ""]); 1115 + insta::assert_snapshot!(output, @r" 1116 + :builtin 1117 + :summary 1118 + :stat 1119 + :types 1120 + :name-only 1121 + :git 1122 + :color-words 1123 + diffedit3 1124 + diffedit3-ssh 1125 + difft 1126 + kdiff3 1127 + meld 1128 + meld-3 1129 + mergiraf 1130 + smerge 1131 + vimdiff 1132 + vscode 1133 + vscodium 1134 + [EOF] 1135 + "); 1136 + // Only includes configured merge editors 1137 + let output = test_env.run_jj_in(dir, ["--", "jj", "resolve", "--tool", ""]); 1138 + insta::assert_snapshot!(output, @r" 1139 + :builtin 1140 + :ours 1141 + :theirs 1142 + kdiff3 1143 + meld 1144 + mergiraf 1145 + smerge 1146 + vimdiff 1147 + vscode 1148 + vscodium 1149 + [EOF] 1150 + "); 1151 + } 1152 + 1092 1153 fn create_commit( 1093 1154 work_dir: &TestWorkDir, 1094 1155 name: &str,

History

1 round 0 comments
sign up or login to add to the discussion
2 commits
expand
cli completion: complete --tool args for merge tools
add new builtin formats
no conflicts, ready to merge
expand 0 comments