Subject: Re: bug#56685: OBOE in string-truncate-left?

From: Stephen Berman <[email-masked]>
Date: Fri, 22 Jul 2022 12:26:09 +0200

On Thu, 21 Jul 2022 17:10:36 -0500 Stefan Kangas <[email-masked]> wrote:

> Try evaluating:
>
>     (length (string-truncate-left "longstring" 8))
>     => 9
>
> But the docstring says "Truncate STRING to LENGTH".
> This seems like a bug.

Yes, and I also think it's counterintuitive that LENGTH includes the
length of "...".  Worse, if STRING is short enough, the resulting string
(with "...") can be longer than LENGTH:

(string-truncate-left "band" 3)
"...d"
(string-truncate-left "band" 2)
"...d"
(string-truncate-left "band" 1)
"...d"
(string-truncate-left "and" 2)
"...d"
(string-truncate-left "and" 1)
"...d"

Note that with the last two examples, the result is longer than the
original string, contradicting the meaning of truncation.  I think
LENGTH should mean just the numbers of characters in STRING after
truncation (excluding "..."), and if the result with the prefix "..." is
not shorter than the original string, there should be no truncation.
The following patch does this.

diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 5037ae47e8..6eefd5d141 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -107,12 +107,13 @@ 'string-reverse

 ;;;###autoload
 (defun string-truncate-left (string length)
-  "Truncate STRING to LENGTH, replacing initial surplus with \"...\"."
+  "Return STRING's last LENGTH characters prefixed with \"...\".
+If the resulting string with the prefix is not shorter than the
+original length of STRING, return STRING unchanged."
   (let ((strlen (length string)))
-    (if (<= strlen length)
+    (if (<= strlen (+ length 3))
 	string
-      (setq length (max 0 (- length 3)))
-      (concat "..." (substring string (max 0 (- strlen 1 length)))))))
+      (concat "..." (substring string (max 0 (- strlen length)))))))

 (defsubst string-blank-p (string)
   "Check whether STRING is either empty or only whitespace.

--
Steve Berman