-
Notifications
You must be signed in to change notification settings - Fork 335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
colors: consistent color parsing and configuration #423
Conversation
This matches the name that git uses (see: color.c:parse_attr).
Used by DiffHighlight when `normal` is blank or undefined.
This makes the initial values of DiffHighlight::OLD_HIGHLIGHT explicit in order to simplify reasoning about them. Since DiffHighlight::OLD_HIGHLIGHT etc. are initialized in `DiffHighlight.pm` from the same values at load time, a missing config would previously always fall back to the fallback values defined in that module, which are undef/reverse as declared here.
Avoids doubt about depending on the mutations to DiffHighlight::NEW_HIGHLIGHT etc. by init_diff_highlight_colors. It also helps consolidate the usage of those colors with other configured colors, so that as far as we're concerned there's a single way to read those.
It's always reached by color('reset') or the cached $reset_color var; nothing ever called get_config_color('reset'). It's useful to remove it because it's different than all the other names in get_config_color(), which map to git config keys.
Uses `git config --get-color … …` to load the ANSI sequence of a named config like `color.diff.new` directly from git. This completely avoids the need to attempt to reproduce that entire color-spec parsing logic. The loaded sequences are cached by name.
git will parse and return the fallback color when given an empty config name, e.g. `git config --get-color '' 'blue'`. This is documented in the git config man pages. This means *none* of that color spec parsing needs to be reproduced by diff-so-fancy. This is much simpler, and also means that the color specs can be consistently represented in the git config, avoiding mismatches between the accepted colors and or modifiers.
This extends `git_ansi_color` to include the fallback value as the second parameter, so that configured colors (e.g. `color.diff.new`) can be interpreted and cached in a single command even falling back to the default. For direct interpretation of color specs ('reset', 'yellow bold reverse') without an associated config key, this just uses the empty string key like `git config` does and caches based on the spec string itself.
They are not accepted by git and so cannot appear in normal color specs like `color.diff.new`. The user can still refer to `214` directly or hex colors.
git's default outputs for `color.diff.old`/`.new` are (non-bold) red and green. If no such config exists, falling back to the same value makes "manually recolored" lines consistent with pass-through lines.
The default DiffHighlight fallback colors were just "reverse" for both added/removed text. Now, added words are reversed + bold, and removed text is dim + italic + strike, in order to distinguish them. These fallbacks only include attributes and don't refer to colors, and so are safely compatible with existing user colors. Unlike `color.diff.new` (which should match the colors we're receiving from git), we _give_ these `color.diff-highlight.*` configs to DiffHighlight. That means we can decide the fallbacks here arbitrarily and don't need to match the hardcoded defaults already in DiffHighlight.m.
This updates the recommended (N.B.: not _fallback_) highlight config to better emphasize changes. Previously *all* non-contextual diff text was bolded. Text is now non-bold by default, but added words are bolded for emphasis, and removed words are italic+strike.
Whoa... there is a LOT going on here. Lots of files and lines changed. My first reaction is that these are some pretty hefty changes that put a decent developmental burden on developers to update/maintain. What exact problem is this solving? We purposely kept the color parsing simple (no more than three octets), and it has generated little or no bug reports. All color parsing is done in Perl save the one call to Also, please see the |
A quick comparison of next
rwe
23 ms vs 40 ms is a slowdown of about 74%. One of our primary goals has been to keep the startup (and ultimately runtime) of this script as low as possible. |
@scottchiefbaker — Thanks for the input and additional measurements. The timing isn't close to what I'd measured, so I think something else must be going on. Note that the benchmark you ran is only consuming the diff during the first run; after that stdin will be empty and no diff is run at all. But…you're right: running the above with quoted args ( Re maintenance: the intention was the opposite, actually. The change removes the need to maintain that parsing logic at all, while at the same time ensuring that the capabilities track to at least what git supports. The commit stream might be a bit too fine-grained, but if you look at the diff, there are broadly two simple changes:
|
@rwe oh good catch. I forgot to put quotes around my commands.
So we're looking at 30 ms vs 54 ms... still about 80% slower. I'm not sure how keen I am to mimic every nuance of Do you know what version of Git |
Forever—some digging shows it was introduced in 2007, git 1.5.4: git/git@9ce0352 The overhead can be removed entirely by bulk-fetching with |
I came here after I stumbled upon the fact that diff-so-fancy neither supports hex colors nor git's default color attributes @rwe Thanks for your work here! This makes the color configuration for diff-so-fancy exactly like I would it expect to work.
I understand that speed is a main goal. 80% slower sounds huge when expressed in relative numbers. In absolute numbers, however, we're talking about a few milliseconds here. I would strongly prefer being able to configure colors just like in git over saving 24ms (which I won't notice, ever). And concerning the technical debt: the patch in this PR actually removes technical debt. It does not try to mimic nuances of Git, it simply uses its features that are already there. Just look at the patchset, it's 415 lines deleted against 71 lines added. So apart from the speed issue (which is a theoretical but not a real-world issue imo) I think there's no decent reason to not accept this change. Is there a chance for this to get merged? Would really appreciate it! |
Switch to custom version from this PR: so-fancy/diff-so-fancy#423 This makes it possible to use hex color codes. Configure these so that the changes portions of a line get highlighted. Also remove the disabling of the markEmptyLines settings so that changed empty lines get visible.
Switch to custom version from this PR: so-fancy/diff-so-fancy#423 This makes it possible to use hex color codes. Configure these so that the changes portions of a line get highlighted. Also remove the disabling of the markEmptyLines settings so that changed empty lines get visible.
Switch to custom version from this PR: so-fancy/diff-so-fancy#423 This makes it possible to use hex color codes. Configure these so that the changes portions of a line get highlighted. Also remove the disabling of the markEmptyLines settings so that changed empty lines get visible.
Switch to custom version from this PR: so-fancy/diff-so-fancy#423 This makes it possible to use hex color codes. Configure these so that the changes portions of a line get highlighted. Also remove the disabling of the markEmptyLines settings so that changed empty lines get visible.
We just landed several other commits that address If there is something specific people want to see added please open another issue specific to that. Hex colors (I didn't even know Git supported that)? At this time, shelling out to |
This replaces both of the
diff-so-fancy
's hand-rolled color reconstruction mechanisms with cached invocations ofgit config --get-color
, which is fast and uses git's own parsing to construct the appropriate ANSI sequences.Those invocations are cached on the config key (if there is one) or the color spec.
This does not introduce noticeable performance impact: timing
git -c pager.log="${dsf}" log -p
on this repo, between this branch and 1.4.2, actually shows a slight speed-up.The commits in this PR progress as follows:
color
subroutine; then realizinggit_ansi_color
is used alongside it, and attempting to fix that too.git config --get-color
anyway instead of just mimicking it; and then proceeding toward some refactors to allow those to be consolidated.Along the way, there are a couple fixes to the default/fallback handling. The last couple commits use some of the newly-exposed features (e.g. italic/strike/dim) for some improved defaults.
Background
diff-so-fancy
introduced three mutually incompatible syntaxes for parsing color specs:git
's parsing, which is used to drive the original coloredgit diff
output, and by thecontrib/diff-highlight
pager bundled with git. That syntax is likewhite brightred bold ul reverse
and supports the 8 standard ANSI names (black/red/green/yellow/blue/magenta/cyan/white) +bright…
variants; ANSI colors like124
, and hex like#ffcccc
. It also supportsbold
,dim
,italic
,ul
,blink
,reverse
, andstrike
attributes and their negations (nobold
etc).colors()
which interpreted syntax likewhite_bold_on_black
; but usedinverse
instead ofreverse
, usedunderline
instead oful
; did not support thedim
orstrike
attributes; did not support attribute negation; and did not understandbright…
names. It also arbitrarily maintained two additional non-standard color names (orange and purple).git_ansi_colors()
which attempted to more-closely mimicgit
's parsing, but only supportedbold
andreverse
. (Nodim
,italic
,underline
,blink
, orstrike
; no negations).This was frustrating, because there are overlapping config keys between
diff-so-fancy
,diff-highlight
, andgit diff
, and the lowest-common-denominator configuration was a little tricky and too limited.While trying to fix a crash related to the
reverse
↔︎inverse
mismatch—which in retrospect was a red herring, sincecolor()
doesn't appear to be invoked on user config…but whatever!—it became clear that this complexity was unnecessary and likely less performant than letting git itself—which is already a dependency—do the work.