Subject: bug#51874: 27.2; [PATCH] `use-region-p' reports non-nil during simple `mouse-1' click

From: Drew Adams <[email-masked]>
Date: Mon, 15 Nov 2021 17:44:45 +0000

The bug is detailed in emacs-devel thread "Fix for `use-region-p'
problem":

https://lists.gnu.org/archive/html/emacs-devel/2021-11/msg00634.html

In any case, I include a description here.

`use-region-p' is designed to let code know that either (1) the active
region is nonempty or (2) the user has OK'd the use of an empty region
(non-nil option `use-empty-active-region').  It should return non-nil in
those cases only.  The problem is in #2: an empty active region that's
declared to be OK to use.

A user (and code) expects `use-region-p' to return non-nil to show that
the region is usable for the _next_ user action (in particular).  In
simple user terms, in this context (being able to act on the active
region) a click of `mouse-1' can be considered a single user action.
There's no intention, and little ability, for a user to act _between_
the down & up events of a click (as opposed to a drag).

If `use-empty-active-region' is non-nil then the active region is usable
even if empty.  The bug is that `use-region-p' returns non-nil as soon
as you use `down-mouse-1'.  If the up-button event, `mouse-1' happens at
the same location then the region is deactivated.  But in the interim, a
call to `use-region-p' returns non-nil.

Especially if such a call is reflected by something user-visible, the
effect for users can be annoyance or confusion, or both.  For example, I
have code that shows, in the mode-line, the state of the region.  When
active, the region size is shown there, highlighted.  Clicking `mouse-1'
produces a flash, momentarily showing the empty region as active.  It
should not - a mouse click should not make the empty region "usable"
during the click.  Doing that is a mistake.

This recipe (from [email-masked]) shows the problem.  Click `mouse-1'
to see that `use-region-p' reports non-nil during clicks.

(setq use-empty-active-region  t)

(setq my-timer
      (run-with-timer 0 0.05
                      (lambda()
                        (message "%s  REG=%s"
                                 (current-time-string)
                                 (use-region-p)))))

The fix I found is to have `use-region-p' not return non-nil for the
empty region if the last event is `down-mouse-1' or it satisfies
`mouse-movement-p'.

This is the fixed definition of `use-region-p' I came up with.

(defun use-region-p ()
  "Return non-nil if it is appropriate to act on the region.
This is used by commands that act specially on the region when
Transient Mark mode is enabled.

The return value is non-nil if the region is active and either of the
following conditions is true; otherwise, the return value is nil.
 * The region is nonempty.
 * Option `use-empty-active-region' is non-nil, and the last input did
   not use `mouse-1' to set or adjust the region.

For some commands, it might be appropriate to ignore the value of
`use-empty-active-region'; in that case, use `region-active-p'."
  (ignore-errors
    (and (region-active-p)
         (or (> (region-end) (region-beginning))
             (and use-empty-active-region
                  (not (eq 'down-mouse-1
                           (car-safe last-input-event)))
                  (not (mouse-movement-p
                         last-input-event)))))))

I'm no expert on events, mouse or otherwise, so if someone sees a better
implementation, great.  (I'm not sure the `ignore-errors' is needed, for
example.)

In GNU Emacs 27.2 (build 1, x86_64-w64-mingw32)
 of 2021-03-26 built on CIRROCUMULUS
Repository revision: deef5efafb70f4b171265b896505b92b6eef24e6
Repository branch: HEAD
Windowing system distributor 'Microsoft Corp.', version 10.0.19042
System Description: Microsoft Windows 10 Pro (v10.0.2009.19042.1288)

Recent messages:
For information about GNU Emacs and the GNU system, type C-h C-a.

Configured using:
 'configure --without-dbus --host=x86_64-w64-mingw32
 --without-compress-install 'CFLAGS=-O2 -static''

Configured features:
XPM JPEG TIFF GIF PNG RSVG SOUND NOTIFY W32NOTIFY ACL GNUTLS LIBXML2
HARFBUZZ ZLIB TOOLKIT_SCROLL_BARS MODULES THREADS JSON PDUMPER LCMS2 GMP

Important settings:
  value of $LANG: ENU
  locale-coding-system: cp1252

Major mode: Dired by name

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
  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 format-spec rfc822 mml
easymenu mml-sec password-cache epa derived epg epg-config gnus-util
rmail rmail-loaddefs text-property-search time-date subr-x seq byte-opt
gv bytecomp byte-compile cconv mm-decode mm-bodies mm-encode mail-parse
rfc2231 mailabbrev gmm-utils mailheader cl-loaddefs cl-lib sendmail
rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils dired
dired-loaddefs tooltip eldoc electric uniquify ediff-hook vc-hooks
lisp-float-type mwheel dos-w32 ls-lisp disp-table term/w32-win w32-win
w32-vars 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 tab-bar menu-bar rfn-eshadow isearch timer
select scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame minibuffer 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 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 w32notify w32 lcms2 multi-tty make-network-process
emacs)

Memory information:
((conses 16 104242 12660)
 (symbols 48 6312 2)
 (strings 32 17300 1548)
 (string-bytes 1 526872)
 (vectors 16 9318)
 (vector-slots 8 126062 13752)
 (floats 8 21 108)
 (intervals 56 20920 4857)
 (buffers 1000 12))