From: Artur Malabarba Date: Sun, 24 May 2015 22:38:53 +0000 (+0100) Subject: * lisp/emacs-lisp/tabulated-list.el: New optional print method X-Git-Tag: emacs-25.0.90~1985 X-Git-Url: https://code.delx.au/gnu-emacs/commitdiff_plain/c205098b6a8bd3b13256803f86f6863558a7a34e * lisp/emacs-lisp/tabulated-list.el: New optional print method (tabulated-list-print): New optional argument, UPDATE. If non-nil, the list is printed by only adding and deleting the changed entries, instead of erasing the whole buffer. This method is much faster when few or no entries have changed. * doc/lispref/modes.texi (Tabulated List Mode): Document it. * etc/NEWS: Document it. --- diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index c325506da1..a8b6bb19c5 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -1055,7 +1055,7 @@ the above variables (in particular, only after setting @code{tabulated-list-format}). @end defun -@defun tabulated-list-print &optional remember-pos +@defun tabulated-list-print &optional remember-pos update This function populates the current buffer with entries. It should be called by the listing command. It erases the buffer, sorts the entries specified by @code{tabulated-list-entries} according to @@ -1065,6 +1065,13 @@ specified by @code{tabulated-list-entries} according to If the optional argument @var{remember-pos} is non-@code{nil}, this function looks for the @var{id} element on the current line, if any, and tries to move to that entry after all the entries are (re)inserted. + +If the optional argument @var{update} is non-@code{nil}, this function +will only erase or add entries that have changed since the last print. +This is several times faster if most entries haven't changed since the +last time this function was called. The only difference in outcome is +that tags placed via @code{tabulated-list-put-tag} will not be removed +from entries that haven't changed (normally all tags are removed). @end defun @node Generic Modes diff --git a/etc/NEWS b/etc/NEWS index 7ad85bae5b..b922a276e0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -205,10 +205,16 @@ font, and (iii) the specified window. `message' and related functions from displaying messages the Echo Area. The output is still logged to the *Messages* buffer. ++++ ** It is now safe for a mode that derives `tabulated-list-mode' to not call `tabulated-list-init-header', in which case it will have no header. ++++ +** `tabulated-list-print' takes a second optional argument, update, +which specifies an alternative printing method which is faster when +few or no entries have changed. + * Editing Changes in Emacs 25.1 diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el index 9d55ab8f53..58b8fd62d2 100644 --- a/lisp/emacs-lisp/tabulated-list.el +++ b/lisp/emacs-lisp/tabulated-list.el @@ -298,7 +298,7 @@ column. Negate the predicate that would be returned if (lambda (a b) (not (funcall sorter a b))) sorter)))) -(defun tabulated-list-print (&optional remember-pos) +(defun tabulated-list-print (&optional remember-pos update) "Populate the current Tabulated List mode buffer. This sorts the `tabulated-list-entries' list if sorting is specified by `tabulated-list-sort-key'. It then erases the @@ -306,7 +306,14 @@ buffer and inserts the entries with `tabulated-list-printer'. Optional argument REMEMBER-POS, if non-nil, means to move point to the entry with the same ID element as the current line and -recenter window line accordingly." +recenter window line accordingly. + +Non-nil UPDATE argument means to use an alternative printing +method which is faster if most entries haven't changed since the +last print. The only difference in outcome is that tags will not +be removed from entries that haven't changed (see +`tabulated-list-put-tag'). Don't use this immediately after +changing `tabulated-list-sort-key'." (let ((inhibit-read-only t) (entries (if (functionp tabulated-list-entries) (funcall tabulated-list-entries) @@ -319,18 +326,47 @@ recenter window line accordingly." (count-screen-lines (window-start) (point)))) (setq entry-id (tabulated-list-get-id)) (setq saved-col (current-column))) - (erase-buffer) - (unless tabulated-list-use-header-line - (tabulated-list-print-fake-header)) ;; Sort the entries, if necessary. (setq entries (sort entries sorter)) (unless (functionp tabulated-list-entries) (setq tabulated-list-entries entries)) + ;; Without a sorter, we have no way to just update. + (when (and update (not sorter)) + (setq update nil)) + (if update (goto-char (point-min)) + ;; Redo the buffer, unless we're just updating. + (erase-buffer) + (unless tabulated-list-use-header-line + (tabulated-list-print-fake-header))) + ;; Finally, print the resulting list. (dolist (elt entries) - (and entry-id - (equal entry-id (car elt)) - (setq saved-pt (point))) - (apply tabulated-list-printer elt)) + (let ((id (car elt))) + (and entry-id + (equal entry-id id) + (setq entry-id nil + saved-pt (point))) + ;; If the buffer this empty, simply print each elt. + (if (eobp) + (apply tabulated-list-printer elt) + (while (let ((local-id (tabulated-list-get-id))) + ;; If we find id, then nothing to update. + (cond ((equal id local-id) + (forward-line 1) + nil) + ;; If this entry sorts after id (or it's the + ;; end), then just insert id and move on. + ((funcall sorter elt + ;; FIXME: Might be faster if + ;; don't construct this list. + (list local-id (tabulated-list-get-entry))) + (apply tabulated-list-printer elt) + nil) + ;; We find an entry that sorts before id, + ;; it needs to be deleted. + (t t))) + (let ((old (point))) + (forward-line 1) + (delete-region old (point))))))) (set-buffer-modified-p nil) ;; If REMEMBER-POS was specified, move to the "old" location. (if saved-pt