(defvar ambrevar/guix-checkout-directory (expand-file-name "~/projects/guix")) (with-eval-after-load 'geiser-guile (when (require 'yasnippet nil t) ;; This is not enough since COMMIT_MSG is not in scheme-mode. ;; TODO: Add to find-file-hook instead and check if parent folder is ~/projects/guix. ;; (add-hook 'scheme-mode-hook 'yas-minor-mode) (with-eval-after-load 'yasnippet (add-to-list 'yas-snippet-dirs (expand-file-name "etc/snippets" ambrevar/guix-checkout-directory))) (yas-global-mode 1)) (add-to-list 'geiser-guile-load-path ambrevar/guix-checkout-directory) (dolist (dir '("~/projects/nonguix" "~/projects/games")) (when (file-directory-p dir) (add-to-list 'geiser-guile-load-path dir)))) ;; To use package declaration from the local checkout: ;; (setq guix-load-path ambrevar/guix-checkout-directory) (defun ambrevar/init-guix () (and buffer-file-name (string-match "\\" buffer-file-name) (guix-devel-mode))) (add-hook 'scheme-mode-hook 'ambrevar/init-guix) (defun ambrevar/guix-debbugs-gnu (&optional severities packages archivedp suppress tags) "Like `debbugs-gnu' but for the Guix project." (interactive) (let ((debbugs-gnu-default-packages '("guix-patches" "guix"))) (if (called-interactively-p) (call-interactively 'debbugs-gnu) (debbugs-gnu severities packages archivedp suppress tags)))) (require 'guix nil 'noerror) (defun ambrevar/guix-generations-list-diff-this () "List guix-generation-list-diff but compare generation at point with previous." (interactive) (let ((diff-fun #'guix-diff) (gen-fun #'guix-profile-generation-packages-buffer)) (funcall diff-fun (funcall gen-fun (1- (bui-list-current-id))) (funcall gen-fun (bui-list-current-id))))) (with-eval-after-load 'guix-ui-generation (define-key guix-generation-list-mode-map "=" #'ambrevar/guix-generations-list-diff-this)) (defvar ambrevar/guix-extra-channels "~/.guix-extra-channels") (defvar ambrevar/guix-extra-profiles "~/.guix-extra-profiles") (defvar ambrevar/guix-manifest-directory "~/.package-lists") (defvar ambrevar/guix-system-directory "~/.config/guix/system") (defvar ambrevar/guix-channel-spec-directory "~/.package-lists") (defvar ambrevar/guix-always-use-channel-specs nil "If non-nil, automatically use a channel specification matching the chosen manifest. The channel specification is looked up in `ambrevar/guix-channel-spec-directory'.") (cl-defun ambrevar/guix-query-file (&key file directory (filter ".") (prompt "File: ") (name-function #'identity) (multiple? nil)) "Query a file matching FILTER in DIRECTORY. Return (NAME FILE). If FILE is non-nil, then this function is useful to derive the name of the manifest. NAME-FUNCTION takes the file base name as argument and returns NAME. If MULTIPLE? is non-nil, return a list of (NAME FILE) of the selected manifests." (cl-flet ((name (file) (replace-regexp-in-string "guix-" "" (funcall name-function (file-name-base file))))) (if file (list (name file) file) (let ((files (mapcar (lambda (file) (list (name file) file)) (directory-files directory 'full filter)))) (if multiple? (mapcar (lambda (name) (assoc name files)) (completing-read-multiple prompt (mapcar #'cl-first files))) (assoc (completing-read prompt (mapcar #'cl-first files)) files)))))) (defun ambrevar/guix-query-manifest (&optional manifest multiple?) "Query a manifest as found in `ambrevar/guix-manifest-directory'. Return (NAME FILE). If MANIFEST is non-nil, then this function is useful to derive the name of the manifest. If MULTIPLE? is non-nil, allow querying multiple manifests." (ambrevar/guix-query-file :file manifest :directory ambrevar/guix-manifest-directory :filter "manifest" :prompt "Manifest(s): " :name-function (lambda (name) (replace-regexp-in-string "-?manifest-?" "" name)) :multiple? multiple?)) (defun ambrevar/guix-query-system (&optional system) "Query a system as found in `ambrevar/guix-system-directory'. Return (NAME FILE). If SYSTEM is non-nil, then this function is useful to derive the name of the system. " (ambrevar/guix-query-file :file system :directory ambrevar/guix-system-directory :filter "scm" :prompt "System: ")) (defun ambrevar/guix-query-channel-spec (&optional channel-spec) "Query a channel specification as found in `ambrevar/guix-channel-spec-directory'. Return (NAME FILE). If CHANNEL-SPEC is non-nil, then this function is useful to derive the name of the channel specification." (ambrevar/guix-query-file :file channel-spec :directory ambrevar/guix-channel-spec-directory :filter "channels" :prompt "Channel specification: " :name-function (lambda (name) (replace-regexp-in-string "-?channels?-?" "" name)))) (defun ambrevar/guix-edit-system (&optional system) "Edit system. If SYSTEM is nil, it is queried from the systems found in `ambrevar/guix-system-directory'." (interactive) (setq system (cl-second (ambrevar/guix-query-system system))) (find-file system)) (global-set-key (kbd "C-x c s") #'ambrevar/guix-edit-system) (with-eval-after-load 'evil ;; For some reason `global-set-key' does not work for Evil at this point. (dolist (mode '(normal insert)) (evil-global-set-key mode (kbd "C-x c s") #'ambrevar/guix-edit-system))) (defun ambrevar/guix-edit-manifest (&optional manifest) "Edit MANIFEST. If MANIFEST is nil, it is queried from the manifests found in `ambrevar/guix-manifest-directory'." (interactive) (setq manifest (cl-second (ambrevar/guix-query-manifest manifest))) (find-file manifest)) (global-set-key (kbd "C-x c g") #'ambrevar/guix-edit-manifest) (with-eval-after-load 'evil ;; For some reason `global-set-key' does not work for Evil at this point. (dolist (mode '(normal insert)) (evil-global-set-key mode (kbd "C-x c g") #'ambrevar/guix-edit-manifest))) (defun ambrevar/guix-find-channel-from-manifest (pattern) "Return the channel specification file matching PATTERN in `ambrevar/guix-channel-spec-directory'." (cl-first (directory-files ambrevar/guix-channel-spec-directory 'full (concat pattern "-channel")))) (defun ambrevar/run-in-eshell (command) (let ((eshell-buffer-name "*guix*")) (eshell) (when (eshell-interactive-process) (eshell t)) (if (get-buffer-process (current-buffer)) (message "Try again after current process termination.") (eshell-interrupt-process) (insert command) (eshell-send-input)))) (defun ambrevar/run-in-shell (command) (shell (get-buffer-create "*guix*")) (let ((process (get-buffer-process (current-buffer)))) (when process (accept-process-output process 0.1))) (if (helm-ff-shell-alive-p major-mode) (message "Try again after current process termination.") (goto-char (point-max)) (comint-delete-input) (insert command) (comint-send-input))) (defun %ambrevar/guix-install-manifest (&optional manifests channel) "Install list of (NAME MANIFEST-FILE) using CHANNEL." (require 'init-shell) ; For `helm-ff-preferred-shell-mode'. (let* ((guix (if channel (let ((dest (expand-file-name ;; TODO: What name should we use with multiple manifests? (cl-first (cl-first manifests)) ambrevar/guix-extra-channels))) (make-directory dest 'parents) (format "guix pull --channels=%s --profile=%s/guix && %s/guix/bin/guix" (shell-quote-argument channel) (shell-quote-argument dest) (shell-quote-argument dest))) "guix"))) (dolist (manifest-pair manifests) (let ((manifest-name (cl-first manifest-pair))) (make-directory (expand-file-name manifest-name ambrevar/guix-extra-profiles) 'parents))) (funcall (if (eq helm-ff-preferred-shell-mode 'eshell-mode) 'ambrevar/run-in-eshell 'ambrevar/run-in-shell) (mapconcat #'identity (mapcar (lambda (manifest-pair) (let ((manifest-name (cl-first manifest-pair)) (manifest (cl-second manifest-pair)) (profile (expand-file-name ambrevar/guix-extra-profiles))) (string-join (list "echo" (format "'==> Installing manifest %S to profile %S'" manifest-name profile) ";" guix "package" (concat "--manifest=" manifest) (if (string= "default" manifest-name) "" (concat "--profile=" profile "/" manifest-name "/" manifest-name))) " "))) manifests) " ; ")))) (defun ambrevar/guix-install-manifest (&optional manifest channel) "Install Guix manifest to `ambrevar/guix-extra-profiles'. Manifest is queried from those found in `ambrevar/guix-manifest-directory'. Guix channel specification is stored in `ambrevar/guix-channel-spec-directory'. With a prefix argument, query for a channel specification file. If CHANNEL is nil and `ambrevar/guix-always-use-channel-specs' is non-nil, then try to use a channel specification file from `ambrevar/guix-channel-spec-directory' if any." (interactive) (let* ((manifest-pair (ambrevar/guix-query-manifest manifest)) (manifest-name (cl-first manifest-pair)) (manifest (cl-second manifest-pair)) (channel (or channel (and current-prefix-arg (cl-second (ambrevar/guix-query-channel-spec))) (and ambrevar/guix-always-use-channel-specs (ambrevar/guix-find-channel-from-manifest manifest-name))))) (%ambrevar/guix-install-manifest (list manifest-pair) channel))) (defun ambrevar/guix-install-manifests (&optional manifests channel) "Install Guix manifests to `ambrevar/guix-extra-profiles'. Manifests are queried from those found in `ambrevar/guix-manifest-directory'. Guix channel specification is stored in `ambrevar/guix-channel-spec-directory'. With a prefix argument, query for a channel specification file." (interactive) (let* ((manifests (or manifests (ambrevar/guix-query-manifest nil :multiple))) (channel (or channel (and current-prefix-arg (cl-second (ambrevar/guix-query-channel-spec)))))) (%ambrevar/guix-install-manifest manifests channel))) (global-set-key (kbd "C-x c G") #'ambrevar/guix-install-manifests) (with-eval-after-load 'evil (dolist (mode '(normal insert)) (evil-global-set-key mode (kbd "C-x c G") #'ambrevar/guix-install-manifests))) (defun ambrevar/guix-install-system (&optional system) "Install Guix system. System is queried from those found in `ambrevar/guix-system-directory'. " (interactive) (require 'init-shell) ; For `helm-ff-preferred-shell-mode'. (let* ((system-pair (ambrevar/guix-query-system system)) (system-name (cl-first system-pair)) (system (cl-second system-pair))) (funcall (if (eq helm-ff-preferred-shell-mode 'eshell-mode) 'ambrevar/run-in-eshell 'ambrevar/run-in-shell) (string-join (list "sudo" "-E" "guix" "system" "-L" ambrevar/guix-system-directory "reconfigure" system) " ")))) (global-set-key (kbd "C-x c S") #'ambrevar/guix-install-system) (with-eval-after-load 'evil ;; For some reason `global-set-key' does not work for Evil at this point. (dolist (mode '(normal insert)) (evil-global-set-key mode (kbd "C-x c S") #'ambrevar/guix-install-system))) ;; TODO: See `guix-apply-manifest' and expand on it. ;; TODO: Use --max-jobs=N. (provide 'init-guix)