dandavison / delta

A syntax-highlighting pager for git, diff, grep, and blame output
https://dandavison.github.io/delta/
MIT License
23.01k stars 382 forks source link

🐛 side-by-side alignment incorrect when tabs present #308

Closed infokiller closed 3 years ago

infokiller commented 4 years ago

image

❯ delta --version
delta 0.4.1

git --no-pager diff output:

diff --git .config/git/config .config/git/config
index bc4ae2e..b058f99 100644
--- .config/git/config
+++ .config/git/config
@@ -7,12 +7,7 @@
    # - `git log --patch` only shows the first commit header with colors
    # - diff-so-fancy looks better to me
    # - There's no option to make the changes in a line bold like diff-so-fancy
-   # Notes for delta args:
-   # - Disable syntax highlighting in delta as I find it distraction.
-   # - Note that args containing hashes must be double quoted (not single quoted)
-   #    so that they're not interpreted as gitconfig comments.
-   # delta --minus-color="#400000" --minus-emph-color="#a00000" --plus-color="#004000" --plus-emph-color="#009000" --hunk-color='yellow' --commit-style=box --theme=none
-   pager = ((diff-so-fancy || cat) 2> /dev/null) | less --tabs=4 -RFX --
+   pager = "delta 2> /dev/null || { { { diff-so-fancy || cat ; } 2> /dev/null ; } | less --tabs=4 -RFX -- ; }"
    excludesFile = ~/.config/git/ignore
    hooksPath = ~/.config/git/hooks
    # Don't convert line endings when checking out files. See also:
@@ -26,7 +21,6 @@
    # Show summary of changes to submodules. See also:
    # https://git-scm.com/book/en/v2/Git-Tools-Submodules
    submoduleSummary = true
-   showStash = true

 [diff]
    tool = vimdiff
@@ -41,6 +35,8 @@
   # Try to break up diffs at blank lines, so you don't get those brackets
   # floating away. Already the default in newer git versions.
   indentHeuristic = true
+   # Enable special coloring of moved lines.
+   colorMoved = default
    # EXP: Remove a/ and b/ on filenames in diffs. There is already a +++
    # and --- in front, so it adds nothing.
   noprefix = true
@@ -51,7 +47,7 @@
 [interactive]
    # The echo before diff-so-fancy is mandatory, see:
    # https://github.com/so-fancy/diff-so-fancy/issues/35#issuecomment-555778997
-  diffFilter = (((command -v diff-so-fancy > /dev/null) && echo && diff-so-fancy) || cat)
+  diffFilter = "{ delta --color-only 2> /dev/null || { { command -v diff-so-fancy > /dev/null ; } && echo && diff-so-fancy; } || cat ; }"

 [merge]
    # Show the original hunk when there are merge conflicts.
@@ -121,6 +117,30 @@
 [receive]
    fsckObjects = true

+# https://github.com/dandavison/delta
+# Disable syntax highlighting in delta as I find it distracting.
+[delta]
+   # As of 2020-09-02, side-by-side output doesn't look right.
+   side-by-side = true
+   # https://github.com/dandavison/delta#navigation-keybindings-for-large-diffs
+   # navigate = true
+   # hyperlinks = true
+   # Disable syntax highlighting in delta as I find it distracting.
+   # syntax-theme = none
+   diff-so-fancy = true
+   # line-numbers-left-format = ""
+   # line-numbers-right-format = "│ "
+
+# NOTE: options containing hashes must be double quoted (not single quoted) so
+# that they're not interpreted as gitconfig comments.
+[delta "my-diff-so-fancy"]
+   minus-style = "normal #400000"
+   minus-emph-style = "normal #a00000"
+   plus-style = "normal #004000"
+   plus-emph-style = "normal #009000"
+   hunk-style = yellow
+   commit-style = box
+
 [color "diff-highlight"]
    oldNormal = red bold
    oldHighlight = red bold 52

Delta config:

❯ delta --show-config
    commit-style                  = bold yellow
    file-style                    = 227
    hunk-header-style             = bold syntax
    minus-style                   = bold red
    minus-non-emph-style          = bold red
    minus-emph-style              = bold red 52
    minus-empty-line-marker-style = normal "#3f0001"
    zero-style                    = normal
    plus-style                    = bold green
    plus-non-emph-style           = bold green
    plus-emph-style               = bold green 22
    plus-empty-line-marker-style  = normal "#002800"
    whitespace-error-style        = reverse purple
    24-bit-color                  = true
    file-added-label              = 'added:'
    file-modified-label           = ''
    file-removed-label            = 'removed:'
    file-renamed-label            = 'renamed:'
    hyperlinks                    = false
    inspect-raw-lines             = true
    keep-plus-minus-markers       = false
    line-numbers                  = true
    line-numbers-minus-style      = 88
    line-numbers-zero-style       = "#444444"
    line-numbers-plus-style       = 28
    line-numbers-left-style       = blue
    line-numbers-right-style      = blue
    line-numbers-left-format      = '│{nm:^4}│'
    line-numbers-right-format     = '│{np:^4}│'
    max-line-distance             = 0.6
    navigate                      = false
    paging                        = auto
    side-by-side                  = true
    syntax-theme                  = Monokai Extended
    width                         = 90
    tabs                          = 0
    word-diff-regex               = '\w+'
dandavison commented 4 years ago

Hi, thanks for the detailed report. I actually am failing to reproduce this, using 0.4.1 and your settings, sending your diff directly to delta's stdin. Would you be able to post the output of cat -A or bat -A on your file before and after the diff? Is there any chance there are tabs (although I think they should be handled) or other not-easily-visible characters, e.g. on l.30 in the old file?

infokiller commented 4 years ago

Indeed, there are tabs. My gitconfig file is formatted with tabs (I usually prefer spaces but don't remember why I chose tabs for this file). When I test it in a file without tabs it works well!

I did another test with a single line change: uncommenting the line with # features = side-by-side. Bellow are the cat -A outputs before and after.

cat -A before the change:

[include]$
^Ipath = config_private$
$
[core]$
^Ipager = "delta 2> /dev/null || { { { diff-so-fancy || cat ; } 2> /dev/null ; } | less --tabs=4 -RFX -- ; }"$
^IexcludesFile = ~/.config/git/ignore$
^IhooksPath = ~/.config/git/hooks$
^I# Don't convert line endings when checking out files. See also:$
^I# https://help.github.com/en/articles/configuring-git-to-handle-line-endings$
^Iautocrlf = input$
^I# Warn me if a line ending conversion is irreversible. Currently has no effect$
^I# because of the autocrlf setting, but a good default.$
^Isafecrlf = warn$
$
[status]$
^I# Show summary of changes to submodules. See also:$
^I# https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^IsubmoduleSummary = true$
$
[diff]$
^Itool = vimdiff$
^I# Show more informative diff for submodules. See also:$
^I# https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^Isubmodule = log$
^I# Detect both renames and copies.$
^Irenames = copies$
^I# "histogram" should provide a good combination of time and diff optimality.$
^I# See also: https://news.ycombinator.com/item?id=11071410$
^Ialgorithm = histogram$
  # Try to break up diffs at blank lines, so you don't get those brackets$
  # floating away. Already the default in newer git versions.$
  indentHeuristic = true$
^I# Enable special coloring of moved lines.$
^IcolorMoved = default$
^I# EXP: Remove a/ and b/ on filenames in diffs. There is already a +++$
^I# and --- in front, so it adds nothing.$
  noprefix = true$
$
[difftool]$
^Iprompt = false$
$
[interactive]$
^I# The echo before diff-so-fancy is mandatory, see:$
^I# https://github.com/so-fancy/diff-so-fancy/issues/35#issuecomment-555778997$
  diffFilter = "{ delta --color-only 2> /dev/null || { { command -v diff-so-fancy > /dev/null ; } && echo && diff-so-fancy; } || cat ; }"$
$
[merge]$
^I# Show the original hunk when there are merge conflicts.$
^Iconflictstyle = diff3$
^IautoStash = true$
$
[rebase]$
^IautoStash = true$
$
[pull]$
^I# EXP: use ff-only merges by default (can override per invocation).$
^Iff = only$
^I# Merge by default. This is git's default value, but without it git 2.27 or$
^I# later warns about not using an explicit reconcile strategy when pulling.$
^I# rebase = false$
$
[log]$
^I# Use `--follow` by default when invoking `git log` on a single path.$
^Ifollow = true$
^I# Set default format.$
^Idate = format:%Y-%m-%d %H:%M$
$
[pretty]$
^I# Aliases for my custom git-log formats.$
^Imyshort = %C(yellow)%h%C(reset) %C(green)%ad%C(red)%d %C(reset)%s%C(blue) [%cn]$
^Imymedium = %C(yellow)%h%C(reset) %C(green)%ad %C(blue)%cn%C(red)%d%n%C(bold)%s%n$
$
[format]$
^I# Use "myshort" format by default.$
^Ipretty = myshort$
$
[push]$
^I# Before pushing the superproject, check that the commits of submodules are$
^I# pushed to their remotes (otherwise, cloning this repo will probably fail).$
^I# See also: https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^IrecurseSubmodules = check$
^I# Automatically set upstream branch. See also:$
^I# https://stackoverflow.com/a/22933955/1014208$
^Idefault = current$
^I# Push annotated tags by default. Unfortunately, there's no equivalent option$
^I# for lightweight tags since they seem to be designated as private/temporary$
^I# labels.$
^IfollowTags = true$
$
[credential]$
^Ihelper = cache --timeout=86400$
$
[submodule]$
^I# Use a "reasonable default" for the number of submodules that are$
^I# fetched/cloned in parallel.$
^IfetchJobs = 0$
$
[grep]$
^IextendedRegexp = true$
$
[rerere]$
^I# Activate recording of resolved conflicts, so that identical conflict hunks$
^I# can be resolved automatically, should they be encountered again.$
  enabled = true$
^I# If rerere resolved a conflict, stage it.$
  autoupdate = true$
$
[transfer]$
^Ifsckobjects = true$
[fetch]$
^Ifsckobjects = true$
[receive]$
^IfsckObjects = true$
$
# https://github.com/dandavison/delta$
# Disable syntax highlighting in delta as I find it distracting.$
[delta]$
^I# https://github.com/dandavison/delta#navigation-keybindings-for-large-diffs$
^Inavigate = true$
^I# As of 2020-09-02, hyperlinks cause a display issue and seem to require$
^I# patched versions of tmux and less.$
^I# hyperlinks = true$
^I# Disable syntax highlighting in delta as I find it distracting.$
^Isyntax-theme = none$
^I# syntax-theme = MySolarizedDark$
^Idiff-so-fancy = true$
^I# As of 2020-09-02, side-by-side output doesn't look right:$
^I# https://github.com/dandavison/delta/issues/308$
^I# features = side-by-side$
$
[delta "side-by-side"]$
^Iside-by-side = true$
^Iline-numbers-left-format = ""$
^Iline-numbers-right-format = "M-bM-^TM-^B "$
$
# NOTE: options containing hashes must be double quoted (not single quoted) so$
# that they're not interpreted as gitconfig comments.$
[delta "my-diff-so-fancy"]$
^Iminus-style = "normal #400000"$
^Iminus-emph-style = "normal #a00000"$
^Iplus-style = "normal #004000"$
^Iplus-emph-style = "normal #009000"$
^Ihunk-style = yellow$
^Icommit-style = box$
$
[color "diff-highlight"]$
^IoldNormal = red bold$
^IoldHighlight = red bold 52$
^InewNormal = green bold$
^InewHighlight = green bold 22$
$
[color "diff"]$
^Iold = red bold$
^Inew = green bold$
^Ifrag = magenta bold$
^Icommit = yellow bold$
^Iwhitespace = red reverse$
^I# Better separation of the filename and unchanged lines, which by default are$
^I# both colored in white.$
^Imeta = 227$
$
# When authentication is needed, SSH is usually easier to use than HTTPS if the$
# key is in the SSH agent, since it only requires the SSH key password, and not$
# the account password. However, when no authentication is needed (for example,$
# for public repos), HTTPS is simpler and safer, since SSH will always use the$
# private key.$
# This configures SSH to be used automatically for any Github and GitLab push,$
# and for any GitLab pull from my account (which manages some private repos).$
[url "git@github.com:"]$
^IpushInsteadOf = https://github.com/$
[url "ssh://git@gitlab.com/"]$
^IpushInsteadOf = https://gitlab.com/$
[url "ssh://git@gitlab.com/infokiller/"]$
^IinsteadOf = https://gitlab.com/infokiller/$
^I$
[advice]$
^IaddIgnoredFile = false$
$
[blacklist-hook]$
^I# One of error, warning, or skip.$
^Imode = error$
$
[secret-detection-hook]$
^I# One of error, warning, or skip.$
^Imode = error$
$
# NOTE: As of 2020-02-12, I disabled automatic PR fetching because it can$
# pollute the git references, which can add too much noise to some$
# autocompletions. If I need to fetch PRs I can use `git-fetch-prs`.$
# Automatically fetch pull and merge requests for Github and Gitlab. See also:$
# - https://github.com/tiimgreen/github-cheat-sheet#checking-out-pull-requests$
# - https://docs.gitlab.com/ee/user/project/merge_requests/#checkout-merge-requests-locally $
# [remote "origin"]$
# ^I# NOTE: As of 2020-02-12, I can't reproduce the activitywatch issue described$
# ^I# below..$
# ^I# Commented out because it causes the error "multiple updates for ref" when$
# ^I# building activitywatch. See also: https://apple.stackexchange.com/a/352666.$
# ^Ifetch = +refs/heads/*:refs/remotes/origin/*$
# ^Ifetch = +refs/pull/*/head:refs/remotes/origin/pr/*$
# ^Ifetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*$
$
# NOTE: As of 2020-02-12, this is disabled because it creates an invalid$
# upstream remote for every repo (it's invalid because there's no URL$
# specified).$
# [remote "upstream"]$
# ^I# See comment above.$
# ^I# fetch = +refs/heads/*:refs/remotes/origin/*$
# ^Ifetch = +refs/pull/*/head:refs/remotes/origin/pr/*$
# ^Ifetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*$

cat -A after the change:

[include]$
^Ipath = config_private$
$
[core]$
^Ipager = "delta 2> /dev/null || { { { diff-so-fancy || cat ; } 2> /dev/null ; } | less --tabs=4 -RFX -- ; }"$
^IexcludesFile = ~/.config/git/ignore$
^IhooksPath = ~/.config/git/hooks$
^I# Don't convert line endings when checking out files. See also:$
^I# https://help.github.com/en/articles/configuring-git-to-handle-line-endings$
^Iautocrlf = input$
^I# Warn me if a line ending conversion is irreversible. Currently has no effect$
^I# because of the autocrlf setting, but a good default.$
^Isafecrlf = warn$
$
[status]$
^I# Show summary of changes to submodules. See also:$
^I# https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^IsubmoduleSummary = true$
$
[diff]$
^Itool = vimdiff$
^I# Show more informative diff for submodules. See also:$
^I# https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^Isubmodule = log$
^I# Detect both renames and copies.$
^Irenames = copies$
^I# "histogram" should provide a good combination of time and diff optimality.$
^I# See also: https://news.ycombinator.com/item?id=11071410$
^Ialgorithm = histogram$
  # Try to break up diffs at blank lines, so you don't get those brackets$
  # floating away. Already the default in newer git versions.$
  indentHeuristic = true$
^I# Enable special coloring of moved lines.$
^IcolorMoved = default$
^I# EXP: Remove a/ and b/ on filenames in diffs. There is already a +++$
^I# and --- in front, so it adds nothing.$
  noprefix = true$
$
[difftool]$
^Iprompt = false$
$
[interactive]$
^I# The echo before diff-so-fancy is mandatory, see:$
^I# https://github.com/so-fancy/diff-so-fancy/issues/35#issuecomment-555778997$
  diffFilter = "{ delta --color-only 2> /dev/null || { { command -v diff-so-fancy > /dev/null ; } && echo && diff-so-fancy; } || cat ; }"$
$
[merge]$
^I# Show the original hunk when there are merge conflicts.$
^Iconflictstyle = diff3$
^IautoStash = true$
$
[rebase]$
^IautoStash = true$
$
[pull]$
^I# EXP: use ff-only merges by default (can override per invocation).$
^Iff = only$
^I# Merge by default. This is git's default value, but without it git 2.27 or$
^I# later warns about not using an explicit reconcile strategy when pulling.$
^I# rebase = false$
$
[log]$
^I# Use `--follow` by default when invoking `git log` on a single path.$
^Ifollow = true$
^I# Set default format.$
^Idate = format:%Y-%m-%d %H:%M$
$
[pretty]$
^I# Aliases for my custom git-log formats.$
^Imyshort = %C(yellow)%h%C(reset) %C(green)%ad%C(red)%d %C(reset)%s%C(blue) [%cn]$
^Imymedium = %C(yellow)%h%C(reset) %C(green)%ad %C(blue)%cn%C(red)%d%n%C(bold)%s%n$
$
[format]$
^I# Use "myshort" format by default.$
^Ipretty = myshort$
$
[push]$
^I# Before pushing the superproject, check that the commits of submodules are$
^I# pushed to their remotes (otherwise, cloning this repo will probably fail).$
^I# See also: https://git-scm.com/book/en/v2/Git-Tools-Submodules$
^IrecurseSubmodules = check$
^I# Automatically set upstream branch. See also:$
^I# https://stackoverflow.com/a/22933955/1014208$
^Idefault = current$
^I# Push annotated tags by default. Unfortunately, there's no equivalent option$
^I# for lightweight tags since they seem to be designated as private/temporary$
^I# labels.$
^IfollowTags = true$
$
[credential]$
^Ihelper = cache --timeout=86400$
$
[submodule]$
^I# Use a "reasonable default" for the number of submodules that are$
^I# fetched/cloned in parallel.$
^IfetchJobs = 0$
$
[grep]$
^IextendedRegexp = true$
$
[rerere]$
^I# Activate recording of resolved conflicts, so that identical conflict hunks$
^I# can be resolved automatically, should they be encountered again.$
  enabled = true$
^I# If rerere resolved a conflict, stage it.$
  autoupdate = true$
$
[transfer]$
^Ifsckobjects = true$
[fetch]$
^Ifsckobjects = true$
[receive]$
^IfsckObjects = true$
$
# https://github.com/dandavison/delta$
# Disable syntax highlighting in delta as I find it distracting.$
[delta]$
^I# https://github.com/dandavison/delta#navigation-keybindings-for-large-diffs$
^Inavigate = true$
^I# As of 2020-09-02, hyperlinks cause a display issue and seem to require$
^I# patched versions of tmux and less.$
^I# hyperlinks = true$
^I# Disable syntax highlighting in delta as I find it distracting.$
^Isyntax-theme = none$
^I# syntax-theme = MySolarizedDark$
^Idiff-so-fancy = true$
^I# As of 2020-09-02, side-by-side output doesn't look right:$
^I# https://github.com/dandavison/delta/issues/308$
^Ifeatures = side-by-side$
$
[delta "side-by-side"]$
^Iside-by-side = true$
^Iline-numbers-left-format = ""$
^Iline-numbers-right-format = "M-bM-^TM-^B "$
$
# NOTE: options containing hashes must be double quoted (not single quoted) so$
# that they're not interpreted as gitconfig comments.$
[delta "my-diff-so-fancy"]$
^Iminus-style = "normal #400000"$
^Iminus-emph-style = "normal #a00000"$
^Iplus-style = "normal #004000"$
^Iplus-emph-style = "normal #009000"$
^Ihunk-style = yellow$
^Icommit-style = box$
$
[color "diff-highlight"]$
^IoldNormal = red bold$
^IoldHighlight = red bold 52$
^InewNormal = green bold$
^InewHighlight = green bold 22$
$
[color "diff"]$
^Iold = red bold$
^Inew = green bold$
^Ifrag = magenta bold$
^Icommit = yellow bold$
^Iwhitespace = red reverse$
^I# Better separation of the filename and unchanged lines, which by default are$
^I# both colored in white.$
^Imeta = 227$
$
# When authentication is needed, SSH is usually easier to use than HTTPS if the$
# key is in the SSH agent, since it only requires the SSH key password, and not$
# the account password. However, when no authentication is needed (for example,$
# for public repos), HTTPS is simpler and safer, since SSH will always use the$
# private key.$
# This configures SSH to be used automatically for any Github and GitLab push,$
# and for any GitLab pull from my account (which manages some private repos).$
[url "git@github.com:"]$
^IpushInsteadOf = https://github.com/$
[url "ssh://git@gitlab.com/"]$
^IpushInsteadOf = https://gitlab.com/$
[url "ssh://git@gitlab.com/infokiller/"]$
^IinsteadOf = https://gitlab.com/infokiller/$
^I$
[advice]$
^IaddIgnoredFile = false$
$
[blacklist-hook]$
^I# One of error, warning, or skip.$
^Imode = error$
$
[secret-detection-hook]$
^I# One of error, warning, or skip.$
^Imode = error$
$
# NOTE: As of 2020-02-12, I disabled automatic PR fetching because it can$
# pollute the git references, which can add too much noise to some$
# autocompletions. If I need to fetch PRs I can use `git-fetch-prs`.$
# Automatically fetch pull and merge requests for Github and Gitlab. See also:$
# - https://github.com/tiimgreen/github-cheat-sheet#checking-out-pull-requests$
# - https://docs.gitlab.com/ee/user/project/merge_requests/#checkout-merge-requests-locally $
# [remote "origin"]$
# ^I# NOTE: As of 2020-02-12, I can't reproduce the activitywatch issue described$
# ^I# below..$
# ^I# Commented out because it causes the error "multiple updates for ref" when$
# ^I# building activitywatch. See also: https://apple.stackexchange.com/a/352666.$
# ^Ifetch = +refs/heads/*:refs/remotes/origin/*$
# ^Ifetch = +refs/pull/*/head:refs/remotes/origin/pr/*$
# ^Ifetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*$
$
# NOTE: As of 2020-02-12, this is disabled because it creates an invalid$
# upstream remote for every repo (it's invalid because there's no URL$
# specified).$
# [remote "upstream"]$
# ^I# See comment above.$
# ^I# fetch = +refs/heads/*:refs/remotes/origin/*$
# ^Ifetch = +refs/pull/*/head:refs/remotes/origin/pr/*$
# ^Ifetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*$
dandavison commented 4 years ago

OK, great, I didn't know there was a bug with tabs. This shouldn't be hard to fix (if it is not already fixed on master).

kbd commented 4 years ago

FWIW this is still present in 0.4.3. I can only reproduce with --diff-so-fancy.

Here are a couple screenshots to illustrate:

Screen Shot 2020-09-24 at 10 20 42 PM

Screen Shot 2020-09-24 at 10 20 15 PM