3

In my ~/.vimrc, I have:

highlight TrailingWhitespace ctermbg=blue
autocmd BufReadPost * syn match TrailingWhitespace /\s\+\%#\@<!$/

This highlights trailing spaces, except when I am still typing at the end of the line. (I obtained the regex pattern from the Vim wiki).

What I want to do now is to change the trailing whitespace pattern only inside a gitcommit diff. Background:

  • When doing git commit --verbose, a diff will be shown at the bottom of the git commit template opened in Vim. The syntax file responsible for git commits is syntax/gitcommit.vim.

  • The first column of a diff is either + (i.e. line added), - (i.e. line deleted), or (i.e. no change). If a blank line is added, the diff for that line will show +. However, if a blank line is unchanged, the diff for that line will show . That is the problem. When the diff for an unchanged blank line shows in the first column, the syntax highlighting scheme above has a defect: it will highlight the as trailing whitespace.

    diff in gitcommit.vim

That is why I want to change trailing whitespace detection pattern for gitcommit diffs. I want to ignore the first column. To that end, I created a file ~/.vim/after/syntax/gitcommit.vim:

" HACK: disable the usual highlighting for trailing whitespace by using a regex
" pattern that never matches anything.
syn match TrailingWhitespace /.^/ containedin=gitcommitDiff

" Ignore the first column when finding trailing whitespace.
syn match diffTrailingWhitespace /^..\{-}\zs\s\+$/ containedin=gitcommitDiff
highlight diffTrailingWhitespace ctermbg=cyan

At this point, the vim configuration seems to work as intended: trailing whitespace is highlighted, and special rules are used for highlighting trailing whitespace in gitcommit diff. The problem is that this all looks like a hack. I wonder if there is a more straightforward, or less hacky way of achieving the same result. Could you provide a better solution?

3
  • This doesn't help with the git-diff problem, but have you considered using listchars and trail?
    – D. Ben Knoble
    Commented Jan 25, 2021 at 19:12
  • @D.BenKnoble The problem with using listchars is that I won't be able to use different colors for nbsp, space, tab, and trail.
    – Flux
    Commented Jan 26, 2021 at 12:04
  • 1
    You can remove a single highlight group with syn clear TrailingWhitespace if you want to avoid the "HACK" in your gitcommit.vim. Commented Jan 26, 2021 at 13:37

1 Answer 1

0

I have found an acceptable solution.

First, use the solution provided in the Vim wiki to highlight trailing whitespace. i.e. add to ~/.vimrc:

autocmd BufReadPost,InsertEnter * match TrailingWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match TrailingWhitespace /\s\+$/
highlight TrailingWhitespace ctermbg=blue

Then, handle gitcommit using special rules. To do so, override and extend the syntax defined by syntax/gitcommit.vim. Add to ~/.vimrc:

function! s:set_gitcommit_trailing_whitespace()
    " Stop the usual highlighting of trailing whitespace.
    highlight TrailingWhitespace NONE

    " gitcommit.vim only contains syntax for the first line of the commit
    " message (i.e. the "commit message summary") and the blank line following
    " the commit message summary. It does not have a syntax for the rest of the
    " commit message (i.e. the "commit message body"), so we need to add a 
    " syntax for it.
    "
    " Step 1: Override gitcommitBlank from gitcommit.vim.
    "         This is necessary for adding a syntax for the commit message body.
    syn match gitcommitBlank "^[^#]*$" contained
        \ nextgroup=mygitcommitBody
        \ skipnl
    " Step 2: Add syntax for the commit message's body.
    syn match mygitcommitBody "^[^#]*$" contained
        \ contains=@Spell
        \ nextgroup=mygitcommitBody
        \ skipempty

    " Highlight trailing whitespace in the commit message (i.e. commit message
    " summary + commit message body).
    syn match commitTrailingWhitespace /\zs\s\+\%#\@<!$/
        \ containedin=gitcommitSummary,mygitcommitBody
    highlight commitTrailingWhitespace ctermbg=blue

    " Highlight trailing whitespace in the diff. Ignores the first column.
    syn match diffTrailingWhitespace /^..\{-}\zs\s\+$/
        \ containedin=gitcommitDiff
    highlight diffTrailingWhitespace ctermbg=cyan
endfunction
autocmd Syntax gitcommit call s:set_gitcommit_trailing_whitespace()

Note 1: this solution depends on the syntax defined in syntax/gitcommit.vim. I have tested this using the "2019 Aug 29" version of gitcommit.vim that is bundled with Vim 8.1.2269 (the version of Vim I am currently using). In the future, this solution may break if there are changes to the syntax defined in gitcommit.vim. The current upstream for gitcommit.vim is vim-git.

Note 2: this solution inherits some of the weaknesses of gitcommit.vim. In particular, gitcommit.vim fails to highlight correctly when the user writes comments (i.e. lines starting with #) in the commit message area. This solution will similarly fail to highlight trailing whitespace correctly if the user writes comments in the commit message area.

1
  • This seems overly complex and an xyproblem? I believe the real need is to not commit files that have lines with trailing spaces: a .git/ hook can do that. And to easily find files that have them: grep " *$" somefiles* and to remove all of them in vim [or using sed -i] : :%s/ *$// (2 spaces in the regexes) Commented Mar 20 at 16:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.