Commit | Line | Data |
---|---|---|
48da7392 | 1 | ;;; tabulated-list.el --- generic major mode for tabulated lists -*- lexical-binding: t -*- |
a83ec3c9 | 2 | |
acaf905b | 3 | ;; Copyright (C) 2011-2012 Free Software Foundation, Inc. |
a83ec3c9 CY |
4 | |
5 | ;; Author: Chong Yidong <cyd@stupidchicken.com> | |
6 | ;; Keywords: extensions, lisp | |
7 | ||
8 | ;; This file is part of GNU Emacs. | |
9 | ||
10 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
11 | ;; it under the terms of the GNU General Public License as published by | |
12 | ;; the Free Software Foundation; either version 3, or (at your option) | |
13 | ;; any later version. | |
14 | ||
15 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | ;; GNU General Public License for more details. | |
19 | ||
20 | ;; You should have received a copy of the GNU General Public License | |
21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
22 | ||
23 | ;;; Commentary: | |
24 | ||
6632d361 CY |
25 | ;; This file defines Tabulated List mode, a generic major mode for |
26 | ;; displaying lists of tabulated data, intended for other major modes | |
27 | ;; to inherit from. It provides several utility routines, e.g. for | |
28 | ;; pretty-printing lines of tabulated data to fit into the appropriate | |
29 | ;; columns. | |
a83ec3c9 CY |
30 | |
31 | ;; For usage information, see the documentation of `tabulated-list-mode'. | |
32 | ||
6632d361 CY |
33 | ;; This package originated from Tom Tromey's Package Menu mode, |
34 | ;; extended and generalized to be used by other modes. | |
a83ec3c9 CY |
35 | |
36 | ;;; Code: | |
37 | ||
38 | (defvar tabulated-list-format nil | |
39 | "The format of the current Tabulated List mode buffer. | |
6632d361 CY |
40 | This should be a vector of elements (NAME WIDTH SORT . PROPS), |
41 | where: | |
a83ec3c9 | 42 | - NAME is a string describing the column. |
6632d361 CY |
43 | This is the label for the column in the header line. |
44 | Different columns must have non-`equal' names. | |
a83ec3c9 CY |
45 | - WIDTH is the width to reserve for the column. |
46 | For the final element, its numerical value is ignored. | |
47 | - SORT specifies how to sort entries by this column. | |
48 | If nil, this column cannot be used for sorting. | |
49 | If t, sort by comparing the string value printed in the column. | |
50 | Otherwise, it should be a predicate function suitable for | |
51 | `sort', accepting arguments with the same form as the elements | |
6632d361 CY |
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).") | |
a83ec3c9 CY |
57 | (make-variable-buffer-local 'tabulated-list-format) |
58 | ||
59 | (defvar tabulated-list-entries nil | |
60 | "Entries displayed in the current Tabulated List buffer. | |
61 | This should be either a function, or a list. | |
62 | If a list, each element has the form (ID [DESC1 ... DESCN]), | |
63 | where: | |
64 | - ID is nil, or a Lisp object uniquely identifying this entry, | |
65 | which is used to keep the cursor on the \"same\" entry when | |
66 | rearranging the list. Comparison is done with `equal'. | |
67 | ||
68 | - Each DESC is a column descriptor, one for each column | |
69 | specified in `tabulated-list-format'. A descriptor is either | |
70 | a string, which is printed as-is, or a list (LABEL . PROPS), | |
71 | which means to use `insert-text-button' to insert a text | |
72 | button with label LABEL and button properties PROPS. | |
73 | The string, or button label, must not contain any newline. | |
74 | ||
75 | If `tabulated-list-entries' is a function, it is called with no | |
76 | arguments and must return a list of the above form.") | |
77 | (make-variable-buffer-local 'tabulated-list-entries) | |
78 | ||
79 | (defvar tabulated-list-padding 0 | |
80 | "Number of characters preceding each Tabulated List mode entry. | |
81 | By default, lines are padded with spaces, but you can use the | |
82 | function `tabulated-list-put-tag' to change this.") | |
83 | (make-variable-buffer-local 'tabulated-list-padding) | |
84 | ||
85 | (defvar tabulated-list-revert-hook nil | |
86 | "Hook run before reverting a Tabulated List buffer. | |
87 | This is commonly used to recompute `tabulated-list-entries'.") | |
88 | ||
89 | (defvar tabulated-list-printer 'tabulated-list-print-entry | |
90 | "Function for inserting a Tabulated List entry at point. | |
91 | It is called with two arguments, ID and COLS. ID is a Lisp | |
92 | object identifying the entry, and COLS is a vector of column | |
93 | descriptors, as documented in `tabulated-list-entries'.") | |
94 | (make-variable-buffer-local 'tabulated-list-printer) | |
95 | ||
96 | (defvar tabulated-list-sort-key nil | |
97 | "Sort key for the current Tabulated List mode buffer. | |
98 | If nil, no additional sorting is performed. | |
99 | Otherwise, this should be a cons cell (NAME . FLIP). | |
100 | NAME is a string matching one of the column names in | |
101 | `tabulated-list-format' (the corresponding SORT entry in | |
102 | `tabulated-list-format' then specifies how to sort). FLIP, if | |
103 | non-nil, means to invert the resulting sort.") | |
104 | (make-variable-buffer-local 'tabulated-list-sort-key) | |
105 | ||
6632d361 CY |
106 | (defsubst tabulated-list-get-id (&optional pos) |
107 | "Return the entry ID of the Tabulated List entry at POS. | |
108 | The value is an ID object from `tabulated-list-entries', or nil. | |
a83ec3c9 CY |
109 | POS, if omitted or nil, defaults to point." |
110 | (get-text-property (or pos (point)) 'tabulated-list-id)) | |
111 | ||
6632d361 CY |
112 | (defsubst tabulated-list-get-entry (&optional pos) |
113 | "Return the Tabulated List entry at POS. | |
114 | The value is a vector of column descriptors, or nil if there is | |
115 | no entry at POS. POS, if omitted or nil, defaults to point." | |
116 | (get-text-property (or pos (point)) 'tabulated-list-entry)) | |
117 | ||
a83ec3c9 CY |
118 | (defun tabulated-list-put-tag (tag &optional advance) |
119 | "Put TAG in the padding area of the current line. | |
120 | TAG should be a string, with length <= `tabulated-list-padding'. | |
121 | If ADVANCE is non-nil, move forward by one line afterwards." | |
122 | (unless (stringp tag) | |
123 | (error "Invalid argument to `tabulated-list-put-tag'")) | |
124 | (unless (> tabulated-list-padding 0) | |
125 | (error "Unable to tag the current line")) | |
126 | (save-excursion | |
127 | (beginning-of-line) | |
6632d361 | 128 | (when (tabulated-list-get-entry) |
a83ec3c9 CY |
129 | (let ((beg (point)) |
130 | (inhibit-read-only t)) | |
131 | (forward-char tabulated-list-padding) | |
132 | (insert-and-inherit | |
6632d361 CY |
133 | (let ((width (string-width tag))) |
134 | (if (<= width tabulated-list-padding) | |
135 | (concat tag | |
136 | (make-string (- tabulated-list-padding width) ?\s)) | |
137 | (truncate-string-to-width tag tabulated-list-padding)))) | |
a83ec3c9 CY |
138 | (delete-region beg (+ beg tabulated-list-padding))))) |
139 | (if advance | |
140 | (forward-line))) | |
141 | ||
142 | (defvar tabulated-list-mode-map | |
143 | (let ((map (copy-keymap special-mode-map))) | |
144 | (set-keymap-parent map button-buffer-map) | |
145 | (define-key map "n" 'next-line) | |
146 | (define-key map "p" 'previous-line) | |
6632d361 | 147 | (define-key map "S" 'tabulated-list-sort-column) |
a83ec3c9 CY |
148 | (define-key map [follow-link] 'mouse-face) |
149 | (define-key map [mouse-2] 'mouse-select-window) | |
150 | map) | |
151 | "Local keymap for `tabulated-list-mode' buffers.") | |
152 | ||
153 | (defvar tabulated-list-sort-button-map | |
154 | (let ((map (make-sparse-keymap))) | |
155 | (define-key map [header-line mouse-1] 'tabulated-list-col-sort) | |
156 | (define-key map [header-line mouse-2] 'tabulated-list-col-sort) | |
157 | (define-key map [follow-link] 'mouse-face) | |
158 | map) | |
159 | "Local keymap for `tabulated-list-mode' sort buttons.") | |
160 | ||
16a43933 CY |
161 | (defvar tabulated-list-glyphless-char-display |
162 | (let ((table (make-char-table 'glyphless-char-display nil))) | |
163 | (set-char-table-parent table glyphless-char-display) | |
fe7a3057 | 164 | ;; Some text terminals can't display the Unicode arrows; be safe. |
16a43933 CY |
165 | (aset table 9650 (cons nil "^")) |
166 | (aset table 9660 (cons nil "v")) | |
167 | table) | |
168 | "The `glyphless-char-display' table in Tabulated List buffers.") | |
169 | ||
a83ec3c9 CY |
170 | (defun tabulated-list-init-header () |
171 | "Set up header line for the Tabulated List buffer." | |
6632d361 | 172 | (let ((x (max tabulated-list-padding 0)) |
a83ec3c9 CY |
173 | (button-props `(help-echo "Click to sort by column" |
174 | mouse-face highlight | |
175 | keymap ,tabulated-list-sort-button-map)) | |
176 | (cols nil)) | |
177 | (if (> tabulated-list-padding 0) | |
178 | (push (propertize " " 'display `(space :align-to ,x)) cols)) | |
179 | (dotimes (n (length tabulated-list-format)) | |
180 | (let* ((col (aref tabulated-list-format n)) | |
6632d361 | 181 | (label (nth 0 col)) |
a83ec3c9 | 182 | (width (nth 1 col)) |
6632d361 CY |
183 | (props (nthcdr 3 col)) |
184 | (pad-right (or (plist-get props :pad-right) 1))) | |
185 | (setq x (+ x pad-right width)) | |
a83ec3c9 CY |
186 | (and (<= tabulated-list-padding 0) |
187 | (= n 0) | |
188 | (setq label (concat " " label))) | |
189 | (push | |
190 | (cond | |
191 | ;; An unsortable column | |
192 | ((not (nth 2 col)) label) | |
193 | ;; The selected sort column | |
194 | ((equal (car col) (car tabulated-list-sort-key)) | |
195 | (apply 'propertize | |
196 | (concat label | |
197 | (cond | |
198 | ((> (+ 2 (length label)) width) | |
199 | "") | |
200 | ((cdr tabulated-list-sort-key) | |
201 | " ▲") | |
202 | (t " ▼"))) | |
203 | 'face 'bold | |
204 | 'tabulated-list-column-name (car col) | |
205 | button-props)) | |
206 | ;; Unselected sortable column. | |
207 | (t (apply 'propertize label | |
208 | 'tabulated-list-column-name (car col) | |
209 | button-props))) | |
6632d361 CY |
210 | cols) |
211 | (if (> pad-right 0) | |
212 | (push (propertize " " | |
213 | 'display `(space :align-to ,x) | |
214 | 'face 'fixed-pitch) | |
215 | cols)))) | |
a83ec3c9 CY |
216 | (setq header-line-format (mapconcat 'identity (nreverse cols) "")))) |
217 | ||
218 | (defun tabulated-list-revert (&rest ignored) | |
219 | "The `revert-buffer-function' for `tabulated-list-mode'. | |
220 | It runs `tabulated-list-revert-hook', then calls `tabulated-list-print'." | |
221 | (interactive) | |
222 | (unless (derived-mode-p 'tabulated-list-mode) | |
223 | (error "The current buffer is not in Tabulated List mode")) | |
224 | (run-hooks 'tabulated-list-revert-hook) | |
225 | (tabulated-list-print t)) | |
226 | ||
6632d361 CY |
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 | ||
a83ec3c9 CY |
238 | (defun tabulated-list-print (&optional remember-pos) |
239 | "Populate the current Tabulated List mode buffer. | |
240 | This sorts the `tabulated-list-entries' list if sorting is | |
241 | specified by `tabulated-list-sort-key'. It then erases the | |
242 | buffer and inserts the entries with `tabulated-list-printer'. | |
243 | ||
244 | Optional argument REMEMBER-POS, if non-nil, means to move point | |
245 | to the entry with the same ID element as the current line." | |
246 | (let ((inhibit-read-only t) | |
2c070447 | 247 | (entries (if (functionp tabulated-list-entries) |
a83ec3c9 CY |
248 | (funcall tabulated-list-entries) |
249 | tabulated-list-entries)) | |
250 | entry-id saved-pt saved-col) | |
251 | (and remember-pos | |
252 | (setq entry-id (tabulated-list-get-id)) | |
253 | (setq saved-col (current-column))) | |
254 | (erase-buffer) | |
255 | ;; Sort the buffers, if necessary. | |
6632d361 CY |
256 | (when (and tabulated-list-sort-key |
257 | (car tabulated-list-sort-key)) | |
258 | (let* ((sort-column (car tabulated-list-sort-key)) | |
259 | (n (tabulated-list--column-number sort-column)) | |
260 | (sorter (nth 2 (aref tabulated-list-format n)))) | |
261 | ;; Is the specified column sortable? | |
262 | (when sorter | |
a83ec3c9 CY |
263 | (when (eq sorter t) |
264 | (setq sorter ; Default sorter checks column N: | |
e67a13ab CY |
265 | (lambda (A B) |
266 | (setq A (aref (cadr A) n)) | |
267 | (setq B (aref (cadr B) n)) | |
268 | (string< (if (stringp A) A (car A)) | |
269 | (if (stringp B) B (car B)))))) | |
a83ec3c9 CY |
270 | (setq entries (sort entries sorter)) |
271 | (if (cdr tabulated-list-sort-key) | |
272 | (setq entries (nreverse entries))) | |
2c070447 | 273 | (unless (functionp tabulated-list-entries) |
a83ec3c9 CY |
274 | (setq tabulated-list-entries entries))))) |
275 | ;; Print the resulting list. | |
276 | (dolist (elt entries) | |
277 | (and entry-id | |
278 | (equal entry-id (car elt)) | |
279 | (setq saved-pt (point))) | |
280 | (apply tabulated-list-printer elt)) | |
281 | (set-buffer-modified-p nil) | |
282 | ;; If REMEMBER-POS was specified, move to the "old" location. | |
283 | (if saved-pt | |
284 | (progn (goto-char saved-pt) | |
3e26a4a2 CY |
285 | (move-to-column saved-col) |
286 | (recenter)) | |
a83ec3c9 CY |
287 | (goto-char (point-min))))) |
288 | ||
289 | (defun tabulated-list-print-entry (id cols) | |
290 | "Insert a Tabulated List entry at point. | |
291 | This is the default `tabulated-list-printer' function. ID is a | |
292 | Lisp object identifying the entry to print, and COLS is a vector | |
293 | of column descriptors." | |
6632d361 CY |
294 | (let ((beg (point)) |
295 | (x (max tabulated-list-padding 0)) | |
296 | (ncols (length tabulated-list-format)) | |
297 | (inhibit-read-only t)) | |
a83ec3c9 CY |
298 | (if (> tabulated-list-padding 0) |
299 | (insert (make-string x ?\s))) | |
6632d361 CY |
300 | (dotimes (n ncols) |
301 | (setq x (tabulated-list-print-col n (aref cols n) x))) | |
a83ec3c9 | 302 | (insert ?\n) |
6632d361 CY |
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. | |
308 | N is the column number, COL-DESC is a column descriptor \(see | |
309 | `tabulated-list-entries'), and X is the column number at point. | |
310 | Return 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. | |
337 | Return a list (ID COLS), where ID is the ID of the deleted entry | |
338 | and COLS is a vector of its column descriptors. Move point to | |
339 | the beginning of the deleted entry. Return nil if there is no | |
340 | entry at point. | |
341 | ||
342 | This 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. | |
354 | COL is the column number to change, or the name of the column to change. | |
355 | DESC is the new column descriptor, which is inserted via | |
356 | `tabulated-list-print-col'. | |
357 | ||
358 | If CHANGE-ENTRY-DATA is non-nil, modify the underlying entry data | |
359 | by setting the appropriate slot of the vector originally used to | |
360 | print this entry. If `tabulated-list-entries' has a list value, | |
361 | this 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)))) | |
a83ec3c9 CY |
393 | |
394 | (defun tabulated-list-col-sort (&optional e) | |
395 | "Sort Tabulated List entries by the column of the mouse click E." | |
396 | (interactive "e") | |
397 | (let* ((pos (event-start e)) | |
398 | (obj (posn-object pos)) | |
399 | (name (get-text-property (if obj (cdr obj) (posn-point pos)) | |
400 | 'tabulated-list-column-name | |
401 | (car obj)))) | |
402 | (with-current-buffer (window-buffer (posn-window pos)) | |
6632d361 CY |
403 | (tabulated-list--sort-by-column-name name)))) |
404 | ||
405 | (defun tabulated-list-sort-column (&optional n) | |
406 | "Sort Tabulated List entries by the column at point. | |
407 | With a numeric prefix argument N, sort the Nth column." | |
408 | (interactive "P") | |
409 | (let ((name (if n | |
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))) | |
a83ec3c9 CY |
424 | |
425 | ;;; The mode definition: | |
426 | ||
427 | ;;;###autoload | |
428 | (define-derived-mode tabulated-list-mode special-mode "Tabulated" | |
429 | "Generic major mode for browsing a list of items. | |
430 | This mode is usually not used directly; instead, other major | |
431 | modes are derived from it, using `define-derived-mode'. | |
432 | ||
433 | In this major mode, the buffer is divided into multiple columns, | |
09e80d9f | 434 | which are labeled using the header line. Each non-empty line |
a83ec3c9 CY |
435 | belongs to one \"entry\", and the entries can be sorted according |
436 | to their column values. | |
437 | ||
438 | An inheriting mode should usually do the following in their body: | |
439 | ||
440 | - Set `tabulated-list-format', specifying the column format. | |
441 | - Set `tabulated-list-revert-hook', if the buffer contents need | |
442 | to be specially recomputed prior to `revert-buffer'. | |
443 | - Maybe set a `tabulated-list-entries' function (see below). | |
444 | - Maybe set `tabulated-list-printer' (see below). | |
445 | - Maybe set `tabulated-list-padding'. | |
446 | - Call `tabulated-list-init-header' to initialize `header-line-format' | |
447 | according to `tabulated-list-format'. | |
448 | ||
449 | An inheriting mode is usually accompanied by a \"list-FOO\" | |
450 | command (e.g. `list-packages', `list-processes'). This command | |
451 | creates or switches to a buffer and enables the major mode in | |
452 | that buffer. If `tabulated-list-entries' is not a function, the | |
453 | command should initialize it to a list of entries for displaying. | |
454 | Finally, it should call `tabulated-list-print'. | |
455 | ||
456 | `tabulated-list-print' calls the printer function specified by | |
457 | `tabulated-list-printer', once for each entry. The default | |
458 | printer is `tabulated-list-print-entry', but a mode that keeps | |
459 | data in an ewoc may instead specify a printer function (e.g., one | |
460 | that calls `ewoc-enter-last'), with `tabulated-list-print-entry' | |
461 | as the ewoc pretty-printer." | |
462 | (setq truncate-lines t) | |
463 | (setq buffer-read-only t) | |
464 | (set (make-local-variable 'revert-buffer-function) | |
16a43933 CY |
465 | 'tabulated-list-revert) |
466 | (set (make-local-variable 'glyphless-char-display) | |
467 | tabulated-list-glyphless-char-display)) | |
a83ec3c9 CY |
468 | |
469 | (put 'tabulated-list-mode 'mode-class 'special) | |
470 | ||
471 | (provide 'tabulated-list) | |
472 | ||
473 | ;; Local Variables: | |
474 | ;; coding: utf-8 | |
a83ec3c9 CY |
475 | ;; End: |
476 | ||
477 | ;;; tabulated-list.el ends here |