diff options
Diffstat (limited to 'emacs/guix-backend.el')
-rw-r--r-- | emacs/guix-backend.el | 393 |
1 files changed, 0 insertions, 393 deletions
diff --git a/emacs/guix-backend.el b/emacs/guix-backend.el deleted file mode 100644 index 6341aacae1..0000000000 --- a/emacs/guix-backend.el +++ /dev/null @@ -1,393 +0,0 @@ -;;; guix-backend.el --- Making and using Guix REPL - -;; Copyright © 2014, 2015, 2016 Alex Kost <alezost@gmail.com> - -;; This file is part of GNU Guix. - -;; GNU Guix is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Guix is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This file provides the code for interacting with Guile using Guix REPL -;; (Geiser REPL with some guix-specific additions). - -;; By default (if `guix-use-guile-server' is non-nil) 2 Guix REPLs are -;; started. The main one (with "guile --listen" process) is used for -;; "interacting" with a user - for showing a progress of -;; installing/deleting Guix packages. The second (internal) REPL is -;; used for synchronous evaluating, e.g. when information about -;; packages/generations should be received for a list/info buffer. -;; -;; This "2 REPLs concept" makes it possible to have a running process of -;; installing/deleting packages and to continue to search/list/get info -;; about other packages at the same time. If you prefer to use a single -;; Guix REPL, do not try to receive any information while there is a -;; running code in the REPL (see -;; <https://github.com/jaor/geiser/issues/28>). -;; -;; Guix REPLs (unlike the usual Geiser REPLs) are not added to -;; `geiser-repl--repls' variable, and thus cannot be used for evaluating -;; while editing scm-files. The only purpose of Guix REPLs is to be an -;; intermediate between "Guix/Guile level" and "Emacs interface level". -;; That being said you can still want to use a Guix REPL while hacking -;; auxiliary scheme-files for "guix.el". You can just use -;; `geiser-connect-local' command with `guix-repl-current-socket' to -;; have a usual Geiser REPL with all stuff defined by "guix.el" package. - -;;; Code: - -(require 'geiser-mode) -(require 'geiser-guile) -(require 'guix-geiser) -(require 'guix-config) -(require 'guix-external) -(require 'guix-emacs) -(require 'guix-profiles) - -(defvar guix-load-path guix-config-emacs-interface-directory - "Directory with scheme files for \"guix.el\" package.") - -(defvar guix-helper-file - (expand-file-name "guix-helper.scm" guix-load-path) - "Auxiliary scheme file for loading.") - - -;;; REPL - -(defgroup guix-repl nil - "Settings for Guix REPLs." - :prefix "guix-repl-" - :group 'guix) - -(defcustom guix-repl-startup-time 30000 - "Time, in milliseconds, to wait for Guix REPL to startup. -Same as `geiser-repl-startup-time' but is used for Guix REPL. -If you have a slow system, try to increase this time." - :type 'integer - :group 'guix-repl) - -(defcustom guix-repl-buffer-name "*Guix REPL*" - "Default name of a Geiser REPL buffer used for Guix." - :type 'string - :group 'guix-repl) - -(defcustom guix-after-start-repl-hook '(guix-set-directory) - "Hook called after Guix REPL is started." - :type 'hook - :group 'guix-repl) - -(defcustom guix-use-guile-server t - "If non-nil, start guile with '--listen' argument. -This allows to receive information about packages using an additional -REPL while some packages are being installed/removed in the main REPL." - :type 'boolean - :group 'guix-repl) - -(defcustom guix-repl-socket-file-name-function - #'guix-repl-socket-file-name - "Function used to define a socket file name used by Guix REPL. -The function is called without arguments." - :type '(choice (function-item guix-repl-socket-file-name) - (function :tag "Other function")) - :group 'guix-repl) - -(defcustom guix-emacs-activate-after-operation t - "Activate Emacs packages after installing. -If nil, do not load autoloads of the Emacs packages after -they are successfully installed." - :type 'boolean - :group 'guix-repl) - -(defvar guix-repl-current-socket nil - "Name of a socket file used by the current Guix REPL.") - -(defvar guix-repl-buffer nil - "Main Geiser REPL buffer used for communicating with Guix. -This REPL is used for processing package actions and for -receiving information if `guix-use-guile-server' is nil.") - -(defvar guix-internal-repl-buffer nil - "Additional Geiser REPL buffer used for communicating with Guix. -This REPL is used for receiving information only if -`guix-use-guile-server' is non-nil.") - -(defvar guix-internal-repl-buffer-name "*Guix Internal REPL*" - "Default name of an internal Guix REPL buffer.") - -(defvar guix-before-repl-operation-hook nil - "Hook run before executing an operation in Guix REPL.") - -(defvar guix-after-repl-operation-hook - '(guix-repl-autoload-emacs-packages-maybe - guix-repl-operation-success-message) - "Hook run after executing successful operation in Guix REPL.") - -(defvar guix-repl-operation-p nil - "Non-nil, if current operation is performed by `guix-eval-in-repl'. -This internal variable is used to distinguish Guix operations -from operations performed in Guix REPL by a user.") - -(defvar guix-repl-operation-type nil - "Type of the current operation performed by `guix-eval-in-repl'. -This internal variable is used to define what actions should be -executed after the current operation succeeds. -See `guix-eval-in-repl' for details.") - -(defun guix-repl-autoload-emacs-packages-maybe () - "Load autoloads for Emacs packages if needed. -See `guix-emacs-activate-after-operation' for details." - (and guix-emacs-activate-after-operation - ;; FIXME Since a user can work with a non-current profile (using - ;; C-u before `guix-search-by-name' and other commands), emacs - ;; packages can be installed to another profile, and the - ;; following code will not work (i.e., the autoloads for this - ;; profile will not be loaded). - (guix-emacs-autoload-packages guix-current-profile))) - -(defun guix-repl-operation-success-message () - "Message telling about successful Guix operation." - (message "Guix operation has been performed.")) - -(defun guix-get-guile-program (&optional socket) - "Return a value suitable for `geiser-guile-binary'." - (if (null socket) - guix-guile-program - (append (if (listp guix-guile-program) - guix-guile-program - (list guix-guile-program)) - (list (concat "--listen=" socket))))) - -(defun guix-repl-socket-file-name () - "Return a name of a socket file used by Guix REPL." - (make-temp-name - (concat (file-name-as-directory temporary-file-directory) - "guix-repl-"))) - -(defun guix-repl-delete-socket-maybe () - "Delete `guix-repl-current-socket' file if it exists." - (and guix-repl-current-socket - (file-exists-p guix-repl-current-socket) - (delete-file guix-repl-current-socket))) - -(add-hook 'kill-emacs-hook 'guix-repl-delete-socket-maybe) - -(defun guix-start-process-maybe (&optional start-msg end-msg) - "Start Geiser REPL configured for Guix if needed. -START-MSG and END-MSG are strings displayed in the minibuffer in -the beginning and in the end of the starting process. If nil, -display default messages." - (guix-start-repl-maybe nil - (or start-msg "Starting Guix REPL ...") - (or end-msg "Guix REPL has been started.")) - (if guix-use-guile-server - (guix-start-repl-maybe 'internal) - (setq guix-internal-repl-buffer guix-repl-buffer))) - -(defun guix-start-repl-maybe (&optional internal start-msg end-msg) - "Start Guix REPL if needed. -If INTERNAL is non-nil, start an internal REPL. - -START-MSG and END-MSG are strings displayed in the minibuffer in -the beginning and in the end of the process. If nil, do not -display messages." - (let* ((repl-var (guix-get-repl-buffer-variable internal)) - (repl (symbol-value repl-var))) - (unless (and (buffer-live-p repl) - (get-buffer-process repl)) - (and start-msg (message start-msg)) - (setq guix-repl-operation-p nil) - (unless internal - ;; Guile leaves socket file after exit, so remove it if it - ;; exists (after the REPL restart). - (guix-repl-delete-socket-maybe) - (setq guix-repl-current-socket - (and guix-use-guile-server - (or guix-repl-current-socket - (funcall guix-repl-socket-file-name-function))))) - (let ((geiser-guile-binary (guix-get-guile-program - (unless internal - guix-repl-current-socket))) - (geiser-guile-init-file (unless internal guix-helper-file)) - (repl (get-buffer-create - (guix-get-repl-buffer-name internal)))) - (guix-start-repl repl (and internal guix-repl-current-socket)) - (set repl-var repl) - (and end-msg (message end-msg)) - (unless internal - (run-hooks 'guix-after-start-repl-hook)))))) - -(defun guix-start-repl (buffer &optional address) - "Start Guix REPL in BUFFER. -If ADDRESS is non-nil, connect to a remote guile process using -this address (it should be defined by -`geiser-repl--read-address')." - ;; A mix of the code from `geiser-repl--start-repl' and - ;; `geiser-repl--to-repl-buffer'. - (let ((impl 'guile) - (geiser-guile-load-path (cons (expand-file-name guix-load-path) - geiser-guile-load-path)) - (geiser-repl-startup-time guix-repl-startup-time)) - (with-current-buffer buffer - (geiser-repl-mode) - (geiser-impl--set-buffer-implementation impl) - (geiser-repl--autodoc-mode -1) - (goto-char (point-max)) - (let ((prompt (geiser-con--combined-prompt - geiser-guile--prompt-regexp - geiser-guile--debugger-prompt-regexp))) - (geiser-repl--save-remote-data address) - (geiser-repl--start-scheme impl address prompt) - (geiser-repl--quit-setup) - (geiser-repl--history-setup) - (setq-local geiser-repl--repls (list buffer)) - (geiser-repl--set-this-buffer-repl buffer) - (setq geiser-repl--connection - (geiser-con--make-connection - (get-buffer-process (current-buffer)) - geiser-guile--prompt-regexp - geiser-guile--debugger-prompt-regexp)) - (geiser-repl--startup impl address) - (geiser-repl--autodoc-mode 1) - (geiser-company--setup geiser-repl-company-p) - (add-hook 'comint-output-filter-functions - 'guix-repl-output-filter - nil t) - (set-process-query-on-exit-flag - (get-buffer-process (current-buffer)) - geiser-repl-query-on-kill-p))))) - -(defun guix-repl-output-filter (str) - "Filter function suitable for `comint-output-filter-functions'. -This is a replacement for `geiser-repl--output-filter'." - (cond - ((string-match-p geiser-guile--prompt-regexp str) - (geiser-autodoc--disinhibit-autodoc) - (when guix-repl-operation-p - (setq guix-repl-operation-p nil) - (run-hooks 'guix-after-repl-operation-hook) - ;; Run hooks specific to the current operation type. - (when guix-repl-operation-type - (let ((type-hook (intern - (concat "guix-after-" - (symbol-name guix-repl-operation-type) - "-hook")))) - (setq guix-repl-operation-type nil) - (and (boundp type-hook) - (run-hooks type-hook)))))) - ((string-match geiser-guile--debugger-prompt-regexp str) - (setq guix-repl-operation-p nil) - (geiser-con--connection-set-debugging geiser-repl--connection - (match-beginning 0)) - (geiser-autodoc--disinhibit-autodoc)))) - -(defun guix-repl-exit (&optional internal no-wait) - "Exit the current Guix REPL. -If INTERNAL is non-nil, exit the internal REPL. -If NO-WAIT is non-nil, do not wait for the REPL process to exit: -send a kill signal to it and return immediately." - (let ((repl (symbol-value (guix-get-repl-buffer-variable internal)))) - (when (get-buffer-process repl) - (with-current-buffer repl - (geiser-con--connection-deactivate geiser-repl--connection t) - (comint-kill-subjob) - (unless no-wait - (while (get-buffer-process repl) - (sleep-for 0.1))))))) - -(defun guix-get-repl-buffer (&optional internal) - "Return Guix REPL buffer; start REPL if needed. -If INTERNAL is non-nil, return an additional internal REPL." - (guix-start-process-maybe) - (let ((repl (symbol-value (guix-get-repl-buffer-variable internal)))) - ;; If a new Geiser REPL is started, `geiser-repl--repl' variable may - ;; be set to the new value in a Guix REPL, so set it back to a - ;; proper value here. - (with-current-buffer repl - (geiser-repl--set-this-buffer-repl repl)) - repl)) - -(defun guix-get-repl-buffer-variable (&optional internal) - "Return the name of a variable with a REPL buffer." - (if internal - 'guix-internal-repl-buffer - 'guix-repl-buffer)) - -(defun guix-get-repl-buffer-name (&optional internal) - "Return the name of a REPL buffer." - (if internal - guix-internal-repl-buffer-name - guix-repl-buffer-name)) - -(defun guix-switch-to-repl (&optional internal) - "Switch to Guix REPL. -If INTERNAL is non-nil (interactively with prefix), switch to the -additional internal REPL if it exists." - (interactive "P") - (geiser-repl--switch-to-buffer (guix-get-repl-buffer internal))) - - -;;; Guix directory - -(defvar guix-directory nil - "Default directory with Guix source. -If it is not set by a user, it is set after starting Guile REPL. -This directory is used to define package locations.") - -(defun guix-read-directory () - "Return `guix-directory' or prompt for it. -This function is intended for using in `interactive' forms." - (if current-prefix-arg - (read-directory-name "Directory with Guix modules: " - guix-directory) - guix-directory)) - -(defun guix-set-directory () - "Set `guix-directory' if needed." - (or guix-directory - (setq guix-directory - (guix-eval-read "%guix-dir")))) - - -;;; Evaluating expressions - -(defvar guix-operation-buffer nil - "Buffer from which the latest Guix operation was performed.") - -(defun guix-eval (str) - "Evaluate STR with guile expression using Guix REPL. -See `guix-geiser-eval' for details." - (guix-geiser-eval str (guix-get-repl-buffer 'internal))) - -(defun guix-eval-read (str) - "Evaluate STR with guile expression using Guix REPL. -See `guix-geiser-eval-read' for details." - (guix-geiser-eval-read str (guix-get-repl-buffer 'internal))) - -(defun guix-eval-in-repl (str &optional operation-buffer operation-type) - "Switch to Guix REPL and evaluate STR with guile expression there. -If OPERATION-BUFFER is non-nil, it should be a buffer from which -the current operation was performed. - -If OPERATION-TYPE is non-nil, it should be a symbol. After -successful executing of the current operation, -`guix-after-OPERATION-TYPE-hook' is called." - (run-hooks 'guix-before-repl-operation-hook) - (setq guix-repl-operation-p t - guix-repl-operation-type operation-type - guix-operation-buffer operation-buffer) - (guix-geiser-eval-in-repl str (guix-get-repl-buffer))) - -(provide 'guix-backend) - -;;; guix-backend.el ends here |