• query-replace and the title bar

    From James K. Lowden@21:1/5 to All on Fri Mar 25 11:35:56 2022
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Pluim@21:1/5 to All on Fri Mar 25 16:56:43 2022
    On Fri, 25 Mar 2022 11:35:56 -0400, "James K. Lowden" <jklowden@speakeasy.net> said:
    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
    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James K. Lowden@21:1/5 to Robert Pluim on Sat Mar 26 20:02:01 2022
    On Fri, 25 Mar 2022 16:56:43 +0100
    Robert Pluim <rpluim@gmail.com> wrote:

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael Heerdegen@21:1/5 to James K. Lowden on Fri Apr 1 04:06:45 2022
    "James K. Lowden" <jklowden@speakeasy.net> writes:

    (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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)