Subject: bug#53606: 29.0.50; Very slow Customize buffer for bibtex-biblatex-entry-alist

From: Mauro Aranda <[email-masked]>
Date: Fri, 7 Feb 2025 07:45:17 -0300

I'm posting my patch for this bug again. It stills shows a 3-3.5
improvement over the current code in wid-edit.el, and together with the
changes Roland made, it brings the time needed to create the buffer from
20+ seconds to about 4 seconds in my machine.

Profiler data has not changed much from the ones I posted back then.
From 499726f3ce6beafbdf4989d8a43f2e1253cfde16 Mon Sep 17 00:00:00 2001
From: Mauro Aranda <[email-masked]>
Date: Thu, 6 Feb 2025 08:01:08 -0300
Subject: [PATCH] Speed up widget creation (Bug#53606)

* lisp/wid-edit.el (widget-default-create, widget-checklist-add-item)
(widget-radio-add-item, widget-editable-list-entry-create): Don't
insert format escapes into the buffer, only to delete them after.
This avoids calls to delete-char and makes widget creation about 3
times faster.
---
 lisp/wid-edit.el | 74 ++++++++++++++++++++++++++++++------------------
 1 file changed, 47 insertions(+), 27 deletions(-)

diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index a1d4c4850ae..2d6075c10a8 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -1776,18 +1776,20 @@ widget--revert-markers-for-outside-insertion
 (defun widget-default-create (widget)
   "Create WIDGET at point in the current buffer."
   (widget-specify-insert
-   (let ((from (point))
+   (let ((str (widget-get widget :format))
+         (onext 0) (next 0)
 	 button-begin button-end
 	 sample-begin sample-end
 	 doc-begin doc-end
          value-pos
          (markers (widget--prepare-markers-for-inside-insertion widget)))
-     (insert (widget-get widget :format))
-     (goto-char from)
      ;; Parse escapes in format.
-     (while (re-search-forward "%\\(.\\)" nil t)
-       (let ((escape (char-after (match-beginning 1))))
-	 (delete-char -2)
+     (while (string-match "%\\(.\\)" str next)
+       (setq next (match-end 1))
+       ;; If we skipped some literal text, insert it.
+       (when (/= (- next onext) 2)
+         (insert (substring str onext (- next 2))))
+       (let ((escape (string-to-char (match-string 1 str))))
 	 (cond ((eq escape ?%)
 		(insert ?%))
 	       ((eq escape ?\[)
@@ -1831,7 +1833,11 @@ widget-default-create
 		    (widget-apply widget :value-create)
 		  (setq value-pos (point))))
 	       (t
-		(widget-apply widget :format-handler escape)))))
+		(widget-apply widget :format-handler escape))))
+       (setq onext next))
+     ;; Insert remaining literal text, if any.
+     (when (> (length str) next)
+       (insert (substring str next)))
      ;; Specify button, sample, and doc, and insert value.
      (and button-begin button-end
 	  (widget-specify-button widget button-begin button-end))
@@ -2578,14 +2584,15 @@ widget-checklist-add-item
 	  (buttons (widget-get widget :buttons))
 	  (button-args (or (widget-get type :sibling-args)
 			   (widget-get widget :button-args)))
-	  (from (point))
+          (str (widget-get widget :entry-format))
+          (onext 0) (next 0)
 	  child button)
-     (insert (widget-get widget :entry-format))
-     (goto-char from)
      ;; Parse % escapes in format.
-     (while (re-search-forward "%\\([bv%]\\)" nil t)
-       (let ((escape (char-after (match-beginning 1))))
-	 (delete-char -2)
+     (while (string-match "%\\([bv%]\\)" str next)
+       (setq next (match-end 1))
+       (when (/= (- next onext) 2)
+         (insert (substring str onext (- next 2))))
+       (let ((escape (string-to-char (match-string 1 str))))
 	 (cond ((eq escape ?%)
 		(insert ?%))
 	       ((eq escape ?b)
@@ -2609,7 +2616,10 @@ widget-checklist-add-item
                              (widget-create-child-value
                               widget type (car (cdr chosen)))))))
 	       (t
-		(error "Unknown escape `%c'" escape)))))
+		(error "Unknown escape `%c'" escape))))
+       (setq onext next))
+     (when (> (length str) next)
+       (insert (substring str next)))
      ;; Update properties.
      (and button child (widget-put child :button button))
      (and button (widget-put widget :buttons (cons button buttons)))
@@ -2756,16 +2766,17 @@ widget-radio-add-item
 	  (buttons (widget-get widget :buttons))
 	  (button-args (or (widget-get type :sibling-args)
 			   (widget-get widget :button-args)))
-	  (from (point))
+          (str (widget-get widget :entry-format))
+          (onext 0) (next 0)
 	  (chosen (and (null (widget-get widget :choice))
 		       (widget-apply type :match value)))
 	  child button)
-     (insert (widget-get widget :entry-format))
-     (goto-char from)
      ;; Parse % escapes in format.
-     (while (re-search-forward "%\\([bv%]\\)" nil t)
-       (let ((escape (char-after (match-beginning 1))))
-	 (delete-char -2)
+     (while (string-match "%\\([bv%]\\)" str next)
+       (setq next (match-end 1))
+       (when (/= (- next onext) 2)
+         (insert (substring str onext (- next 2))))
+       (let ((escape (string-to-char (match-string 1 str))))
 	 (cond ((eq escape ?%)
 		(insert ?%))
 	       ((eq escape ?b)
@@ -2784,7 +2795,10 @@ widget-radio-add-item
 			(to (widget-get child :to)))
                     (widget-specify-unselected child from to))))
 	       (t
-		(error "Unknown escape `%c'" escape)))))
+		(error "Unknown escape `%c'" escape))))
+       (setq onext next))
+     (when (> (length str) next)
+       (insert (substring str next)))
      ;; Update properties.
      (when chosen
        (widget-put widget :choice type))
@@ -3053,17 +3067,20 @@ widget-editable-list-entry-create
   ;; Create a new entry to the list.
   (let ((type (nth 0 (widget-get widget :args)))
 	;; (widget-push-button-gui widget-editable-list-gui)
+        (str (widget-get widget :entry-format))
+        (onext 0) (next 0)
 	child delete insert)
     (widget-specify-insert
      (save-excursion
        (and (widget--should-indent-p)
             (widget-get widget :indent)
-            (insert-char ?\s (widget-get widget :indent)))
-       (insert (widget-get widget :entry-format)))
+            (insert-char ?\s (widget-get widget :indent))))
      ;; Parse % escapes in format.
-     (while (re-search-forward "%\\(.\\)" nil t)
-       (let ((escape (char-after (match-beginning 1))))
-	 (delete-char -2)
+     (while (string-match "%\\(.\\)" str next)
+       (setq next (match-end 1))
+       (when (/= (- next onext) 2)
+         (insert (substring str onext (- next 2))))
+       (let ((escape (string-to-char (match-string 1 str))))
 	 (cond ((eq escape ?%)
 		(insert ?%))
 	       ((eq escape ?i)
@@ -3079,7 +3096,10 @@ widget-editable-list-entry-create
 		             widget type
 		             (if conv value (widget-default-get type)))))
 	       (t
-		(error "Unknown escape `%c'" escape)))))
+		(error "Unknown escape `%c'" escape))))
+       (setq onext next))
+     (when (> (length str) next)
+       (insert (substring str next)))
      (let ((buttons (widget-get widget :buttons)))
        (if insert (push insert buttons))
        (if delete (push delete buttons))
-- 
2.34.1