Subject: 28.2; erc-track: handle faces modified with erc-button-add-face

From: Nacho Barrientos <[email-masked]>
Date: Sat, 03 Dec 2022 12:28:08 +0100

Hi,

Some packages like erc-hl-nicks [0] use `erc-button-add-face' to add extra
faces to some strings, notably nicknames.

For instance, on a coloured current nickname mention for the nick
'nacho' (current nick), `erc-faces-in' would return:

λ> (erc-faces-in (buffer-substring 348 349))
((erc-hl-nicks-nick-nacho-face erc-current-nick-face))

instead of:

λ> (erc-faces-in (buffer-substring 323 324))
(erc-current-nick-face)

when `erc-hl-nicks-mode` is not enabled. Note the nesting.

This is problematic because `erc-track-modified-channels` does this
comparison:

...
(let ((faces (erc-faces-in (buffer-string))))
...
		   (not (catch 'found
			  (dolist (f faces)
			    (when (member f erc-track-faces-priority-list)
			      (throw 'found t))))))
...

failing to detect that `erc-current-nick-face' is indeed active and,
assuming that `erc-current-nick-face' is member of
`erc-track-faces-priority-list', therefore failing to add the
buffer modified flag to the modeline.

I'm not an expert in this package so perhaps erc-hl-nicks shouldn't be
doing this at all to add the extra faces to colour nicks. However, this
situation can be easily addressed from the erc-track side by flattening
the list that `erc-faces-in' returns as the attached patch (against
current master) suggests. Hope that the modifications that I've done to
the test help clarifying even more the issue.

With the patch applied, the example call to `erc-faces-in' would return:

λ> (erc-faces-in (buffer-substring 348 349))
(erc-hl-nicks-nick-nacho-face erc-current-nick-face)

and erc-track would work as expected when `erc-hl-nicks-mode` is
enabled.

Thanks.

[0] https://github.com/leathekd/erc-hl-nicks

>From d9573f9346e8af7be8d853503c0cbe10ec89d274 Mon Sep 17 00:00:00 2001
From: Nacho Barrientos <[email-masked]>
Date: Sat, 3 Dec 2022 13:35:00 +0100
Subject: [PATCH] ERC: Track: Handle face text properties that are lists

---
 lisp/erc/erc-track.el            | 2 +-
 test/lisp/erc/erc-track-tests.el | 8 ++++++--
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el
index ef9a8c243e9..4832926c4b2 100644
--- a/lisp/erc/erc-track.el
+++ b/lisp/erc/erc-track.el
@@ -847,7 +847,7 @@ erc-faces-in
       (and (setq cur (get-text-property i 'face str))
 	   (not (member cur faces))
 	   (push cur faces)))
-    faces))
+    (flatten-list faces)))
 
 ;;; Buffer switching
 
diff --git a/test/lisp/erc/erc-track-tests.el b/test/lisp/erc/erc-track-tests.el
index 475a436bb1d..1e0409e9df2 100644
--- a/test/lisp/erc/erc-track-tests.el
+++ b/test/lisp/erc/erc-track-tests.el
@@ -116,8 +116,12 @@ erc-track--erc-faces-in
     (put-text-property 3 (length str0) 'font-lock-face
                        '(bold erc-current-nick-face) str0)
     (put-text-property 3 (length str1) 'face
-                       '(bold erc-current-nick-face) str1)
+                       'bold str1)
     (should (erc-faces-in str0))
-    (should (erc-faces-in str1)) ))
+    (should (length= (erc-faces-in str0) 2))
+    (should (equal (erc-faces-in str0) '(bold erc-current-nick-face)))
+    (should (erc-faces-in str1))
+    (should (length= (erc-faces-in str1) 1))
+    (should (equal (erc-faces-in str1) '(bold))) ))
 
 ;;; erc-track-tests.el ends here
-- 
2.38.1

In GNU Emacs 28.2 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.34, cairo version 1.17.6)
 of 2022-09-12 built on frederik
Windowing system distributor 'The X.Org Foundation', version 11.0.12101004
System Description: Arch Linux

Configured using:
 'configure --with-x-toolkit=gtk3 --with-native-compilation
 --sysconfdir=/etc --prefix=/usr --libexecdir=/usr/lib
 --localstatedir=/var --with-cairo --with-harfbuzz --with-libsystemd
 --with-modules 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt
 -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -g
 -ffile-prefix-map=/build/emacs/src=/usr/src/debug -flto=auto'
 'LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -flto=auto''

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSYSTEMD LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND THREADS TIFF TOOLKIT_SCROLL_BARS
X11 XDBE XIM XPM GTK3 ZLIB

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

Major mode: Lisp Interaction

Minor modes in effect:
  paredit-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  erc-list-mode: t
  erc-menu-mode: t
  erc-ring-mode: t
  erc-pcomplete-mode: t
  erc-netsplit-mode: t
  recentf-mode: t
  goto-address-prog-mode: t
  erc-services-mode: t
  erc-autojoin-mode: t
  erc-networks-mode: t
  erc-track-mode: t
  erc-track-minor-mode: t
  erc-match-mode: t
  erc-spelling-mode: t
  erc-hl-nicks-mode: t
  erc-button-mode: t
  erc-fill-mode: t
  erc-stamp-mode: t
  erc-irccontrols-mode: t
  erc-noncommands-mode: t
  erc-move-to-prompt-mode: t
  erc-readonly-mode: t
  flyspell-mode: t
  display-time-mode: t
  engine-mode: t
  mu4e-column-faces-mode: t
  beginend-global-mode: t
  beginend-prog-mode: t
  outline-minor-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  editorconfig-mode: t
  hexl-follow-ascii: t
  csv-field-index-mode: t
  doom-modeline-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  all-the-icons-ivy-rich-mode: t
  ivy-rich-project-root-cache-mode: t
  ivy-rich-mode: t
  ivy-posframe-mode: t
  ivy-mode: t
  shell-dirtrack-mode: t
  whole-line-or-region-global-mode: t
  whole-line-or-region-local-mode: t
  delete-selection-mode: t
  global-whitespace-mode: t
  override-global-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  window-divider-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  auto-fill-function: yas--auto-fill
  transient-mark-mode: t
  hs-minor-mode: t

Load-path shadows:
/home/nacho/.emacs.d/elpa/transient-20221124.2341/transient hides /usr/share/emacs/28.2/lisp/transient

Features:
(shadow emacsbug cal-move transpose-frame paredit autoload pcmpl-unix
dired-subtree dired-hacks-utils mhtml-mode css-mode-expansions css-mode
html-mode-expansions sgml-mode facemenu org-duration misearch
multi-isearch cl-print sql mastodon-search calc-alg calc-ext calc-menu
calc calc-loaddefs rect calc-macs descr-text animate misc avy tabify man
shr-color projectile rg rg-info-hack rg-menu rg-ibuffer rg-result
wgrep-rg wgrep rg-history rg-header ibuf-ext ibuffer ibuffer-loaddefs
grep cus-edit cus-start dired-aux pkg-info epl pcase git-rebase
magit-patch magit-subtree magit-gitignore magit-ediff ediff ediff-merg
ediff-mult ediff-wind ediff-diff ediff-help ediff-init ediff-util
forge-list forge-commands forge-semi forge-bitbucket buck forge-gogs
gogs forge-gitea gtea forge-gitlab glab forge-github ghub-graphql treepy
gsexp ghub forge-notify forge-revnote forge-pullreq forge-issue
forge-topic yaml forge-post markdown-mode forge-repo forge forge-core
forge-db closql emacsql-sqlite emacsql emacsql-compiler expand-region
yaml-mode-expansions subword-mode-expansions text-mode-expansions
cc-mode-expansions the-org-mode-expansions ruby-mode-expansions
python-mode-expansions js-mode-expansions web-mode-expansions
er-basic-expansions expand-region-core expand-region-custom mailalias
view mastodon-notifications smiley gnus-cite qp mm-archive mail-extr
mastodon-profile mastodon-media mastodon-tl ts url-http url-gw url-cache
url-auth mastodon-auth mastodon-client plstore mastodon mastodon-toot
mastodon-iso persist mastodon-http magit-extras info-colors crux mwim
goto-chg undo-tree queue gnutls network-stream nsm secrets erc-list
erc-menu erc-ring erc-pcomplete erc-netsplit tramp-cache bug-reference
recentf tree-widget counsel swiper em-unix em-term term ehelp em-script
em-prompt em-hist em-pred em-glob em-cmpl em-basic em-banner em-alias
goto-addr erc-services erc-join erc-networks erc-track erc-match
erc-spelling erc-hl-nicks erc-button erc-fill erc-stamp erc-goodies erc
erc-backend erc-loaddefs cern-ldap cap-words superword subword ldap
net-utils quail flyspell ispell ol-eww ol-rmail ol-mhe ol-irc ol-info
ol-gnus nnselect gnus-search eieio-opt speedbar ezimage dframe
ol-docview doc-view jka-compr image-mode exif ol-bibtex ol-bbdb ol-w3m
ol-doi org-link-doi appt diary-lib diary-loaddefs notifications ox-gfm
ox-md ox-odt rng-loc rng-uri rng-parse rng-match rng-dt rng-util
rng-pttrn nxml-parse nxml-ns nxml-enc xmltok nxml-util ox-latex
ox-icalendar org-agenda ox-html table ox-ascii ox-publish ox org-element
org-tree-slide org-timer org-clock org-capture org-refile org-protocol
ielm bluetooth desktop-environment time exwm-systemtray xcb-systemtray
xcb-xembed exwm exwm-input xcb-keysyms xcb-xkb exwm-manage exwm-floating
xcb-cursor xcb-render exwm-layout exwm-workspace exwm-core xcb-ewmh
xcb-icccm xcb xcb-xproto xcb-types xcb-debug engine-mode eww xdg mm-url
pipewire pipewire-lib pipewire-cli pipewire-access eradio elfeed-show
elfeed-search vc-mtn vc-hg vc-git vc-bzr vc-src vc-sccs vc-svn vc-cvs
vc-rcs vc vc-dispatcher elfeed-csv elfeed elfeed-curl elfeed-log
elfeed-db elfeed-lib avl-tree url-queue xml-query mu4e-column-faces
inline mu4e mu4e-org mu4e-main mu4e-view gnus-art mm-uu mml2015 mm-view
mml-smime smime dig gnus-sum gnus-group gnus-undo gnus-start gnus-dbus
dbus gnus-cloud nnimap nnmail mail-source utf7 netrc nnoo gnus-spec
gnus-int gnus-range gnus-win gnus nnheader wid-edit mu4e-headers
mu4e-compose mu4e-draft mu4e-actions smtpmail sendmail mu4e-search
mu4e-lists mu4e-bookmarks mu4e-mark mu4e-message shr kinsoku svg xml
flow-fill mule-util hl-line mu4e-contacts mu4e-update mu4e-folders
mu4e-server mu4e-context mu4e-vars mu4e-helpers mu4e-config ido helpful
cc-langs trace edebug help-fns radix-tree elisp-refs beginend git-modes
gitignore-mode gitconfig-mode gitattributes-mode git-link orgit org ob
ob-tangle ob-ref ob-lob ob-table ob-exp org-macro org-footnote org-src
ob-comint org-pcomplete org-list org-faces org-entities
outline-minor-faces noutline outline org-version ob-emacs-lisp ob-core
ob-eval org-table oc-basic bibtex ol org-keys oc org-compat org-macs
cal-menu calendar cal-loaddefs magit-bookmark 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 magit-diff smerge-mode diff
diff-mode git-commit log-edit message rmc puny rfc822 mml mml-sec epa
derived epg rfc6068 epg-config gnus-util rmail rmail-loaddefs mm-decode
mm-bodies mm-encode mailabbrev gmm-utils pcvs-util add-log magit-core
magit-autorevert magit-margin magit-transient magit-process with-editor
magit-mode transient magit-git magit-base magit-section crm compat-27
compat-26 eshell-prompt-extras em-dirs esh-var esh-mode em-ls
eshell-bookmark bookmark eshell powerthesaurus request mailheader
autorevert filenotify mail-utils dom virtualenvwrapper
gitignore-templates editorconfig editorconfig-core
editorconfig-core-handle editorconfig-fnmatch nhexl-mode hexl sqlformat
reformatter jq-mode csv-mode sort web-mode disp-table sh-script
executable systemd conf-mode archive-rpm bindat archive-cpio arc-mode
archive-mode rpm-spec-mode yaml-mode json-mode json-snatcher js cc-mode
cc-fonts cc-guess cc-menus cc-styles cc-align go-dlv gud go-mode
find-file ffap etags fileloop generator xref rspec-mode python-mode
info-look advice org-loaddefs which-func hideshow hippie-exp
flymake-proc flymake project thingatpt ert pp ewoc debug backtrace
compile cc-cmds cc-engine cc-vars cc-defs ruby-mode smie
flycheck-package package-lint let-alist imenu finder lisp-mnt mail-parse
rfc2231 rfc2047 rfc2045 mm-util ietf-drums mail-prsvr flycheck find-func
rainbow-mode xterm-color doom-modeline doom-modeline-segments
doom-modeline-env doom-modeline-core shrink-path f f-shortdoc shortdoc
text-property-search dash compat compat-macs doom-themes-ext-visual-bell
face-remap doom-tokyo-night-theme doom-themes doom-themes-base
yasnippet-snippets yasnippet amx comp comp-cstr warnings s
all-the-icons-ivy-rich ivy-rich ivy-posframe posframe all-the-icons-ivy
ivy ivy-faces ivy-overlay colir color all-the-icons all-the-icons-faces
data-material data-weathericons data-octicons data-fileicons
data-faicons data-alltheicons disk-usage dired dired-loaddefs pinentry
tramp-cmds em-tramp esh-cmd esh-ext esh-opt esh-module esh-groups
esh-proc esh-io esh-arg esh-util tramp-sh tramp tramp-loaddefs trampver
tramp-integration files-x tramp-compat shell pcomplete comint ansi-color
ring parse-time iso8601 time-date ls-lisp format-spec
whole-line-or-region delsel cus-load whitespace cl-extra help-mode
use-package use-package-ensure use-package-delight use-package-diminish
use-package-bind-key bind-key easy-mmode use-package-core finder-inf
edmacro kmacro avoid server rx info package browse-url url url-proxy
url-privacy url-expand url-methods url-history url-cookie url-domsuf
url-util mailcap url-handlers url-parse auth-source cl-seq eieio
eieio-core cl-macs eieio-loaddefs password-cache json subr-x map
url-vars seq byte-opt gv bytecomp byte-compile cconv cl-loaddefs cl-lib
iso-transl tooltip eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax 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 emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help simple abbrev obarray
cl-preloaded nadvice button loaddefs faces cus-face macroexp files
window text-properties overlay sha1 md5 base64 format env code-pages
mule custom widget hashtable-print-readable backquote threads dbusbind
inotify lcms2 dynamic-setting system-font-setting font-render-setting
cairo move-toolbar gtk x-toolkit x multi-tty make-network-process
native-compile emacs)

Memory information:
((conses 16 3838378 613339)
 (symbols 48 118745 6)
 (strings 32 646898 68799)
 (string-bytes 1 35930634)
 (vectors 16 249201)
 (vector-slots 8 5285026 676668)
 (floats 8 6777 8859)
 (intervals 56 188731 21866)
 (buffers 992 211))

--
 bye
 Nacho
 http://cern.ch/nacho