Commit | Line | Data |
---|---|---|
c80ce104 | 1 | ;;; guix-list.el --- 'List' buffer interface for displaying data -*- lexical-binding: t -*- |
457f60fa | 2 | |
51dac383 | 3 | ;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com> |
457f60fa AK |
4 | |
5 | ;; This file is part of GNU Guix. | |
6 | ||
7 | ;; GNU Guix is free software; you can redistribute it and/or modify | |
8 | ;; it under the terms of the GNU General Public License as published by | |
9 | ;; the Free Software Foundation, either version 3 of the License, or | |
10 | ;; (at your option) any later version. | |
11 | ||
12 | ;; GNU Guix is distributed in the hope that it will be useful, | |
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | ;; GNU General Public License for more details. | |
16 | ||
17 | ;; You should have received a copy of the GNU General Public License | |
18 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | ||
20 | ;;; Commentary: | |
21 | ||
c80ce104 AK |
22 | ;; This file provides 'list' buffer interface for displaying an arbitrary |
23 | ;; data. | |
457f60fa AK |
24 | |
25 | ;;; Code: | |
26 | ||
27 | (require 'cl-lib) | |
28 | (require 'tabulated-list) | |
c80ce104 | 29 | (require 'guix-buffer) |
457f60fa | 30 | (require 'guix-info) |
73ce3c19 | 31 | (require 'guix-entry) |
457f60fa AK |
32 | (require 'guix-utils) |
33 | ||
8ed2c92e | 34 | (guix-define-buffer-type list) |
46e17df6 | 35 | |
0a2a2b33 AK |
36 | (defface guix-list-file-name |
37 | '((t :inherit guix-info-file-name)) | |
38 | "Face used for file names." | |
46e17df6 | 39 | :group 'guix-list-faces) |
457f60fa | 40 | |
6dd460c8 AK |
41 | (defface guix-list-url |
42 | '((t :inherit guix-info-url)) | |
43 | "Face used for URLs." | |
44 | :group 'guix-list-faces) | |
45 | ||
d7a58c60 AK |
46 | (defface guix-list-time |
47 | '((t :inherit guix-info-time)) | |
48 | "Face used for time stamps." | |
49 | :group 'guix-list-faces) | |
50 | ||
7b31510f AK |
51 | (defun guix-list-describe (&optional mark-names) |
52 | "Describe entries marked with a general mark. | |
53 | 'Describe' means display entries in 'info' buffer. | |
54 | If no entries are marked, describe the current entry. | |
55 | With prefix argument, describe entries marked with any mark." | |
56 | (interactive (list (unless current-prefix-arg '(general)))) | |
57 | (let* ((ids (or (apply #'guix-list-get-marked-id-list mark-names) | |
58 | (list (guix-list-current-id)))) | |
59 | (count (length ids)) | |
6c40b7b7 | 60 | (entry-type (guix-buffer-current-entry-type))) |
7b31510f AK |
61 | (when (or (<= count (guix-list-describe-warning-count entry-type)) |
62 | (y-or-n-p (format "Do you really want to describe %d entries? " | |
63 | count))) | |
64 | (guix-list-describe-entries entry-type ids)))) | |
65 | ||
f768e4b3 AK |
66 | \f |
67 | ;;; Wrappers for 'list' variables | |
68 | ||
69 | (defvar guix-list-data nil | |
70 | "Alist with 'list' data. | |
7735c503 | 71 | This alist is filled by `guix-list-define-interface' macro.") |
f768e4b3 AK |
72 | |
73 | (defun guix-list-value (entry-type symbol) | |
74 | "Return SYMBOL's value for ENTRY-TYPE from `guix-list-data'." | |
75 | (symbol-value (guix-assq-value guix-list-data entry-type symbol))) | |
76 | ||
0b9cd320 AK |
77 | (defun guix-list-param-title (entry-type param) |
78 | "Return column title of an ENTRY-TYPE parameter PARAM." | |
574f6727 | 79 | (guix-buffer-param-title 'list entry-type param)) |
457f60fa | 80 | |
0b9cd320 | 81 | (defun guix-list-format (entry-type) |
457f60fa | 82 | "Return column format for ENTRY-TYPE." |
cf910cef | 83 | (guix-list-value entry-type 'format)) |
457f60fa | 84 | |
0b9cd320 AK |
85 | (defun guix-list-displayed-params (entry-type) |
86 | "Return a list of ENTRY-TYPE parameters that should be displayed." | |
87 | (mapcar #'car (guix-list-format entry-type))) | |
457f60fa | 88 | |
649df667 AK |
89 | (defun guix-list-sort-key (entry-type) |
90 | "Return sort key for ENTRY-TYPE." | |
91 | (guix-list-value entry-type 'sort-key)) | |
92 | ||
f768e4b3 AK |
93 | (defun guix-list-additional-marks (entry-type) |
94 | "Return alist of additional marks for ENTRY-TYPE." | |
95 | (guix-list-value entry-type 'marks)) | |
96 | ||
e29d1924 AK |
97 | (defun guix-list-single-entry? (entry-type) |
98 | "Return non-nil, if a single entry of ENTRY-TYPE should be listed." | |
99 | (guix-list-value entry-type 'list-single)) | |
100 | ||
7b31510f AK |
101 | (defun guix-list-describe-warning-count (entry-type) |
102 | "Return the maximum number of ENTRY-TYPE entries to describe." | |
103 | (guix-list-value entry-type 'describe-count)) | |
104 | ||
105 | (defun guix-list-describe-entries (entry-type ids) | |
106 | "Describe ENTRY-TYPE entries with IDS in 'info' buffer" | |
107 | (funcall (guix-list-value entry-type 'describe) | |
108 | ids)) | |
109 | ||
f768e4b3 AK |
110 | \f |
111 | ;;; Tabulated list internals | |
112 | ||
457f60fa AK |
113 | (defun guix-list-sort-numerically (column a b) |
114 | "Compare COLUMN of tabulated entries A and B numerically. | |
e86a43d4 | 115 | This function is used for sort predicates for `tabulated-list-format'. |
457f60fa AK |
116 | Return non-nil, if B is bigger than A." |
117 | (cl-flet ((num (entry) | |
118 | (string-to-number (aref (cadr entry) column)))) | |
119 | (> (num b) (num a)))) | |
120 | ||
e86a43d4 AK |
121 | (defmacro guix-list-define-numerical-sorter (column) |
122 | "Define numerical sort predicate for COLUMN. | |
123 | See `guix-list-sort-numerically' for details." | |
124 | (let ((name (intern (format "guix-list-sort-numerically-%d" column))) | |
125 | (doc (format "\ | |
126 | Predicate to sort tabulated list by column %d numerically. | |
127 | See `guix-list-sort-numerically' for details." | |
128 | column))) | |
129 | `(defun ,name (a b) | |
130 | ,doc | |
131 | (guix-list-sort-numerically ,column a b)))) | |
132 | ||
133 | (defmacro guix-list-define-numerical-sorters (n) | |
134 | "Define numerical sort predicates for columns from 0 to N. | |
135 | See `guix-list-define-numerical-sorter' for details." | |
136 | `(progn | |
137 | ,@(mapcar (lambda (i) | |
138 | `(guix-list-define-numerical-sorter ,i)) | |
139 | (number-sequence 0 n)))) | |
140 | ||
141 | (guix-list-define-numerical-sorters 9) | |
142 | ||
649df667 AK |
143 | (defun guix-list-tabulated-sort-key (entry-type) |
144 | "Return ENTRY-TYPE sort key for `tabulated-list-sort-key'." | |
145 | (let ((sort-key (guix-list-sort-key entry-type))) | |
146 | (and sort-key | |
147 | (cons (guix-list-param-title entry-type (car sort-key)) | |
148 | (cdr sort-key))))) | |
0b9cd320 AK |
149 | |
150 | (defun guix-list-tabulated-vector (entry-type fun) | |
457f60fa AK |
151 | "Call FUN on each column specification for ENTRY-TYPE. |
152 | ||
376af769 AK |
153 | FUN is applied to column specification as arguments (see |
154 | `guix-list-format'). | |
457f60fa AK |
155 | |
156 | Return a vector made of values of FUN calls." | |
157 | (apply #'vector | |
158 | (mapcar (lambda (col-spec) | |
376af769 | 159 | (apply fun col-spec)) |
0b9cd320 | 160 | (guix-list-format entry-type)))) |
457f60fa | 161 | |
0b9cd320 | 162 | (defun guix-list-tabulated-format (entry-type) |
457f60fa | 163 | "Return ENTRY-TYPE list specification for `tabulated-list-format'." |
0b9cd320 | 164 | (guix-list-tabulated-vector |
457f60fa | 165 | entry-type |
376af769 | 166 | (lambda (param _ &rest rest-spec) |
0b9cd320 | 167 | (cons (guix-list-param-title entry-type param) |
376af769 | 168 | rest-spec)))) |
457f60fa | 169 | |
0b9cd320 AK |
170 | (defun guix-list-tabulated-entries (entries entry-type) |
171 | "Return a list of ENTRY-TYPE values for `tabulated-list-entries'." | |
457f60fa | 172 | (mapcar (lambda (entry) |
73ce3c19 | 173 | (list (guix-entry-id entry) |
0b9cd320 | 174 | (guix-list-tabulated-entry entry entry-type))) |
457f60fa AK |
175 | entries)) |
176 | ||
0b9cd320 | 177 | (defun guix-list-tabulated-entry (entry entry-type) |
457f60fa | 178 | "Return array of values for `tabulated-list-entries'. |
0b9cd320 AK |
179 | Parameters are taken from ENTRY-TYPE ENTRY." |
180 | (guix-list-tabulated-vector | |
457f60fa | 181 | entry-type |
376af769 AK |
182 | (lambda (param fun &rest _) |
183 | (let ((val (guix-entry-value entry param))) | |
abc5b829 | 184 | (if fun |
457f60fa AK |
185 | (funcall fun val entry) |
186 | (guix-get-string val)))))) | |
187 | ||
ddf6948b AK |
188 | \f |
189 | ;;; Displaying entries | |
190 | ||
191 | (defun guix-list-get-display-entries (entry-type &rest args) | |
192 | "Search for entries and show them in a 'list' buffer preferably." | |
193 | (let ((entries (guix-buffer-get-entries 'list entry-type args))) | |
194 | (if (or (null entries) ; = 0 | |
195 | (cdr entries) ; > 1 | |
196 | (guix-list-single-entry? entry-type) | |
197 | (null (guix-buffer-value 'info entry-type 'show-entries))) | |
198 | (guix-buffer-display-entries entries 'list entry-type args 'add) | |
199 | (if (equal (guix-buffer-value 'info entry-type 'get-entries) | |
200 | (guix-buffer-value 'list entry-type 'get-entries)) | |
201 | (guix-buffer-display-entries entries 'info entry-type args 'add) | |
202 | (guix-buffer-get-display-entries 'info entry-type args 'add))))) | |
203 | ||
204 | (defun guix-list-insert-entries (entries entry-type) | |
205 | "Print ENTRY-TYPE ENTRIES in the current buffer." | |
206 | (setq tabulated-list-entries | |
207 | (guix-list-tabulated-entries entries entry-type)) | |
208 | (tabulated-list-print)) | |
209 | ||
abc5b829 AK |
210 | (defun guix-list-get-one-line (val &optional _) |
211 | "Return one-line string from a multi-line string VAL. | |
212 | VAL may be nil." | |
213 | (if val | |
214 | (guix-get-one-line val) | |
215 | (guix-get-string nil))) | |
457f60fa AK |
216 | |
217 | (defun guix-list-get-time (seconds &optional _) | |
218 | "Return formatted time string from SECONDS." | |
d7a58c60 AK |
219 | (guix-get-string (guix-get-time-string seconds) |
220 | 'guix-list-time)) | |
457f60fa | 221 | |
0a2a2b33 AK |
222 | (defun guix-list-get-file-name (file-name &optional _) |
223 | "Return FILE-NAME button specification for `tabulated-list-entries'." | |
224 | (list file-name | |
225 | 'face 'guix-list-file-name | |
457f60fa AK |
226 | 'action (lambda (btn) (find-file (button-label btn))) |
227 | 'follow-link t | |
228 | 'help-echo "Find file")) | |
229 | ||
6dd460c8 AK |
230 | (defun guix-list-get-url (url &optional _) |
231 | "Return URL button specification for `tabulated-list-entries'." | |
232 | (list url | |
233 | 'face 'guix-list-url | |
234 | 'action (lambda (btn) (browse-url (button-label btn))) | |
235 | 'follow-link t | |
236 | 'help-echo "Browse URL")) | |
237 | ||
6c40b7b7 AK |
238 | \f |
239 | ;;; 'List' lines | |
240 | ||
457f60fa | 241 | (defun guix-list-current-id () |
6c40b7b7 | 242 | "Return ID of the entry at point." |
457f60fa AK |
243 | (or (tabulated-list-get-id) |
244 | (user-error "No entry here"))) | |
245 | ||
246 | (defun guix-list-current-entry () | |
6c40b7b7 AK |
247 | "Return entry at point." |
248 | (guix-entry-by-id (guix-list-current-id) | |
249 | (guix-buffer-current-entries))) | |
457f60fa AK |
250 | |
251 | (defun guix-list-for-each-line (fun &rest args) | |
252 | "Call FUN with ARGS for each entry line." | |
253 | (or (derived-mode-p 'guix-list-mode) | |
254 | (error "The current buffer is not in Guix List mode")) | |
255 | (save-excursion | |
256 | (goto-char (point-min)) | |
257 | (while (not (eobp)) | |
258 | (apply fun args) | |
259 | (forward-line)))) | |
260 | ||
261 | (defun guix-list-fold-lines (fun init) | |
262 | "Fold over entry lines in the current list buffer. | |
263 | Call FUN with RESULT as argument for each line, using INIT as | |
264 | the initial value of RESULT. Return the final result." | |
265 | (let ((res init)) | |
266 | (guix-list-for-each-line | |
267 | (lambda () (setq res (funcall fun res)))) | |
268 | res)) | |
269 | ||
270 | \f | |
271 | ;;; Marking and sorting | |
272 | ||
273 | (defvar-local guix-list-marked nil | |
274 | "List of the marked entries. | |
275 | Each element of the list has a form: | |
276 | ||
277 | (ID MARK-NAME . ARGS) | |
278 | ||
279 | ID is an entry ID. | |
f768e4b3 | 280 | MARK-NAME is a symbol from `guix-list-marks'. |
457f60fa AK |
281 | ARGS is a list of additional values.") |
282 | ||
f768e4b3 AK |
283 | (defvar-local guix-list-marks nil |
284 | "Alist of available mark names and mark characters.") | |
285 | ||
286 | (defvar guix-list-default-marks | |
457f60fa AK |
287 | '((empty . ?\s) |
288 | (general . ?*)) | |
f768e4b3 AK |
289 | "Alist of default mark names and mark characters.") |
290 | ||
291 | (defun guix-list-marks (entry-type) | |
292 | "Return alist of available marks for ENTRY-TYPE." | |
293 | (append guix-list-default-marks | |
294 | (guix-list-additional-marks entry-type))) | |
457f60fa | 295 | |
f768e4b3 | 296 | (defun guix-list-get-mark (name) |
457f60fa | 297 | "Return mark character by its NAME." |
f768e4b3 | 298 | (or (guix-assq-value guix-list-marks name) |
457f60fa AK |
299 | (error "Mark '%S' not found" name))) |
300 | ||
f768e4b3 | 301 | (defun guix-list-get-mark-string (name) |
457f60fa AK |
302 | "Return mark string by its NAME." |
303 | (string (guix-list-get-mark name))) | |
304 | ||
305 | (defun guix-list-current-mark () | |
306 | "Return mark character of the current line." | |
307 | (char-after (line-beginning-position))) | |
308 | ||
309 | (defun guix-list-get-marked (&rest mark-names) | |
310 | "Return list of specs of entries marked with any mark from MARK-NAMES. | |
311 | Entry specs are elements from `guix-list-marked' list. | |
312 | If MARK-NAMES are not specified, use all marks from | |
f768e4b3 | 313 | `guix-list-marks' except the `empty' one." |
457f60fa AK |
314 | (or mark-names |
315 | (setq mark-names | |
316 | (delq 'empty | |
f768e4b3 | 317 | (mapcar #'car guix-list-marks)))) |
457f60fa AK |
318 | (cl-remove-if-not (lambda (assoc) |
319 | (memq (cadr assoc) mark-names)) | |
320 | guix-list-marked)) | |
321 | ||
322 | (defun guix-list-get-marked-args (mark-name) | |
323 | "Return list of (ID . ARGS) elements from lines marked with MARK-NAME. | |
324 | See `guix-list-marked' for the meaning of ARGS." | |
325 | (mapcar (lambda (spec) | |
326 | (let ((id (car spec)) | |
327 | (args (cddr spec))) | |
328 | (cons id args))) | |
329 | (guix-list-get-marked mark-name))) | |
330 | ||
331 | (defun guix-list-get-marked-id-list (&rest mark-names) | |
332 | "Return list of IDs of entries marked with any mark from MARK-NAMES. | |
333 | See `guix-list-get-marked' for details." | |
334 | (mapcar #'car (apply #'guix-list-get-marked mark-names))) | |
335 | ||
578b98da | 336 | (defun guix-list--mark (mark-name &optional advance &rest args) |
457f60fa AK |
337 | "Put a mark on the current line. |
338 | Also add the current entry to `guix-list-marked' using its ID and ARGS. | |
f768e4b3 | 339 | MARK-NAME is a symbol from `guix-list-marks'. |
578b98da | 340 | If ADVANCE is non-nil, move forward by one line after marking." |
457f60fa AK |
341 | (let ((id (guix-list-current-id))) |
342 | (if (eq mark-name 'empty) | |
343 | (setq guix-list-marked (assq-delete-all id guix-list-marked)) | |
344 | (let ((assoc (assq id guix-list-marked)) | |
345 | (val (cons mark-name args))) | |
346 | (if assoc | |
347 | (setcdr assoc val) | |
348 | (push (cons id val) guix-list-marked))))) | |
349 | (tabulated-list-put-tag (guix-list-get-mark-string mark-name) | |
350 | advance)) | |
351 | ||
578b98da AK |
352 | (defun guix-list-mark (&optional arg) |
353 | "Mark the current line and move to the next line. | |
354 | With ARG, mark all lines." | |
355 | (interactive "P") | |
356 | (if arg | |
357 | (guix-list-mark-all) | |
358 | (guix-list--mark 'general t))) | |
359 | ||
360 | (defun guix-list-mark-all (&optional mark-name) | |
457f60fa | 361 | "Mark all lines with MARK-NAME mark. |
f768e4b3 | 362 | MARK-NAME is a symbol from `guix-list-marks'. |
457f60fa | 363 | Interactively, put a general mark on all lines." |
578b98da AK |
364 | (interactive) |
365 | (or mark-name (setq mark-name 'general)) | |
366 | (guix-list-for-each-line #'guix-list--mark mark-name)) | |
457f60fa | 367 | |
91cc37a1 AK |
368 | (defun guix-list-unmark (&optional arg) |
369 | "Unmark the current line and move to the next line. | |
370 | With ARG, unmark all lines." | |
371 | (interactive "P") | |
372 | (if arg | |
373 | (guix-list-unmark-all) | |
578b98da | 374 | (guix-list--mark 'empty t))) |
457f60fa AK |
375 | |
376 | (defun guix-list-unmark-backward () | |
377 | "Move up one line and unmark it." | |
378 | (interactive) | |
379 | (forward-line -1) | |
578b98da | 380 | (guix-list--mark 'empty)) |
457f60fa AK |
381 | |
382 | (defun guix-list-unmark-all () | |
383 | "Unmark all lines." | |
384 | (interactive) | |
385 | (guix-list-mark-all 'empty)) | |
386 | ||
387 | (defun guix-list-restore-marks () | |
f768e4b3 | 388 | "Put marks according to `guix-list-marked'." |
457f60fa AK |
389 | (guix-list-for-each-line |
390 | (lambda () | |
51dac383 AK |
391 | (let ((mark-name (car (guix-assq-value guix-list-marked |
392 | (guix-list-current-id))))) | |
457f60fa AK |
393 | (tabulated-list-put-tag |
394 | (guix-list-get-mark-string (or mark-name 'empty))))))) | |
395 | ||
396 | (defun guix-list-sort (&optional n) | |
397 | "Sort guix list entries by the column at point. | |
398 | With a numeric prefix argument N, sort the Nth column. | |
399 | Same as `tabulated-list-sort', but also restore marks after sorting." | |
400 | (interactive "P") | |
401 | (tabulated-list-sort n) | |
402 | (guix-list-restore-marks)) | |
403 | ||
404 | \f | |
78fdbdc0 AK |
405 | ;;; Major mode and interface definer |
406 | ||
457f60fa AK |
407 | (defvar guix-list-mode-map |
408 | (let ((map (make-sparse-keymap))) | |
74cc6737 | 409 | (set-keymap-parent |
e40b3c33 | 410 | map (make-composed-keymap guix-buffer-map |
74cc6737 | 411 | tabulated-list-mode-map)) |
dfeb0239 | 412 | (define-key map (kbd "RET") 'guix-list-describe) |
7b31510f | 413 | (define-key map (kbd "i") 'guix-list-describe) |
457f60fa AK |
414 | (define-key map (kbd "m") 'guix-list-mark) |
415 | (define-key map (kbd "*") 'guix-list-mark) | |
457f60fa | 416 | (define-key map (kbd "u") 'guix-list-unmark) |
457f60fa AK |
417 | (define-key map (kbd "DEL") 'guix-list-unmark-backward) |
418 | (define-key map [remap tabulated-list-sort] 'guix-list-sort) | |
419 | map) | |
78fdbdc0 | 420 | "Keymap for `guix-list-mode' buffers.") |
457f60fa AK |
421 | |
422 | (define-derived-mode guix-list-mode tabulated-list-mode "Guix-List" | |
78fdbdc0 AK |
423 | "Parent mode for displaying data in 'list' form.") |
424 | ||
425 | (defun guix-list-mode-initialize (entry-type) | |
426 | "Set up the current 'list' buffer for displaying ENTRY-TYPE entries." | |
427 | (setq tabulated-list-padding 2 | |
428 | tabulated-list-format (guix-list-tabulated-format entry-type) | |
429 | tabulated-list-sort-key (guix-list-tabulated-sort-key entry-type)) | |
430 | (setq-local guix-list-marks (guix-list-marks entry-type)) | |
431 | (tabulated-list-init-header)) | |
457f60fa | 432 | |
7735c503 AK |
433 | (defmacro guix-list-define-interface (entry-type &rest args) |
434 | "Define 'list' interface for displaying ENTRY-TYPE entries. | |
cf910cef | 435 | Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... |
457f60fa | 436 | |
cf910cef AK |
437 | Required keywords: |
438 | ||
439 | - `:format' - default value of the generated | |
440 | `guix-ENTRY-TYPE-list-format' variable. | |
441 | ||
442 | Optional keywords: | |
457f60fa | 443 | |
649df667 AK |
444 | - `:sort-key' - default value of the generated |
445 | `guix-ENTRY-TYPE-list-sort-key' variable. | |
457f60fa | 446 | |
7b31510f AK |
447 | - `:describe-function' - default value of the generated |
448 | `guix-ENTRY-TYPE-describe-function' variable. | |
449 | ||
e29d1924 AK |
450 | - `:list-single?' - default value of the generated |
451 | `guix-ENTRY-TYPE-list-single' variable. | |
452 | ||
f768e4b3 AK |
453 | - `:marks' - default value of the generated |
454 | `guix-ENTRY-TYPE-list-marks' variable. | |
7735c503 AK |
455 | |
456 | The rest keyword arguments are passed to | |
457 | `guix-buffer-define-interface' macro." | |
458 | (declare (indent 1)) | |
7b31510f AK |
459 | (let* ((entry-type-str (symbol-name entry-type)) |
460 | (prefix (concat "guix-" entry-type-str "-list")) | |
461 | (group (intern prefix)) | |
7b31510f AK |
462 | (describe-var (intern (concat prefix "-describe-function"))) |
463 | (describe-count-var (intern (concat prefix | |
464 | "-describe-warning-count"))) | |
cf910cef | 465 | (format-var (intern (concat prefix "-format"))) |
649df667 | 466 | (sort-key-var (intern (concat prefix "-sort-key"))) |
e29d1924 | 467 | (list-single-var (intern (concat prefix "-single"))) |
7b31510f | 468 | (marks-var (intern (concat prefix "-marks")))) |
4ba476f9 | 469 | (guix-keyword-args-let args |
6c40b7b7 AK |
470 | ((show-entries-val :show-entries-function) |
471 | (describe-val :describe-function) | |
7b31510f | 472 | (describe-count-val :describe-count 10) |
cf910cef | 473 | (format-val :format) |
649df667 | 474 | (sort-key-val :sort-key) |
e29d1924 | 475 | (list-single-val :list-single?) |
7b31510f | 476 | (marks-val :marks)) |
4ba476f9 | 477 | `(progn |
cf910cef AK |
478 | (defcustom ,format-var ,format-val |
479 | ,(format "\ | |
480 | List of format values of the displayed columns. | |
481 | Each element of the list has a form: | |
482 | ||
483 | (PARAM VALUE-FUN WIDTH SORT . PROPS) | |
484 | ||
485 | PARAM is a name of '%s' entry parameter. | |
486 | ||
487 | VALUE-FUN may be either nil or a function returning a value that | |
488 | will be inserted. The function is called with 2 arguments: the | |
489 | first one is the value of the parameter; the second one is an | |
490 | entry (alist of parameter names and values). | |
491 | ||
492 | For the meaning of WIDTH, SORT and PROPS, see | |
493 | `tabulated-list-format'." | |
494 | entry-type-str) | |
495 | :type 'sexp | |
496 | :group ',group) | |
497 | ||
649df667 AK |
498 | (defcustom ,sort-key-var ,sort-key-val |
499 | ,(format "\ | |
500 | Default sort key for 'list' buffer with '%s' entries. | |
501 | Should be nil (no sort) or have a form: | |
502 | ||
503 | (PARAM . FLIP) | |
504 | ||
505 | PARAM is the name of '%s' entry parameter. For the meaning of | |
506 | FLIP, see `tabulated-list-sort-key'." | |
507 | entry-type-str entry-type-str) | |
508 | :type '(choice (const :tag "No sort" nil) | |
509 | (cons symbol boolean)) | |
510 | :group ',group) | |
511 | ||
f768e4b3 AK |
512 | (defvar ,marks-var ,marks-val |
513 | ,(format "\ | |
514 | Alist of additional marks for 'list' buffer with '%s' entries. | |
515 | Marks from this list are used along with `guix-list-default-marks'." | |
516 | entry-type-str)) | |
4ba476f9 | 517 | |
e29d1924 AK |
518 | (defcustom ,list-single-var ,list-single-val |
519 | ,(format "\ | |
520 | If non-nil, list '%s' entry even if it is the only matching result. | |
521 | If nil, show a single '%s' entry in the 'info' buffer." | |
522 | entry-type-str entry-type-str) | |
523 | :type 'boolean | |
524 | :group ',group) | |
525 | ||
7b31510f AK |
526 | (defcustom ,describe-count-var ,describe-count-val |
527 | ,(format "\ | |
528 | The maximum number of '%s' entries to describe without a warning. | |
529 | If a user wants to describe more than this number of marked | |
530 | entries, he will be prompted for confirmation. | |
531 | See also `guix-list-describe'." | |
532 | entry-type-str) | |
533 | :type 'integer | |
534 | :group ',group) | |
535 | ||
536 | (defvar ,describe-var ,describe-val | |
537 | ,(format "Function used to describe '%s' entries." | |
538 | entry-type-str)) | |
539 | ||
f768e4b3 | 540 | (guix-alist-put! |
7b31510f AK |
541 | '((describe . ,describe-var) |
542 | (describe-count . ,describe-count-var) | |
cf910cef | 543 | (format . ,format-var) |
649df667 | 544 | (sort-key . ,sort-key-var) |
e29d1924 | 545 | (list-single . ,list-single-var) |
7b31510f | 546 | (marks . ,marks-var)) |
7735c503 | 547 | 'guix-list-data ',entry-type) |
457f60fa | 548 | |
6c40b7b7 AK |
549 | ,(if show-entries-val |
550 | `(guix-buffer-define-interface list ,entry-type | |
551 | :show-entries-function ,show-entries-val | |
552 | ,@%foreign-args) | |
553 | ||
554 | (let ((insert-fun (intern (concat prefix "-insert-entries"))) | |
555 | (mode-init-fun (intern (concat prefix "-mode-initialize")))) | |
556 | `(progn | |
557 | (defun ,insert-fun (entries) | |
558 | ,(format "\ | |
559 | Print '%s' ENTRIES in the current 'list' buffer." | |
560 | entry-type-str) | |
561 | (guix-list-insert-entries entries ',entry-type)) | |
562 | ||
563 | (defun ,mode-init-fun () | |
564 | ,(format "\ | |
565 | Set up the current 'list' buffer for displaying '%s' entries." | |
566 | entry-type-str) | |
567 | (guix-list-mode-initialize ',entry-type)) | |
568 | ||
569 | (guix-buffer-define-interface list ,entry-type | |
570 | :insert-entries-function ',insert-fun | |
571 | :mode-init-function ',mode-init-fun | |
572 | ,@%foreign-args)))))))) | |
457f60fa AK |
573 | |
574 | \f | |
7735c503 AK |
575 | (defvar guix-list-font-lock-keywords |
576 | (eval-when-compile | |
577 | `((,(rx "(" (group "guix-list-define-interface") | |
578 | symbol-end) | |
579 | . 1)))) | |
580 | ||
581 | (font-lock-add-keywords 'emacs-lisp-mode guix-list-font-lock-keywords) | |
582 | ||
457f60fa AK |
583 | (provide 'guix-list) |
584 | ||
585 | ;;; guix-list.el ends here |