Discussion:
query-replace and the title bar
(too old to reply)
James K. Lowden
2022-03-25 15:35:56 UTC
Permalink
I have a faint hint of a glimpse at the root of a problem that has
plagued me since upgrading emacs to

GNU Emacs 26.3 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.14)
of 2020-03-26, modified by Debian

The symptom: When my titlebar is set using the function below,
query-replace stops working. Deep inside, I get an error about args
out of range. I believe the problem stems from which match is
referenced by match-beginning and match-end.

Why?

1. The problem goes away when I don't define frame-title-format using
my short-host-name function, which uses string-match.

2. The error always reports args of (0,9), and the length of
(system-name) on my machine is, you guessed it: 9 bytes.

I don't remember anymore why I bothered with short-host-name. When I
change the definition of frame-title-format not to use it,
query-replace starts working again.

My hypothesis:

While query-replace is being processed (before the first
interactive prompt) the title bar is updated. The :eval processes
string-match after query-replace starts building its own (complicated)
string-match stack, and before calling match-end.

After the title bar is updated, query-replace continues, but
match-end now references the system-name matched for the title bar, and
not the string being replaced by query-replace.

I don't know elisp well enough to debug further. I leave it to someone
who does. I'm happy to provide further information if asked.

Here is the broken version:

;; short host name, like `hostname -s`, remote shell likes this better
(defun short-host-name ()
(string-match "[^.]+" (system-name))
(substring (system-name) (match-beginning 0) (match-end 0)))

; from http://emacs-fu.blogspot.com/2011/01/setting-frame-title.html
(setq frame-title-format
'(multiple-frames
("%*" (:eval (short-host-name))
": " (:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name)) "%
b")) " @" server-name )
"%b"
))

and the working version:

; from http://emacs-fu.blogspot.com/2011/01/setting-frame-title.html
(setq frame-title-format
'(multiple-frames
("%*" (:eval (system-name))
": " (:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name)) "%
b")) " @" server-name )
"%b"
))

and simpler still, because no use is made of the multiple-frame status:

(setq frame-title-format
'("%*" (:eval (system-name))
": " (:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b"))
(if server-name (" @" server-name) "" )
)
)

--jkl
Robert Pluim
2022-03-25 15:56:43 UTC
Permalink
James> ;; short host name, like `hostname -s`, remote shell likes this better
James> (defun short-host-name ()
James> (string-match "[^.]+" (system-name))
James> (substring (system-name) (match-beginning 0) (match-end 0)))

Try the following instead

(defun short-host-name ()
(save-match-data
(let ((name (system-name)))
(string-match "[^.]+" name)
(match-string name))))

Robert
--
James K. Lowden
2022-03-27 00:02:01 UTC
Permalink
On Fri, 25 Mar 2022 16:56:43 +0100
Post by Robert Pluim
James> (string-match "[^.]+" (system-name))
James> (substring (system-name) (match-beginning 0) (match-end
James> 0)))
Try the following instead
(defun short-host-name ()
(save-match-data
(let ((name (system-name)))
(string-match "[^.]+" name)
(match-string name))))
Works like a charm, Robert, thanks!

Could you tell me why this charm works, though? Am I supposed to know
there are rules about :eval in frame-title-format, or something? Or by
using "let", am I avoiding messing with the global namespace, and I'm
sort of always supposed to do that?

Thanks again.

--jkl
Michael Heerdegen
2022-04-01 02:06:45 UTC
Permalink
Post by James K. Lowden
Post by James K. Lowden
(defun short-host-name ()
(save-match-data
(let ((name (system-name)))
(string-match "[^.]+" name)
(match-string name))))
Works like a charm, Robert, thanks!
Could you tell me why this charm works, though? Am I supposed to know
there are rules about :eval in frame-title-format, or something? Or by
using "let", am I avoiding messing with the global namespace, and I'm
sort of always supposed to do that?
No no, the crucial thing is `save-match-data'. The "match data" is part
of the global state. query-replace uses the match data. Your original
function changed the match data by side effect (`string-match' does
that). `save-match-data' prevents the match data from being
changed...all in the manuals if you are interested in the details.

That's already all.

Michael.

Loading...