aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChong Yidong2012-05-06 16:32:37 +0800
committerChong Yidong2012-05-06 16:32:37 +0800
commit6632d361114f2d104b689e2213dce1eb3474de0a (patch)
treef632b6c5ac65baebc931028cc33d28597573b5aa
parent52b61776c594e1e4f30f8e281e7ead79d56383d5 (diff)
downloademacs-6632d361114f2d104b689e2213dce1eb3474de0a.tar.gz
emacs-6632d361114f2d104b689e2213dce1eb3474de0a.zip
Improvements for Tabulated List mode.
* lisp/emacs-lisp/tabulated-list.el (tabulated-list-format): Accept additional plist in column descriptors. (tabulated-list-init-header): Obey it. (tabulated-list-get-entry): New function. (tabulated-list-put-tag): Use it. Use string-width instead of length. (tabulated-list--column-number): New function. (tabulated-list-print): Use it. (tabulated-list-print-col): New function. Set `tabulated-list-column-name' property on each column's text. (tabulated-list-print-entry): Use it. (tabulated-list-delete-entry, tabulated-list-set-col): New functions. (tabulated-list-sort-column): New command. Fixes: debbugs:11337
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/ChangeLog17
-rw-r--r--lisp/emacs-lisp/tabulated-list.el245
3 files changed, 200 insertions, 67 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 57c492ffa4c..df386fa7e4f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -161,6 +161,11 @@ details.
161The function `notifications-get-capabilities' returns the supported 161The function `notifications-get-capabilities' returns the supported
162server properties. 162server properties.
163 163
164** Tabulated List and packages derived from it
165
166*** New command `tabulated-list-sort-column' bound to `S' sorts column
167at point, or the Nth column if a numeric prefix argument is given.
168
164** Obsolete packages: 169** Obsolete packages:
165 170
166*** assoc.el 171*** assoc.el
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 134c208e544..b925e47880b 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,20 @@
12012-05-06 Chong Yidong <cyd@gnu.org>
2
3 * emacs-lisp/tabulated-list.el (tabulated-list-format): Accept
4 additional plist in column descriptors.
5 (tabulated-list-init-header): Obey it.
6 (tabulated-list-get-entry): New function.
7 (tabulated-list-put-tag): Use it. Use string-width instead of
8 length.
9 (tabulated-list--column-number): New function.
10 (tabulated-list-print): Use it.
11 (tabulated-list-print-col): New function. Set
12 `tabulated-list-column-name' property on each column's text.
13 (tabulated-list-print-entry): Use it.
14 (tabulated-list-delete-entry, tabulated-list-set-col): New
15 functions.
16 (tabulated-list-sort-column): New command (Bug#11337).
17
12012-05-06 Troels Nielsen <bn.troels@gmail.com> (tiny change) 182012-05-06 Troels Nielsen <bn.troels@gmail.com> (tiny change)
2 19
3 * progmodes/compile.el (compilation-internal-error-properties): 20 * progmodes/compile.el (compilation-internal-error-properties):
diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el
index 9439fba2b86..bd734a4fbe0 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -22,22 +22,26 @@
22 22
23;;; Commentary: 23;;; Commentary:
24 24
25;; This file defines `tabulated-list-mode', a generic major mode for displaying 25;; This file defines Tabulated List mode, a generic major mode for
26;; lists of tabulated data, intended for other major modes to inherit from. It 26;; displaying lists of tabulated data, intended for other major modes
27;; provides several utility routines, e.g. for pretty-printing lines of 27;; to inherit from. It provides several utility routines, e.g. for
28;; tabulated data to fit into the appropriate columns. 28;; pretty-printing lines of tabulated data to fit into the appropriate
29;; columns.
29 30
30;; For usage information, see the documentation of `tabulated-list-mode'. 31;; For usage information, see the documentation of `tabulated-list-mode'.
31 32
32;; This package originated from Tom Tromey's Package Menu mode, extended and 33;; This package originated from Tom Tromey's Package Menu mode,
33;; generalized to be used by other modes. 34;; extended and generalized to be used by other modes.
34 35
35;;; Code: 36;;; Code:
36 37
37(defvar tabulated-list-format nil 38(defvar tabulated-list-format nil
38 "The format of the current Tabulated List mode buffer. 39 "The format of the current Tabulated List mode buffer.
39This should be a vector of elements (NAME WIDTH SORT), where: 40This should be a vector of elements (NAME WIDTH SORT . PROPS),
41where:
40 - NAME is a string describing the column. 42 - NAME is a string describing the column.
43 This is the label for the column in the header line.
44 Different columns must have non-`equal' names.
41 - WIDTH is the width to reserve for the column. 45 - WIDTH is the width to reserve for the column.
42 For the final element, its numerical value is ignored. 46 For the final element, its numerical value is ignored.
43 - SORT specifies how to sort entries by this column. 47 - SORT specifies how to sort entries by this column.
@@ -45,7 +49,11 @@ This should be a vector of elements (NAME WIDTH SORT), where:
45 If t, sort by comparing the string value printed in the column. 49 If t, sort by comparing the string value printed in the column.
46 Otherwise, it should be a predicate function suitable for 50 Otherwise, it should be a predicate function suitable for
47 `sort', accepting arguments with the same form as the elements 51 `sort', accepting arguments with the same form as the elements
48 of `tabulated-list-entries'.") 52 of `tabulated-list-entries'.
53 - PROPS is a plist of additional column properties.
54 Currently supported properties are:
55 - `:pad-right': Number of additional padding spaces to the
56 right of the column (defaults to 1 if omitted).")
49(make-variable-buffer-local 'tabulated-list-format) 57(make-variable-buffer-local 'tabulated-list-format)
50 58
51(defvar tabulated-list-entries nil 59(defvar tabulated-list-entries nil
@@ -95,12 +103,18 @@ NAME is a string matching one of the column names in
95non-nil, means to invert the resulting sort.") 103non-nil, means to invert the resulting sort.")
96(make-variable-buffer-local 'tabulated-list-sort-key) 104(make-variable-buffer-local 'tabulated-list-sort-key)
97 105
98(defun tabulated-list-get-id (&optional pos) 106(defsubst tabulated-list-get-id (&optional pos)
99 "Obtain the entry ID of the Tabulated List mode entry at POS. 107 "Return the entry ID of the Tabulated List entry at POS.
100This is an ID object from `tabulated-list-entries', or nil. 108The value is an ID object from `tabulated-list-entries', or nil.
101POS, if omitted or nil, defaults to point." 109POS, if omitted or nil, defaults to point."
102 (get-text-property (or pos (point)) 'tabulated-list-id)) 110 (get-text-property (or pos (point)) 'tabulated-list-id))
103 111
112(defsubst tabulated-list-get-entry (&optional pos)
113 "Return the Tabulated List entry at POS.
114The value is a vector of column descriptors, or nil if there is
115no entry at POS. POS, if omitted or nil, defaults to point."
116 (get-text-property (or pos (point)) 'tabulated-list-entry))
117
104(defun tabulated-list-put-tag (tag &optional advance) 118(defun tabulated-list-put-tag (tag &optional advance)
105 "Put TAG in the padding area of the current line. 119 "Put TAG in the padding area of the current line.
106TAG should be a string, with length <= `tabulated-list-padding'. 120TAG should be a string, with length <= `tabulated-list-padding'.
@@ -111,16 +125,16 @@ If ADVANCE is non-nil, move forward by one line afterwards."
111 (error "Unable to tag the current line")) 125 (error "Unable to tag the current line"))
112 (save-excursion 126 (save-excursion
113 (beginning-of-line) 127 (beginning-of-line)
114 (when (get-text-property (point) 'tabulated-list-id) 128 (when (tabulated-list-get-entry)
115 (let ((beg (point)) 129 (let ((beg (point))
116 (inhibit-read-only t)) 130 (inhibit-read-only t))
117 (forward-char tabulated-list-padding) 131 (forward-char tabulated-list-padding)
118 (insert-and-inherit 132 (insert-and-inherit
119 (if (<= (length tag) tabulated-list-padding) 133 (let ((width (string-width tag)))
120 (concat tag 134 (if (<= width tabulated-list-padding)
121 (make-string (- tabulated-list-padding (length tag)) 135 (concat tag
122 ?\s)) 136 (make-string (- tabulated-list-padding width) ?\s))
123 (substring tag 0 tabulated-list-padding))) 137 (truncate-string-to-width tag tabulated-list-padding))))
124 (delete-region beg (+ beg tabulated-list-padding))))) 138 (delete-region beg (+ beg tabulated-list-padding)))))
125 (if advance 139 (if advance
126 (forward-line))) 140 (forward-line)))
@@ -130,6 +144,7 @@ If ADVANCE is non-nil, move forward by one line afterwards."
130 (set-keymap-parent map button-buffer-map) 144 (set-keymap-parent map button-buffer-map)
131 (define-key map "n" 'next-line) 145 (define-key map "n" 'next-line)
132 (define-key map "p" 'previous-line) 146 (define-key map "p" 'previous-line)
147 (define-key map "S" 'tabulated-list-sort-column)
133 (define-key map [follow-link] 'mouse-face) 148 (define-key map [follow-link] 'mouse-face)
134 (define-key map [mouse-2] 'mouse-select-window) 149 (define-key map [mouse-2] 'mouse-select-window)
135 map) 150 map)
@@ -154,7 +169,7 @@ If ADVANCE is non-nil, move forward by one line afterwards."
154 169
155(defun tabulated-list-init-header () 170(defun tabulated-list-init-header ()
156 "Set up header line for the Tabulated List buffer." 171 "Set up header line for the Tabulated List buffer."
157 (let ((x tabulated-list-padding) 172 (let ((x (max tabulated-list-padding 0))
158 (button-props `(help-echo "Click to sort by column" 173 (button-props `(help-echo "Click to sort by column"
159 mouse-face highlight 174 mouse-face highlight
160 keymap ,tabulated-list-sort-button-map)) 175 keymap ,tabulated-list-sort-button-map))
@@ -163,9 +178,11 @@ If ADVANCE is non-nil, move forward by one line afterwards."
163 (push (propertize " " 'display `(space :align-to ,x)) cols)) 178 (push (propertize " " 'display `(space :align-to ,x)) cols))
164 (dotimes (n (length tabulated-list-format)) 179 (dotimes (n (length tabulated-list-format))
165 (let* ((col (aref tabulated-list-format n)) 180 (let* ((col (aref tabulated-list-format n))
181 (label (nth 0 col))
166 (width (nth 1 col)) 182 (width (nth 1 col))
167 (label (car col))) 183 (props (nthcdr 3 col))
168 (setq x (+ x 1 width)) 184 (pad-right (or (plist-get props :pad-right) 1)))
185 (setq x (+ x pad-right width))
169 (and (<= tabulated-list-padding 0) 186 (and (<= tabulated-list-padding 0)
170 (= n 0) 187 (= n 0)
171 (setq label (concat " " label))) 188 (setq label (concat " " label)))
@@ -190,11 +207,12 @@ If ADVANCE is non-nil, move forward by one line afterwards."
190 (t (apply 'propertize label 207 (t (apply 'propertize label
191 'tabulated-list-column-name (car col) 208 'tabulated-list-column-name (car col)
192 button-props))) 209 button-props)))
193 cols)) 210 cols)
194 (push (propertize " " 211 (if (> pad-right 0)
195 'display (list 'space :align-to x) 212 (push (propertize " "
196 'face 'fixed-pitch) 213 'display `(space :align-to ,x)
197 cols)) 214 'face 'fixed-pitch)
215 cols))))
198 (setq header-line-format (mapconcat 'identity (nreverse cols) "")))) 216 (setq header-line-format (mapconcat 'identity (nreverse cols) ""))))
199 217
200(defun tabulated-list-revert (&rest ignored) 218(defun tabulated-list-revert (&rest ignored)
@@ -206,6 +224,17 @@ It runs `tabulated-list-revert-hook', then calls `tabulated-list-print'."
206 (run-hooks 'tabulated-list-revert-hook) 224 (run-hooks 'tabulated-list-revert-hook)
207 (tabulated-list-print t)) 225 (tabulated-list-print t))
208 226
227(defun tabulated-list--column-number (name)
228 (let ((len (length tabulated-list-format))
229 (n 0)
230 found)
231 (while (and (< n len) (null found))
232 (if (equal (car (aref tabulated-list-format n)) name)
233 (setq found n))
234 (setq n (1+ n)))
235 (or found
236 (error "No column named %s" name))))
237
209(defun tabulated-list-print (&optional remember-pos) 238(defun tabulated-list-print (&optional remember-pos)
210 "Populate the current Tabulated List mode buffer. 239 "Populate the current Tabulated List mode buffer.
211This sorts the `tabulated-list-entries' list if sorting is 240This sorts the `tabulated-list-entries' list if sorting is
@@ -224,18 +253,13 @@ to the entry with the same ID element as the current line."
224 (setq saved-col (current-column))) 253 (setq saved-col (current-column)))
225 (erase-buffer) 254 (erase-buffer)
226 ;; Sort the buffers, if necessary. 255 ;; Sort the buffers, if necessary.
227 (when tabulated-list-sort-key 256 (when (and tabulated-list-sort-key
228 (let ((sort-column (car tabulated-list-sort-key)) 257 (car tabulated-list-sort-key))
229 (len (length tabulated-list-format)) 258 (let* ((sort-column (car tabulated-list-sort-key))
230 (n 0) 259 (n (tabulated-list--column-number sort-column))
231 sorter) 260 (sorter (nth 2 (aref tabulated-list-format n))))
232 ;; Which column is to be sorted? 261 ;; Is the specified column sortable?
233 (while (and (< n len) 262 (when sorter
234 (not (equal (car (aref tabulated-list-format n))
235 sort-column)))
236 (setq n (1+ n)))
237 (when (< n len)
238 (setq sorter (nth 2 (aref tabulated-list-format n)))
239 (when (eq sorter t) 263 (when (eq sorter t)
240 (setq sorter ; Default sorter checks column N: 264 (setq sorter ; Default sorter checks column N:
241 (lambda (A B) 265 (lambda (A B)
@@ -267,31 +291,105 @@ to the entry with the same ID element as the current line."
267This is the default `tabulated-list-printer' function. ID is a 291This is the default `tabulated-list-printer' function. ID is a
268Lisp object identifying the entry to print, and COLS is a vector 292Lisp object identifying the entry to print, and COLS is a vector
269of column descriptors." 293of column descriptors."
270 (let ((beg (point)) 294 (let ((beg (point))
271 (x (max tabulated-list-padding 0)) 295 (x (max tabulated-list-padding 0))
272 (len (length tabulated-list-format))) 296 (ncols (length tabulated-list-format))
297 (inhibit-read-only t))
273 (if (> tabulated-list-padding 0) 298 (if (> tabulated-list-padding 0)
274 (insert (make-string x ?\s))) 299 (insert (make-string x ?\s)))
275 (dotimes (n len) 300 (dotimes (n ncols)
276 (let* ((format (aref tabulated-list-format n)) 301 (setq x (tabulated-list-print-col n (aref cols n) x)))
277 (desc (aref cols n))
278 (width (nth 1 format))
279 (label (if (stringp desc) desc (car desc)))
280 (help-echo (concat (car format) ": " label)))
281 ;; Truncate labels if necessary (except last column).
282 (and (< (1+ n) len)
283 (> (string-width label) width)
284 (setq label (truncate-string-to-width label width nil nil t)))
285 (setq label (bidi-string-mark-left-to-right label))
286 (if (stringp desc)
287 (insert (propertize label 'help-echo help-echo))
288 (apply 'insert-text-button label (cdr desc)))
289 (setq x (+ x 1 width)))
290 ;; No need to append any spaces if this is the last column.
291 (if (< (1+ n) len)
292 (indent-to x 1)))
293 (insert ?\n) 302 (insert ?\n)
294 (put-text-property beg (point) 'tabulated-list-id id))) 303 (put-text-property beg (point) 'tabulated-list-id id)
304 (put-text-property beg (point) 'tabulated-list-entry cols)))
305
306(defun tabulated-list-print-col (n col-desc x)
307 "Insert a specified Tabulated List entry at point.
308N is the column number, COL-DESC is a column descriptor \(see
309`tabulated-list-entries'), and X is the column number at point.
310Return the column number after insertion."
311 (let* ((format (aref tabulated-list-format n))
312 (name (nth 0 format))
313 (width (nth 1 format))
314 (props (nthcdr 3 format))
315 (pad-right (or (plist-get props :pad-right) 1))
316 (label (if (stringp col-desc) col-desc (car col-desc)))
317 (help-echo (concat (car format) ": " label))
318 (opoint (point))
319 (not-last-col (< (1+ n) (length tabulated-list-format))))
320 ;; Truncate labels if necessary (except last column).
321 (and not-last-col
322 (> (string-width label) width)
323 (setq label (truncate-string-to-width label width nil nil t)))
324 (setq label (bidi-string-mark-left-to-right label))
325 (if (stringp col-desc)
326 (insert (propertize label 'help-echo help-echo))
327 (apply 'insert-text-button label (cdr col-desc)))
328 (setq x (+ x pad-right width))
329 ;; No need to append any spaces if this is the last column.
330 (if not-last-col
331 (indent-to x pad-right))
332 (put-text-property opoint (point) 'tabulated-list-column-name name)
333 x))
334
335(defun tabulated-list-delete-entry ()
336 "Delete the Tabulated List entry at point.
337Return a list (ID COLS), where ID is the ID of the deleted entry
338and COLS is a vector of its column descriptors. Move point to
339the beginning of the deleted entry. Return nil if there is no
340entry at point.
341
342This function only changes the buffer contents; it does not alter
343`tabulated-list-entries'."
344 ;; Assume that each entry occupies one line.
345 (let* ((id (tabulated-list-get-id))
346 (cols (tabulated-list-get-entry))
347 (inhibit-read-only t))
348 (when cols
349 (delete-region (line-beginning-position) (1+ (line-end-position)))
350 (list id cols))))
351
352(defun tabulated-list-set-col (col desc &optional change-entry-data)
353 "Change the Tabulated List entry at point, setting COL to DESC.
354COL is the column number to change, or the name of the column to change.
355DESC is the new column descriptor, which is inserted via
356`tabulated-list-print-col'.
357
358If CHANGE-ENTRY-DATA is non-nil, modify the underlying entry data
359by setting the appropriate slot of the vector originally used to
360print this entry. If `tabulated-list-entries' has a list value,
361this is the vector stored within it."
362 (let* ((opoint (point))
363 (eol (line-end-position))
364 (pos (line-beginning-position))
365 (id (tabulated-list-get-id pos))
366 (entry (tabulated-list-get-entry pos))
367 (prop 'tabulated-list-column-name)
368 (inhibit-read-only t)
369 name)
370 (cond ((numberp col)
371 (setq name (car (aref tabulated-list-format col))))
372 ((stringp col)
373 (setq name col
374 col (tabulated-list--column-number col)))
375 (t
376 (error "Invalid column %s" col)))
377 (unless entry
378 (error "No Tabulated List entry at position %s" opoint))
379 (unless (equal (get-text-property pos prop) name)
380 (while (and (setq pos
381 (next-single-property-change pos prop nil eol))
382 (< pos eol)
383 (not (equal (get-text-property pos prop) name)))))
384 (when (< pos eol)
385 (delete-region pos (next-single-property-change pos prop nil eol))
386 (goto-char pos)
387 (tabulated-list-print-col col desc (current-column))
388 (if change-entry-data
389 (aset entry col desc))
390 (put-text-property pos (point) 'tabulated-list-id id)
391 (put-text-property pos (point) 'tabulated-list-entry entry)
392 (goto-char opoint))))
295 393
296(defun tabulated-list-col-sort (&optional e) 394(defun tabulated-list-col-sort (&optional e)
297 "Sort Tabulated List entries by the column of the mouse click E." 395 "Sort Tabulated List entries by the column of the mouse click E."
@@ -302,14 +400,27 @@ of column descriptors."
302 'tabulated-list-column-name 400 'tabulated-list-column-name
303 (car obj)))) 401 (car obj))))
304 (with-current-buffer (window-buffer (posn-window pos)) 402 (with-current-buffer (window-buffer (posn-window pos))
305 (when (derived-mode-p 'tabulated-list-mode) 403 (tabulated-list--sort-by-column-name name))))
306 ;; Flip the sort order on a second click. 404
307 (if (equal name (car tabulated-list-sort-key)) 405(defun tabulated-list-sort-column (&optional n)
308 (setcdr tabulated-list-sort-key 406 "Sort Tabulated List entries by the column at point.
309 (not (cdr tabulated-list-sort-key))) 407With a numeric prefix argument N, sort the Nth column."
310 (setq tabulated-list-sort-key (cons name nil))) 408 (interactive "P")
311 (tabulated-list-init-header) 409 (let ((name (if n
312 (tabulated-list-print t))))) 410 (car (aref tabulated-list-format n))
411 (get-text-property (point)
412 'tabulated-list-column-name))))
413 (tabulated-list--sort-by-column-name name)))
414
415(defun tabulated-list--sort-by-column-name (name)
416 (when (derived-mode-p 'tabulated-list-mode)
417 ;; Flip the sort order on a second click.
418 (if (equal name (car tabulated-list-sort-key))
419 (setcdr tabulated-list-sort-key
420 (not (cdr tabulated-list-sort-key)))
421 (setq tabulated-list-sort-key (cons name nil)))
422 (tabulated-list-init-header)
423 (tabulated-list-print t)))
313 424
314;;; The mode definition: 425;;; The mode definition:
315 426