* 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.
This commit is contained in:
Artur Malabarba 2015-05-24 23:38:53 +01:00
parent d38350984e
commit c205098b6a
3 changed files with 59 additions and 10 deletions

View file

@ -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

View file

@ -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

View file

@ -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