Support external subcommands: rg, diff, git-show (etc.)#1769
Conversation
|
Nice, very interesting!
Some miscellaneous comments about subcommands; not sure how relevant to this PR:
|
I used
Yes, and in that case I would give priority to the subcommand. By using
I think these are not used that often, I would leave them as flags (these being located in
Already present, see
Sure, should be possible as well. |
Now also possible! However enabling all git-X commands directly would mean checking if "git $X" is a valid git subcommand first (while parsing the command line, repeatedly), so I use a specific list: pub const SUBCOMMANDS: &[&str] = &[RG, "show", "log", "diff", "grep", "blame", GIT];Are there any other useful commands? Also, can you check why the rg test fails on macOS? |
The ones that are coming to mind are |
It fails because of delta/src/subcommands/generic_subcmd.rs Line 127 in acb7c12 https://docs.rs/grep-cli/latest/grep_cli/fn.resolve_binary.html
This API has always seemed very surprising to me. I've opened BurntSushi/ripgrep#2928 to ask about it and maybe make the docstring clearer. |
|
I use |
| // start subprocesses: | ||
| // diff (fileA, fileB), and generic subcommands | ||
| pub mod diff; | ||
| mod generic_subcmd; |
There was a problem hiding this comment.
That name is a bit ... generic I think. Ideas?
Done.
Oh, thanks! Never would have suspected that, re-implemented some of
Indeed, I have also asked myself that for simpler stuff like writing my own |
And one more: I recently learned of the existence of |
Just to mention one more -- I'm attracted to adding a "doctor" command that users can run to obtain a standard suite of diagnostics describing their environment, something like the direction started in #1193. Would something like that fit with the work in this branch as a |
Added. The |
There was a problem hiding this comment.
I've gone through the code changes now; all looks very nice. Please excuse/ignore the code golf suggestions -- that was just helping me get into the code.
Before we proceed let's revisit the overall argument here. For rg this is clearly an improvement: otherwise to make delta-rg integration ergonomic you have to write a shell wrapper.
Another possibility along the lines of the rg integration might be visualizing difftastic JSON output.
But what's your thinking about the utility of this for git xxx commands? delta integrates with git transparently via git's external pager mechanism, and many shell completion projects exist giving intricate completion over git commands. Is there a clear advantage to being able to invoke git commands prefixed by delta, especially given that it wouldn't have shell completion? Also, if we were to do it, are we sure that we want to inject 9 (currently) git command names into delta's top-level subcommand namespace? Or should it only support delta git xxx? delta diff in particular seems to have potentially confusing semantics given the presence of the delta a b diff. Sorry I've been slow to bring up these hesitations.
| if let Some(subcmd) = &minus_file { | ||
| if let Some(arg) = subcmd.to_str() { |
There was a problem hiding this comment.
This is basically just code golf, done while trying to get into the substance of the PR. Feel free to ignore.
| if let Some(subcmd) = &minus_file { | |
| if let Some(arg) = subcmd.to_str() { | |
| if let Some(arg) = minus_file.as_ref().and_then(|p| p.to_str()) { |
There was a problem hiding this comment.
Let's wait for if-let chains :)
| } | ||
| Ok(matches) => { | ||
| // subcommands take precedence over diffs | ||
| let minus_file = matches.get_one::<PathBuf>("minus_file").map(PathBuf::from); |
There was a problem hiding this comment.
Is our support for delta file_a file_b going to cause trouble when introducing standard clap subcommands?
There was a problem hiding this comment.
Everything clap parses takes precedence, in fact, here intervention is required to "steal" this argument from the command line parser.
| for (i, arg) in args.iter().enumerate() { | ||
| let arg = if let Some(arg) = arg.to_str() { | ||
| arg | ||
| } else { | ||
| continue; | ||
| }; |
There was a problem hiding this comment.
Sorry, more code golf suggestions:
| for (i, arg) in args.iter().enumerate() { | |
| let arg = if let Some(arg) = arg.to_str() { | |
| arg | |
| } else { | |
| continue; | |
| }; | |
| for (i, arg) in args.iter().filter_map(|a| a.to_str()).enumerate() { |
| SubCmdKind::Rg | ||
| } else { | ||
| SubCmdKind::Git( | ||
| args[i..] |
There was a problem hiding this comment.
This constructs Git("git") in the case of delta git show. Is that intentional / does it matter?
There was a problem hiding this comment.
Yes, the single .pop() is not enough, fixed.
| .to_string(), | ||
| ) | ||
| }; | ||
| subcmd.extend(args[i + 1..].iter().map(|arg| arg.into())); |
There was a problem hiding this comment.
More ignorable code golf:
There might be an attractive alternative way to write the above using .split_first()? The best I've come up with is
let invalid_placeholder = OsString::from("?");
let (subsubcmd, rest) = args[i..]
.split_first()
.unwrap_or((&invalid_placeholder, &[]));
let kind = if arg == RG {
SubCmdKind::Rg
} else {
SubCmdKind::Git(subsubcmd.to_string_lossy().to_string())
};
subcmd.extend(rest.iter().cloned());There was a problem hiding this comment.
Done, merged with the above.
| } | ||
| } | ||
|
|
||
| pub fn extract(args: &[OsString], orig_error: Error) -> (ArgMatches, SubCommand) { |
There was a problem hiding this comment.
A docstring could be good. My attempt:
| pub fn extract(args: &[OsString], orig_error: Error) -> (ArgMatches, SubCommand) { | |
| /// Find the first arg that is a registered external subcommand and return a | |
| /// tuple containing: | |
| /// 0. The args prior to that point | |
| /// 1. A SubCommand representing the external subcommand and its subsequent args | |
| pub fn extract(args: &[OsString], orig_error: Error) -> (ArgMatches, SubCommand) { |
| Err(e) => { | ||
| e.exit(); | ||
| // Calls `e.exit()` if error persists. | ||
| let (matches, subcmd) = subcommands::extract(args, e); |
There was a problem hiding this comment.
Is that... praise? It must be, so elegant!
| } | ||
| let mut cmd = cmd.unwrap(); | ||
|
|
||
| let cmd_stdout = cmd.stdout.as_mut().expect("Failed to open stdout"); |
There was a problem hiding this comment.
We have a so-far-upheld tradition of using unwrap_or_else(|| panic(...)) because of my personal belief that expect() is confusing as reader and author, but I'd certainly understand if you wished to discontinue that tradition.
|
This is slightly off-topic because I think this would be an internal rather than an external subcommand, but I just found myself doing |
|
Apart from I sometimes try to use |
Let's make them sit behind |
|
Done. This is untested on Windows, but the process logic is from |
|
Apologies if the problem is at my end but I think that something might have gone wrong in the last change? When I do |
482affb to
53ae80c
Compare
|
No, you are perfectly right! Fixed, and not just testing Ah, the test doesn't work because the CI runners do a shallow |
The possible command line now is: delta <delta-args> [SUBCMD <subcmd-args>] If the entire command line fails to parse because SUBCMD is unknown, then try (until the next arg fails) parsing <delta-args> only, and then parse and call SUBCMD with args, its output is piped into delta. Other subcommands also take precedence over the diff/git-diff mode (`delta a b`, where e.g. a=git and b=show), and any diff call gets converted into an external subcommand first. Available are: delta rg .. => rg --json .. | delta delta a b .. => git diff --no-index a b .. | delta delta git show .. => git <color-on> show .. | delta and all other git-CMDS, of which add -p, blame, checkout -p, diff, grep, log -p, reflog -p, and stash show -p produce a diff. Because --json is automatically added for `delta rg ..`, it avoids the parsing ambiguities of and is easier to type than `rg .. | delta`. The piping is not done by the shell but delta, so the subcommands are child processes of delta.
This info then takes precedence over whatever start_determining_calling_process_in_thread() finds or rather doesn't find. (The simple yet generous SeqCst is used on purpose for the atomic operations.)
|
Excellent, merged! |
|
Both git and ripgrep have special behavior when their output is redirected, aimed at creating predictable and parseable output when it's being consumed by a machine. Should we consider not invoking delta at all in that case, instead |
Booo, squash merged again :) You envision someone doing |
Oh I'm sorry, that is annoying. The button is usually defaulted to squash since we use it for other delta contributors so it is a bit of an easy mistake for me to make, especially because we only ever squash at work, so my brain sees "Squash and merge" as "This is the green merge button"... But perhaps now I will not forget again.
Perhaps, but I think that I might have a good counterargument to that: today, if a user wants to save delta output with ANSI escapes, they have to do For |
|
Our git workflow uses Gerrit and is heavily based on "stacked PRs" (as it is now called) - so exactly the opposite, and clearly superior ;) Agreed, the direct exec subcommand PR is #1925, without an opt-out. Let's see if someone runs into that and comes up with another argument for |
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [dandavison/delta](https://github.com/dandavison/delta) | minor | `0.18.2` → `0.19.1` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>dandavison/delta (dandavison/delta)</summary> ### [`v0.19.1`](https://github.com/dandavison/delta/releases/tag/0.19.1) [Compare Source](dandavison/delta@0.19.0...0.19.1) This patch release fixes a release automation [bug](dandavison/delta#2127): release [0.19.0](https://github.com/dandavison/delta/releases/tag/0.19.0) did not include all release binaries. #### What's Changed - Fix CD: replace defunct ubuntu-20.04 runners by [@​dandavison](https://github.com/dandavison) in [#​2129](dandavison/delta#2129) **Full Changelog**: <dandavison/delta@0.19.0...0.19.1> ### [`v0.19.0`](https://github.com/dandavison/delta/releases/tag/0.19.0) [Compare Source](dandavison/delta@0.18.2...0.19.0) Tons of improvements; thanks very much to all delta contributors. **This release is missing several binaries due to a [bug in the GitHub Actions release workflow](dandavison/delta#2127). Please use <https://github.com/dandavison/delta/releases/tag/0.19.1> instead.** #### What's Changed - Do not double panic in FakeParentArgs::drop by [@​dvermd](https://github.com/dvermd) in [#​1856](dandavison/delta#1856) - Improve blame file type detection ([#​1803](dandavison/delta#1803)) by [@​dvermd](https://github.com/dvermd) in [#​1829](dandavison/delta#1829) - Use same example config in manual as README by [@​dandavison](https://github.com/dandavison) in [#​1879](dandavison/delta#1879) - Clarify grep highlighter-related code by [@​dandavison](https://github.com/dandavison) in [#​1877](dandavison/delta#1877) - Don't set colorMoved in introductory instructions by [@​dandavison](https://github.com/dandavison) in [#​1884](dandavison/delta#1884) - Recommend zdiff3 merge.conflictStyle by [@​adamchainz](https://github.com/adamchainz) in [#​1260](dandavison/delta#1260) - Add themes `weeping-willow`, `mirthful-willow` by [@​lvdh](https://github.com/lvdh) in [#​1864](dandavison/delta#1864) - CI: switch to macos-13, as 12 will be unsupported soon by [@​th1000s](https://github.com/th1000s) in [#​1890](dandavison/delta#1890) - Add optional capture-output writer to run\_app() by [@​th1000s](https://github.com/th1000s) in [#​1889](dandavison/delta#1889) - Center-align README content by [@​dandavison](https://github.com/dandavison) in [#​1893](dandavison/delta#1893) - Redundant Option Checks, Unwrap Safety by [@​sharma-shray](https://github.com/sharma-shray) in [#​1892](dandavison/delta#1892) - Add console commands for git configuration by [@​harmtemolder](https://github.com/harmtemolder) in [#​1896](dandavison/delta#1896) - Honor pager path when checking less version by [@​dandavison](https://github.com/dandavison) in [#​1903](dandavison/delta#1903) - Rename some variables by [@​dandavison](https://github.com/dandavison) in [#​1904](dandavison/delta#1904) - Delete now-unused pricate homebrew formula step from Makefile (III) by [@​dvermd](https://github.com/dvermd) in [#​1830](dandavison/delta#1830) - Support {host} in hyperlinks by [@​dandavison](https://github.com/dandavison) in [#​1901](dandavison/delta#1901) - Allow multiple hyperlinks per line by [@​th1000s](https://github.com/th1000s) in [#​1909](dandavison/delta#1909) - Clippy 1.83 by [@​th1000s](https://github.com/th1000s) in [#​1918](dandavison/delta#1918) - Support external subcommands: rg, diff, git-show (etc.) by [@​th1000s](https://github.com/th1000s) in [#​1769](dandavison/delta#1769) - Don't keep subcommand stdout around by [@​th1000s](https://github.com/th1000s) in [#​1920](dandavison/delta#1920) - hyperlink commit hashes of length 7 as well by [@​th1000s](https://github.com/th1000s) in [#​1924](dandavison/delta#1924) - clippy 1.84 fix by [@​th1000s](https://github.com/th1000s) in [#​1938](dandavison/delta#1938) - chore(deps): update git2 to use libgit2 1.9 by [@​chenrui333](https://github.com/chenrui333) in [#​1930](dandavison/delta#1930) - Suggest minimum bat version in manual by [@​ernstki](https://github.com/ernstki) in [#​1941](dandavison/delta#1941) - Upgrade unicode-width to v0.1.14 (but still < 0.2.0) by [@​th1000s](https://github.com/th1000s) in [#​1937](dandavison/delta#1937) - Update terminal-colorsaurus version to 0.4.8 by [@​rashil2000](https://github.com/rashil2000) in [#​1936](dandavison/delta#1936) - Fix index out of bounds crash for '@​@​ @​@​' hunk header by [@​adamchainz](https://github.com/adamchainz) in [#​1995](dandavison/delta#1995) - Tune themes weeping-willow, mirthful-willow by [@​lvdh](https://github.com/lvdh) in [#​2011](dandavison/delta#2011) - clippy 1.88 fixes by [@​injust](https://github.com/injust) in [#​2016](dandavison/delta#2016) - Fix diff output when a diff ends with a mode change by [@​th1000s](https://github.com/th1000s) in [#​2023](dandavison/delta#2023) - fix: `cargo install git-delta --locked` fails on systems with GCC 15 by [@​tquin](https://github.com/tquin) in [#​2007](dandavison/delta#2007) - Normalize `merge.conflictStyle` casing by [@​injust](https://github.com/injust) in [#​2015](dandavison/delta#2015) - Styled zero lines fix by [@​th1000s](https://github.com/th1000s) in [#​1916](dandavison/delta#1916) - config: add setup instructions for Jujutsu (jj) vcs by [@​arjunmahishi](https://github.com/arjunmahishi) in [#​2048](dandavison/delta#2048) - all: fix clippy complaints by [@​charlievieth](https://github.com/charlievieth) in [#​2038](dandavison/delta#2038) - Cache the Git remote URL to speed up rendering hyperlinks by [@​charlievieth](https://github.com/charlievieth) in [#​1940](dandavison/delta#1940) - deps: update cc by [@​ognevny](https://github.com/ognevny) in [#​2053](dandavison/delta#2053) - rg json handling: fix line comment highlighting by [@​th1000s](https://github.com/th1000s) in [#​2057](dandavison/delta#2057) - Update dirs dependency to version 6 by [@​musicinmybrain](https://github.com/musicinmybrain) in [#​2063](dandavison/delta#2063) - default line number to 1 for hyperlinks by [@​schpet](https://github.com/schpet) in [#​2061](dandavison/delta#2061) - Fix line numbers showing filename when hyperlinks enabled by [@​tsvikas](https://github.com/tsvikas) in [#​2058](dandavison/delta#2058) - less hist file: look at xdg paths by [@​th1000s](https://github.com/th1000s) in [#​2065](dandavison/delta#2065) - CI: remove x86 apple/macOS by [@​th1000s](https://github.com/th1000s) in [#​2074](dandavison/delta#2074) - Update bat dependency from 0.24 to 0.26 by [@​dandavison](https://github.com/dandavison) in [#​2115](dandavison/delta#2115) #### New Contributors - [@​dvermd](https://github.com/dvermd) made their first contribution in [#​1856](dandavison/delta#1856) - [@​lvdh](https://github.com/lvdh) made their first contribution in [#​1864](dandavison/delta#1864) - [@​sharma-shray](https://github.com/sharma-shray) made their first contribution in [#​1892](dandavison/delta#1892) - [@​harmtemolder](https://github.com/harmtemolder) made their first contribution in [#​1896](dandavison/delta#1896) - [@​ernstki](https://github.com/ernstki) made their first contribution in [#​1941](dandavison/delta#1941) - [@​tquin](https://github.com/tquin) made their first contribution in [#​2007](dandavison/delta#2007) - [@​arjunmahishi](https://github.com/arjunmahishi) made their first contribution in [#​2048](dandavison/delta#2048) - [@​charlievieth](https://github.com/charlievieth) made their first contribution in [#​2038](dandavison/delta#2038) - [@​ognevny](https://github.com/ognevny) made their first contribution in [#​2053](dandavison/delta#2053) - [@​musicinmybrain](https://github.com/musicinmybrain) made their first contribution in [#​2063](dandavison/delta#2063) - [@​schpet](https://github.com/schpet) made their first contribution in [#​2061](dandavison/delta#2061) - [@​tsvikas](https://github.com/tsvikas) made their first contribution in [#​2058](dandavison/delta#2058) **Full Changelog**: <dandavison/delta@0.18.2...0.19.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44OS42IiwidXBkYXRlZEluVmVyIjoiNDMuODkuNiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
The possible command line now is:
delta <delta-args> [SUBCMD <subcmd-args>]
If the entire command line fails to parse because SUBCMD is unknown,
then try (until the next arg fails) parsing only,
and then parse and call SUBCMD.., output is piped into delta.
Other subcommands also take precedence over the diff/git-diff mode
(
delta a b, where e.g. a=show and b=HEAD), and any diff call getsconverted into an external subcommand first.
Available are:
delta rg .. => rg --json .. | delta
delta a b .. => git diff a b .. | delta
delta show .. => git <color-on> show .. | delta
And various other git-CMDS: add, blame, checkout, diff, grep, log, reflog and stash.
The piping is not done by the shell, but delta, so the subcommands
are now child processes of delta.
This info then takes precedence over whatever
start_determining_calling_process_in_thread()finds or ratherdoesn't find.
(The simple yet generous SeqCst is used on purpose for the atomic operations.)
Looking at the
Call { Delta, .. }enum from the--helpPR, and how the diff sub-command works (both how-@is needed to add extra args, and how its ouput is fed back into delta when running as a child process) I had the idea of generalizing that.Deltas
rgintegration is great, butrg --json .. | deltais a bit clunky, now it is justdelta rg ... This also allows using delta without touching any git configs ("try before you edit anything"), as a proof of conceptdelta showis implemented here.This is a bit ... creative with the clap command line parser, but it is all quite contained.