guix-ui-generation.el 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. ;;; guix-ui-generation.el --- Interface for displaying generations -*- lexical-binding: t -*-
  2. ;; Copyright © 2014–2018 Alex Kost <alezost@gmail.com>
  3. ;; This file is part of Emacs-Guix.
  4. ;; Emacs-Guix is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation, either version 3 of the License, or
  7. ;; (at your option) any later version.
  8. ;;
  9. ;; Emacs-Guix is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with Emacs-Guix. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This file provides an interface for displaying profile generations in
  18. ;; 'list' and 'info' buffers, and commands for working with them.
  19. ;;; Code:
  20. (require 'cl-lib)
  21. (require 'dash)
  22. (require 'bui)
  23. (require 'guix nil t)
  24. (require 'guix-ui)
  25. (require 'guix-ui-package)
  26. (require 'guix-ui-profile)
  27. (require 'guix-ui-store-item)
  28. (require 'guix-misc)
  29. (require 'guix-repl)
  30. (require 'guix-guile)
  31. (require 'guix-utils)
  32. (require 'guix-profiles)
  33. (guix-ui-define-entry-type generation)
  34. (defun guix-generation-get-entries (proc profile search-type
  35. search-values params)
  36. "Return 'generation' or 'system-generation' entries.
  37. PROC is the name of a Scheme procedure (either 'generation-sexps'
  38. or 'system-generation-sexps')."
  39. (apply #'guix-modify-objects
  40. (guix-eval-read (guix-make-guile-expression
  41. proc profile search-type search-values params))
  42. (when (or (null params)
  43. (memq 'number-of-packages params))
  44. (list
  45. (lambda (entry)
  46. (let ((generation (bui-entry-non-void-value entry 'number)))
  47. (if generation
  48. `((number-of-packages
  49. . ,(guix-profile-number-of-packages
  50. profile generation))
  51. ,@entry)
  52. entry)))))))
  53. (defun guix-generation-get-display (profile search-type &rest search-values)
  54. "Search for generations and show results.
  55. If PROFILE is nil, use `guix-current-profile'.
  56. See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
  57. SEARCH-VALUES."
  58. (apply #'bui-list-get-display-entries
  59. 'guix-generation
  60. (or profile guix-current-profile)
  61. search-type search-values))
  62. (defun guix-delete-generations (profile generations
  63. &optional operation-buffer)
  64. "Delete GENERATIONS from PROFILE.
  65. Each element from GENERATIONS is a generation number."
  66. (when (or (not guix-operation-confirm)
  67. (y-or-n-p
  68. (let ((count (length generations)))
  69. (if (> count 1)
  70. (format "Delete %d generations from profile '%s'? "
  71. count profile)
  72. (format "Delete generation %d from profile '%s'? "
  73. (car generations) profile)))))
  74. (guix-eval-in-repl
  75. (guix-make-guile-expression
  76. 'delete-generations* profile generations)
  77. operation-buffer)))
  78. (defun guix-switch-to-generation (profile generation
  79. &optional operation-buffer)
  80. "Switch PROFILE to GENERATION."
  81. (when (or (not guix-operation-confirm)
  82. (y-or-n-p (format "Switch profile '%s' to generation %d? "
  83. profile generation)))
  84. (guix-eval-in-repl
  85. (guix-make-guile-expression
  86. 'switch-to-generation* profile generation)
  87. operation-buffer)))
  88. (defun guix-generation-current-package-profile (&optional generation)
  89. "Return a directory where packages are installed for the
  90. current profile's GENERATION."
  91. (guix-package-profile (guix-ui-current-profile) generation))
  92. ;;; Generation 'info'
  93. (guix-ui-define-interface generation info
  94. :mode-name "Generation-Info"
  95. :buffer-name "*Guix Generation Info*"
  96. :get-entries-function 'guix-generation-info-get-entries
  97. :format '(guix-generation-info-insert-heading
  98. nil
  99. guix-generation-info-insert-buttons
  100. (prev-number format guix-generation-info-insert-previous)
  101. (current format guix-generation-info-insert-current)
  102. (number-of-packages format guix-generation-info-insert-packages)
  103. (file-name simple (guix-generation-info-insert-file-name))
  104. (time format (time)))
  105. :hint 'guix-generation-info-hint
  106. :titles '((prev-number . "Prev. generation"))
  107. :required '(id number))
  108. (defun guix-generation-info-hint ()
  109. (bui-format-hints
  110. guix-ui-hint
  111. (bui-default-hint)))
  112. (defface guix-generation-info-heading
  113. '((t :inherit bui-info-heading))
  114. "Face used for generation heading."
  115. :group 'guix-generation-info-faces)
  116. (defface guix-generation-info-current
  117. '((t :inherit guix-true))
  118. "Face used if a generation is the current one."
  119. :group 'guix-generation-info-faces)
  120. (defface guix-generation-info-not-current
  121. '((t :inherit guix-false))
  122. "Face used if a generation is not the current one."
  123. :group 'guix-generation-info-faces)
  124. (defun guix-generation-info-get-entries (profile search-type
  125. &rest search-values)
  126. "Return 'generation' entries for displaying them in 'info' buffer."
  127. (guix-generation-get-entries
  128. 'generation-sexps
  129. profile search-type search-values
  130. (cl-union guix-generation-info-required-params
  131. (bui-info-displayed-params 'guix-generation))))
  132. (defun guix-generation-info-insert-heading (entry)
  133. "Insert generation ENTRY heading at point."
  134. (bui-format-insert
  135. (concat "Generation "
  136. (number-to-string (bui-entry-value entry 'number)))
  137. 'guix-generation-info-heading)
  138. (bui-newline))
  139. (defun guix-generation-info-insert-profile-button (file-name)
  140. "Insert 'Profile' button for generation FILE-NAME."
  141. (let ((profile (guix-generation-file-name->profile file-name)))
  142. (bui-insert-action-button
  143. "Profile"
  144. (lambda (btn)
  145. (bui-get-display-entries
  146. 'guix-profile 'info
  147. (list 'file-name (button-get btn 'file-name))))
  148. (format "Show profile '%s'" profile)
  149. 'file-name profile)))
  150. (defun guix-generation-info-insert-buttons (entry)
  151. "Insert some buttons for generation ENTRY at point."
  152. (let ((file-name (bui-entry-non-void-value entry 'file-name)))
  153. (guix-generation-info-insert-profile-button file-name)
  154. (bui-insert-indent)
  155. (guix-profile-info-insert-search-paths-button file-name)
  156. (bui-newline)))
  157. (defun guix-generation-info-insert-previous (prev-number entry)
  158. "Insert PREV-NUMBER and button to compare generations."
  159. (bui-format-insert prev-number)
  160. (bui-insert-indent)
  161. (when (> prev-number 0)
  162. (let ((number (bui-entry-non-void-value entry 'number)))
  163. (bui-insert-action-button
  164. "Compare"
  165. (lambda (btn)
  166. (guix-diff
  167. (guix-profile-generation-packages-buffer
  168. (button-get btn 'prev-number))
  169. (guix-profile-generation-packages-buffer
  170. (button-get btn 'number))))
  171. (format "Show Diff of packages installed in generations %d and %d"
  172. prev-number number)
  173. 'prev-number prev-number
  174. 'number number))))
  175. (defun guix-generation-info-insert-packages (number entry)
  176. "Insert the NUMBER of packages and button to display packages."
  177. (bui-format-insert number)
  178. (bui-insert-indent)
  179. (let ((number (bui-entry-non-void-value entry 'number)))
  180. (bui-insert-action-button
  181. "Packages"
  182. (lambda (btn)
  183. (guix-package-get-display
  184. (guix-generation-current-package-profile
  185. (button-get btn 'number))
  186. 'installed))
  187. (format "Show packages installed in generation %d" number)
  188. 'number number)))
  189. (defun guix-generation-info-insert-current (val entry)
  190. "Insert boolean value VAL showing whether this generation is current."
  191. (if val
  192. (bui-info-insert-value-format "Yes" 'guix-generation-info-current)
  193. (bui-info-insert-value-format "No" 'guix-generation-info-not-current)
  194. (bui-insert-indent)
  195. (let ((number (bui-entry-non-void-value entry 'number)))
  196. (bui-insert-action-button
  197. "Switch"
  198. (lambda (btn)
  199. (guix-switch-to-generation (guix-ui-current-profile)
  200. (button-get btn 'number)
  201. (current-buffer)))
  202. (format "Switch to generation %d (make it the current one)"
  203. number)
  204. 'number number)
  205. (bui-insert-indent)
  206. (bui-insert-action-button
  207. "Delete"
  208. (lambda (btn)
  209. (guix-delete-generations (guix-ui-current-profile)
  210. (list (button-get btn 'number))
  211. (current-buffer)))
  212. (format "Delete generation %d" number)
  213. 'number number))))
  214. (defun guix-generation-info-insert-file-name (file-name)
  215. "Insert generation FILE-NAME at point."
  216. (bui-info-insert-value-indent file-name 'bui-file)
  217. (guix-info-insert-store-items (file-truename file-name)))
  218. ;;; Generation 'list'
  219. (guix-ui-define-interface generation list
  220. :mode-name "Generation-List"
  221. :buffer-name "*Guix Generations*"
  222. :get-entries-function 'guix-generation-list-get-entries
  223. :describe-function 'guix-ui-list-describe
  224. :format '((number nil 5 bui-list-sort-numerically-0 :right-align t)
  225. (current guix-generation-list-get-current 10 t)
  226. (number-of-packages nil 11 bui-list-sort-numerically-2
  227. :right-align t)
  228. (time bui-list-get-time 20 t)
  229. (file-name bui-list-get-file-name 30 t))
  230. :titles '((number . "N.")
  231. (number-of-packages . "Packages"))
  232. :hint 'guix-generation-list-hint
  233. :sort-key '(number . t)
  234. :marks '((delete . ?D)))
  235. (let ((map guix-generation-list-mode-map))
  236. (define-key map (kbd "E") 'guix-generation-list-show-search-paths)
  237. (define-key map (kbd "P") 'guix-generation-list-show-packages)
  238. (define-key map (kbd "+") 'guix-generation-list-show-added-packages)
  239. (define-key map (kbd "-") 'guix-generation-list-show-removed-packages)
  240. (define-key map (kbd "=") 'guix-generation-list-diff)
  241. (define-key map (kbd "e") 'guix-generation-list-ediff)
  242. (define-key map (kbd "x") 'guix-generation-list-execute)
  243. (define-key map (kbd "c") 'guix-generation-list-set-current)
  244. (define-key map (kbd "d") 'guix-generation-list-mark-delete))
  245. (defvar guix-generation-list-default-hint
  246. '(("\\[guix-generation-list-show-packages]") " show packages;\n"
  247. ("\\[guix-generation-list-show-search-paths]") " show search paths;\n"
  248. ("\\[guix-generation-list-set-current]") " set current generation;\n"
  249. ("\\[guix-generation-list-diff]") " show Diff of the marked generations;\n"
  250. ("\\[guix-generation-list-mark-delete]") " mark for deletion; "
  251. ("\\[guix-generation-list-execute]") " execute operation (deletions);\n"))
  252. (defun guix-generation-list-hint ()
  253. (bui-format-hints
  254. guix-generation-list-default-hint
  255. guix-ui-hint
  256. (bui-default-hint)))
  257. (defun guix-generation-list-marked-file-names ()
  258. "Return a list of file names of the marked generations.
  259. If nothing is marked, return a list with generation at point."
  260. (let ((entries (bui-current-entries)))
  261. (bui-list-map-marked
  262. (lambda (id)
  263. (bui-entry-non-void-value (bui-entry-by-id entries id)
  264. 'file-name)))))
  265. (defun guix-generation-list-get-entries (profile search-type
  266. &rest search-values)
  267. "Return 'generation' entries for displaying them in 'list' buffer."
  268. (guix-generation-get-entries
  269. 'generation-sexps
  270. profile search-type search-values
  271. (cl-union guix-generation-list-required-params
  272. (bui-list-displayed-params 'guix-generation))))
  273. (defun guix-generation-list-get-current (val &optional _)
  274. "Return string from VAL showing whether this generation is current.
  275. VAL is a boolean value."
  276. (if val "(current)" ""))
  277. (defun guix-generation-list-set-current ()
  278. "Switch current profile to the generation at point."
  279. (interactive)
  280. (let* ((entry (bui-list-current-entry))
  281. (current (bui-entry-non-void-value entry 'current))
  282. (number (bui-entry-non-void-value entry 'number)))
  283. (if current
  284. (user-error "This generation is already the current one")
  285. (guix-switch-to-generation (guix-ui-current-profile)
  286. number (current-buffer)))))
  287. (defun guix-generation-list-show-packages ()
  288. "List installed packages for the generation at point."
  289. (interactive)
  290. (guix-package-get-display
  291. (guix-generation-current-package-profile (bui-list-current-id))
  292. 'installed))
  293. (defun guix-generation-list-show-search-paths (&optional type)
  294. "Display 'search paths' environment variables for the marked generations.
  295. If nothing is marked, use generation on the current line."
  296. (interactive (list (guix-read-search-paths-type)))
  297. (guix-show-search-paths
  298. (guix-generation-list-marked-file-names)
  299. type))
  300. (defun guix-generation-list-generations-to-compare ()
  301. "Return a sorted list of 2 marked generations for comparing."
  302. (let ((numbers (bui-list-get-marked-id-list 'general)))
  303. (if (/= (length numbers) 2)
  304. (user-error "2 generations should be marked for comparing")
  305. (sort numbers #'<))))
  306. (defun guix-generation-list-profiles-to-compare ()
  307. "Return a sorted list of 2 marked generation profiles for comparing."
  308. (mapcar #'guix-generation-current-package-profile
  309. (guix-generation-list-generations-to-compare)))
  310. (defun guix-generation-list-show-added-packages ()
  311. "List package outputs added to the latest marked generation.
  312. If 2 generations are marked with \\[guix-list-mark], display
  313. outputs installed in the latest marked generation that were not
  314. installed in the other one."
  315. (interactive)
  316. (bui-get-display-entries
  317. 'guix-output 'list
  318. (cl-list* (guix-ui-current-profile)
  319. 'profile-diff
  320. (reverse (guix-generation-list-profiles-to-compare)))
  321. 'add))
  322. (defun guix-generation-list-show-removed-packages ()
  323. "List package outputs removed from the latest marked generation.
  324. If 2 generations are marked with \\[guix-list-mark], display
  325. outputs not installed in the latest marked generation that were
  326. installed in the other one."
  327. (interactive)
  328. (bui-get-display-entries
  329. 'guix-output 'list
  330. (cl-list* (guix-ui-current-profile)
  331. 'profile-diff
  332. (guix-generation-list-profiles-to-compare))
  333. 'add))
  334. (defun guix-generation-list-compare (diff-fun gen-fun)
  335. "Run GEN-FUN on the 2 marked generations and run DIFF-FUN on the results."
  336. (cl-multiple-value-bind (gen1 gen2)
  337. (guix-generation-list-generations-to-compare)
  338. (funcall diff-fun
  339. (funcall gen-fun gen1)
  340. (funcall gen-fun gen2))))
  341. (defun guix-generation-list-ediff-manifests ()
  342. "Run Ediff on manifests of the 2 marked generations."
  343. (interactive)
  344. (guix-generation-list-compare
  345. #'ediff-files
  346. #'guix-profile-generation-manifest-file))
  347. (defun guix-generation-list-diff-manifests ()
  348. "Run Diff on manifests of the 2 marked generations."
  349. (interactive)
  350. (guix-generation-list-compare
  351. #'guix-diff
  352. #'guix-profile-generation-manifest-file))
  353. (defun guix-generation-list-ediff-packages ()
  354. "Run Ediff on package outputs installed in the 2 marked generations."
  355. (interactive)
  356. (guix-generation-list-compare
  357. #'ediff-buffers
  358. #'guix-profile-generation-packages-buffer))
  359. (defun guix-generation-list-diff-packages ()
  360. "Run Diff on package outputs installed in the 2 marked generations."
  361. (interactive)
  362. (guix-generation-list-compare
  363. #'guix-diff
  364. #'guix-profile-generation-packages-buffer))
  365. (defun guix-generation-list-ediff (arg)
  366. "Run Ediff on package outputs installed in the 2 marked generations.
  367. With ARG, run Ediff on manifests of the marked generations."
  368. (interactive "P")
  369. (if arg
  370. (guix-generation-list-ediff-manifests)
  371. (guix-generation-list-ediff-packages)))
  372. (defun guix-generation-list-diff (arg)
  373. "Run Diff on package outputs installed in the 2 marked generations.
  374. With ARG, run Diff on manifests of the marked generations."
  375. (interactive "P")
  376. (if arg
  377. (guix-generation-list-diff-manifests)
  378. (guix-generation-list-diff-packages)))
  379. (defun guix-generation-list-mark-delete (&optional arg)
  380. "Mark the current generation for deletion and move to the next line.
  381. With ARG, mark all generations for deletion."
  382. (interactive "P")
  383. (if arg
  384. (bui-list-mark-all 'delete)
  385. (bui-list--mark 'delete t)))
  386. (defun guix-generation-list-execute ()
  387. "Delete marked generations."
  388. (interactive)
  389. (let ((marked (bui-list-get-marked-id-list 'delete)))
  390. (or marked
  391. (user-error "No generations marked for deletion"))
  392. (guix-delete-generations (guix-ui-current-profile)
  393. marked (current-buffer))))
  394. ;;; Inserting packages to compare generations
  395. (defcustom guix-generation-packages-buffer-name-function
  396. #'guix-generation-packages-buffer-name-default
  397. "Function used to define name of a buffer with generation packages.
  398. This function is called with 2 arguments: PROFILE (string) and
  399. GENERATION (number)."
  400. :type '(choice (function-item guix-generation-packages-buffer-name-default)
  401. (function-item guix-generation-packages-buffer-name-long)
  402. (function :tag "Other function"))
  403. :group 'guix-generation)
  404. (defcustom guix-generation-packages-update-buffer t
  405. "If non-nil, always update list of packages during comparing generations.
  406. If nil, generation packages are received only once. So when you
  407. compare generation 1 and generation 2, the packages for both
  408. generations will be received. Then if you compare generation 1
  409. and generation 3, only the packages for generation 3 will be
  410. received. Thus if you use comparing of different generations a
  411. lot, you may set this variable to nil to improve the
  412. performance."
  413. :type 'boolean
  414. :group 'guix-generation)
  415. (defvar guix-generation-output-name-width 30
  416. "Width of an output name \"column\".
  417. This variable is used in auxiliary buffers for comparing generations.")
  418. (defun guix-generation-packages (profile)
  419. "Return a list of sorted packages installed in PROFILE.
  420. Each element of the list is a list of the package specification
  421. and its store file name."
  422. (sort (guix-eval-read
  423. (guix-make-guile-expression
  424. 'profile->specifications+file-names profile))
  425. (lambda (a b)
  426. (string< (car a) (car b)))))
  427. (defun guix-generation-packages-buffer-name-default (profile generation)
  428. "Return name of a buffer for displaying GENERATION's package outputs.
  429. Use base name of PROFILE file name."
  430. (let ((profile-name (file-name-base (guix-file-name profile))))
  431. (format "*Guix %s: generation %s*"
  432. profile-name generation)))
  433. (defun guix-generation-packages-buffer-name-long (profile generation)
  434. "Return name of a buffer for displaying GENERATION's package outputs.
  435. Use the full PROFILE file name."
  436. (format "*Guix generation %s (%s)*"
  437. generation profile))
  438. (defun guix-generation-packages-buffer-name (profile generation)
  439. "Return name of a buffer for displaying GENERATION's package outputs."
  440. (funcall guix-generation-packages-buffer-name-function
  441. profile generation))
  442. (defun guix-generation-insert-package (name file-name)
  443. "Insert package output NAME and store FILE-NAME at point."
  444. (insert name)
  445. (indent-to guix-generation-output-name-width 2)
  446. (insert file-name "\n"))
  447. (defun guix-generation-insert-packages (buffer profile)
  448. "Insert package outputs installed in PROFILE in BUFFER."
  449. (with-current-buffer buffer
  450. (setq buffer-read-only nil
  451. indent-tabs-mode nil)
  452. (erase-buffer)
  453. (mapc (-lambda ((name file-name))
  454. (guix-generation-insert-package name file-name))
  455. (guix-generation-packages profile))))
  456. (defun guix-generation-packages-buffer (profile generation)
  457. "Return buffer with package outputs installed in PROFILE's GENERATION.
  458. Create the buffer if needed."
  459. (let ((buf-name (guix-generation-packages-buffer-name
  460. profile generation)))
  461. (or (and (null guix-generation-packages-update-buffer)
  462. (get-buffer buf-name))
  463. (let ((buf (get-buffer-create buf-name)))
  464. (guix-generation-insert-packages
  465. buf
  466. (guix-package-profile profile generation))
  467. buf))))
  468. (defun guix-profile-generation-manifest-file (generation)
  469. "Return the file name of a GENERATION's manifest.
  470. GENERATION is a generation number of the current profile."
  471. (guix-manifest-file (guix-ui-current-profile) generation))
  472. (defun guix-profile-generation-packages-buffer (generation)
  473. "Insert GENERATION's package outputs in a buffer and return it.
  474. GENERATION is a generation number of the current profile."
  475. (guix-generation-packages-buffer (guix-ui-current-profile)
  476. generation))
  477. ;;; Interactive commands
  478. (declare-function guix-system-generations "guix-ui-system-generation" t)
  479. (declare-function guix-last-system-generations "guix-ui-system-generation" t)
  480. (declare-function guix-system-generations-by-time "guix-ui-system-generation" t)
  481. ;;;###autoload
  482. (defun guix-generations (&optional profile)
  483. "Display information about all generations.
  484. If PROFILE is nil, use `guix-current-profile'.
  485. Interactively with prefix, prompt for PROFILE."
  486. (interactive (list (guix-ui-read-generation-profile)))
  487. (let ((profile (guix-profile profile)))
  488. (if (guix-system-profile? profile)
  489. (guix-system-generations)
  490. (guix-generation-get-display profile 'all))))
  491. ;;;###autoload
  492. (defun guix-last-generations (number &optional profile)
  493. "Display information about last NUMBER generations.
  494. If PROFILE is nil, use `guix-current-profile'.
  495. Interactively with prefix, prompt for PROFILE."
  496. (interactive
  497. (list (read-number "The number of last generations: ")
  498. (guix-ui-read-generation-profile)))
  499. (let ((profile (guix-profile profile)))
  500. (if (guix-system-profile? profile)
  501. (guix-last-system-generations number)
  502. (guix-generation-get-display profile 'last number))))
  503. ;;;###autoload
  504. (defun guix-generations-by-time (from to &optional profile)
  505. "Display information about generations created between FROM and TO.
  506. FROM and TO should be time values.
  507. If PROFILE is nil, use `guix-current-profile'.
  508. Interactively with prefix, prompt for PROFILE."
  509. (interactive
  510. (list (guix-read-date "Find generations (from): ")
  511. (guix-read-date "Find generations (to): ")
  512. (guix-ui-read-generation-profile)))
  513. (let ((profile (guix-profile profile)))
  514. (if (guix-system-profile? profile)
  515. (guix-system-generations-by-time from to)
  516. (guix-generation-get-display profile 'time
  517. (float-time from)
  518. (float-time to)))))
  519. (provide 'guix-ui-generation)
  520. ;;; guix-ui-generation.el ends here