GNU bug report logs - #79141
31.0.50; The tab-bar-format-align-right cache doesn't take face changes and per-frame faces into account

Previous Next

Package: emacs;

Reported by: Steven Allen <steven <at> stebalien.com>

Date: Fri, 1 Aug 2025 20:58:02 UTC

Severity: normal

Fixed in version 31.0.50

Done: Juri Linkov <juri <at> linkov.net>

To reply to this bug, email your comments to 79141 AT debbugs.gnu.org.
There is no need to reopen the bug first.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to juri <at> linkov.net, bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Fri, 01 Aug 2025 20:58:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Steven Allen <steven <at> stebalien.com>:
New bug report received and forwarded. Copy sent to juri <at> linkov.net, bug-gnu-emacs <at> gnu.org. (Fri, 01 Aug 2025 20:58:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Steven Allen <steven <at> stebalien.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50; The tab-bar-format-align-right cache doesn't take face
 changes and per-frame faces into account
Date: Fri, 01 Aug 2025 13:47:32 -0700
[Message part 1 (text/plain, inline)]
The the `tab-bar-format-align-right' width cache isn't updated when
faces change and doesn't take into account the fact that faces can have
different attributes on different frames.

To reproduce, launch "emacs -Q" then yank the following into the scratch
buffer:

    (setq global-mode-string
          (list "" "GNU  Emacs  is a version of Emacs...")
          tab-bar-format
          '(tab-bar-format-align-right tab-bar-format-global))
    (tab-bar-mode 1)

And evaluate the buffer (M-x eval-buffer RET).

Finally, increase the font size with `global-text-scale-adjust': C-x C-M-0 +++++

The tab-bar text will get pushed off the right side on Emacs master but
not in the latest release. See the attached screenshots.

Furthermore, different frames can have different text sizes. Personally,
I use this to scale each of my frames' text based on the monitor's
DPI. To reproduce, launch "emacs -Q", yank the following into the
scratch buffer, and evaluate it:

    (setq global-mode-string
          (list "" "GNU  Emacs  is a version of Emacs...")
          tab-bar-format
          '(tab-bar-format-align-right tab-bar-format-global))
    (tab-bar-mode 1)
    (set-face-attribute 'default (selected-frame) :height (* 2 (face-attribute 'default :height)))
    (make-frame)

The tab-bar text will be correctly aligned in the new frame but not in
the original frame.

The per-frame issue can be fixed by making the cache per-frame but
reacting to face changes is harder. Given that this is simply a cache,
I'd recommend reverting the change and living with the slight
performance hit.


In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, cairo version
 1.18.4) of 2025-07-31 built on Laptop
Repository revision: 58a3fcbfaf3eee92aec2cce069c0c87475d9d060
Repository branch: makepkg
Windowing system distributor 'The X.Org Foundation', version 11.0.12101018
System Description: Arch Linux

Configured using:
 'configure
 'CPPFLAGS=-I/run/user/1000/build/emacs-git/src/mps-git/build/include '
 'LDFLAGS=-L/run/user/1000/build/emacs-git/src/mps-git/build/lib -Wl,-O1
 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now
 -Wl,-z,pack-relative-relocs -flto=auto' --prefix=/usr --sysconfdir=/etc
 --libexecdir=/usr/lib --localstatedir=/var --mandir=/usr/share/man
 --with-gameuser=:games --with-modules --without-m17n-flt
 --without-selinux --without-pop --without-gconf --disable-gc-mark-trace
 --with-mps=yes --enable-link-time-optimization
 --with-native-compilation=yes --with-xinput2 --with-x-toolkit=no
 --without-toolkit-scroll-bars --without-xaw3d --without-gsettings
 --with-cairo-xcb --without-xft --with-sound=no --with-tree-sitter
 --without-gpm --without-compress-install
 '--program-transform-name=s/\([ec]tags\)/\1.emacs/'
 'CFLAGS=-march=native -mtune=native -O3 -pipe -fno-plt -fexceptions
 -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -fomit-frame-pointer
 -fno-math-errno -fno-trapping-math -fno-math-errno -fno-trapping-math
 -flto=auto''

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS HARFBUZZ JPEG LCMS2 LIBOTF
LIBSYSTEMD LIBXML2 MODULES MPS NATIVE_COMP NOTIFY INOTIFY OLDXMENU
PDUMPER PNG RSVG SECCOMP SQLITE3 THREADS TIFF TREE_SITTER WEBP X11 XDBE
XIM XINERAMA XINPUT2 XPM XRANDR ZLIB

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

Major mode: Elisp/l

Minor modes in effect:
  rainbow-mode: t
  windmove-mode: t
  rainbow-delimiters-mode: t
  global-atomic-chrome-edit-mode: t
  iwd-agent-mode: t
  i3bar-mode: t
  ednc-mode: t
  exwm-xsettings-mode: t
  exwm-background-mode: t
  exwm-systemtray-mode: t
  exwm-randr-mode: t
  exwm-wm-mode: t
  spacious-padding-mode: t
  ligature-mode: t
  stripspace-local-mode: t
  flymake-mode: t
  simple-auto-compile-mode: t
  save-place-mode: t
  savehist-mode: t
  global-org-modern-mode: t
  notmuch-bookmarks-mode: t
  goto-address-prog-mode: t
  eat-eshell-mode: t
  bug-reference-prog-mode: t
  magit-todos-mode: t
  nerd-icons-grep-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  server-mode: t
  llama-fontify-mode: t
  global-diff-hl-mode: t
  diff-hl-mode: t
  dired-async-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  ultra-scroll-mode: t
  pixel-scroll-precision-mode: t
  global-hl-todo-mode: t
  hl-todo-mode: t
  marginalia-mode: t
  nerd-icons-completion-mode: t
  global-jinx-mode: t
  jinx-mode: t
  visual-replace-global-mode: t
  vertico-multiform-mode: t
  vertico-mode: t
  corfu-popupinfo-mode: t
  global-corfu-mode: t
  corfu-mode: t
  isearch-mb-mode: t
  global-evil-surround-mode: t
  evil-surround-mode: t
  evil-goggles-mode: t
  recentf-mode: t
  editorconfig-mode: t
  page-break-mode: t
  global-auto-revert-mode: t
  minibuffer-depth-indicate-mode: t
  minibuffer-electric-default-mode: t
  evil-mode: t
  evil-local-mode: t
  desktop-environment-mode: t
  elpaca-use-package-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
  prettify-symbols-mode: t
  tab-bar-history-mode: t
  tab-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  minibuffer-regexp-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/steb/.config/emacs/elpaca/builds/package-lint-flymake/package-lint-flymake hides /home/steb/.config/emacs/elpaca/builds/package-lint/package-lint-flymake
/home/steb/.config/emacs/elpaca/builds/transient/transient hides /usr/share/emacs/31.0.50/lisp/transient
/home/steb/.config/emacs/elpaca/builds/org/ox hides /usr/share/emacs/31.0.50/lisp/org/ox
/home/steb/.config/emacs/elpaca/builds/org/ox-texinfo hides /usr/share/emacs/31.0.50/lisp/org/ox-texinfo
/home/steb/.config/emacs/elpaca/builds/org/ox-publish hides /usr/share/emacs/31.0.50/lisp/org/ox-publish
/home/steb/.config/emacs/elpaca/builds/org/ox-org hides /usr/share/emacs/31.0.50/lisp/org/ox-org
/home/steb/.config/emacs/elpaca/builds/org/ox-odt hides /usr/share/emacs/31.0.50/lisp/org/ox-odt
/home/steb/.config/emacs/elpaca/builds/org/ox-md hides /usr/share/emacs/31.0.50/lisp/org/ox-md
/home/steb/.config/emacs/elpaca/builds/org/ox-man hides /usr/share/emacs/31.0.50/lisp/org/ox-man
/home/steb/.config/emacs/elpaca/builds/org/ox-latex hides /usr/share/emacs/31.0.50/lisp/org/ox-latex
/home/steb/.config/emacs/elpaca/builds/org/ox-koma-letter hides /usr/share/emacs/31.0.50/lisp/org/ox-koma-letter
/home/steb/.config/emacs/elpaca/builds/org/ox-icalendar hides /usr/share/emacs/31.0.50/lisp/org/ox-icalendar
/home/steb/.config/emacs/elpaca/builds/org/ox-html hides /usr/share/emacs/31.0.50/lisp/org/ox-html
/home/steb/.config/emacs/elpaca/builds/org/ox-beamer hides /usr/share/emacs/31.0.50/lisp/org/ox-beamer
/home/steb/.config/emacs/elpaca/builds/org/ox-ascii hides /usr/share/emacs/31.0.50/lisp/org/ox-ascii
/home/steb/.config/emacs/elpaca/builds/org/org hides /usr/share/emacs/31.0.50/lisp/org/org
/home/steb/.config/emacs/elpaca/builds/org/org-version hides /usr/share/emacs/31.0.50/lisp/org/org-version
/home/steb/.config/emacs/elpaca/builds/org/org-timer hides /usr/share/emacs/31.0.50/lisp/org/org-timer
/home/steb/.config/emacs/elpaca/builds/org/org-tempo hides /usr/share/emacs/31.0.50/lisp/org/org-tempo
/home/steb/.config/emacs/elpaca/builds/org/org-table hides /usr/share/emacs/31.0.50/lisp/org/org-table
/home/steb/.config/emacs/elpaca/builds/org/org-src hides /usr/share/emacs/31.0.50/lisp/org/org-src
/home/steb/.config/emacs/elpaca/builds/org/org-refile hides /usr/share/emacs/31.0.50/lisp/org/org-refile
/home/steb/.config/emacs/elpaca/builds/org/org-protocol hides /usr/share/emacs/31.0.50/lisp/org/org-protocol
/home/steb/.config/emacs/elpaca/builds/org/org-plot hides /usr/share/emacs/31.0.50/lisp/org/org-plot
/home/steb/.config/emacs/elpaca/builds/org/org-persist hides /usr/share/emacs/31.0.50/lisp/org/org-persist
/home/steb/.config/emacs/elpaca/builds/org/org-pcomplete hides /usr/share/emacs/31.0.50/lisp/org/org-pcomplete
/home/steb/.config/emacs/elpaca/builds/org/org-num hides /usr/share/emacs/31.0.50/lisp/org/org-num
/home/steb/.config/emacs/elpaca/builds/org/org-mouse hides /usr/share/emacs/31.0.50/lisp/org/org-mouse
/home/steb/.config/emacs/elpaca/builds/org/org-mobile hides /usr/share/emacs/31.0.50/lisp/org/org-mobile
/home/steb/.config/emacs/elpaca/builds/org/org-macs hides /usr/share/emacs/31.0.50/lisp/org/org-macs
/home/steb/.config/emacs/elpaca/builds/org/org-macro hides /usr/share/emacs/31.0.50/lisp/org/org-macro
/home/steb/.config/emacs/elpaca/builds/org/org-loaddefs hides /usr/share/emacs/31.0.50/lisp/org/org-loaddefs
/home/steb/.config/emacs/elpaca/builds/org/org-list hides /usr/share/emacs/31.0.50/lisp/org/org-list
/home/steb/.config/emacs/elpaca/builds/org/org-lint hides /usr/share/emacs/31.0.50/lisp/org/org-lint
/home/steb/.config/emacs/elpaca/builds/org/org-keys hides /usr/share/emacs/31.0.50/lisp/org/org-keys
/home/steb/.config/emacs/elpaca/builds/org/org-inlinetask hides /usr/share/emacs/31.0.50/lisp/org/org-inlinetask
/home/steb/.config/emacs/elpaca/builds/org/org-indent hides /usr/share/emacs/31.0.50/lisp/org/org-indent
/home/steb/.config/emacs/elpaca/builds/org/org-id hides /usr/share/emacs/31.0.50/lisp/org/org-id
/home/steb/.config/emacs/elpaca/builds/org/org-habit hides /usr/share/emacs/31.0.50/lisp/org/org-habit
/home/steb/.config/emacs/elpaca/builds/org/org-goto hides /usr/share/emacs/31.0.50/lisp/org/org-goto
/home/steb/.config/emacs/elpaca/builds/org/org-footnote hides /usr/share/emacs/31.0.50/lisp/org/org-footnote
/home/steb/.config/emacs/elpaca/builds/org/org-fold hides /usr/share/emacs/31.0.50/lisp/org/org-fold
/home/steb/.config/emacs/elpaca/builds/org/org-fold-core hides /usr/share/emacs/31.0.50/lisp/org/org-fold-core
/home/steb/.config/emacs/elpaca/builds/org/org-feed hides /usr/share/emacs/31.0.50/lisp/org/org-feed
/home/steb/.config/emacs/elpaca/builds/org/org-faces hides /usr/share/emacs/31.0.50/lisp/org/org-faces
/home/steb/.config/emacs/elpaca/builds/org/org-entities hides /usr/share/emacs/31.0.50/lisp/org/org-entities
/home/steb/.config/emacs/elpaca/builds/org/org-element hides /usr/share/emacs/31.0.50/lisp/org/org-element
/home/steb/.config/emacs/elpaca/builds/org/org-element-ast hides /usr/share/emacs/31.0.50/lisp/org/org-element-ast
/home/steb/.config/emacs/elpaca/builds/org/org-duration hides /usr/share/emacs/31.0.50/lisp/org/org-duration
/home/steb/.config/emacs/elpaca/builds/org/org-datetree hides /usr/share/emacs/31.0.50/lisp/org/org-datetree
/home/steb/.config/emacs/elpaca/builds/org/org-cycle hides /usr/share/emacs/31.0.50/lisp/org/org-cycle
/home/steb/.config/emacs/elpaca/builds/org/org-ctags hides /usr/share/emacs/31.0.50/lisp/org/org-ctags
/home/steb/.config/emacs/elpaca/builds/org/org-crypt hides /usr/share/emacs/31.0.50/lisp/org/org-crypt
/home/steb/.config/emacs/elpaca/builds/org/org-compat hides /usr/share/emacs/31.0.50/lisp/org/org-compat
/home/steb/.config/emacs/elpaca/builds/org/org-colview hides /usr/share/emacs/31.0.50/lisp/org/org-colview
/home/steb/.config/emacs/elpaca/builds/org/org-clock hides /usr/share/emacs/31.0.50/lisp/org/org-clock
/home/steb/.config/emacs/elpaca/builds/org/org-capture hides /usr/share/emacs/31.0.50/lisp/org/org-capture
/home/steb/.config/emacs/elpaca/builds/org/org-attach hides /usr/share/emacs/31.0.50/lisp/org/org-attach
/home/steb/.config/emacs/elpaca/builds/org/org-attach-git hides /usr/share/emacs/31.0.50/lisp/org/org-attach-git
/home/steb/.config/emacs/elpaca/builds/org/org-archive hides /usr/share/emacs/31.0.50/lisp/org/org-archive
/home/steb/.config/emacs/elpaca/builds/org/org-agenda hides /usr/share/emacs/31.0.50/lisp/org/org-agenda
/home/steb/.config/emacs/elpaca/builds/org/ol hides /usr/share/emacs/31.0.50/lisp/org/ol
/home/steb/.config/emacs/elpaca/builds/org/ol-w3m hides /usr/share/emacs/31.0.50/lisp/org/ol-w3m
/home/steb/.config/emacs/elpaca/builds/org/ol-rmail hides /usr/share/emacs/31.0.50/lisp/org/ol-rmail
/home/steb/.config/emacs/elpaca/builds/org/ol-mhe hides /usr/share/emacs/31.0.50/lisp/org/ol-mhe
/home/steb/.config/emacs/elpaca/builds/org/ol-man hides /usr/share/emacs/31.0.50/lisp/org/ol-man
/home/steb/.config/emacs/elpaca/builds/org/ol-irc hides /usr/share/emacs/31.0.50/lisp/org/ol-irc
/home/steb/.config/emacs/elpaca/builds/org/ol-info hides /usr/share/emacs/31.0.50/lisp/org/ol-info
/home/steb/.config/emacs/elpaca/builds/org/ol-gnus hides /usr/share/emacs/31.0.50/lisp/org/ol-gnus
/home/steb/.config/emacs/elpaca/builds/org/ol-eww hides /usr/share/emacs/31.0.50/lisp/org/ol-eww
/home/steb/.config/emacs/elpaca/builds/org/ol-eshell hides /usr/share/emacs/31.0.50/lisp/org/ol-eshell
/home/steb/.config/emacs/elpaca/builds/org/ol-doi hides /usr/share/emacs/31.0.50/lisp/org/ol-doi
/home/steb/.config/emacs/elpaca/builds/org/ol-docview hides /usr/share/emacs/31.0.50/lisp/org/ol-docview
/home/steb/.config/emacs/elpaca/builds/org/ol-bibtex hides /usr/share/emacs/31.0.50/lisp/org/ol-bibtex
/home/steb/.config/emacs/elpaca/builds/org/ol-bbdb hides /usr/share/emacs/31.0.50/lisp/org/ol-bbdb
/home/steb/.config/emacs/elpaca/builds/org/oc hides /usr/share/emacs/31.0.50/lisp/org/oc
/home/steb/.config/emacs/elpaca/builds/org/oc-natbib hides /usr/share/emacs/31.0.50/lisp/org/oc-natbib
/home/steb/.config/emacs/elpaca/builds/org/oc-csl hides /usr/share/emacs/31.0.50/lisp/org/oc-csl
/home/steb/.config/emacs/elpaca/builds/org/oc-bibtex hides /usr/share/emacs/31.0.50/lisp/org/oc-bibtex
/home/steb/.config/emacs/elpaca/builds/org/oc-biblatex hides /usr/share/emacs/31.0.50/lisp/org/oc-biblatex
/home/steb/.config/emacs/elpaca/builds/org/oc-basic hides /usr/share/emacs/31.0.50/lisp/org/oc-basic
/home/steb/.config/emacs/elpaca/builds/org/ob hides /usr/share/emacs/31.0.50/lisp/org/ob
/home/steb/.config/emacs/elpaca/builds/org/ob-tangle hides /usr/share/emacs/31.0.50/lisp/org/ob-tangle
/home/steb/.config/emacs/elpaca/builds/org/ob-table hides /usr/share/emacs/31.0.50/lisp/org/ob-table
/home/steb/.config/emacs/elpaca/builds/org/ob-sqlite hides /usr/share/emacs/31.0.50/lisp/org/ob-sqlite
/home/steb/.config/emacs/elpaca/builds/org/ob-sql hides /usr/share/emacs/31.0.50/lisp/org/ob-sql
/home/steb/.config/emacs/elpaca/builds/org/ob-shell hides /usr/share/emacs/31.0.50/lisp/org/ob-shell
/home/steb/.config/emacs/elpaca/builds/org/ob-sed hides /usr/share/emacs/31.0.50/lisp/org/ob-sed
/home/steb/.config/emacs/elpaca/builds/org/ob-screen hides /usr/share/emacs/31.0.50/lisp/org/ob-screen
/home/steb/.config/emacs/elpaca/builds/org/ob-scheme hides /usr/share/emacs/31.0.50/lisp/org/ob-scheme
/home/steb/.config/emacs/elpaca/builds/org/ob-sass hides /usr/share/emacs/31.0.50/lisp/org/ob-sass
/home/steb/.config/emacs/elpaca/builds/org/ob-ruby hides /usr/share/emacs/31.0.50/lisp/org/ob-ruby
/home/steb/.config/emacs/elpaca/builds/org/ob-ref hides /usr/share/emacs/31.0.50/lisp/org/ob-ref
/home/steb/.config/emacs/elpaca/builds/org/ob-python hides /usr/share/emacs/31.0.50/lisp/org/ob-python
/home/steb/.config/emacs/elpaca/builds/org/ob-processing hides /usr/share/emacs/31.0.50/lisp/org/ob-processing
/home/steb/.config/emacs/elpaca/builds/org/ob-plantuml hides /usr/share/emacs/31.0.50/lisp/org/ob-plantuml
/home/steb/.config/emacs/elpaca/builds/org/ob-perl hides /usr/share/emacs/31.0.50/lisp/org/ob-perl
/home/steb/.config/emacs/elpaca/builds/org/ob-org hides /usr/share/emacs/31.0.50/lisp/org/ob-org
/home/steb/.config/emacs/elpaca/builds/org/ob-octave hides /usr/share/emacs/31.0.50/lisp/org/ob-octave
/home/steb/.config/emacs/elpaca/builds/org/ob-ocaml hides /usr/share/emacs/31.0.50/lisp/org/ob-ocaml
/home/steb/.config/emacs/elpaca/builds/org/ob-maxima hides /usr/share/emacs/31.0.50/lisp/org/ob-maxima
/home/steb/.config/emacs/elpaca/builds/org/ob-matlab hides /usr/share/emacs/31.0.50/lisp/org/ob-matlab
/home/steb/.config/emacs/elpaca/builds/org/ob-makefile hides /usr/share/emacs/31.0.50/lisp/org/ob-makefile
/home/steb/.config/emacs/elpaca/builds/org/ob-lua hides /usr/share/emacs/31.0.50/lisp/org/ob-lua
/home/steb/.config/emacs/elpaca/builds/org/ob-lob hides /usr/share/emacs/31.0.50/lisp/org/ob-lob
/home/steb/.config/emacs/elpaca/builds/org/ob-lisp hides /usr/share/emacs/31.0.50/lisp/org/ob-lisp
/home/steb/.config/emacs/elpaca/builds/org/ob-lilypond hides /usr/share/emacs/31.0.50/lisp/org/ob-lilypond
/home/steb/.config/emacs/elpaca/builds/org/ob-latex hides /usr/share/emacs/31.0.50/lisp/org/ob-latex
/home/steb/.config/emacs/elpaca/builds/org/ob-julia hides /usr/share/emacs/31.0.50/lisp/org/ob-julia
/home/steb/.config/emacs/elpaca/builds/org/ob-js hides /usr/share/emacs/31.0.50/lisp/org/ob-js
/home/steb/.config/emacs/elpaca/builds/org/ob-java hides /usr/share/emacs/31.0.50/lisp/org/ob-java
/home/steb/.config/emacs/elpaca/builds/org/ob-haskell hides /usr/share/emacs/31.0.50/lisp/org/ob-haskell
/home/steb/.config/emacs/elpaca/builds/org/ob-groovy hides /usr/share/emacs/31.0.50/lisp/org/ob-groovy
/home/steb/.config/emacs/elpaca/builds/org/ob-gnuplot hides /usr/share/emacs/31.0.50/lisp/org/ob-gnuplot
/home/steb/.config/emacs/elpaca/builds/org/ob-fortran hides /usr/share/emacs/31.0.50/lisp/org/ob-fortran
/home/steb/.config/emacs/elpaca/builds/org/ob-forth hides /usr/share/emacs/31.0.50/lisp/org/ob-forth
/home/steb/.config/emacs/elpaca/builds/org/ob-exp hides /usr/share/emacs/31.0.50/lisp/org/ob-exp
/home/steb/.config/emacs/elpaca/builds/org/ob-eval hides /usr/share/emacs/31.0.50/lisp/org/ob-eval
/home/steb/.config/emacs/elpaca/builds/org/ob-eshell hides /usr/share/emacs/31.0.50/lisp/org/ob-eshell
/home/steb/.config/emacs/elpaca/builds/org/ob-emacs-lisp hides /usr/share/emacs/31.0.50/lisp/org/ob-emacs-lisp
/home/steb/.config/emacs/elpaca/builds/org/ob-dot hides /usr/share/emacs/31.0.50/lisp/org/ob-dot
/home/steb/.config/emacs/elpaca/builds/org/ob-ditaa hides /usr/share/emacs/31.0.50/lisp/org/ob-ditaa
/home/steb/.config/emacs/elpaca/builds/org/ob-css hides /usr/share/emacs/31.0.50/lisp/org/ob-css
/home/steb/.config/emacs/elpaca/builds/org/ob-core hides /usr/share/emacs/31.0.50/lisp/org/ob-core
/home/steb/.config/emacs/elpaca/builds/org/ob-comint hides /usr/share/emacs/31.0.50/lisp/org/ob-comint
/home/steb/.config/emacs/elpaca/builds/org/ob-clojure hides /usr/share/emacs/31.0.50/lisp/org/ob-clojure
/home/steb/.config/emacs/elpaca/builds/org/ob-calc hides /usr/share/emacs/31.0.50/lisp/org/ob-calc
/home/steb/.config/emacs/elpaca/builds/org/ob-awk hides /usr/share/emacs/31.0.50/lisp/org/ob-awk
/home/steb/.config/emacs/elpaca/builds/org/ob-R hides /usr/share/emacs/31.0.50/lisp/org/ob-R
/home/steb/.config/emacs/elpaca/builds/org/ob-C hides /usr/share/emacs/31.0.50/lisp/org/ob-C

Features:
(shadow emacsbug checkdoc package-lint-flymake package-lint
evil-collection-finder finder finder-inf lisp-mnt elpaca-info
evil-collection-man man conf-mode consult-org info-colors dabbrev
hippie-exp evil-collection-helpful helpful cc-langs cc-vars cc-defs
trace evil-collection-edebug edebug info-look evil-collection-info info
evil-collection-elisp-refs elisp-refs display-line-numbers
bluetooth-battery evil-collection-bluetooth bluetooth bluetooth-plugin
bluetooth-uuid bluetooth-pa bluetooth-device bluetooth-lib app-launcher
rainbow-mode systemctl sgml-mode facemenu evil-collection-dictionary
dictionary dictionary-connection go-ts-mode shr-tag-pre-highlight
language-detection network-stream url-cache sort cape mail-extr
visual-wrap textsec uni-scripts idna-mapping ucs-normalize
uni-confusable textsec-check journalctl bash-completion capf-autosuggest
eshell-syntax-highlighting em-elecslash em-ls em-glob em-extpipe em-dirs
em-basic em-alias display-fill-column-indicator emacsql-sqlite-builtin
help-fns radix-tree tramp-cmds vc-hg vc-bzr vc-src vc-sccs vc-svn vc-cvs
vc-rcs consult-xref misearch multi-isearch windmove
evil-collection-eglot eglot external-completion jsonrpc
evil-collection-ert ert evil-collection-debug debug backtrace
evil-collection-vc-git vc-git rainbow-delimiters rust-ts-mode
c-ts-common tramp-archive tramp-gvfs evil-collection-xref xref
find-dired vertico-sort vertico-repeat evil-collection-consult consult
filechooser pinentry evil-collection-atomic-chrome atomic-chrome
websocket bindat iwd hex-util i3bar ednc exwm-xsettings xcb-xsettings
exwm-background exwm-systemtray xcb-systemtray xcb-xembed exwm-randr
xcb-randr 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 spacious-padding
visual-fill-column org-contacts org-capture evil-org-agenda org-agenda
gnus-art mm-uu mml2015 gnus-sum gnus-group gnus-undo gnus-start
gnus-dbus gnus-cloud nnimap nnmail mail-source utf7 nnoo gnus-spec
gnus-int gnus-range gnus-win ligature org-appear valign stripspace
flymake-languagetool evil-collection-flymake flymake oc-basic bibtex
ol-man ol-info ol-docview evil-collection-doc-view doc-view jka-compr
evil-collection-image image-mode exif tramp-cache time-stamp
simple-auto-compile saveplace savehist show-font org-protocol
org-bookmark-heading evil-org org-modern ob-dot ob-latex ob-python
evil-collection-python python ob-gnuplot ob-calc calc-store calc-trail
calc-ext evil-collection-calc calc calc-loaddefs calc-macs ob-shell
edraw-org edraw-util edraw-msg cl-print org-element org-persist org-id
org-refile avl-tree evil-collection-org org-crypt org org-macro
ob-plantuml ob ob-tangle ob-ref ob-lob ob-table ob-exp org-src
evil-collection-sh-script sh-script smie treesit executable ob-comint
org-element-ast inline org-pcomplete org-list org-footnote org-faces
org-entities ob-emacs-lisp ob-core ob-eval org-cycle org-table org-keys
oc notmuch-addr notmuch-bookmarks evil-collection-notmuch notmuch
notmuch-tree notmuch-jump notmuch-hello notmuch-show notmuch-print
notmuch-crypto notmuch-mua notmuch-message notmuch-draft
notmuch-maildir-fcc notmuch-address notmuch-company notmuch-parser
notmuch-wash coolj goto-addr icalendar diary-lib diary-loaddefs
evil-collection-calendar cal-menu calendar cal-loaddefs notmuch-tag
notmuch-lib notmuch-compat mm-view mml-smime smime dig
evil-collection-eat eat term/xterm xterm tramp-sh tramp trampver
tramp-integration tramp-message tramp-compat tramp-loaddefs
evil-collection-term term ehelp evil-collection-forge forge-repos
forge-tablist hl-line forge-topics forge-commands forge-semi
forge-bitbucket forge-gogs forge-gitea forge-gitlab forge-github
forge-client forge-forgejo forge-notify forge-revnote forge-pullreq
forge-issue forge-discussion forge-topic yaml parse-time iso8601
eieio-custom bug-reference magit-bookmark evil-collection-bookmark
bookmark forge-post evil-collection-markdown-mode markdown-mode
edit-indirect evil-collection-outline noutline outline forge-repo forge
forge-core ghub-legacy ghub-graphql treepy gsexp ghub url-http url-gw
nsm url-auth let-alist gnutls forge-db closql emacsql-sqlite emacsql
emacsql-compiler eieio-base evil-collection-magit-todos magit-todos
pcre2el rxt advice re-builder f s dash nerd-icons-grep
evil-collection-grep grep evil-collection-compile compile
evil-collection-magit magit-submodule magit-blame magit-stash
magit-reflog magit-bisect magit-push magit-pull magit-fetch magit-clone
magit-remote gptel-commit gptel-gh secrets dbus posframe
evil-collection-gptel gptel gptel-openai magit-commit magit-sequence
magit-notes magit-worktree magit-tag magit-merge magit-branch
magit-reset magit-files magit-refs magit-status magit
evil-collection-package-menu package url-handlers
evil-collection-magit-repos magit-repos magit-apply magit-wip magit-log
which-func evil-collection-imenu imenu magit-diff
evil-collection-smerge-mode smerge-mode diff git-commit magit-core
magit-autorevert magit-margin magit-transient magit-process
evil-collection-with-editor with-editor shell server magit-mode
transient evil-collection-eww ol-eww ol org-fold org-fold-core
org-compat org-version org-macs eww vtable mule-util url-queue shr
pixel-fill kinsoku url-file svg xml dom mm-url evil-collection-gnus gnus
nnheader range browse-url benchmark magit-git magit-base
evil-collection-magit-section magit-section format-spec cursor-sensor
crm llama evil-collection-diff-hl diff-hl evil-collection-log-view
log-view evil-collection-log-edit log-edit message sendmail yank-media
puny dired-async async dired-aux evil-collection-dired dired
dired-loaddefs rfc822 mml mml-sec evil-collection-epa epa epg rfc6068
epg-config gnus-util time-date mm-decode mm-bodies mm-encode mail-parse
rfc2231 rfc2047 rfc2045 mm-util ietf-drums mail-prsvr mailabbrev
mail-utils gmm-utils mailheader add-log pcvs-util evil-collection-vc-dir
vc-dir ewoc vc vc-dispatcher diff-mode track-changes yasnippet
evil-collection-ultra-scroll ultra-scroll pixel-scroll cua-base hl-todo
marginalia nerd-icons-corfu nerd-icons-completion nerd-icons
nerd-icons-faces nerd-icons-data nerd-icons-data-mdicon
nerd-icons-data-flicon nerd-icons-data-codicon nerd-icons-data-devicon
nerd-icons-data-sucicon nerd-icons-data-wicon nerd-icons-data-faicon
nerd-icons-data-powerline nerd-icons-data-octicon
nerd-icons-data-pomicon nerd-icons-data-ipsicon jinx visual-replace
evil-collection-which-key which-key vertico-multiform vertico
corfu-popupinfo evil-collection-corfu corfu orderless isearch-mb
evil-surround evil-goggles pulse face-remap color
evil-collection-tabulated-list evil-collection-tab-bar
evil-collection-simple evil-collection-replace
evil-collection-process-menu evil-collection-kmacro
evil-collection-indent evil-collection-help evil-collection-elisp-mode
evil-collection-eldoc evil-collection-elpaca evil-collection-custom
evil-collection-comint evil-collection-buff-menu evil-collection
annalist evil-args ednc-autoloads i3bar-autoloads discomfort-autoloads
debase-autoloads iwd-autoloads bluetooth-autoloads
app-launcher-autoloads atomic-chrome-autoloads websocket-autoloads
pinentry-autoloads playerctl-autoloads exwm-autoloads xelb-autoloads
ol-notmuch-autoloads microdata-autoloads notmuch-addr-autoloads
eshell-syntax-highlighting-autoloads eat-autoloads git-link-autoloads
pr-review-autoloads igist-autoloads magit-todos-autoloads
diff-hl-autoloads wgrep-autoloads watch-autoloads tmr-autoloads
syncthing-autoloads pcre2el-autoloads qrencode-autoloads
password-store-menu-autoloads password-store-autoloads
named-pipe-autoloads elpher-autoloads dwim-shell-command-autoloads
info-colors-autoloads helpful-autoloads elisp-refs-autoloads
devdocs-autoloads whisper-autoloads gptel-commit-autoloads
gptel-autoloads journalctl-autoloads systemctl-autoloads
proced-narrow-autoloads show-font-autoloads dbus-explore-autoloads
disk-usage-autoloads dired-filter-autoloads dired-hacks-utils-autoloads
f-autoloads s-autoloads diredfl-autoloads org-bookmark-heading-autoloads
notmuch-bookmarks-autoloads pdf-tools-autoloads tablist-autoloads
nov-autoloads esxml-autoloads calibre-autoloads vcard-autoloads
org-contacts-autoloads calfw-autoloads valign-autoloads
orgit-forge-autoloads forge-autoloads closql-autoloads emacsql-autoloads
ghub-autoloads treepy-autoloads yaml-autoloads orgit-autoloads
magit-autoloads magit-section-autoloads with-editor-autoloads
evil-org-autoloads org-download-autoloads async-autoloads
org-appear-autoloads org-modern-autoloads org-loaddefs
edit-indirect-autoloads markdown-mode-autoloads web-mode-autoloads
wat-ts-mode-autoloads comint-mime-autoloads mathjax-autoloads
flymake-ruff-autoloads package-lint-flymake-autoloads
package-lint-autoloads eff-autoloads pkgbuild-mode-autoloads
gnuplot-autoloads graphviz-dot-mode-autoloads edraw-autoloads
plantuml-mode-autoloads deflate-autoloads dash-autoloads
csv-mode-autoloads udev-mode-autoloads systemd-autoloads
ssh-config-mode-autoloads nftables-mode-autoloads git-modes-autoloads
rmsbolt-autoloads rust-playground-autoloads
flymake-languagetool-autoloads consult-eglot-autoloads dape-autoloads
casual-autoloads notmuch-transient-autoloads transient-autoloads
bash-completion-autoloads cape-autoloads yasnippet-autoloads
iwindow-autoloads consult-project-extra-autoloads
spacious-padding-autoloads ultra-scroll-autoloads ligature-autoloads
hl-todo-autoloads rainbow-mode-autoloads marginalia-autoloads
posframe-autoloads shr-tag-pre-highlight-autoloads
language-detection-autoloads rainbow-delimiters-autoloads
visual-fill-column-autoloads nerd-icons-grep-autoloads
nerd-icons-corfu-autoloads nerd-icons-ibuffer-autoloads
nerd-icons-completion-autoloads nerd-icons-dired-autoloads
nerd-icons-autoloads link-hint-autoloads avy-autoloads
snapshot-timemachine-autoloads stripspace-autoloads vundo-autoloads
jinx-autoloads visual-replace-autoloads capf-autosuggest-autoloads
vertico-autoloads corfu-autoloads orderless-autoloads
isearch-mb-autoloads embark-consult-autoloads consult-autoloads
embark-autoloads evil-surround-autoloads evil-goggles-autoloads
evil-nerd-commenter-autoloads evil-collection-autoloads
annalist-autoloads evil-args-autoloads filechooser-autoloads em-prompt
text-property-search em-hist em-unix em-pred esh-mode esh-var eshell
esh-cmd generator esh-ext esh-proc esh-opt esh-io esh-arg pcomplete
comint ansi-osc ansi-color esh-module esh-module-loaddefs esh-util
help-at-pt files-x recentf tree-widget editorconfig editorconfig-core
editorconfig-core-handle editorconfig-fnmatch project disp-table
modus-operandi-theme modus-themes derived pcase autorevert filenotify
mb-depth minibuf-eldef evil evil-integration evil-maps evil-commands
reveal evil-jumps evil-command-window evil-types evil-search evil-ex
evil-macros evil-repeat evil-states evil-core comp-run evil-common
thingatpt rect evil-vars ring edmacro kmacro evil-autoloads
goto-chg-autoloads general general-autoloads llama-autoloads
simple-auto-compile-autoloads mode-local find-func elpaca-menu-elpa
no-littering compat no-littering-autoloads elpaca-menu-melpa
elpaca-menu-org elpaca-use-package use-package use-package-ensure
use-package-delight use-package-diminish use-package-bind-key bind-key
easy-mmode use-package-core elpaca-use-package-autoloads elpaca-log
elpaca-ui url url-proxy url-privacy url-expand url-methods url-history
url-cookie generate-lisp-file url-domsuf url-util url-parse auth-source
eieio eieio-core cl-macs password-cache json map byte-opt url-vars
mailcap elpaca elpaca-process elpaca-autoloads notmuch-version comp
cl-seq comp-cstr cl-extra help-mode comp-common warnings subr-x rx gv
bytecomp byte-compile xdg cus-edit pp cus-start cus-load icons wid-edit
cl-loaddefs cl-lib rmc iso-transl tooltip cconv eldoc paren electric
uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel
term/x-win x-win term/common-win x-dnd touch-screen 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 nadvice seq simple cl-generic
indonesian philippine 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 abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind inotify lcms2
dynamic-setting font-render-setting cairo xinput2 x multi-tty
move-toolbar make-network-process tty-child-frames native-compile mps
emacs)

Memory information:
((conses 24 0 0) (symbols 56 0 0) (strings 40 0 0) (string-bytes 1 0) (vectors 24 0)
 (vector-slots 8 0 0) (floats 24 0 0) (intervals 64 0 0) (buffers 1072 0))

[before.png (image/png, attachment)]
[after.png (image/png, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 05 Aug 2025 17:47:02 GMT) Full text and rfc822 format available.

Message #8 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Steven Allen <steven <at> stebalien.com>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 05 Aug 2025 20:43:00 +0300
Thanks for the detailed test cases that demonstrate the problem.

> The the `tab-bar-format-align-right' width cache isn't updated when
> faces change and doesn't take into account the fact that faces can have
> different attributes on different frames.
>
> To reproduce, launch "emacs -Q" then yank the following into the scratch
> buffer:
>
>     (setq global-mode-string
>           (list "" "GNU  Emacs  is a version of Emacs...")
>           tab-bar-format
>           '(tab-bar-format-align-right tab-bar-format-global))
>     (tab-bar-mode 1)
>
> And evaluate the buffer (M-x eval-buffer RET).
>
> Finally, increase the font size with `global-text-scale-adjust': C-x C-M-0 +++++
>
> The tab-bar text will get pushed off the right side on Emacs master but
> not in the latest release. See the attached screenshots.
>
> Furthermore, different frames can have different text sizes. Personally,
> I use this to scale each of my frames' text based on the monitor's
> DPI. To reproduce, launch "emacs -Q", yank the following into the
> scratch buffer, and evaluate it:
>
>     (setq global-mode-string
>           (list "" "GNU  Emacs  is a version of Emacs...")
>           tab-bar-format
>           '(tab-bar-format-align-right tab-bar-format-global))
>     (tab-bar-mode 1)
>     (set-face-attribute 'default (selected-frame) :height (* 2 (face-attribute 'default :height)))
>     (make-frame)
>
> The tab-bar text will be correctly aligned in the new frame but not in
> the original frame.
>
> The per-frame issue can be fixed by making the cache per-frame but
> reacting to face changes is harder. Given that this is simply a cache,
> I'd recommend reverting the change and living with the slight
> performance hit.

Instead of reverting the change maybe it would be better to add
a customizable cache function that anyone could change to adapt caching
to own needs by adding more cache keys such as e.g. '(selected-frame)',
'(face-all-attributes 'default)', 'face-remapping-alist', ...




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 06 Aug 2025 11:28:02 GMT) Full text and rfc822 format available.

Message #11 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Stéphane Marks <shipmints <at> gmail.com>
To: Juri Linkov <juri <at> linkov.net>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>,
 Steven Allen <steven <at> stebalien.com>, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache doesn't
 take face changes and per-frame faces into account
Date: Wed, 6 Aug 2025 07:26:53 -0400
[Message part 1 (text/plain, inline)]
On Wed, Aug 6, 2025 at 3:51 AM Juri Linkov <juri <at> linkov.net> wrote:

> Thanks for the detailed test cases that demonstrate the problem.
>
> > The the `tab-bar-format-align-right' width cache isn't updated when
> > faces change and doesn't take into account the fact that faces can have
> > different attributes on different frames.
> >
> > To reproduce, launch "emacs -Q" then yank the following into the scratch
> > buffer:
> >
> >     (setq global-mode-string
> >           (list "" "GNU  Emacs  is a version of Emacs...")
> >           tab-bar-format
> >           '(tab-bar-format-align-right tab-bar-format-global))
> >     (tab-bar-mode 1)
> >
> > And evaluate the buffer (M-x eval-buffer RET).
> >
> > Finally, increase the font size with `global-text-scale-adjust': C-x
> C-M-0 +++++
> >
> > The tab-bar text will get pushed off the right side on Emacs master but
> > not in the latest release. See the attached screenshots.
> >
> > Furthermore, different frames can have different text sizes. Personally,
> > I use this to scale each of my frames' text based on the monitor's
> > DPI. To reproduce, launch "emacs -Q", yank the following into the
> > scratch buffer, and evaluate it:
> >
> >     (setq global-mode-string
> >           (list "" "GNU  Emacs  is a version of Emacs...")
> >           tab-bar-format
> >           '(tab-bar-format-align-right tab-bar-format-global))
> >     (tab-bar-mode 1)
> >     (set-face-attribute 'default (selected-frame) :height (* 2
> (face-attribute 'default :height)))
> >     (make-frame)
> >
> > The tab-bar text will be correctly aligned in the new frame but not in
> > the original frame.
> >
> > The per-frame issue can be fixed by making the cache per-frame but
> > reacting to face changes is harder. Given that this is simply a cache,
> > I'd recommend reverting the change and living with the slight
> > performance hit.
>
> Instead of reverting the change maybe it would be better to add
> a customizable cache function that anyone could change to adapt caching
> to own needs by adding more cache keys such as e.g. '(selected-frame)',
> '(face-all-attributes 'default)', 'face-remapping-alist', ...
>

I right align text on my tab bar and also use multiple text scales on
different frames.  IMO, customizing the cache function pushes too much of
the cache (and string-pixel-width) implementation details on users, and
seems a likely source of confusion.
[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 06 Aug 2025 16:55:02 GMT) Full text and rfc822 format available.

Message #14 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Steven Allen <steven <at> stebalien.com>
To: Juri Linkov <juri <at> linkov.net>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 06 Aug 2025 09:32:19 -0700
Juri Linkov <juri <at> linkov.net> writes:
> Instead of reverting the change maybe it would be better to add
> a customizable cache function that anyone could change to adapt caching
> to own needs by adding more cache keys such as e.g. '(selected-frame)',
> '(face-all-attributes 'default)', 'face-remapping-alist', ...

That's going to lead to a very poor user experience; we can't reasonably
ask users just "figure it out". I'd be less concerned if this were just
a weird edge-case, but causes issues with `global-text-scale-adjust'.

I've looked into what it would take to make a bug-free cache without
modifying Emacs core and/or advice, and it's not simple. You'd need to
use `object-intervals' to extract all the face properties, then parse
the face properties to extract the faces (both inline faces and named
faces), then use face-all-attributes to extract the attributes. At that
point, you might as well just recompute the width.

If modifying Emacs core is on the table, the simplest solution I can
think of is to:

1. Have a per-frame cache.
2. Update a global `face-modification-tick' (similar to
`buffer-modification-tick') whenever faces are updated, invalidating the
cache whenever this changes.

Unfortunately, that second part would likely require modifying
`internal-set-lisp-face-attribute' (in C) and it feels kind of icky. On
the other hand, having the ability to detect that faces have changed
_is_ kind of useful.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 06 Aug 2025 17:38:02 GMT) Full text and rfc822 format available.

Message #17 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Steven Allen <steven <at> stebalien.com>
Cc: 79141 <at> debbugs.gnu.org, Juri Linkov <juri <at> linkov.net>
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 06 Aug 2025 19:37:37 +0200
Steven Allen <steven <at> stebalien.com> writes:

> Juri Linkov <juri <at> linkov.net> writes:
>> Instead of reverting the change maybe it would be better to add
>> a customizable cache function that anyone could change to adapt caching
>> to own needs by adding more cache keys such as e.g. '(selected-frame)',
>> '(face-all-attributes 'default)', 'face-remapping-alist', ...
>
> That's going to lead to a very poor user experience; we can't reasonably
> ask users just "figure it out". I'd be less concerned if this were just
> a weird edge-case, but causes issues with `global-text-scale-adjust'.
>
> I've looked into what it would take to make a bug-free cache without
> modifying Emacs core and/or advice, and it's not simple. You'd need to
> use `object-intervals' to extract all the face properties, then parse
> the face properties to extract the faces (both inline faces and named
> faces), then use face-all-attributes to extract the attributes. At that
> point, you might as well just recompute the width.

A simpler solution, which might cover most scenarios, would be to only
compute the default line pixel height or pixel height of the tab-bar
face and use that together with the propertized string as the
frame-local cache key.

> If modifying Emacs core is on the table, the simplest solution I can
> think of is to:
>
> 1. Have a per-frame cache.
> 2. Update a global `face-modification-tick' (similar to
> `buffer-modification-tick') whenever faces are updated, invalidating the
> cache whenever this changes.

A face-modification-tick or hook sounds like a great idea, independent
of the problem here. I'd imagine there are plenty of other use cases,
which right now require ugly hacks or potentially expensive
recomputations.

> Unfortunately, that second part would likely require modifying
> `internal-set-lisp-face-attribute' (in C) and it feels kind of icky. On
> the other hand, having the ability to detect that faces have changed
> _is_ kind of useful.

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 06 Aug 2025 19:26:02 GMT) Full text and rfc822 format available.

Message #20 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Steven Allen <steven <at> stebalien.com>
Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50;
 The tab-bar-format-align-right cache doesn't take face changes and
 per-frame faces into account
Date: Wed, 06 Aug 2025 22:25:03 +0300
> Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 79141 <at> debbugs.gnu.org
> Date: Wed, 06 Aug 2025 09:32:19 -0700
> From:  Steven Allen via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
> 
> If modifying Emacs core is on the table, the simplest solution I can
> think of is to:
> 
> 1. Have a per-frame cache.
> 2. Update a global `face-modification-tick' (similar to
> `buffer-modification-tick') whenever faces are updated, invalidating the
> cache whenever this changes.

Please be advised that the current code invalidates the frame's face
cache and frees all the faces whenever some face is changed.  That's
because we don't currently track face dependencies, and so have no
idea what other faces will change as result of changing some face.  So
if you want a more efficient face cache, you first need to solve this
dependency-tracking issue.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 06 Aug 2025 19:49:01 GMT) Full text and rfc822 format available.

Message #23 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 79141 <at> debbugs.gnu.org, Steven Allen <steven <at> stebalien.com>, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 06 Aug 2025 21:47:57 +0200
Eli Zaretskii <eliz <at> gnu.org> writes:

>> Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 79141 <at> debbugs.gnu.org
>> Date: Wed, 06 Aug 2025 09:32:19 -0700
>> From:  Steven Allen via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
>> 
>> If modifying Emacs core is on the table, the simplest solution I can
>> think of is to:
>> 
>> 1. Have a per-frame cache.
>> 2. Update a global `face-modification-tick' (similar to
>> `buffer-modification-tick') whenever faces are updated, invalidating the
>> cache whenever this changes.
>
> Please be advised that the current code invalidates the frame's face
> cache and frees all the faces whenever some face is changed.  That's
> because we don't currently track face dependencies, and so have no
> idea what other faces will change as result of changing some face.  So
> if you want a more efficient face cache, you first need to solve this
> dependency-tracking issue.

Couldn't this face cache invalidation logic be used here? Whenever such
a face cache invalidation happens, the proposed `face-modification-tick'
variable would be incremented.

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Thu, 07 Aug 2025 04:43:02 GMT) Full text and rfc822 format available.

Message #26 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 79141 <at> debbugs.gnu.org, steven <at> stebalien.com, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Thu, 07 Aug 2025 07:42:34 +0300
> From: Daniel Mendler <mail <at> daniel-mendler.de>
> Cc: Steven Allen <steven <at> stebalien.com>,  juri <at> linkov.net,
>   79141 <at> debbugs.gnu.org
> Date: Wed, 06 Aug 2025 21:47:57 +0200
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> >> 1. Have a per-frame cache.
> >> 2. Update a global `face-modification-tick' (similar to
> >> `buffer-modification-tick') whenever faces are updated, invalidating the
> >> cache whenever this changes.
> >
> > Please be advised that the current code invalidates the frame's face
> > cache and frees all the faces whenever some face is changed.  That's
> > because we don't currently track face dependencies, and so have no
> > idea what other faces will change as result of changing some face.  So
> > if you want a more efficient face cache, you first need to solve this
> > dependency-tracking issue.
> 
> Couldn't this face cache invalidation logic be used here? Whenever such
> a face cache invalidation happens, the proposed `face-modification-tick'
> variable would be incremented.

I don't quite understand what would that tick be used for, but we
already have in Emacs indications that the face cache was invalidated,
so maybe there's no need for introducing that tick.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Sun, 10 Aug 2025 19:04:03 GMT) Full text and rfc822 format available.

Message #29 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, steven <at> stebalien.com,
 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Sun, 10 Aug 2025 21:52:32 +0300
>> >> 1. Have a per-frame cache.
>> >> 2. Update a global `face-modification-tick' (similar to
>> >> `buffer-modification-tick') whenever faces are updated, invalidating the
>> >> cache whenever this changes.
>> >
>> > Please be advised that the current code invalidates the frame's face
>> > cache and frees all the faces whenever some face is changed.  That's
>> > because we don't currently track face dependencies, and so have no
>> > idea what other faces will change as result of changing some face.  So
>> > if you want a more efficient face cache, you first need to solve this
>> > dependency-tracking issue.
>> 
>> Couldn't this face cache invalidation logic be used here? Whenever such
>> a face cache invalidation happens, the proposed `face-modification-tick'
>> variable would be incremented.
>
> I don't quite understand what would that tick be used for, but we
> already have in Emacs indications that the face cache was invalidated,
> so maybe there's no need for introducing that tick.

Are these indications exposed to Lisp?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Mon, 11 Aug 2025 12:20:02 GMT) Full text and rfc822 format available.

Message #32 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Juri Linkov <juri <at> linkov.net>
Cc: mail <at> daniel-mendler.de, steven <at> stebalien.com, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Mon, 11 Aug 2025 15:19:35 +0300
> From: Juri Linkov <juri <at> linkov.net>
> Cc: Daniel Mendler <mail <at> daniel-mendler.de>,  steven <at> stebalien.com,
>   79141 <at> debbugs.gnu.org
> Date: Sun, 10 Aug 2025 21:52:32 +0300
> 
> >> >> 1. Have a per-frame cache.
> >> >> 2. Update a global `face-modification-tick' (similar to
> >> >> `buffer-modification-tick') whenever faces are updated, invalidating the
> >> >> cache whenever this changes.
> >> >
> >> > Please be advised that the current code invalidates the frame's face
> >> > cache and frees all the faces whenever some face is changed.  That's
> >> > because we don't currently track face dependencies, and so have no
> >> > idea what other faces will change as result of changing some face.  So
> >> > if you want a more efficient face cache, you first need to solve this
> >> > dependency-tracking issue.
> >> 
> >> Couldn't this face cache invalidation logic be used here? Whenever such
> >> a face cache invalidation happens, the proposed `face-modification-tick'
> >> variable would be incremented.
> >
> > I don't quite understand what would that tick be used for, but we
> > already have in Emacs indications that the face cache was invalidated,
> > so maybe there's no need for introducing that tick.
> 
> Are these indications exposed to Lisp?

No.  Faces are generally managed in C, not in Lisp.  But like I said:
I don't have a clear idea what should the proposed modification hook be
used for, so I'm not sure whether we need to expose anything to Lisp.
If you or someone else could describe the proposed use of such a tick,
I could try providing a more useful advice.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 17:53:02 GMT) Full text and rfc822 format available.

Message #35 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, steven <at> stebalien.com, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 20:49:20 +0300
close 79141 31.0.50
quit

> I don't have a clear idea what should the proposed modification hook be
> used for, so I'm not sure whether we need to expose anything to Lisp.
> If you or someone else could describe the proposed use of such a tick,
> I could try providing a more useful advice.

Since no one has an idea how to use such a tick,
I had to revert the last change.

Anyone who needs a (possibly unreliable) caching version, it's easy
to create a copy of the existing 'tab-bar-format-align-right',
add caching to it, and use in 'tab-bar-format'.




bug marked as fixed in version 31.0.50, send any further explanations to 79141 <at> debbugs.gnu.org and Steven Allen <steven <at> stebalien.com> Request was from Juri Linkov <juri <at> linkov.net> to control <at> debbugs.gnu.org. (Tue, 12 Aug 2025 17:53:03 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 18:03:02 GMT) Full text and rfc822 format available.

Message #40 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Juri Linkov <juri <at> linkov.net>
Cc: Eli Zaretskii <eliz <at> gnu.org>, steven <at> stebalien.com, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 20:02:41 +0200
Juri Linkov <juri <at> linkov.net> writes:

> close 79141 31.0.50
> quit
>
>> I don't have a clear idea what should the proposed modification hook be
>> used for, so I'm not sure whether we need to expose anything to Lisp.
>> If you or someone else could describe the proposed use of such a tick,
>> I could try providing a more useful advice.
>
> Since no one has an idea how to use such a tick,
> I had to revert the last change.

To me the idea seemed clear. Expose a face change tick variable, which
is incremented when the face cache is flushed. Assuming that the face
cache logic is valid, the tick would be usable to monitor face changes.

> Anyone who needs a (possibly unreliable) caching version, it's easy
> to create a copy of the existing 'tab-bar-format-align-right',
> add caching to it, and use in 'tab-bar-format'.

Indeed, this is what I had before.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 18:08:01 GMT) Full text and rfc822 format available.

Message #43 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Steven Allen <steven <at> stebalien.com>
To: Juri Linkov <juri <at> linkov.net>, Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 11:07:08 -0700
Juri Linkov <juri <at> linkov.net> writes:

> close 79141 31.0.50
> quit
>
>> I don't have a clear idea what should the proposed modification hook be
>> used for, so I'm not sure whether we need to expose anything to Lisp.
>> If you or someone else could describe the proposed use of such a tick,
>> I could try providing a more useful advice.
>
> Since no one has an idea how to use such a tick,
> I had to revert the last change.
>
> Anyone who needs a (possibly unreliable) caching version, it's easy
> to create a copy of the existing 'tab-bar-format-align-right',
> add caching to it, and use in 'tab-bar-format'.

I can describe it in this case, at least:

1. Any time any face attribute is updated, the "tick" would be incremented.
2. We'd change `tab-bar--align-right-cache' to be (cached-tick . cached-string).
3. When checking the cache, we'd check if (new-tick . new-string) is
    `equal-including-properties' to (cached-tick . cached-string).

IMO, the real question is: where else would/could we use this. Adding a
new `face-modification-tick' just to support this one cache seems a bit overkill.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 18:15:01 GMT) Full text and rfc822 format available.

Message #46 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 79141 <at> debbugs.gnu.org, steven <at> stebalien.com, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 21:14:32 +0300
> From: Daniel Mendler <mail <at> daniel-mendler.de>
> Cc: Eli Zaretskii <eliz <at> gnu.org>,  steven <at> stebalien.com,  79141 <at> debbugs.gnu.org
> Date: Tue, 12 Aug 2025 20:02:41 +0200
> 
> Juri Linkov <juri <at> linkov.net> writes:
> 
> > close 79141 31.0.50
> > quit
> >
> >> I don't have a clear idea what should the proposed modification hook be
> >> used for, so I'm not sure whether we need to expose anything to Lisp.
> >> If you or someone else could describe the proposed use of such a tick,
> >> I could try providing a more useful advice.
> >
> > Since no one has an idea how to use such a tick,
> > I had to revert the last change.
> 
> To me the idea seemed clear. Expose a face change tick variable, which
> is incremented when the face cache is flushed. Assuming that the face
> cache logic is valid, the tick would be usable to monitor face changes.

What would you do with this monitoring?  IOW, how would you act when
Emacs indicates that it recomputed all the faces on a frame?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 18:19:02 GMT) Full text and rfc822 format available.

Message #49 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Steven Allen <steven <at> stebalien.com>
Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 21:18:07 +0300
> From: Steven Allen <steven <at> stebalien.com>
> Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org
> Date: Tue, 12 Aug 2025 11:07:08 -0700
> 
> > Anyone who needs a (possibly unreliable) caching version, it's easy
> > to create a copy of the existing 'tab-bar-format-align-right',
> > add caching to it, and use in 'tab-bar-format'.
> 
> I can describe it in this case, at least:
> 
> 1. Any time any face attribute is updated, the "tick" would be incremented.

Why do you think it makes any sense to do that?  When an attribute of
any face is modified, Emacs discards and recomputes all the faces on
that frame.  How would the tick help you?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 18:41:01 GMT) Full text and rfc822 format available.

Message #52 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Steven Allen <steven <at> stebalien.com>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 79141 <at> debbugs.gnu.org,
 Juri Linkov <juri <at> linkov.net>
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 20:40:13 +0200
Steven Allen <steven <at> stebalien.com> writes:

> Juri Linkov <juri <at> linkov.net> writes:
>
>> close 79141 31.0.50
>> quit
>>
>>> I don't have a clear idea what should the proposed modification hook be
>>> used for, so I'm not sure whether we need to expose anything to Lisp.
>>> If you or someone else could describe the proposed use of such a tick,
>>> I could try providing a more useful advice.
>>
>> Since no one has an idea how to use such a tick,
>> I had to revert the last change.
>>
>> Anyone who needs a (possibly unreliable) caching version, it's easy
>> to create a copy of the existing 'tab-bar-format-align-right',
>> add caching to it, and use in 'tab-bar-format'.
>
> I can describe it in this case, at least:
>
> 1. Any time any face attribute is updated, the "tick" would be incremented.
> 2. We'd change `tab-bar--align-right-cache' to be (cached-tick . cached-string).
> 3. When checking the cache, we'd check if (new-tick . new-string) is
>     `equal-including-properties' to (cached-tick . cached-string).

Yes, this is how I understood the proposal.

> IMO, the real question is: where else would/could we use this. Adding a
> new `face-modification-tick' just to support this one cache seems a bit overkill.

Agree. Right now I don't have other good use case in mind, but I
remember that I had some problems with face recomputation in my corfu
and org-modern packages. However the face change tick might be too
rough.

I went back to my own pixel width caching for my system status bar.
Maybe in this scenario the pixel width computation is particularly
expensive since the tab elements to the right of
tab-bar-format-align-right are long. During redisplay dominated loads,
e.g. when doing pixel precise scrolling, the pixel width computation
accounts for 5% or more of the runtime according to the profiler. With
pixel caching the tab bar recomputation disappears from the profile. I
have seen similar issues with expensive mode line elements.

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Tue, 12 Aug 2025 19:55:01 GMT) Full text and rfc822 format available.

Message #55 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Steven Allen <steven <at> stebalien.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Tue, 12 Aug 2025 12:54:13 -0700
Eli Zaretskii <eliz <at> gnu.org> writes:

>> From: Steven Allen <steven <at> stebalien.com>
>> Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org
>> Date: Tue, 12 Aug 2025 11:07:08 -0700
>>
>> > Anyone who needs a (possibly unreliable) caching version, it's easy
>> > to create a copy of the existing 'tab-bar-format-align-right',
>> > add caching to it, and use in 'tab-bar-format'.
>>
>> I can describe it in this case, at least:
>>
>> 1. Any time any face attribute is updated, the "tick" would be incremented.
>
> Why do you think it makes any sense to do that?  When an attribute of
> any face is modified, Emacs discards and recomputes all the faces on
> that frame.  How would the tick help you?

We're caching the pixel-width of the right-aligned tab-bar segment.
The pixel-width of that segment depends on the faces used to display
that segment; when those faces are modified, the width of that segment
can change and there's currently no easy way to detect this situation.

Adding a `face-modification-tick' would make it possible to detect when
face attributes have changed, allowing us to invalidate the cached
width.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 06:50:03 GMT) Full text and rfc822 format available.

Message #58 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: Eli Zaretskii <eliz <at> gnu.org>, Steven Allen <steven <at> stebalien.com>,
 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 09:49:02 +0300
> I went back to my own pixel width caching for my system status bar.
> Maybe in this scenario the pixel width computation is particularly
> expensive since the tab elements to the right of
> tab-bar-format-align-right are long. During redisplay dominated loads,
> e.g. when doing pixel precise scrolling, the pixel width computation
> accounts for 5% or more of the runtime according to the profiler. With
> pixel caching the tab bar recomputation disappears from the profile. I
> have seen similar issues with expensive mode line elements.

How often your tab-bar is updated?  For frequent string-pixel-width calls
using debounce or throttle like from https://github.com/karthink/timeout
could help.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 06:55:02 GMT) Full text and rfc822 format available.

Message #61 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Juri Linkov <juri <at> linkov.net>
Cc: Eli Zaretskii <eliz <at> gnu.org>, Steven Allen <steven <at> stebalien.com>,
 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 08:54:40 +0200
Juri Linkov <juri <at> linkov.net> writes:

>> I went back to my own pixel width caching for my system status bar.
>> Maybe in this scenario the pixel width computation is particularly
>> expensive since the tab elements to the right of
>> tab-bar-format-align-right are long. During redisplay dominated loads,
>> e.g. when doing pixel precise scrolling, the pixel width computation
>> accounts for 5% or more of the runtime according to the profiler. With
>> pixel caching the tab bar recomputation disappears from the profile. I
>> have seen similar issues with expensive mode line elements.
>
> How often your tab-bar is updated?  For frequent string-pixel-width calls
> using debounce or throttle like from https://github.com/karthink/timeout
> could help.

It is recomputed completely on every redisplay. It might be possible to
throttle the tab bar recomputation, such that it happens only a few
times per second. This will lead to other difficulties however, since in
some cases the tab bar should react immediately (e.g., when creating new
tabs or when toggling some elements).

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 11:20:02 GMT) Full text and rfc822 format available.

Message #64 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Steven Allen <steven <at> stebalien.com>
Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 14:19:20 +0300
> From: Steven Allen <steven <at> stebalien.com>
> Cc: juri <at> linkov.net, mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org
> Date: Tue, 12 Aug 2025 12:54:13 -0700
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> >> From: Steven Allen <steven <at> stebalien.com>
> >> Cc: mail <at> daniel-mendler.de, 79141 <at> debbugs.gnu.org
> >> Date: Tue, 12 Aug 2025 11:07:08 -0700
> >>
> >> > Anyone who needs a (possibly unreliable) caching version, it's easy
> >> > to create a copy of the existing 'tab-bar-format-align-right',
> >> > add caching to it, and use in 'tab-bar-format'.
> >>
> >> I can describe it in this case, at least:
> >>
> >> 1. Any time any face attribute is updated, the "tick" would be incremented.
> >
> > Why do you think it makes any sense to do that?  When an attribute of
> > any face is modified, Emacs discards and recomputes all the faces on
> > that frame.  How would the tick help you?
> 
> We're caching the pixel-width of the right-aligned tab-bar segment.

Ah.  But isn't it better and simpler not to cache, recomputing it anew
each time?

> Adding a `face-modification-tick' would make it possible to detect when
> face attributes have changed, allowing us to invalidate the cached
> width.

But then whatever problems caused you to cache the pixel width will
happen again once Emacs recomputes the faces (which happens quite
frequently).  So I'm not sure this will be a useful feature, at least
in your case.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 11:36:01 GMT) Full text and rfc822 format available.

Message #67 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 79141 <at> debbugs.gnu.org, Steven Allen <steven <at> stebalien.com>, juri <at> linkov.net
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 13:35:03 +0200
Eli Zaretskii <eliz <at> gnu.org> writes:

>> >> > Anyone who needs a (possibly unreliable) caching version, it's easy
>> >> > to create a copy of the existing 'tab-bar-format-align-right',
>> >> > add caching to it, and use in 'tab-bar-format'.
>> >>
>> >> I can describe it in this case, at least:
>> >>
>> >> 1. Any time any face attribute is updated, the "tick" would be incremented.
>> >
>> > Why do you think it makes any sense to do that?  When an attribute of
>> > any face is modified, Emacs discards and recomputes all the faces on
>> > that frame.  How would the tick help you?
>> 
>> We're caching the pixel-width of the right-aligned tab-bar segment.
>
> Ah.  But isn't it better and simpler not to cache, recomputing it anew
> each time?

Obviously simpler. In my profiling the string-pixel-width computation
took a significant fraction of the runtime.

>> Adding a `face-modification-tick' would make it possible to detect when
>> face attributes have changed, allowing us to invalidate the cached
>> width.
>
> But then whatever problems caused you to cache the pixel width will
> happen again once Emacs recomputes the faces (which happens quite
> frequently).  So I'm not sure this will be a useful feature, at least
> in your case.

Tbh I cannot really tell. My assumption was that faces are not
recomputed often, but this assumption might not be correct. Could you
clarify when face recomputations happen? Only when attributes change?
Attribute changes should not happen, right?

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 17:11:02 GMT) Full text and rfc822 format available.

Message #70 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: Eli Zaretskii <eliz <at> gnu.org>, Steven Allen <steven <at> stebalien.com>,
 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 20:08:09 +0300
>> How often your tab-bar is updated?  For frequent string-pixel-width calls
>> using debounce or throttle like from https://github.com/karthink/timeout
>> could help.
>
> It is recomputed completely on every redisplay. It might be possible to
> throttle the tab bar recomputation, such that it happens only a few
> times per second. This will lead to other difficulties however, since in
> some cases the tab bar should react immediately (e.g., when creating new
> tabs or when toggling some elements).

What I actually meant is more like setting a short cache eviction time,
for example 1 sec after changing the faces the tab-bar will be
in a broken state using an old cached value, but later the width
will be recomputed and updated.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79141; Package emacs. (Wed, 13 Aug 2025 17:20:02 GMT) Full text and rfc822 format available.

Message #73 received at 79141 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Juri Linkov <juri <at> linkov.net>
Cc: Eli Zaretskii <eliz <at> gnu.org>, Steven Allen <steven <at> stebalien.com>,
 79141 <at> debbugs.gnu.org
Subject: Re: bug#79141: 31.0.50; The tab-bar-format-align-right cache
 doesn't take face changes and per-frame faces into account
Date: Wed, 13 Aug 2025 19:19:09 +0200
Juri Linkov <juri <at> linkov.net> writes:

>>> How often your tab-bar is updated?  For frequent string-pixel-width calls
>>> using debounce or throttle like from https://github.com/karthink/timeout
>>> could help.
>>
>> It is recomputed completely on every redisplay. It might be possible to
>> throttle the tab bar recomputation, such that it happens only a few
>> times per second. This will lead to other difficulties however, since in
>> some cases the tab bar should react immediately (e.g., when creating new
>> tabs or when toggling some elements).
>
> What I actually meant is more like setting a short cache eviction time,
> for example 1 sec after changing the faces the tab-bar will be
> in a broken state using an old cached value, but later the width
> will be recomputed and updated.

Yes, this would work, but it might also look a bit broken. A
face-modified-tick sounds like a better solution to me.

Daniel




This bug report was last modified 2 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.