guix-misc.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. ;;; guix-misc.el --- Miscellaneous definitions -*- lexical-binding: t -*-
  2. ;; Copyright © 2014–2018 Alex Kost <alezost@gmail.com>
  3. ;; Copyright © 2018, 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
  4. ;; This file is part of Emacs-Guix.
  5. ;; Emacs-Guix is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;;
  10. ;; Emacs-Guix is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;;
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with Emacs-Guix. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This file provides some miscellaneous code that does not find its
  19. ;; home in any other file (in a perfect world this file wouldn't exist).
  20. ;;; Code:
  21. (require 'cl-lib)
  22. (require 'guix nil t)
  23. (require 'guix-repl)
  24. (require 'guix-guile)
  25. (require 'guix-read)
  26. (require 'guix-utils)
  27. (require 'guix-ui)
  28. (require 'guix-profiles)
  29. ;;; Actions on packages and generations
  30. (defface guix-operation-option-key
  31. '((t :inherit font-lock-warning-face))
  32. "Face used for the keys of operation options."
  33. :group 'guix-faces)
  34. (defcustom guix-operation-confirm t
  35. "If nil, do not prompt to confirm an operation."
  36. :type 'boolean
  37. :group 'guix)
  38. (defcustom guix-use-substitutes t
  39. "If non-nil, use substitutes for the Guix packages."
  40. :type 'boolean
  41. :group 'guix)
  42. (defvar guix-dry-run nil
  43. "If non-nil, do not perform the real actions, just simulate.")
  44. (defvar guix-temp-buffer-name " *Guix temp*"
  45. "Name of a buffer used for displaying info before executing operation.")
  46. (defvar guix-operation-option-true-string "yes"
  47. "String displayed in the mode-line when operation option is t.")
  48. (defvar guix-operation-option-false-string "no "
  49. "String displayed in the mode-line when operation option is nil.")
  50. (defvar guix-operation-option-separator " | "
  51. "String used in the mode-line to separate operation options.")
  52. (defvar guix-operation-options
  53. '((?s "substitutes" guix-use-substitutes)
  54. (?d "dry-run" guix-dry-run))
  55. "List of available operation options.
  56. Each element of the list has a form:
  57. (KEY NAME VARIABLE)
  58. KEY is a character that may be pressed during confirmation to
  59. toggle the option.
  60. NAME is a string displayed in the mode-line.
  61. VARIABLE is a name of an option variable.")
  62. (defun guix-operation-option-by-key (key)
  63. "Return operation option by KEY (character)."
  64. (assq key guix-operation-options))
  65. (defun guix-operation-option-key (option)
  66. "Return key (character) of the operation OPTION."
  67. (car option))
  68. (defun guix-operation-option-name (option)
  69. "Return name of the operation OPTION."
  70. (nth 1 option))
  71. (defun guix-operation-option-variable (option)
  72. "Return name of the variable of the operation OPTION."
  73. (nth 2 option))
  74. (defun guix-operation-option-value (option)
  75. "Return boolean value of the operation OPTION."
  76. (symbol-value (guix-operation-option-variable option)))
  77. (defun guix-operation-option-string-value (option)
  78. "Convert boolean value of the operation OPTION to string and return it."
  79. (if (guix-operation-option-value option)
  80. guix-operation-option-true-string
  81. guix-operation-option-false-string))
  82. (defun guix-operation-prompt (&optional prompt)
  83. "Prompt a user for continuing the current operation.
  84. Return non-nil, if the operation should be continued; nil otherwise.
  85. Ask a user with PROMPT for continuing an operation."
  86. (let* ((option-keys (mapcar #'guix-operation-option-key
  87. guix-operation-options))
  88. (keys (append '(?y ?n) option-keys))
  89. (prompt (concat (propertize (or prompt "Continue operation?")
  90. 'face 'minibuffer-prompt)
  91. " ("
  92. (mapconcat
  93. (lambda (key)
  94. (propertize (string key)
  95. 'face 'guix-operation-option-key))
  96. keys
  97. ", ")
  98. ") ")))
  99. (let ((mode-line mode-line-format))
  100. (prog1 (guix-operation-prompt-1 prompt keys)
  101. (setq mode-line-format mode-line)
  102. ;; Clear the minibuffer after prompting.
  103. (message "")))))
  104. (defun guix-operation-prompt-1 (prompt keys)
  105. "This function is internal for `guix-operation-prompt'."
  106. (guix-operation-set-mode-line)
  107. (let ((key (read-char-choice prompt (cons ?\C-g keys) t)))
  108. (cl-case key
  109. (?y t)
  110. ((?n ?\C-g) nil)
  111. (t (let* ((option (guix-operation-option-by-key key))
  112. (var (guix-operation-option-variable option)))
  113. (set var (not (symbol-value var)))
  114. (guix-operation-prompt-1 prompt keys))))))
  115. (defun guix-operation-set-mode-line ()
  116. "Display operation options in the mode-line of the current buffer."
  117. (setq mode-line-format
  118. (concat (propertize " Options: "
  119. 'face 'mode-line-buffer-id)
  120. (mapconcat
  121. (lambda (option)
  122. (let ((key (guix-operation-option-key option))
  123. (name (guix-operation-option-name option))
  124. (val (guix-operation-option-string-value option)))
  125. (concat name
  126. " ("
  127. (propertize (string key)
  128. 'face 'guix-operation-option-key)
  129. "): " val)))
  130. guix-operation-options
  131. guix-operation-option-separator)))
  132. (force-mode-line-update))
  133. ;;;###autoload
  134. (defun guix-apply-manifest (profile file &optional operation-buffer)
  135. "Apply manifest from FILE to PROFILE.
  136. This function has the same meaning as 'guix package --manifest' command.
  137. See Info node `(guix) Invoking guix package' for details.
  138. Interactively, use the current profile and prompt for manifest
  139. FILE. With a prefix argument, also prompt for PROFILE."
  140. (interactive
  141. (let* ((current-profile (guix-ui-current-profile))
  142. (profile (if current-prefix-arg
  143. (guix-read-package-profile)
  144. (or current-profile guix-current-profile)))
  145. (file (guix-read-manifest-file-name))
  146. (buffer (and current-profile (current-buffer))))
  147. (list profile file buffer)))
  148. (guix-assert-non-system-profile profile)
  149. (when (or (not guix-operation-confirm)
  150. (y-or-n-p (format "Apply manifest from '%s' to profile '%s'? "
  151. file profile)))
  152. (guix-eval-in-repl
  153. (guix-make-guile-expression
  154. 'guix-command
  155. "package"
  156. (concat "--profile=" (guix-file-name profile))
  157. (concat "--manifest=" (guix-file-name file)))
  158. operation-buffer)))
  159. (defcustom guix-search-paths-buffer-name "*Guix Search Paths*"
  160. "Name of a buffer for displaying 'search paths' environment variables."
  161. :type 'string
  162. :group 'guix)
  163. (defun guix-show-search-paths (profiles &optional type)
  164. "Display 'search paths' environment variables for PROFILES."
  165. (let* ((profiles (mapcar #'guix-package-profile profiles))
  166. (type (or type "exact"))
  167. (type-symbol (intern type))
  168. (paths (guix-eval-read
  169. (guix-make-guile-expression
  170. 'search-paths profiles :type type-symbol))))
  171. (with-current-buffer (get-buffer-create guix-search-paths-buffer-name)
  172. (let ((inhibit-read-only t))
  173. (erase-buffer)
  174. (insert
  175. "# \"Search paths\" environment variables for the Guix "
  176. (if (cdr profiles) "profiles" "profile")
  177. ".
  178. #
  179. # Shell command to reproduce:
  180. #
  181. # guix package --search-paths=" type " "
  182. (mapconcat (lambda (p)
  183. (concat "--profile=" (shell-quote-argument p)))
  184. profiles
  185. " ")
  186. "\n\n"
  187. (mapconcat #'identity paths "\n")
  188. "\n"))
  189. (sh-mode))
  190. (guix-display-buffer guix-search-paths-buffer-name)))
  191. ;;;###autoload
  192. (defun guix-set-emacs-environment (&optional profile)
  193. "Set Emacs environment to match PROFILE.
  194. PROFILE can be a named profile (like '~/.guix-profile',
  195. '~/.config/guix/work') or a direct link to profile from the
  196. store, like GUIX_ENVIRONMENT variable (see Info node `(guix)
  197. Invoking guix environment' for details).
  198. If PROFILE is nil, use `guix-current-profile'."
  199. (interactive (list (guix-read-profile)))
  200. (let ((specs (guix-eval-read
  201. (guix-make-guile-expression
  202. 'search-paths-specifications
  203. (guix-file-name profile)))))
  204. (dolist (spec specs)
  205. (-let* (((variable separator path) spec)
  206. (current-value (getenv variable))
  207. (value (if (and separator current-value)
  208. (concat path separator current-value)
  209. path)))
  210. (setenv variable value)
  211. (when (equal variable "PATH")
  212. (setq exec-path (split-string value ":")))))))
  213. ;;; Executing guix commands
  214. (defcustom guix-run-in-shell-function #'guix-run-in-shell
  215. "Function used to run guix command.
  216. The function is called with a single argument - a command line string."
  217. :type '(choice (function-item guix-run-in-shell)
  218. (function-item guix-run-in-eshell)
  219. (function :tag "Other function"))
  220. :group 'guix)
  221. (defcustom guix-shell-buffer-name "*shell*"
  222. "Default name of a shell buffer used for running guix commands."
  223. :type 'string
  224. :group 'guix)
  225. (declare-function comint-send-input "comint" t)
  226. (defun guix-run-in-shell (string)
  227. "Run command line STRING in `guix-shell-buffer-name' buffer."
  228. (shell guix-shell-buffer-name)
  229. (goto-char (point-max))
  230. (insert string)
  231. (comint-send-input))
  232. (declare-function eshell-send-input "esh-mode" t)
  233. (defun guix-run-in-eshell (string)
  234. "Run command line STRING in eshell buffer."
  235. (eshell)
  236. (goto-char (point-max))
  237. (insert string)
  238. (eshell-send-input))
  239. (defun guix-run-command-in-shell (args)
  240. "Execute 'guix ARGS ...' command in a shell buffer."
  241. (funcall guix-run-in-shell-function
  242. (guix-command-string args)))
  243. (defun guix-run-command-in-repl (args)
  244. "Execute 'guix ARGS ...' command in Guix REPL."
  245. (guix-eval-in-repl
  246. (apply #'guix-make-guile-expression
  247. 'guix-command args)))
  248. (defun guix-command-output (args)
  249. "Return string with 'guix ARGS ...' output."
  250. (cl-multiple-value-bind (output error)
  251. (guix-eval (apply #'guix-make-guile-expression
  252. 'guix-command-output args))
  253. ;; Remove trailing new space from the error string.
  254. (message (replace-regexp-in-string "\n\\'" "" (read error)))
  255. (read output)))
  256. (defun guix-help-string (&optional commands)
  257. "Return string with 'guix COMMANDS ... --help' output."
  258. (guix-eval-read
  259. (apply #'guix-make-guile-expression
  260. 'help-string commands)))
  261. ;;; Pull
  262. (defcustom guix-update-after-pull t
  263. "If non-nil, update Guix buffers after performing \\[guix-pull]."
  264. :type 'boolean
  265. :group 'guix)
  266. (defvar guix-after-pull-hook
  267. '(guix-restart-repl-after-pull guix-update-buffers-maybe-after-pull)
  268. "Hook run after successful performing `guix-pull' operation.")
  269. (defun guix-restart-repl-after-pull ()
  270. "Restart Guix REPL after `guix-pull' operation."
  271. (guix-repl-exit)
  272. (guix-start-process-maybe
  273. "Restarting Guix REPL after pull operation ..."))
  274. (defun guix-update-buffers-maybe-after-pull ()
  275. "Update buffers depending on `guix-update-after-pull'."
  276. (when guix-update-after-pull
  277. ;; No need to update "generation" buffers.
  278. (dolist (buffer (guix-operation-buffers
  279. '(guix-package-list-mode
  280. guix-package-info-mode
  281. guix-output-list-mode)))
  282. (with-current-buffer buffer
  283. (revert-buffer nil t)))
  284. (message "Guix buffers have been updated.")))
  285. ;;;###autoload
  286. (defun guix-pull (&optional verbose)
  287. "Run Guix pull operation.
  288. If VERBOSE is non-nil (with prefix argument), produce verbose output."
  289. (interactive "P")
  290. (let ((args (and verbose '("--verbose"))))
  291. (guix-eval-in-repl
  292. (apply #'guix-make-guile-expression
  293. 'guix-command "pull" args)
  294. nil 'pull)))
  295. ;;; Reporting Guix bugs
  296. (defvar guix-bug-address "bug-guix@gnu.org"
  297. "Email address for the GNU Guix bugs.")
  298. ;;;###autoload
  299. (defun guix-report-bug (subject)
  300. "Report GNU Guix bug.
  301. Prompt for bug subject and open a mail buffer."
  302. (interactive "sBug Subject: ")
  303. (compose-mail guix-bug-address subject))
  304. (provide 'guix-misc)
  305. ;;; guix-misc.el ends here