Subject: bug#65540: 29.1.50; treesit-inspect-mode does not escape `treesit--inspect-name`

From: Augustin Chéneau (BTuin) <[email-masked]>
Date: Sat, 26 Aug 2023 00:08:00 +0200

The minor mode `treesit-inspect-mode` uses the mode-line to display the
current node. However, a node name containing a string is displayed
incorrecly because mode-line will try to interpret it as a
'%-contruct'. Each '%' needs to be escaped as a double percent ('%%').

The attached patch should fix the issue (works for me).

In GNU Emacs 29.1.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
  3.24.38, cairo version 1.17.8) of 2023-08-23 built on inspiron-5567
Repository revision: b72f23a532bf57ead565182f8f2d5f2818032da8
Repository branch: makepkg
Windowing system distributor 'The X.Org Foundation', version 11.0.12302000
System Description: Arch Linux

Configured using:
  'configure --prefix=/opt --sysconfdir=/etc --libexecdir=/opt/lib
  --localstatedir=/var --mandir=/opt/share/man --with-gameuser=:games
  --with-modules --without-libotf --without-m17n-flt --without-gconf
  --with-native-compilation=yes --with-native-compilation=aot
  --with-xinput2 --with-x-toolkit=gtk3 --without-xaw3d --with-sound=no
  --with-tree-sitter --without-gpm --without-compress-install
  '--program-transform-name=s/\([ec]tags\)/\1.emacs/'
  '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'
  LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now'

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

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

Major mode: Magit

Minor modes in effect:
   magit-delta-mode: t
   windmove-mode: t
   yas-global-mode: t
   yas-minor-mode: t
   delete-selection-mode: t
   global-subword-mode: t
   subword-mode: t
   save-place-mode: t
   smartparens-global-mode: t
   treemacs-filewatch-mode: t
   treemacs-follow-mode: t
   treemacs-git-mode: t
   treemacs-fringe-indicator-mode: t
   global-hl-line-mode: t
   editorconfig-mode: t
   drag-stuff-global-mode: t
   drag-stuff-mode: t
   doom-modeline-mode: t
   global-git-commit-mode: t
   magit-auto-revert-mode: t
   global-auto-revert-mode: t
   shell-dirtrack-mode: t
   server-mode: t
   adaptive-wrap-prefix-mode: t
   global-undo-tree-mode: t
   undo-tree-mode: t
   global-anzu-mode: t
   anzu-mode: t
   which-key-mode: t
   projectile-mode: t
   global-corfu-mode: t
   corfu-mode: t
   TeX-PDF-mode: t
   TeX-source-correlate-mode: t
   vertico-mode: t
   marginalia-mode: t
   override-global-mode: t
   straight-use-package-mode: t
   straight-package-neutering-mode: t
   savehist-mode: t
   global-display-line-numbers-mode: t
   tooltip-mode: t
   global-eldoc-mode: t
   show-paren-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
   buffer-read-only: t
   size-indication-mode: t
   column-number-mode: t
   line-number-mode: t
   global-visual-line-mode: t
   visual-line-mode: t
   indent-tabs-mode: t
   transient-mark-mode: t
   auto-composition-mode: t
   auto-encryption-mode: t
   auto-compression-mode: t

Load-path shadows:
/home/arch-aug/.config/emacs-neo/straight/build/transient/transient 
hides /opt/share/emacs/29.1.50/lisp/transient
/home/arch-aug/.config/emacs-neo/straight/build/jsonrpc/jsonrpc hides 
/opt/share/emacs/29.1.50/lisp/jsonrpc
/home/arch-aug/.config/emacs-neo/straight/build/external-completion/external-completion 
hides /opt/share/emacs/29.1.50/lisp/external-completion
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-lint 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-lint
/home/arch-aug/.config/emacs-neo/straight/build/bind-key/bind-key hides 
/opt/share/emacs/29.1.50/lisp/use-package/bind-key
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-diminish 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-diminish
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-jump 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-jump
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-core 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-core
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-bind-key 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-bind-key
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-delight 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-delight
/home/arch-aug/.config/emacs-neo/straight/build/use-package/use-package-ensure 
hides /opt/share/emacs/29.1.50/lisp/use-package/use-package-ensure
/home/arch-aug/.config/emacs-neo/straight/build/xref/xref hides 
/opt/share/emacs/29.1.50/lisp/progmodes/xref
/home/arch-aug/.config/emacs-neo/straight/build/project/project hides 
/opt/share/emacs/29.1.50/lisp/progmodes/project
/home/arch-aug/.config/emacs-neo/straight/build/flymake/flymake hides 
/opt/share/emacs/29.1.50/lisp/progmodes/flymake
/home/arch-aug/.config/emacs-neo/straight/build/eglot/eglot hides 
/opt/share/emacs/29.1.50/lisp/progmodes/eglot
/home/arch-aug/.config/emacs-neo/straight/build/soap-client/soap-inspect 
hides /opt/share/emacs/29.1.50/lisp/net/soap-inspect
/home/arch-aug/.config/emacs-neo/straight/build/soap-client/soap-client 
hides /opt/share/emacs/29.1.50/lisp/net/soap-client
/home/arch-aug/.config/emacs-neo/straight/build/eldoc/eldoc hides 
/opt/share/emacs/29.1.50/lisp/emacs-lisp/eldoc
/home/arch-aug/.config/emacs-neo/straight/build/let-alist/let-alist 
hides /opt/share/emacs/29.1.50/lisp/emacs-lisp/let-alist

Features:
(shadow sort mail-extr emacsbug magit-extras magit-delta xterm-color
bug-reference macros expand-region subword-mode-expansions
text-mode-expansions cc-mode-expansions latex-mode-expansions
er-basic-expansions expand-region-core expand-region-custom textutils
cl-print edebug misearch multi-isearch dabbrev cape consult-xref
windmove shortdoc bison-ts-mode noutline outline mule-util eglot
external-completion jsonrpc ert ewoc debug backtrace c-ts-mode
c-ts-common vertico-directory checkdoc doom-snippets doom-snippets-lib
yasnippet rainbow-delimiters ws-butler delsel cap-words superword
subword saveplace flex-mode jison-mode bison-mode cc-mode cc-fonts
cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs
bison-mode-autoloads dired-x cmake-mode rst cmake-mode-autoloads utop
utop-minor-mode tuareg tuareg-compat tuareg-opam caml-help find-file
utop-autoloads flycheck-ocaml flycheck find-func
flycheck-ocaml-autoloads merlin-eldoc-autoloads merlin-xref merlin-cap
merlin caml-types merlin-autoloads dune dune-autoloads tuareg-autoloads
caml-autoloads pkgbuild-mode tramp tramp-loaddefs trampver
tramp-integration files-x tramp-compat parse-time iso8601 sh-script smie
executable pkgbuild-mode-autoloads geiser-guile info-look geiser-debug
geiser-repl geiser-image geiser-capf geiser-doc geiser-menu
geiser-autodoc geiser-edit etags fileloop generator geiser-completion
geiser-eval geiser-connection tq geiser-syntax scheme geiser-impl
help-fns radix-tree geiser-log geiser-popup view geiser-custom
geiser-base geiser-guile-autoloads geiser geiser-autoloads lua-mode
lua-mode-autoloads debbugs soap-client url-http url-auth url-gw nsm
rng-xsd rng-dt rng-util xsd-regexp debbugs-autoloads
soap-client-autoloads expand-region-autoloads jinx-autoloads smartparens
loadhist smartparens-autoloads builder vc-git vc-dispatcher dir-var
builder-autoloads treemacs treemacs-header-line treemacs-compatibility
treemacs-mode treemacs-bookmarks treemacs-tags xref treemacs-interface
treemacs-persistence treemacs-filewatch-mode treemacs-follow-mode
treemacs-rendering treemacs-annotations treemacs-async
treemacs-workspaces treemacs-dom treemacs-visuals
treemacs-fringe-indicator treemacs-scope pulse color treemacs-faces
treemacs-icons treemacs-themes treemacs-core-utils pfuture inline
hl-line ht treemacs-logging treemacs-customization treemacs-macros
treemacs-autoloads cfrs-autoloads posframe-autoloads ht-autoloads
hydra-autoloads lv-autoloads pfuture-autoloads ace-window-autoloads
avy-autoloads hare-mode-autoloads eglot-autoloads
external-completion-autoloads jsonrpc-autoloads flymake-autoloads
project-autoloads xref-autoloads eldoc-autoloads flycheck-autoloads
let-alist-autoloads pkg-info-autoloads epl-autoloads vertico-search
general general-autoloads combobulate-autoloads treesit editorconfig
editorconfig-core editorconfig-core-handle editorconfig-fnmatch pcase
editorconfig-autoloads ws-butler-autoloads bel-coloration
rainbow-delimiters-autoloads drag-stuff drag-stuff-autoloads
doom-modeline doom-modeline-segments doom-modeline-env
doom-modeline-core shrink-path f f-shortdoc s doom-modeline-autoloads
shrink-path-autoloads f-autoloads s-autoloads all-the-icons
all-the-icons-faces data-material data-weathericons data-octicons
data-fileicons data-faicons data-alltheicons all-the-icons-autoloads
doom-themes-ext-org doom-themes-ext-treemacs doom-themes-ext-neotree
doom-themes-ext-visual-bell face-remap doom-dracula-theme doom-themes
doom-themes-base doom-themes-autoloads magit-delta-autoloads
xterm-color-autoloads 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 package browse-url
url url-proxy url-privacy url-expand url-methods url-history url-cookie
generate-lisp-file url-domsuf url-util url-handlers url-parse
auth-source json map url-vars magit-repos magit-apply magit-wip
magit-log which-func imenu magit-diff smerge-mode diff-mode git-commit
log-edit message sendmail mailcap yank-media puny dired dired-loaddefs
rfc822 mml mml-sec password-cache epa derived 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 pcvs-util add-log magit-core magit-autorevert
autorevert filenotify magit-margin magit-transient magit-process
with-editor shell pcomplete server magit-mode transient magit-git
magit-base magit-section format-spec eieio eieio-core dash
magit-autoloads magit-section-autoloads git-commit-autoloads
with-editor-autoloads transient-autoloads dash-autoloads consult-vertico
consult recentf tree-widget wid-edit bookmark pp consult-autoloads
adaptive-wrap adaptive-wrap-autoloads undo-tree diff queue
undo-tree-autoloads queue-autoloads anzu anzu-autoloads which-key
which-key-autoloads comp comp-cstr rx projectile lisp-mnt grep ibuf-ext
ibuffer ibuffer-loaddefs projectile-autoloads doom-snippets-autoloads
yasnippet-autoloads finder-inf cape-autoloads corfu corfu-autoloads
auctex-latexmk advice latex latex-flymake flymake-proc flymake project
byte-opt compile text-property-search comint ansi-osc ansi-color ring
warnings icons thingatpt tex-ispell tex-style auctex-latexmk-autoloads
tex dbus xml crm texmathp auctex-autoloads tex-site vertico
vertico-autoloads marginalia compat edmacro kmacro marginalia-autoloads
compat-autoloads use-package-bind-key bind-key easy-mmode orderless
orderless-autoloads use-package-ensure use-package-autoloads info
bind-key-autoloads straight-autoloads cl-seq cl-extra help-mode straight
subr-x cl-macs gv use-package-core cl-loaddefs cl-lib bytecomp
byte-compile savehist display-line-numbers elec-pair cus-load 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 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 system-font-setting
font-render-setting cairo move-toolbar gtk x-toolkit xinput2 x multi-tty
make-network-process native-compile emacs)

Memory information:
((conses 16 796087 203632)
  (symbols 48 45985 4)
  (strings 32 174286 18968)
  (string-bytes 1 6895227)
  (vectors 16 106771)
  (vector-slots 8 2576123 281310)
  (floats 8 1663 1257)
  (intervals 56 13814 3929)
  (buffers 984 32))

From 99be4333aa9a662fb49a182140e4079ffcecb570 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Augustin=20Ch=C3=A9neau?= <[email-masked]>
Date: Sat, 26 Aug 2023 00:03:41 +0200
Subject: [PATCH] Escape strings displayed by the mode-line

Strings containing a percent character need to be escaped to be
displayed properly, as a percent ('%') in the mode-line denotes a
'%-contruct' with a special meaning.

* lisp/treesit.el (treesit-inspect-mode): Escape `treesit--inspect-name`
---
 lisp/treesit.el | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 04d460fdea4..2ae94594f75 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2466,6 +2466,13 @@ treesit-inspect-node-at-point
           (message "%s" treesit--inspect-name)
         (message "No node at point")))))
 
+(defun treesit--escape-mode-line (str)
+  "Escape the string STR that will be displayed in the mode-line.
+
+It replaces the percent character '%' with a double percent '%%', as a single
+percent has a special meaning in the mode-line."
+  (string-replace "%" "%%" str))
+
 (define-minor-mode treesit-inspect-mode
   "Minor mode that displays in the mode-line the node which starts at point.
 
@@ -2490,11 +2497,11 @@ treesit-inspect-mode
         (add-hook 'post-command-hook
                   #'treesit-inspect-node-at-point 0 t)
         (add-to-list 'mode-line-misc-info
-                     '(:eval treesit--inspect-name)))
+                     '(:eval (treesit--escape-mode-line treesit--inspect-name))))
     (remove-hook 'post-command-hook
                  #'treesit-inspect-node-at-point t)
     (setq mode-line-misc-info
-          (remove '(:eval treesit--inspect-name)
+          (remove '(:eval (treesit--escape-mode-line treesit--inspect-name))
                   mode-line-misc-info))))
 
 (defun treesit-query-validate (language query)
-- 
2.42.0