Package: emacs;
Reported by: Spencer Baugh <sbaugh <at> janestreet.com>
Date: Wed, 27 Nov 2024 20:26:02 UTC
Severity: wishlist
Tags: patch
Done: Stefan Monnier <monnier <at> iro.umontreal.ca>
Bug is archived. No further changes may be made.
Message #34 received at 74561 <at> debbugs.gnu.org (full text, mbox):
From: sbaugh <at> catern.com To: Spencer Baugh <sbaugh <at> janestreet.com> Cc: João Távora <joaotavora <at> gmail.com>, dmitry <at> gutov.dev, 74561 <at> debbugs.gnu.org, Stefan Monnier <monnier <at> iro.umontreal.ca>, juri <at> linkov.net Subject: Re: bug#74561: [PATCH] Allow limiting the size of *Completions* Date: Tue, 11 Feb 2025 15:03:31 +0000 (UTC)
[Message part 1 (text/plain, inline)]
Improved the patch further on feedback from Stefan. It now also uses completion-lazy-hilit, which further improves performance. I'm not sure if completion-lazy-hilit-fn needs to be saved and restored (like group-fun is being saved and restored). The current version of this patch doesn't save completion-lazy-hilit-fn, and that seems to work fine, because completion-lazy-hilit-fn hasn't changed by the time we call completion--lazy-insert-strings. But maybe this is not robust in some case?
[0001-Lazily-highlight-and-insert-candidates-in-Completion.patch (text/x-diff, inline)]
From 454ce335856c6ab550447f7ed1d79dbb67d6f3ef Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh <at> catern.com> Date: Mon, 13 Jan 2025 14:32:18 -0500 Subject: [PATCH] Lazily highlight and insert candidates in *Completions* From profiling, the main bottleneck in completion over large completion sets is display-completion-list, when there are many available candidates. For example, in my large monorepo, when completing over the 589196 files or the 73897 branches, even with the candidates narrowed down by typing some prefix to complete, TAB (when it shows *Completions*) or ? is slow, mostly in display-completion-list. However, rendering all the completion candidates is unnecessary if the *Completions* window is never scrolled to see those candiates. By eagerly inserting only some candidates and lazily highlighting and inserting the remaining candidates only when necessary, performance is much improved. * lisp/minibuffer.el (completions-insert-lazily): Add defcustom for inserting completions lazily. (bug#74561) (completion--lazy-insert-completions) (completion--lazy-insert-group-fun) (completion--lazy-insert-start, completion--lazy-insert-end): Add. (completion--insert-strings): Check completions-insert-lazily. (minibuffer-completion-help): Set completion-lazy-hilit. (completion--lazy-insert-strings): Add. (with-minibuffer-completions-window): Call completion--lazy-insert-strings. * lisp/simple.el (completion-setup-function): Preserve buffer-locals required for lazy completion insertion. (switch-to-completions): Call completion--lazy-insert-strings. --- lisp/minibuffer.el | 49 ++++++++++++++++++++++++++++++++++++++++++++-- lisp/simple.el | 5 ++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 1f33bda5f65..9dca6bb2d15 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2230,6 +2230,16 @@ completions-header-format (string :tag "Format string for heading line")) :version "29.1") +(defcustom completions-insert-lazily 200 + "If non-nil, completions are inserted lazily. + +When a number, only that many completions are inserted, and the +rest are inserted lazily." + :type '(choice (const :tag "Render all completions immediately" nil) + (integer :tag "Only render this many completions"))) + +(defvar-local completion--lazy-insert-button nil) + (defun completion--insert-strings (strings &optional group-fun) "Insert a list of STRINGS into the current buffer. The candidate strings are inserted into the buffer depending on the @@ -2257,8 +2267,41 @@ completion--insert-strings ;; Align to tab positions for the case ;; when the caller uses tabs inside prefix. (setq colwidth (- colwidth (mod colwidth completion-tab-width)))) - (funcall (intern (format "completion--insert-%s" completions-format)) - strings group-fun length wwidth colwidth columns)))) + (let ((truncated-strings + (and completions-insert-lazily + (< completions-insert-lazily (length strings)) + (take completions-insert-lazily strings))) + (start (point))) + (funcall (intern (format "completion--insert-%s" completions-format)) + (mapcar (lambda (candidate) + (if (consp candidate) + (setcar candidate (completion-lazy-hilit (car candidate))) + (completion-lazy-hilit candidate))) + (or truncated-strings strings)) + group-fun length wwidth colwidth columns) + (when truncated-strings + (newline) + (setq-local completion--lazy-insert-button + (insert-button "[Completions truncated, click here to insert the rest.]" + 'action #'completion--lazy-insert-strings)) + (button-put completion--lazy-insert-button 'group-fun group-fun) + (button-put completion--lazy-insert-button 'completions-start (copy-marker start)) + (button-put completion--lazy-insert-button 'completion-strings strings)))))) + +(defun completion--lazy-insert-strings (&optional button) + (setq button (or button completion--lazy-insert-button)) + (when button + (let ((completions-insert-lazily nil) + (completion-lazy-hilit t) + (standard-output (current-buffer)) + (inhibit-read-only t) + (group-fun (button-get button 'group-fun)) + (strings (button-get button 'completion-strings))) + (save-excursion + (goto-char (button-get button 'completions-start)) + (delete-region (point) (button-end button)) + (setq-local completion--lazy-insert-button nil) + (completion--insert-strings strings group-fun))))) (defun completion--insert-horizontal (strings group-fun length wwidth @@ -2620,6 +2663,7 @@ minibuffer-completion-help (end (or end (point-max))) (string (buffer-substring start end)) (md (completion--field-metadata start)) + (completion-lazy-hilit completions-insert-lazily) (completions (completion-all-completions string minibuffer-completion-table @@ -4962,6 +5006,7 @@ with-minibuffer-completions-window (get-buffer-window "*Completions*" 0))))) (when window (with-selected-window window + (completion--lazy-insert-strings) ,@body)))) (defcustom minibuffer-completion-auto-choose t diff --git a/lisp/simple.el b/lisp/simple.el index e1c0dd4a092..57553343c04 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10455,10 +10455,12 @@ completion-setup-function (buffer-substring (minibuffer-prompt-end) (point))))))) (with-current-buffer standard-output (let ((base-position completion-base-position) - (insert-fun completion-list-insert-choice-function)) + (insert-fun completion-list-insert-choice-function) + (lazy-button completion--lazy-insert-button)) (completion-list-mode) (when completions-highlight-face (setq-local cursor-face-highlight-nonselected-window t)) + (setq-local completion--lazy-insert-button lazy-button) (setq-local completion-base-position base-position) (setq-local completion-list-insert-choice-function insert-fun)) (setq-local completion-reference-buffer mainbuf) @@ -10504,6 +10506,7 @@ switch-to-completions (progn (minibuffer-completion-help) (get-buffer-window "*Completions*" 0))))) (select-window window) + (completion--lazy-insert-strings) (when (bobp) (cond ((and (memq this-command '(completion-at-point minibuffer-complete)) -- 2.44.0
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.