unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#37467: 26.3; whitespace-mode doesn't update BOB/EOB highlights
@ 2019-09-20  5:30 Dale Sedivec
  2019-10-09 13:42 ` bug#37467: info from whitespace mode author to help fix bug Christian Seberino
  2022-06-29 21:28 ` bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting Richard Hansen
  0 siblings, 2 replies; 9+ messages in thread
From: Dale Sedivec @ 2019-09-20  5:30 UTC (permalink / raw)
  To: 37467

whitespace-mode doesn't seem to un-highlight empty lines at BOB/EOB if you insert non-white-space characters in the middle of a run of blank lines.

Steps to reproduce:

1. emacs -Q

2. M-x whitespace-mode RET in the *scratch* buffer you start in.

3. Type C-2 RET C-2 C-p.  The bottom three lines of the buffer are now blank lines, point is on the penultimate line, and whitespace-mode has thus correctly highlighted the three blank lines at EOB with face whitespace-empty.

4. Type x.

To be clear, the buffer contents are now as follows, with point at "|":

~~~~~~
;; This buffer is for text that is not saved, and for Lisp evaluation.
;; To create a file, visit it with C-x C-f and enter text in its buffer.

x|

~~~~~~

Expected results: The bottom line is highlighted by whitespace-mode, no other lines are highlighted.

Observed results: The bottom line is correctly highlighted, and the highlighting is cleared from line 4 with the "x", but line 3 remains highlighted with face whitespace-empty.

There is a second bug too:

5. Type <backspace> to delete the x.

Expected results: The bottom lines 3–5 are now highlighted with whitespace-empty.

Observed results: Only the bottom two blank lines 4–5 are highlighted with whitespace-empty.  The adjacent blank line 3 is not highlighted.

Note that you can get line 3 correctly re-highlighted by moving to EOB then up one line (M-> C-p).

I believe you can reproduce this same behavior at the beginning of the buffer as well, using similar steps.

These bugs may seem obscure, but I hit them surprisingly often at EOB when programming: I enter a few blank lines at EOB as I prepare to append code to the buffer, but then stop and move upwards to make some changes, before returning to near-EOB and trying to enter some lines, at which point I am left with some incorrect whitespace-mode highlights.

My first guess at fixing this (only at EOB so far, not BOB) was to use font lock multiline:

* I set the font-lock-multiline property non-nil in whitespace-font-lock-keywords for whitespace-empty-at-eob-regexp.

* I make whitespace-mode install a new buffer-local function to font-lock-extend-region-functions to correctly identify the region for fontification at EOB.  (Note that (whitespace-looking-back whitespace-empty-at-eob-regexp nil) was handy here.)

However, this only solved the first problem, removing highlighting as desired at the end of step 4, above.  I have not yet been able to solve the problem of re-highlighting line 3 at the end of my step 5.  whitespace-post-command-hook is doing some work to determine what to refontify, and understanding of that function has thus far eluded me.  I suspect a solution for one or both of these bugs lies in there, however.

Thanks for your consideration!

Dale


In GNU Emacs 26.3 (build 1, x86_64-apple-darwin18.7.0, NS appkit-1671.60 Version 10.14.6 (Build 18G87))
of 2019-08-29 built on mojave.internal.macports.net
Windowing system distributor 'Apple', version 10.3.1671
Recent messages:
For information about GNU Emacs and the GNU system, type C-h C-a.

Configured using:
'configure --prefix=/opt/local --without-dbus --without-gconf
--without-libotf --without-m17n-flt --without-gpm --with-gnutls
--with-xml2 --with-modules --infodir /opt/local/share/info/emacs
--with-ns --with-lcms2 'CFLAGS=-pipe -Os
-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk
-arch x86_64' 'CPPFLAGS=-I/opt/local/include
-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk'
'LDFLAGS=-L/opt/local/lib -Wl,-headerpad_max_install_names -Wl,-no_pie
-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk
-arch x86_64''

Configured features:
JPEG NOTIFY ACL GNUTLS LIBXML2 ZLIB TOOLKIT_SCROLL_BARS NS MODULES
THREADS LCMS2

Important settings:
  value of $LC_COLLATE: C
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Fundamental

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  buffer-read-only: t
  line-number-mode: t
  transient-mark-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message rmc puny seq byte-opt gv
bytecomp byte-compile cconv cl-loaddefs cl-lib dired dired-loaddefs
format-spec rfc822 mml easymenu mml-sec password-cache epa derived epg
epg-config gnus-util rmail rmail-loaddefs mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047
rfc2045 ietf-drums mm-util mail-prsvr mail-utils elec-pair time-date
tooltip eldoc electric uniquify ediff-hook vc-hooks lisp-float-type
mwheel term/ns-win ns-win ucs-normalize mule-util term/common-win
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode elisp-mode lisp-mode prog-mode register page
menu-bar rfn-eshadow isearch timer select scroll-bar mouse jit-lock
font-lock syntax facemenu font-core term/tty-colors frame cl-generic
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite charscript charprop
case-table epa-hook jka-cmpr-hook help simple abbrev obarray minibuffer
cl-preloaded nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote threads kqueue cocoa ns
lcms2 multi-tty make-network-process emacs)

Memory information:
((conses 16 203911 12801)
(symbols 48 20094 2)
(miscs 40 70 181)
(strings 32 28190 1477)
(string-bytes 1 764094)
(vectors 16 35016)
(vector-slots 8 723944 9708)
(floats 8 48 69)
(intervals 56 236 0)
(buffers 992 12))





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#37467: info from whitespace mode author to help fix bug
  2019-09-20  5:30 bug#37467: 26.3; whitespace-mode doesn't update BOB/EOB highlights Dale Sedivec
@ 2019-10-09 13:42 ` Christian Seberino
  2022-06-29 21:28 ` bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting Richard Hansen
  1 sibling, 0 replies; 9+ messages in thread
From: Christian Seberino @ 2019-10-09 13:42 UTC (permalink / raw)
  To: 37467

[-- Attachment #1: Type: text/plain, Size: 816 bytes --]

(Basically the EmacsWiki version does NOT show this bug but follow on
changes added this bug.)

From Vinicius Latorre.....

---------------------

I've just tested the steps in bug report #37467, my results:

1. Using whitespace-mode distributed with Emacs 27.0.50, the bug happens.

2. Using whitespace-mode in EmacsWiki, the bug does not happen.

I didn't see in deep to verify exactly what is going on.

Vinicius

PS1: The whitespace.el file distributed with Emacs has a lot of
modifications, probably it could start with whitespace.el in EmacsWiki and
adding one by one the modifications until reach the bug.

PS2: I saw that all save-match-data was removed from whitespace.el, this
could be a starting point, that is, add again save-match-data at original
point and check if the bug happens.

-----------------

[-- Attachment #2: Type: text/html, Size: 1199 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#42110: Whitespace Newline Face Doesn't Disappear
@ 2020-06-28 15:37 Robin via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2020-06-28 16:28 ` Basil L. Contovounesios
  0 siblings, 1 reply; 9+ messages in thread
From: Robin via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2020-06-28 15:37 UTC (permalink / raw)
  To: 42110

[-- Attachment #1: Type: text/plain, Size: 7145 bytes --]

From: Robin Choudhury <robin_choudhury1@protonmail.com>
To: bug-gnu-emacs@gnu.org
Subject: 26.3; Whitespace Newline Face Doesn't Disappear
-----

When typing at the bottom of a document, a newline is inserted. An
additional newline is then inserted. Upon insertion, a face higlights
the first newline's emptiness. Typing in the second newline should make
this face disappear. However, it remains.

The whitespace configuration (13.2.2):
(use-package whitespace
:ensure t
:init
(setq prelude-whitespace nil)
(setq whitespace-line-column 80)
;; Highlight trailing whitespace
(setq whitespace-style
;; There's a bug in empty where the face doesn't go away after
;; typing below the newline.
'(face trailing lines-tail indentation::space tab-mark empty))
:config
(global-whitespace-mode t))

-----

In GNU Emacs 26.3 (build 1, x86_64-apple-darwin19.5.0, NS appkit-1894.50 Version 10.15.5 (Build 19F101))
of 2020-06-27 built on jupiter.local
Windowing system distributor 'Apple', version 10.3.1894
Recent messages:
Checking for load-path shadows...done
Auto-saving...done
Saving file /Users/robin/*message*-20200628-083045...
Wrote /Users/robin/*message*-20200628-083045
Auto-saving...done
Quit [2 times]
Mark saved where search started [2 times]
Saving file /Users/robin/*message*-20200628-083045...
Wrote /Users/robin/*message*-20200628-083045
Mark saved where search started [2 times]

Configured using:
'configure --disable-dependency-tracking --disable-silent-rules
--enable-locallisppath=/usr/local/share/emacs/site-lisp
--infodir=/usr/local/Cellar/emacs-plus@26/26.3/share/info/emacs
--prefix=/usr/local/Cellar/emacs-plus@26/26.3 --with-xml2 --with-gnutls
--with-imagemagick --with-modules --with-rsvg --with-ns
--disable-ns-self-contained'

Configured features:
JPEG RSVG IMAGEMAGICK GLIB NOTIFY ACL GNUTLS LIBXML2 ZLIB
TOOLKIT_SCROLL_BARS NS MODULES THREADS LCMS2

Important settings:
value of $LANG: en_US.UTF-8
locale-coding-system: utf-8-unix

Major mode: Help

Minor modes in effect:
projectile-mode: t
doom-modeline-mode: t
helm-mode: t
helm--remap-mouse-mode: t
evil-leader-mode: t
global-magit-file-mode: t
diff-auto-refine-mode: t
magit-auto-revert-mode: t
global-git-commit-mode: t
async-bytecomp-package-mode: t
global-undo-tree-mode: t
undo-tree-mode: t
shell-dirtrack-mode: t
evil-mode: t
evil-local-mode: t
global-whitespace-mode: t
show-paren-mode: t
mode-icons-mode: t
global-hl-line-mode: t
global-hl-todo-mode: t
display-time-mode: t
global-linum-mode: t
linum-mode: t
override-global-mode: t
tooltip-mode: t
global-eldoc-mode: t
electric-indent-mode: t
mouse-wheel-mode: t
file-name-shadow-mode: t
global-font-lock-mode: t
font-lock-mode: t
auto-composition-mode: t
auto-encryption-mode: t
auto-compression-mode: t
buffer-read-only: t
column-number-mode: t
line-number-mode: t
transient-mark-mode: t

Load-path shadows:
None found.

Features:
(misearch multi-isearch pp shadow sort mail-extr emacsbug sendmail
winner helm-command helm-elisp helm-eval edebug helm-info face-remap
org-rmail org-mhe org-irc org-info org-gnus nnir gnus-sum gnus-group
gnus-undo gnus-start gnus-cloud nnimap nnmail mail-source tls gnutls
utf7 netrc nnoo parse-time gnus-spec gnus-int gnus-range gnus-win gnus
nnheader wid-edit org-docview doc-view jka-compr image-mode org-bibtex
bibtex org-bbdb org-w3m vc-git vc-bzr vc-src vc-sccs vc-svn vc-cvs
vc-rcs vc vc-dispatcher project ido projectile grep compile ibuf-ext
ibuffer ibuffer-loaddefs rainbow-delimiters elec-pair doom-modeline
doom-modeline-segments doom-modeline-env doom-modeline-core shrink-path
rx f s all-the-icons all-the-icons-faces data-material data-weathericons
data-octicons data-fileicons data-faicons data-alltheicons memoize
exec-path-from-shell diminish helm-mode helm-files helm-buffers
helm-occur helm-tags helm-locate helm-grep helm-regexp helm-utils
helm-help helm-types helm helm-source eieio-compat helm-multi-match
helm-lib org-bullets org-element avl-tree generator org org-macro
org-footnote org-pcomplete org-list org-faces org-entities noutline
outline org-version ob-emacs-lisp ob ob-tangle org-src ob-ref ob-lob
ob-table ob-keys ob-exp ob-comint ob-core ob-eval org-compat org-macs
org-loaddefs find-func cal-menu calendar cal-loaddefs evil-leader
evil-magit magit-submodule magit-obsolete magit-blame magit-stash
magit-reflog magit-bisect magit-push magit-pull magit-fetch magit-clone
magit-remote magit-commit magit-sequence magit-notes magit-worktree
magit-tag magit-merge magit-branch magit-reset magit-files magit-refs
magit-status magit magit-repos magit-apply magit-wip magit-log
which-func imenu magit-diff smerge-mode diff-mode magit-core
magit-autorevert autorevert filenotify magit-margin magit-transient
magit-process magit-mode git-commit transient magit-git magit-section
magit-utils crm log-edit message rmc puny dired dired-loaddefs
format-spec rfc822 mml mml-sec epa derived epg gnus-util rmail
rmail-loaddefs mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047
rfc2045 mm-util ietf-drums mail-prsvr mailabbrev mail-utils gmm-utils
mailheader pcvs-util add-log with-editor async-bytecomp async server
subr-x dash evil evil-keybindings evil-integration undo-tree diff
evil-maps evil-commands reveal flyspell ispell evil-jumps
evil-command-window evil-types evil-search evil-ex shell pcomplete
comint ansi-color evil-macros evil-repeat evil-states evil-core
evil-common windmove thingatpt rect evil-digraphs evil-vars ring edmacro
kmacro doom-oceanic-next-theme doom-themes doom-themes-base disp-table
whitespace paren time-date warnings mode-icons advice color hl-line
hl-todo cl-extra help-mode time linum use-package use-package-ensure
use-package-delight use-package-diminish use-package-bind-key bind-key
easy-mmode use-package-core finder-inf info package easymenu epg-config
url-handlers url-parse auth-source cl-seq eieio eieio-core cl-macs
eieio-loaddefs password-cache url-vars seq byte-opt gv bytecomp
byte-compile cconv cl-loaddefs cl-lib tooltip eldoc electric uniquify
ediff-hook vc-hooks lisp-float-type mwheel term/ns-win ns-win
ucs-normalize mule-util term/common-win tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode elisp-mode
lisp-mode prog-mode register page menu-bar rfn-eshadow isearch timer
select scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame cl-generic cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese composite charscript charprop case-table epa-hook jka-cmpr-hook
help simple abbrev obarray minibuffer cl-preloaded nadvice loaddefs
button faces cus-face macroexp files text-properties overlay sha1 md5
base64 format env code-pages mule custom widget hashtable-print-readable
backquote threads kqueue cocoa ns lcms2 multi-tty make-network-process
emacs)

Memory information:
((conses 16 738153 45480)
(symbols 48 51716 2)
(miscs 40 668 440)
(strings 32 190321 8761)
(string-bytes 1 5406976)
(vectors 16 84206)
(vector-slots 8 1287169 41462)
(floats 8 980 400)
(intervals 56 2014 1003)
(buffers 992 20))

[-- Attachment #2: Type: text/html, Size: 10208 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#42110: Whitespace Newline Face Doesn't Disappear
  2020-06-28 15:37 Robin via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2020-06-28 16:28 ` Basil L. Contovounesios
       [not found]   ` <6DSVEaDOI_Q9I_GH8R12lTvRDtbDQ2-4su3haR_jnJeCf6nQ_xCHD0aXRdfwezFBOxGGpwpnfIUdXT-J8_5K_3T9ntDxRQKjFuRfxn9Lkso=@protonmail.com>
  2020-06-28 18:08   ` Reuben Thomas via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 2 replies; 9+ messages in thread
From: Basil L. Contovounesios @ 2020-06-28 16:28 UTC (permalink / raw)
  To: 42110; +Cc: Robin, Reuben Thomas

found 37467 25.3
forcemerge 37467 42110
quit

> From: Robin Choudhury <robin_choudhury1@protonmail.com>
> To: bug-gnu-emacs@gnu.org
> Subject: 26.3; Whitespace Newline Face Doesn't Disappear
> -----
>
> When typing at the bottom of a document, a newline is inserted. An
> additional newline is then inserted. Upon insertion, a face higlights
> the first newline's emptiness. Typing in the second newline should make
> this face disappear. However, it remains.

Thanks, I can reproduce this in Emacs versions 25.3-28 inclusive, but
not in 24.5.  I think this is a duplicate of bug#37467, and sounds like
it might be related/due to bug#24745; CCing Reuben.

Some minor notes on your configuration:

> The whitespace configuration (13.2.2):
> (use-package whitespace
>   :ensure t
    ^^^^^^^^^
No need for this, lisp/whitespace.el is built-in.

>   :init
>   (setq prelude-whitespace nil)
>   (setq whitespace-line-column 80)
>   ;; Highlight trailing whitespace
>   (setq whitespace-style
>         ;; There's a bug in empty where the face doesn't go away after
>         ;; typing below the newline.
>         '(face trailing lines-tail indentation::space tab-mark empty))
>   :config
>   (global-whitespace-mode t))
                           ^^^
No well behaved mode function documents t as a valid argument for
enabling it.  Instead, you should write one of the following or
equivalent:

  (global-whitespace-mode)
  (global-whitespace-mode 1)

Here's a simpler reproduction recipe:

0. emacs -Q
1. C-o
2. (progn (setq whitespace-style '(face empty))
          (whitespace-mode))
3. C-x C-e
4. C-m C-m
5. asd

Expected: the yellow highlight on the empty line 6 disappears, since
line 6 is not at EOB any longer.

Observed: the empty line 6 is still highlighted with the
whitespace-empty face.

Thanks,

-- 
Basil





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#42110: Whitespace Newline Face Doesn't Disappear
       [not found]   ` <6DSVEaDOI_Q9I_GH8R12lTvRDtbDQ2-4su3haR_jnJeCf6nQ_xCHD0aXRdfwezFBOxGGpwpnfIUdXT-J8_5K_3T9ntDxRQKjFuRfxn9Lkso=@protonmail.com>
@ 2020-06-28 17:21     ` Basil L. Contovounesios
  0 siblings, 0 replies; 9+ messages in thread
From: Basil L. Contovounesios @ 2020-06-28 17:21 UTC (permalink / raw)
  To: Robin; +Cc: 42110

Robin <robin_choudhury1@protonmail.com> writes:

>> 0. emacs -Q
>> 1. C-o
>> 2. (progn (setq whitespace-style '(face empty))
>>           (whitespace-mode))
>> 3. C-x C-e
>> 4. C-m C-m
>> 5. asd
>
> I was able to reproduce it (screenshot attached).

Thanks for confirming.

[ In general, please keep the bug address <42110@debbugs.gnu.org> CCed
  (e.g. via "Reply to All") so that all relevant parts of the discussion
  are archived by the bug tracker.  Thanks.  ]

-- 
Basil





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#42110: Whitespace Newline Face Doesn't Disappear
  2020-06-28 16:28 ` Basil L. Contovounesios
       [not found]   ` <6DSVEaDOI_Q9I_GH8R12lTvRDtbDQ2-4su3haR_jnJeCf6nQ_xCHD0aXRdfwezFBOxGGpwpnfIUdXT-J8_5K_3T9ntDxRQKjFuRfxn9Lkso=@protonmail.com>
@ 2020-06-28 18:08   ` Reuben Thomas via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 0 replies; 9+ messages in thread
From: Reuben Thomas via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2020-06-28 18:08 UTC (permalink / raw)
  To: Basil L. Contovounesios; +Cc: Robin, 42110

[-- Attachment #1: Type: text/plain, Size: 1236 bytes --]

On Sun, 28 Jun 2020 at 17:28, Basil L. Contovounesios <contovob@tcd.ie>
wrote:

> found 37467 25.3
> forcemerge 37467 42110
> quit
>
> > From: Robin Choudhury <robin_choudhury1@protonmail.com>
> > To: bug-gnu-emacs@gnu.org
> > Subject: 26.3; Whitespace Newline Face Doesn't Disappear
> > -----
> >
> > When typing at the bottom of a document, a newline is inserted. An
> > additional newline is then inserted. Upon insertion, a face higlights
> > the first newline's emptiness. Typing in the second newline should make
> > this face disappear. However, it remains.
>
> Thanks, I can reproduce this in Emacs versions 25.3-28 inclusive, but
> not in 24.5.  I think this is a duplicate of bug#37467, and sounds like
> it might be related/due to bug#24745; CCing Reuben.
>

Sorry, I had a look at this and can see how it is related to the bug I
fixed, but can't see how, before the fix, the unhighlighting would have
happened, i.e. how space highlighted at the end of the document would have
been unhighlighted after inserting characters at EOB.

I suppose that would be the logic in the function
whitespace-empty-at-eob-regexp? I don't understand how regions are
un-highlighted in this mode/in font-lock, though.

-- 
https://rrt.sc3d.org

[-- Attachment #2: Type: text/html, Size: 2200 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting
  2019-09-20  5:30 bug#37467: 26.3; whitespace-mode doesn't update BOB/EOB highlights Dale Sedivec
  2019-10-09 13:42 ` bug#37467: info from whitespace mode author to help fix bug Christian Seberino
@ 2022-06-29 21:28 ` Richard Hansen
  2022-07-17  4:49   ` bug#37467: Patches to fix Emacs whitespace-mode " Richard Hansen
  2022-09-11 11:46   ` bug#42110: Whitespace Newline Face Doesn't Disappear Lars Ingebrigtsen
  1 sibling, 2 replies; 9+ messages in thread
From: Richard Hansen @ 2022-06-29 21:28 UTC (permalink / raw)
  To: 37467


[-- Attachment #1.1.1: Type: text/plain, Size: 2198 bytes --]

The attached patch series should fix this bug.

Patch 1:

     ert-x: New `ert-with-test-buffer-selected' convenience macro

     * lisp/emacs-lisp/ert-x.el (ert-with-test-buffer-selected): New
     convenience macro that extends `ert-with-test-buffer' by displaying
     the test buffer in a temporary selected window.  This makes it easier
     to simulate user input in the body via `execute-kbd-macro'.
     * test/lisp/emacs-lisp/ert-x-tests.el
     (ert-test-test-buffer-selected/*): Add tests.

Patch 2:

     ; whitespace: Delete unused `whitespace-font-lock-refontify' var

Patch 3:

     ; whitespace: Use `defvar-local' for buffer-local vars

Patch 4:

     ; whitespace: Fix indentation

Patch 5:

     whitespace: Reset `whitespace-buffer-changed' when refontifying

     * lisp/whitespace.el (whitespace-post-command-hook): Add missing reset
     of `whitespace-buffer-changed' back to nil between commands.

Patch 6:

     whitespace: Include empty final line in BoB empty match

     * lisp/whitespace.el (whitespace-empty-at-bob-regexp): Include any
     last line trailing whitespace in the BoB empty line match to ensure
     that those characters get highlighted.

Patch 7:

     whitespace: Redo BoB/EoB empty line highlighting

     * lisp/whitespace.el (whitespace--empty-at-bob-matcher,
     whitespace--empty-at-eob-matcher, whitespace--update-bob-eob,
     whitespace-color-off, whitespace-color-on,
     whitespace-empty-at-bob-regexp, whitespace-empty-at-eob-regexp,
     whitespace-looking-back, whitespace-post-command-hook): Redo the
     `empty' line highlighting logic to ensure that a buffer change causes
     all affected `empty' lines to become (un)highlighted (bug#37467).
     Also, for improved UX, don't highlight BoB empty lines at or below
     point (not just when point is at 1), or EoB empty lines at or above
     point (not just when point is `eobp').
     (whitespace-bob-marker, whitespace-eob-marker): Clarify documentation.
     * test/lisp/whitespace-tests.el (whitespace--with-test-buffer,
     whitespace--fu, whitespace-tests--empty-bob,
     whitespace-tests--empty-eob): Add tests.

[-- Attachment #1.1.2: 0001-ert-x-New-ert-with-test-buffer-selected-convenience-.patch --]
[-- Type: text/x-patch, Size: 3621 bytes --]

From 26a45057408c8b4217339ff1f8e5abae5c114903 Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Tue, 28 Jun 2022 01:10:48 -0400
Subject: [PATCH 1/7] ert-x: New `ert-with-test-buffer-selected' convenience
 macro

* lisp/emacs-lisp/ert-x.el (ert-with-test-buffer-selected): New
convenience macro that extends `ert-with-test-buffer' by displaying
the test buffer in a temporary selected window.  This makes it easier
to simulate user input in the body via `execute-kbd-macro'.
* test/lisp/emacs-lisp/ert-x-tests.el
(ert-test-test-buffer-selected/*): Add tests.
---
 lisp/emacs-lisp/ert-x.el            | 29 +++++++++++++++++++++++++++++
 test/lisp/emacs-lisp/ert-x-tests.el | 15 +++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
index de18adff5b..8b2f89a69e 100644
--- a/lisp/emacs-lisp/ert-x.el
+++ b/lisp/emacs-lisp/ert-x.el
@@ -102,6 +102,35 @@ ert-with-test-buffer
            (indent 1))
   `(ert--call-with-test-buffer ,name-form (lambda () ,@body)))
 
+(cl-defmacro ert-with-test-buffer-selected ((&key name)
+                                            &body body)
+  "Create a test buffer, switch to it, and run BODY.
+
+This extends `ert-with-test-buffer' by displaying the test
+buffer (whose name is derived from NAME) in a temporary window.
+The temporary window becomes the `selected-window' before BODY is
+evaluated.  The modification hooks `before-change-functions' and
+`after-change-functions' are not inhibited during the evaluation
+of BODY, which makes it easier to use `execute-kbd-macro' to
+simulate user interaction.  The window configuration is restored
+before returning, even if BODY exits nonlocally.  The return
+value is the last form in BODY."
+  (declare (debug ((":name" form) def-body))
+           (indent 1))
+  (let ((ret (make-symbol "ert--with-test-buffer-selected-ret")))
+    `(save-window-excursion
+       (let (,ret)
+         (ert-with-test-buffer (:name ,name)
+           (with-current-buffer-window (current-buffer)
+               `(display-buffer-below-selected
+                 (body-function
+                  . ,(lambda (window)
+                       (select-window window t)
+                       (let ((inhibit-modification-hooks nil))
+                         (setq ,ret (progn ,@body))))))
+             nil))
+         ,ret))))
+
 ;;;###autoload
 (defun ert-kill-all-test-buffers ()
   "Kill all test buffers that are still live."
diff --git a/test/lisp/emacs-lisp/ert-x-tests.el b/test/lisp/emacs-lisp/ert-x-tests.el
index 3869804110..63e7cd7608 100644
--- a/test/lisp/emacs-lisp/ert-x-tests.el
+++ b/test/lisp/emacs-lisp/ert-x-tests.el
@@ -82,6 +82,21 @@ ert-test-test-buffers
         (should-not (buffer-live-p buffer-1))
         (should (buffer-live-p buffer-2))))))
 
+(ert-deftest ert-test-with-test-buffer-selected/selected ()
+  (ert-with-test-buffer-selected ()
+    (should (eq (window-buffer) (current-buffer)))))
+
+(ert-deftest ert-test-with-test-buffer-selected/modification-hooks ()
+  (ert-with-test-buffer-selected ()
+    (should (null inhibit-modification-hooks))))
+
+(ert-deftest ert-test-with-test-buffer-selected/return-value ()
+  (should (equal (ert-with-test-buffer-selected () "foo") "foo")))
+
+(ert-deftest ert-test-with-test-buffer-selected/buffer-name ()
+  (should (equal (ert-with-test-buffer (:name "foo") (buffer-name))
+                 (ert-with-test-buffer-selected (:name "foo")
+                   (buffer-name)))))
 
 (ert-deftest ert-filter-string ()
   (should (equal (ert-filter-string "foo bar baz" "quux")
-- 
2.36.1


[-- Attachment #1.1.3: 0002-whitespace-Delete-unused-whitespace-font-lock-refont.patch --]
[-- Type: text/x-patch, Size: 1312 bytes --]

From 477ab0595fa0815437747a8b6383adffa118d761 Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Wed, 22 Jun 2022 18:06:49 -0400
Subject: [PATCH 2/7] ; whitespace: Delete unused
 `whitespace-font-lock-refontify' var

---
 lisp/whitespace.el | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index 98f21ce9a5..e598418709 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -1138,10 +1138,6 @@ whitespace-point
 (defvar-local whitespace-point--used nil
   "Region whose highlighting depends on `whitespace-point'.")
 
-(defvar whitespace-font-lock-refontify nil
-  "Used to save locally the font-lock refontify state.
-Used by function `whitespace-post-command-hook' (which see).")
-
 (defvar whitespace-bob-marker nil
   "Used to save locally the bob marker value.
 Used by function `whitespace-post-command-hook' (which see).")
@@ -2061,7 +2057,6 @@ whitespace-color-on
     (setq whitespace-point--used
           (let ((ol (make-overlay (point) (point) nil nil t)))
             (delete-overlay ol) ol))
-    (setq-local whitespace-font-lock-refontify 0)
     (setq-local whitespace-bob-marker (point-min-marker))
     (setq-local whitespace-eob-marker (point-max-marker))
     (setq-local whitespace-buffer-changed nil)
-- 
2.36.1


[-- Attachment #1.1.4: 0003-whitespace-Use-defvar-local-for-buffer-local-vars.patch --]
[-- Type: text/x-patch, Size: 2260 bytes --]

From 18a88d376220855c32f9c6c19fd731c98b841bec Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Wed, 22 Jun 2022 19:02:42 -0400
Subject: [PATCH 3/7] ; whitespace: Use `defvar-local' for buffer-local vars

---
 lisp/whitespace.el | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index e598418709..e2285f7b82 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -1129,24 +1129,24 @@ whitespace-toggle-option-alist
 	See `whitespace-style-value-list'.")
 
 
-(defvar whitespace-active-style nil
+(defvar-local whitespace-active-style nil
   "Used to save locally `whitespace-style' value.")
 
-(defvar whitespace-point (point)
+(defvar-local whitespace-point (point)
   "Used to save locally current point value.
 Used by function `whitespace-trailing-regexp' (which see).")
 (defvar-local whitespace-point--used nil
   "Region whose highlighting depends on `whitespace-point'.")
 
-(defvar whitespace-bob-marker nil
+(defvar-local whitespace-bob-marker nil
   "Used to save locally the bob marker value.
 Used by function `whitespace-post-command-hook' (which see).")
 
-(defvar whitespace-eob-marker nil
+(defvar-local whitespace-eob-marker nil
   "Used to save locally the eob marker value.
 Used by function `whitespace-post-command-hook' (which see).")
 
-(defvar whitespace-buffer-changed nil
+(defvar-local whitespace-buffer-changed nil
   "Used to indicate locally if buffer changed.
 Used by `whitespace-post-command-hook' and `whitespace-buffer-changed'
 functions (which see).")
@@ -1766,7 +1766,7 @@ whitespace-report-region
 ;;;; Internal functions
 
 
-(defvar whitespace-font-lock-keywords nil
+(defvar-local whitespace-font-lock-keywords nil
   "Used to save the value `whitespace-color-on' adds to `font-lock-keywords'.")
 
 
@@ -1993,10 +1993,10 @@ whitespace-toggle-list
   the-list)
 
 
-(defvar whitespace-display-table nil
+(defvar-local whitespace-display-table nil
   "Used to save a local display table.")
 
-(defvar whitespace-display-table-was-local nil
+(defvar-local whitespace-display-table-was-local nil
   "Used to remember whether a buffer initially had a local display table.")
 
 (defun whitespace-turn-on ()
-- 
2.36.1


[-- Attachment #1.1.5: 0004-whitespace-Fix-indentation.patch --]
[-- Type: text/x-patch, Size: 945 bytes --]

From 56b1cc671bb940774c70dccd74e281cb626e121f Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Wed, 22 Jun 2022 19:03:16 -0400
Subject: [PATCH 4/7] ; whitespace: Fix indentation

---
 lisp/whitespace.el | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index e2285f7b82..5ea2f782a3 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -2399,8 +2399,8 @@ whitespace-display-char-on
       (unless whitespace-display-table-was-local
 	(setq whitespace-display-table-was-local t)
         (unless (or whitespace-mode global-whitespace-mode)
-	      (setq whitespace-display-table
-	      (copy-sequence buffer-display-table)))
+          (setq whitespace-display-table
+                (copy-sequence buffer-display-table)))
 	;; Assure `buffer-display-table' is unique
 	;; when two or more windows are visible.
 	(setq buffer-display-table
-- 
2.36.1


[-- Attachment #1.1.6: 0005-whitespace-Reset-whitespace-buffer-changed-when-refo.patch --]
[-- Type: text/x-patch, Size: 914 bytes --]

From 0912b013c95aad3fdbd28ed23da26d0d1beff0d1 Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Wed, 22 Jun 2022 17:29:39 -0400
Subject: [PATCH 5/7] whitespace: Reset `whitespace-buffer-changed' when
 refontifying

* lisp/whitespace.el (whitespace-post-command-hook): Add missing reset
of `whitespace-buffer-changed' back to nil between commands.
---
 lisp/whitespace.el | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index 5ea2f782a3..e75b85ffac 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -2292,6 +2292,7 @@ whitespace-post-command-hook
 Also refontify when necessary."
   (unless (and (eq whitespace-point (point))
                (not whitespace-buffer-changed))
+    (setq-local whitespace-buffer-changed nil)
     (setq whitespace-point (point))	; current point position
     (let ((refontify
            (cond
-- 
2.36.1


[-- Attachment #1.1.7: 0006-whitespace-Include-empty-final-line-in-BoB-empty-mat.patch --]
[-- Type: text/x-patch, Size: 968 bytes --]

From 9e7658ac8d90af2fe1ddcb5d5625db827d2d5560 Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Tue, 28 Jun 2022 15:05:04 -0400
Subject: [PATCH 6/7] whitespace: Include empty final line in BoB empty match

* lisp/whitespace.el (whitespace-empty-at-bob-regexp): Include any
last line trailing whitespace in the BoB empty line match to ensure
that those characters get highlighted.
---
 lisp/whitespace.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index e75b85ffac..fa212e6701 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -730,7 +730,7 @@ whitespace-indentation-regexp
   :group 'whitespace)
 
 
-(defcustom whitespace-empty-at-bob-regexp "\\`\\(\\([ \t]*\n\\)+\\)"
+(defcustom whitespace-empty-at-bob-regexp "\\`\\([ \t\n]*\\(?:\n\\|$\\)\\)"
   "Specify regexp for empty lines at beginning of buffer.
 
 Used when `whitespace-style' includes `empty'."
-- 
2.36.1


[-- Attachment #1.1.8: 0007-whitespace-Redo-BoB-EoB-empty-line-highlighting.patch --]
[-- Type: text/x-patch, Size: 26566 bytes --]

From e85931832da2a03fd72b8a2b88e14184e6d2c596 Mon Sep 17 00:00:00 2001
From: Richard Hansen <rhansen@rhansen.org>
Date: Tue, 28 Jun 2022 16:25:43 -0400
Subject: [PATCH 7/7] whitespace: Redo BoB/EoB empty line highlighting

* lisp/whitespace.el (whitespace--empty-at-bob-matcher,
whitespace--empty-at-eob-matcher, whitespace--update-bob-eob,
whitespace-color-off, whitespace-color-on,
whitespace-empty-at-bob-regexp, whitespace-empty-at-eob-regexp,
whitespace-looking-back, whitespace-post-command-hook): Redo the
`empty' line highlighting logic to ensure that a buffer change causes
all affected `empty' lines to become (un)highlighted (bug#37467).
Also, for improved UX, don't highlight BoB empty lines at or below
point (not just when point is at 1), or EoB empty lines at or above
point (not just when point is `eobp').
(whitespace-bob-marker, whitespace-eob-marker): Clarify documentation.
* test/lisp/whitespace-tests.el (whitespace--with-test-buffer,
whitespace--fu, whitespace-tests--empty-bob,
whitespace-tests--empty-eob): Add tests.
---
 lisp/whitespace.el            | 253 +++++++++++++++++++++-------------
 test/lisp/whitespace-tests.el | 230 +++++++++++++++++++++++++++++++
 2 files changed, 384 insertions(+), 99 deletions(-)

diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index fa212e6701..cc26e73515 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -1139,12 +1139,21 @@ whitespace-point--used
   "Region whose highlighting depends on `whitespace-point'.")
 
 (defvar-local whitespace-bob-marker nil
-  "Used to save locally the bob marker value.
-Used by function `whitespace-post-command-hook' (which see).")
+  "Position of the buffer's first non-empty line.
+This marker is positioned at the beginning of the first line in
+the buffer that contains a non-space character.  If no such line
+exists, this is positioned at the end of the buffer (which could
+be after `whitespace-eob-marker' if the buffer contains nothing
+but empty lines).")
 
 (defvar-local whitespace-eob-marker nil
-  "Used to save locally the eob marker value.
-Used by function `whitespace-post-command-hook' (which see).")
+  "Position after the buffer's last non-empty line.
+This marker is positioned at the beginning of the first line
+immediately following the last line in the buffer that contains a
+non-space character.  If no such line exists, this is positioned
+at the beginning of the buffer (which could be before
+`whitespace-bob-marker' if the buffer contains nothing but empty
+lines).")
 
 (defvar-local whitespace-buffer-changed nil
   "Used to indicate locally if buffer changed.
@@ -2059,9 +2068,14 @@ whitespace-color-on
             (delete-overlay ol) ol))
     (setq-local whitespace-bob-marker (point-min-marker))
     (setq-local whitespace-eob-marker (point-max-marker))
+    (whitespace--update-bob-eob)
     (setq-local whitespace-buffer-changed nil)
     (add-hook 'post-command-hook #'whitespace-post-command-hook nil t)
     (add-hook 'before-change-functions #'whitespace-buffer-changed nil t)
+    (add-hook 'after-change-functions #'whitespace--update-bob-eob
+              ;; The -1 ensures that it runs before any
+              ;; `font-lock-mode' hook functions.
+              -1 t)
     ;; Add whitespace-mode color into font lock.
     (setq
      whitespace-font-lock-keywords
@@ -2114,11 +2128,11 @@ whitespace-color-on
            `((,whitespace-big-indent-regexp 1 'whitespace-big-indent t)))
        ,@(when (memq 'empty whitespace-active-style)
            ;; Show empty lines at beginning of buffer.
-           `((,#'whitespace-empty-at-bob-regexp
-              1 whitespace-empty t)
+           `((,#'whitespace--empty-at-bob-matcher
+              0 whitespace-empty t)
              ;; Show empty lines at end of buffer.
-             (,#'whitespace-empty-at-eob-regexp
-              1 whitespace-empty t)))
+             (,#'whitespace--empty-at-eob-matcher
+              0 whitespace-empty t)))
        ,@(when (or (memq 'space-after-tab whitespace-active-style)
                    (memq 'space-after-tab::tab whitespace-active-style)
                    (memq 'space-after-tab::space whitespace-active-style))
@@ -2153,6 +2167,8 @@ whitespace-color-off
   (when (whitespace-style-face-p)
     (remove-hook 'post-command-hook #'whitespace-post-command-hook t)
     (remove-hook 'before-change-functions #'whitespace-buffer-changed t)
+    (remove-hook 'after-change-functions #'whitespace--update-bob-eob
+                 t)
     (font-lock-remove-keywords nil whitespace-font-lock-keywords)
     (font-lock-flush)))
 
@@ -2201,115 +2217,83 @@ whitespace-lines-regexp
           (format ".\\{%d\\}" rem)))))
    limit t))
 
-(defun whitespace-empty-at-bob-regexp (limit)
-  "Match spaces at beginning of buffer (BOB) which do not contain point at BOB."
-  (let ((b (point))
-	r)
-    (cond
-     ;; at bob
-     ((= b 1)
-      (setq r (and (looking-at whitespace-empty-at-bob-regexp)
-                   (or (/= whitespace-point 1)
-                       (progn (whitespace-point--used (match-beginning 0)
-                                                      (match-end 0))
-                              nil))))
-      (set-marker whitespace-bob-marker (if r (match-end 1) b)))
-     ;; inside bob empty region
-     ((<= limit whitespace-bob-marker)
-      (setq r (looking-at whitespace-empty-at-bob-regexp))
-      (if r
-	  (when (< (match-end 1) limit)
-	    (set-marker whitespace-bob-marker (match-end 1)))
-	(set-marker whitespace-bob-marker b)))
-     ;; intersection with end of bob empty region
-     ((<= b whitespace-bob-marker)
-      (setq r (looking-at whitespace-empty-at-bob-regexp))
-      (set-marker whitespace-bob-marker (if r (match-end 1) b)))
-     ;; it is not inside bob empty region
-     (t
-      (setq r nil)))
-    ;; move to end of matching
-    (and r (goto-char (match-end 1)))
-    r))
+(defun whitespace--empty-at-bob-matcher (limit)
+  "Match empty/space-only lines at beginning of buffer (BoB).
+Match does not extend past position LIMIT.  For improved UX, the
+line containing `whitespace-point' and subsequent lines are
+excluded from the match.  (The idea is that the user might be
+about to start typing, and if they do, that line and any
+following empty lines will no longer be BoB empty lines.
+Highlighting those lines can be distracting.)"
+  (let ((p (point))
+        (e (min whitespace-bob-marker limit
+                ;; EoB marker will be before BoB marker if the buffer
+                ;; has nothing but empty lines.
+                whitespace-eob-marker
+                (save-excursion (goto-char whitespace-point)
+                                (line-beginning-position)))))
+    (when (= p 1)
+      ;; See the comment in `whitespace--update-bob-eob' for why this
+      ;; text property is added here.
+      (put-text-property 1 whitespace-bob-marker
+                         'font-lock-multiline t))
+    (when (< p e)
+      (set-match-data (list p e))
+      (goto-char e))))
 
-
-(defsubst whitespace-looking-back (regexp limit)
+(defsubst whitespace--looking-back (regexp)
   (save-excursion
-    (when (/= 0 (skip-chars-backward " \t\n" limit))
+    (when (/= 0 (skip-chars-backward " \t\n"))
       (unless (bolp)
 	(forward-line 1))
       (looking-at regexp))))
 
-
-(defun whitespace-empty-at-eob-regexp (limit)
-  "Match spaces at end of buffer which do not contain the point at end of \
-buffer."
-  (let ((b (point))
-	(e (1+ (buffer-size)))
-	r)
-    (cond
-     ;; at eob
-     ((= limit e)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (when (and r (= whitespace-point e))
-        (setq r nil)
-        (whitespace-point--used (match-beginning 0) (match-end 0)))
-      (if r
-	  (set-marker whitespace-eob-marker (match-beginning 1))
-	(set-marker whitespace-eob-marker limit)
-	(goto-char b)))			; return back to initial position
-     ;; inside eob empty region
-     ((>= b whitespace-eob-marker)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (if r
-	  (when (> (match-beginning 1) b)
-	    (set-marker whitespace-eob-marker (match-beginning 1)))
-	(set-marker whitespace-eob-marker limit)
-	(goto-char b)))			; return back to initial position
-     ;; intersection with beginning of eob empty region
-     ((>= limit whitespace-eob-marker)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (if r
-	  (set-marker whitespace-eob-marker (match-beginning 1))
-	(set-marker whitespace-eob-marker limit)
-	(goto-char b)))			; return back to initial position
-     ;; it is not inside eob empty region
-     (t
-      (setq r nil)))
-    r))
-
+(defun whitespace--empty-at-eob-matcher (limit)
+  "Match empty/space-only lines at end of buffer (EoB).
+Match does not extend past position LIMIT.  For improved UX, the
+line containing `whitespace-point' and preceding lines are
+excluded from the match.  (The idea is that the user might be
+about to start typing, and if they do, that line and previous
+empty lines will no longer be EoB empty lines.  Highlighting
+those lines can be distracting.)"
+  (when (= limit (1+ (buffer-size)))
+    ;; See the comment in `whitespace--update-bob-eob' for why this
+    ;; text property is added here.
+    (put-text-property whitespace-eob-marker limit
+                       'font-lock-multiline t))
+  (let ((b (max (point) whitespace-eob-marker
+                whitespace-bob-marker ; See comment in the bob func.
+                (save-excursion (goto-char whitespace-point)
+                                (forward-line 1)
+                                (point)))))
+    (when (< b limit)
+      (set-match-data (list b limit))
+      (goto-char limit))))
 
 (defun whitespace-buffer-changed (_beg _end)
   "Set `whitespace-buffer-changed' variable to t."
   (setq whitespace-buffer-changed t))
 
-
 (defun whitespace-post-command-hook ()
   "Save current point into `whitespace-point' variable.
 Also refontify when necessary."
   (unless (and (eq whitespace-point (point))
                (not whitespace-buffer-changed))
+    (when (and (not whitespace-buffer-changed)
+               (memq 'empty whitespace-active-style))
+      ;; No need to handle the `whitespace-buffer-changed' case here
+      ;; because that is taken care of by the `font-lock-multiline'
+      ;; text property.
+      (when (<= (min (point) whitespace-point) whitespace-bob-marker)
+        (font-lock-flush 1 whitespace-bob-marker))
+      (when (>= (max (point) whitespace-point) whitespace-eob-marker)
+        (font-lock-flush whitespace-eob-marker (1+ (buffer-size)))))
     (setq-local whitespace-buffer-changed nil)
     (setq whitespace-point (point))	; current point position
-    (let ((refontify
-           (cond
-            ;; It is at end of buffer (eob).
-            ((= whitespace-point (1+ (buffer-size)))
-             (when (whitespace-looking-back whitespace-empty-at-eob-regexp
-                                            nil)
-               (match-beginning 0)))
-            ;; It is at end of line ...
-            ((and (eolp)
-                  ;; ... with trailing SPACE or TAB
-                  (or (memq (preceding-char) '(?\s ?\t))))
-             (line-beginning-position))
-            ;; It is at beginning of buffer (bob).
-            ((and (= whitespace-point 1)
-                  (looking-at whitespace-empty-at-bob-regexp))
-             (match-end 0))))
+    (let ((refontify (and (eolp) ; It is at end of line ...
+                          ;; ... with trailing SPACE or TAB
+                          (or (memq (preceding-char) '(?\s ?\t)))
+                          (line-beginning-position)))
           (ostart (overlay-start whitespace-point--used)))
       (cond
        ((not refontify)
@@ -2363,6 +2347,77 @@ whitespace--variable-watcher
       (when whitespace-mode
         (font-lock-flush)))))
 
+(defun whitespace--update-bob-eob (&optional beg end &rest _)
+  "Update `whitespace-bob-marker' and `whitespace-eob-marker'.
+Also apply `font-lock-multiline' text property.  If BEG and END
+are non-nil, assume that only characters in that range have
+changed since the last call to this function (for optimization
+purposes)."
+  (when (memq 'empty whitespace-active-style)
+    ;; When a line is changed, `font-lock-mode' normally limits
+    ;; re-processing to only the changed line.  That behavior is
+    ;; problematic for highlighting `empty' lines because adding or
+    ;; deleting a character might affect lines before or after the
+    ;; change.  To address this, all `empty' lines are marked with a
+    ;; non-nil `font-lock-multiline' text property.  This forces
+    ;; `font-lock-mode' to re-process all of the lines whenever
+    ;; there's an edit within any one of them.
+    ;;
+    ;; The text property must be set on `empty' lines twice per
+    ;; relevant change:
+    ;;
+    ;;   1. Before the change.  This is necessary to ensure that
+    ;;      previously highlighted lines become un-highlighted if
+    ;;      necessary.  The text property must be added after the
+    ;;      previous `font-lock-mode' run (the run in reaction to the
+    ;;      previous change) because `font-lock-mode' clears the text
+    ;;      property when it runs.
+    ;;
+    ;;   2. After the change, but before `font-lock-mode' reacts to
+    ;;      the change.  This is necessary to ensure that new `empty'
+    ;;      lines become highlighted.
+    ;;
+    ;; This hook function is responsible for #2, while the
+    ;; `whitespace--empty-at-bob-matcher' and
+    ;; `whitespace--empty-at-eob-matcher' functions are responsible
+    ;; for #1.  (Those functions run after `font-lock-mode' clears the
+    ;; text property and before the next change.)
+    (save-excursion
+      (save-restriction
+        (widen)
+        (when (or (null beg)
+                  (<= beg (save-excursion
+                            (goto-char whitespace-bob-marker)
+                            ;; Any change in the first non-`empty'
+                            ;; line, even if it's not the first
+                            ;; character in the line, can potentially
+                            ;; cause subsequent lines to become
+                            ;; classified as `empty' (e.g., delete the
+                            ;; "x" from " x").
+                            (forward-line 1)
+                            (point))))
+          (goto-char 1)
+          (set-marker whitespace-bob-marker (point))
+          (save-match-data
+            (when (looking-at whitespace-empty-at-bob-regexp)
+              (set-marker whitespace-bob-marker (match-end 1))
+              (put-text-property (match-beginning 1) (match-end 1)
+                                 'font-lock-multiline t))))
+        (when (or (null end)
+                  (>= end (save-excursion
+                            (goto-char whitespace-eob-marker)
+                            ;; See above comment for the BoB case.
+                            (forward-line -1)
+                            (point))))
+          (goto-char (1+ (buffer-size)))
+          (set-marker whitespace-eob-marker (point))
+          (save-match-data
+            (when (whitespace--looking-back
+                   whitespace-empty-at-eob-regexp)
+              (set-marker whitespace-eob-marker (match-beginning 1))
+              (put-text-property (match-beginning 1) (match-end 1)
+                                 'font-lock-multiline t))))))))
+
 \f
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;; Hacked from visws.el (Miles Bader <miles@gnu.org>)
diff --git a/test/lisp/whitespace-tests.el b/test/lisp/whitespace-tests.el
index 07eaad4862..bc59d22837 100644
--- a/test/lisp/whitespace-tests.el
+++ b/test/lisp/whitespace-tests.el
@@ -20,8 +20,35 @@
 ;;; Code:
 
 (require 'ert)
+(require 'ert-x)
+(require 'faceup)
 (require 'whitespace)
 
+(defmacro whitespace-tests--with-test-buffer (style &rest body)
+  "Run BODY in a buffer with `whitespace-mode' style STYLE.
+The buffer is displayed in `selected-window', and
+`noninteractive' is set to nil even in batch mode."
+  (declare (debug ((style form) def-body))
+           (indent 1))
+  `(ert-with-test-buffer-selected ()
+     ;; In case global-*-mode is enabled.
+     (whitespace-mode -1)
+     (font-lock-mode -1)
+     (let ((noninteractive nil)
+           (whitespace-style ,style))
+       (font-lock-mode 1)
+       (whitespace-mode 1)
+       ,@body)))
+
+(defun whitespace-tests--faceup (&rest lines)
+  "Convenience wrapper around `faceup-test-font-lock-buffer'.
+Returns non-nil if the concatenated LINES match the current
+buffer's content."
+  (faceup-test-font-lock-buffer nil (apply #'concat lines)))
+(let ((x (get 'faceup-test-font-lock-buffer 'ert-explainer)))
+  (put 'whitespace-tests--faceup 'ert-explainer
+       (lambda (&rest lines) (funcall x nil (apply #'concat lines)))))
+
 (defun whitespace-tests--cleanup-string (string)
   (with-temp-buffer
     (insert string)
@@ -81,6 +108,209 @@ whitespace-tests-display-tables
                             (whitespace-turn-off)
                             buffer-display-table))))))
 
+(ert-deftest whitespace-tests--empty-bob ()
+  (whitespace-tests--with-test-buffer '(face empty)
+    (electric-indent-mode -1)
+
+    ;; Insert some empty lines.  None of the lines should be
+    ;; highlighted even though point is on the last line because the
+    ;; entire buffer is empty lines.
+    (execute-kbd-macro (kbd "SPC RET C-q TAB RET RET SPC"))
+    (should (equal (buffer-string) " \n\t\n\n "))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " "))
+
+    ;; Adding content on the last line (and keeping point there)
+    ;; should cause the previous lines to be highlighted.  Note that
+    ;; the `whitespace-empty' face applies to the newline just before
+    ;; the last line, which has the desired property of extending the
+    ;; highlight the full width of the window.
+    (execute-kbd-macro (kbd "x"))
+    (should (equal (buffer-string) " \n\t\n\n x"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))
+
+    ;; Lines should become un-highlighted as point moves up into the
+    ;; empty lines.
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "»\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\t\n"
+                                      "\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<up> <home>"))
+    (should (equal (point) 1))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Line 1 should be un-highlighted when point is in line 1 even if
+    ;; point is not bobp.
+    (execute-kbd-macro (kbd "<right>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (> (point) 1))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Make sure lines become re-highlighted as point moves down.
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\t\n"
+                                      "\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "»\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))
+
+    ;; Inserting content on line 2 should un-highlight lines 2 and 3.
+    (execute-kbd-macro (kbd "<up> <up> <end>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (equal (- (point) (line-beginning-position)) 1))
+    (execute-kbd-macro (kbd "y <down> <down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\ty\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Removing the content on line 2 should re-highlight lines 2 and
+    ;; 3.
+    (execute-kbd-macro (kbd "<up> <up> <end>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (equal (- (point) (line-beginning-position)) 2))
+    (execute-kbd-macro (kbd "DEL <down> <down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))))
+
+(ert-deftest whitespace-tests--empty-eob ()
+  (whitespace-tests--with-test-buffer '(face empty)
+    (electric-indent-mode -1)
+
+    ;; Insert some empty lines.  None of the lines should be
+    ;; highlighted even though point is on line 1 because the entire
+    ;; buffer is empty lines.
+    (execute-kbd-macro (kbd "RET RET C-q TAB RET SPC C-<home>"))
+    (should (equal (buffer-string) "\n\n\t\n "))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; Adding content on the first line (and keeping point there)
+    ;; should cause the subsequent lines to be highlighted.
+    (execute-kbd-macro (kbd "x"))
+    (should (equal (buffer-string) "x\n\n\t\n "))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))
+
+    ;; Lines should become un-highlighted as point moves down into the
+    ;; empty lines.
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "«:whitespace-empty:\t\n"
+                                      " »"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      "«:whitespace-empty: »"))
+    (execute-kbd-macro (kbd "C-<end>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (eobp))
+    (should (equal (- (point) (line-beginning-position)) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; The last line should be un-highlighted when point is in that
+    ;; line even if point is not eobp.
+    (execute-kbd-macro (kbd "<left>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (not (eobp)))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; Make sure lines become re-highlighted as point moves up.
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      "«:whitespace-empty: »"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "«:whitespace-empty:\t\n"
+                                      " »"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))
+
+    ;; Inserting content on line 3 should un-highlight lines 2 and 3.
+    (execute-kbd-macro (kbd "<down> <down> <home>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (equal (- (point) (line-beginning-position)) 0))
+    (execute-kbd-macro (kbd "y <up> <up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "y\t\n"
+                                      "«:whitespace-empty: »"))
+
+    ;; Removing the content on line 3 should re-highlight lines 2 and
+    ;; 3.
+    (execute-kbd-macro (kbd "<down> <down> <home>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (equal (- (point) (line-beginning-position)) 0))
+    (execute-kbd-macro (kbd "<deletechar> <up> <up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))))
+
 (provide 'whitespace-tests)
 
 ;;; whitespace-tests.el ends here
-- 
2.36.1


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* bug#37467: Patches to fix Emacs whitespace-mode BoB/EoB empty line highlighting
  2022-06-29 21:28 ` bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting Richard Hansen
@ 2022-07-17  4:49   ` Richard Hansen
  2022-09-11 11:46   ` bug#42110: Whitespace Newline Face Doesn't Disappear Lars Ingebrigtsen
  1 sibling, 0 replies; 9+ messages in thread
From: Richard Hansen @ 2022-07-17  4:49 UTC (permalink / raw)
  To: 37467; +Cc: contovob, cseberino, rrt, robin_choudhury1, dale


[-- Attachment #1.1: Type: text/plain, Size: 215 bytes --]

I recently posted some patches [1] that should fix this bug.  Would one of you kind individuals be willing to review and test them?

Thanks,
Richard

[1] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=37467#15

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#42110: Whitespace Newline Face Doesn't Disappear
  2022-06-29 21:28 ` bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting Richard Hansen
  2022-07-17  4:49   ` bug#37467: Patches to fix Emacs whitespace-mode " Richard Hansen
@ 2022-09-11 11:46   ` Lars Ingebrigtsen
  1 sibling, 0 replies; 9+ messages in thread
From: Lars Ingebrigtsen @ 2022-09-11 11:46 UTC (permalink / raw)
  To: Richard Hansen; +Cc: 37467, 42110

Richard Hansen <rhansen@rhansen.org> writes:

> The attached patch series should fix this bug.

Thanks.  I've now tested the patch series, and it seems to fix the
reported problems, so I've now pushed it to Emacs 29.





^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-09-11 11:46 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-20  5:30 bug#37467: 26.3; whitespace-mode doesn't update BOB/EOB highlights Dale Sedivec
2019-10-09 13:42 ` bug#37467: info from whitespace mode author to help fix bug Christian Seberino
2022-06-29 21:28 ` bug#37467: [PATCH] whitespace: Redo BoB/EoB empty line highlighting Richard Hansen
2022-07-17  4:49   ` bug#37467: Patches to fix Emacs whitespace-mode " Richard Hansen
2022-09-11 11:46   ` bug#42110: Whitespace Newline Face Doesn't Disappear Lars Ingebrigtsen
  -- strict thread matches above, loose matches on Subject: below --
2020-06-28 15:37 Robin via Bug reports for GNU Emacs, the Swiss army knife of text editors
2020-06-28 16:28 ` Basil L. Contovounesios
     [not found]   ` <6DSVEaDOI_Q9I_GH8R12lTvRDtbDQ2-4su3haR_jnJeCf6nQ_xCHD0aXRdfwezFBOxGGpwpnfIUdXT-J8_5K_3T9ntDxRQKjFuRfxn9Lkso=@protonmail.com>
2020-06-28 17:21     ` Basil L. Contovounesios
2020-06-28 18:08   ` Reuben Thomas via Bug reports for GNU Emacs, the Swiss army knife of text editors

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).