1 ;;; guix-backend.el --- Communication with Geiser
3 ;; Copyright © 2014 Alex Kost <alezost@gmail.com>
5 ;; This file is part of GNU Guix.
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.
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.
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/>.
22 ;; This file provides the code for interacting with Guile using Geiser.
24 ;; By default (if `guix-use-guile-server' is non-nil) 2 Geiser REPLs are
25 ;; started. The main one (with "guile --listen" process) is used for
26 ;; "interacting" with a user - for showing a progress of
27 ;; installing/deleting Guix packages. The second (internal) REPL is
28 ;; used for synchronous evaluating, e.g. when information about
29 ;; packages/generations should be received for a list/info buffer.
31 ;; This "2 REPLs concept" makes it possible to have a running process of
32 ;; installing/deleting packages and to continue to search/list/get info
33 ;; about other packages at the same time. If you prefer to use a single
34 ;; Guix REPL, do not try to receive any information while there is a
35 ;; running code in the REPL (see
36 ;; <https://github.com/jaor/geiser/issues/28>).
38 ;; If you need to use "guix.el" in another Emacs (i.e. when there is
39 ;; a runnig "guile --listen..." REPL somewhere), you can either change
40 ;; `guix-default-port' in that Emacs instance or set
41 ;; `guix-use-guile-server' to t.
43 ;; Guix REPLs (unlike the usual Geiser REPLs) are not added to
44 ;; `geiser-repl--repls' variable, and thus cannot be used for evaluating
45 ;; while editing scm-files. The only purpose of Guix REPLs is to be an
46 ;; intermediate between "Guix/Guile level" and "Emacs interface level".
47 ;; That being said you can still want to use a Guix REPL while hacking
48 ;; auxiliary scheme-files for "guix.el". You can just use "M-x
49 ;; connect-to-guile" (connect to "localhost" and `guix-default-port') to
50 ;; have a usual Geiser REPL with all stuff defined by "guix.el" package.
54 (require 'geiser-mode
)
56 (defvar guix-load-path
57 (file-name-directory (or load-file-name
58 (locate-library "guix")))
59 "Directory with scheme files for \"guix.el\" package.")
61 (defvar guix-helper-file
62 (expand-file-name "guix-helper.scm" guix-load-path
)
63 "Auxiliary scheme file for loading.")
65 (defvar guix-guile-program
(or geiser-guile-binary
"guile")
66 "Name of the guile executable used for Guix REPL.
67 May be either a string (the name of the executable) or a list of
72 Where ARGS is a list of arguments to the guile program.")
77 (defgroup guix-repl nil
78 "Settings for Guix REPLs."
82 (defcustom guix-repl-startup-time
30000
83 "Time, in milliseconds, to wait for Guix REPL to startup.
84 Same as `geiser-repl-startup-time' but is used for Guix REPL.
85 If you have a slow system, try to increase this time."
89 (defcustom guix-repl-buffer-name
"*Guix REPL*"
90 "Default name of a Geiser REPL buffer used for Guix."
94 (defcustom guix-after-start-repl-hook
()
95 "Hook called after Guix REPL is started."
99 (defcustom guix-use-guile-server t
100 "If non-nil, start guile with '--listen' argument.
101 This allows to receive information about packages using an additional
102 REPL while some packages are being installed/removed in the main REPL."
106 (defcustom guix-default-port
37246
107 "Default port used if `guix-use-guile-server' is non-nil."
111 (defvar guix-repl-buffer nil
112 "Main Geiser REPL buffer used for communicating with Guix.
113 This REPL is used for processing package actions and for
114 receiving information if `guix-use-guile-server' is nil.")
116 (defvar guix-internal-repl-buffer nil
117 "Additional Geiser REPL buffer used for communicating with Guix.
118 This REPL is used for receiving information only if
119 `guix-use-guile-server' is non-nil.")
121 (defvar guix-internal-repl-buffer-name
"*Guix Internal REPL*"
122 "Default name of an internal Guix REPL buffer.")
124 (defun guix-get-guile-program (&optional internal
)
125 "Return a value suitable for `geiser-guile-binary'."
127 (not guix-use-guile-server
))
129 (append (if (listp guix-guile-program
)
131 (list guix-guile-program
))
132 ;; Guile understands "--listen=..." but not "--listen ..."
133 (list (concat "--listen="
134 (number-to-string guix-default-port
))))))
136 (defun guix-start-process-maybe ()
137 "Start Geiser REPL configured for Guix if needed."
138 (guix-start-repl-maybe)
139 (if guix-use-guile-server
140 (guix-start-repl-maybe 'internal
)
141 (setq guix-internal-repl-buffer guix-repl-buffer
)))
143 (defun guix-start-repl-maybe (&optional internal
)
144 "Start Guix REPL if needed.
145 If INTERNAL is non-nil, start an internal REPL."
146 (let* ((repl-var (guix-get-repl-buffer-variable internal
))
147 (repl (symbol-value repl-var
)))
148 (unless (and (buffer-live-p repl
)
149 (get-buffer-process repl
))
150 ;; Kill REPL buffer with a dead process
151 (and (buffer-live-p repl
) (kill-buffer repl
))
153 (message "Starting Geiser REPL for Guix ..."))
154 (let ((geiser-guile-binary (guix-get-guile-program internal
))
155 (geiser-guile-init-file (or internal guix-helper-file
))
156 (repl (get-buffer-create
157 (guix-get-repl-buffer-name internal
))))
159 (guix-start-repl repl
161 (geiser-repl--read-address
162 "localhost" guix-default-port
)))
164 (error (concat "Couldn't start Guix REPL. Perhaps the port %s is busy.\n"
165 "See buffer '%s' for details")
166 guix-default-port
(buffer-name repl
))))
169 (message "Guix REPL has been started.")
170 (run-hooks 'guix-after-start-repl-hook
))))))
172 (defun guix-start-repl (buffer &optional address
)
173 "Start Guix REPL in BUFFER.
174 If ADDRESS is non-nil, connect to a remote guile process using
175 this address (it should be defined by
176 `geiser-repl--read-address')."
177 ;; A mix of the code from `geiser-repl--start-repl' and
178 ;; `geiser-repl--to-repl-buffer'.
180 (geiser-guile-load-path (list guix-load-path
))
181 (geiser-repl-startup-time guix-repl-startup-time
))
182 (with-current-buffer buffer
184 (geiser-impl--set-buffer-implementation impl
)
185 (geiser-repl--autodoc-mode -
1)
186 (goto-char (point-max))
187 (let* ((prompt-re (geiser-repl--prompt-regexp impl
))
188 (deb-prompt-re (geiser-repl--debugger-prompt-regexp impl
))
189 (prompt (geiser-con--combined-prompt prompt-re deb-prompt-re
)))
191 (error "Oh no! Guix REPL in the buffer '%s' has not been started"
192 (buffer-name buffer
)))
193 (geiser-repl--save-remote-data address
)
194 (geiser-repl--start-scheme impl address prompt
)
195 (geiser-repl--quit-setup)
196 (geiser-repl--history-setup)
197 (setq-local geiser-repl--repls
(list buffer
))
198 (geiser-repl--set-this-buffer-repl buffer
)
199 (setq geiser-repl--connection
200 (geiser-con--make-connection
201 (get-buffer-process (current-buffer))
204 (geiser-repl--startup impl address
)
205 (geiser-repl--autodoc-mode 1)
206 (geiser-company--setup geiser-repl-company-p
)
207 (add-hook 'comint-output-filter-functions
208 'geiser-repl--output-filter
210 (set-process-query-on-exit-flag
211 (get-buffer-process (current-buffer))
212 geiser-repl-query-on-kill-p
)))))
214 (defun guix-get-repl-buffer (&optional internal
)
215 "Return Guix REPL buffer; start REPL if needed.
216 If INTERNAL is non-nil, return an additional internal REPL."
217 (guix-start-process-maybe)
218 (let ((repl (symbol-value (guix-get-repl-buffer-variable internal
))))
219 ;; If a new Geiser REPL is started, `geiser-repl--repl' variable may
220 ;; be set to the new value in a Guix REPL, so set it back to a
221 ;; proper value here.
222 (with-current-buffer repl
223 (geiser-repl--set-this-buffer-repl repl
))
226 (defun guix-get-repl-buffer-variable (&optional internal
)
227 "Return the name of a variable with a REPL buffer."
229 'guix-internal-repl-buffer
232 (defun guix-get-repl-buffer-name (&optional internal
)
233 "Return the name of a REPL buffer."
235 guix-internal-repl-buffer-name
236 guix-repl-buffer-name
))
238 (defun guix-switch-to-repl (&optional internal
)
239 "Switch to Guix REPL.
240 If INTERNAL is non-nil (interactively with prefix), switch to the
241 additional internal REPL if it exists."
243 (geiser-repl--switch-to-buffer (guix-get-repl-buffer internal
)))
246 ;;; Evaluating expressions
248 (defun guix-make-guile-expression (fun &rest args
)
249 "Return string containing a guile expression for calling FUN with ARGS."
250 (format "(%S %s)" fun
256 ;; An ugly hack to separate 'false' from nil
259 (concat "#" (prin1-to-string arg t
)))
260 ((or (symbolp arg
) (listp arg
))
261 (concat "'" (prin1-to-string arg
)))
262 (t (prin1-to-string arg
))))
266 (defun guix-eval (str &optional wrap
)
267 "Evaluate guile expression STR.
268 If WRAP is non-nil, wrap STR into (begin ...) form.
269 Return a list of strings with result values of evaluation."
270 (with-current-buffer (guix-get-repl-buffer 'internal
)
271 (let* ((wrapped (if wrap
(geiser-debug--wrap-region str
) str
))
272 (code `(:eval
(:scm
,wrapped
)))
273 (ret (geiser-eval--send/wait code
)))
274 (if (geiser-eval--retort-error ret
)
275 (error "Error in evaluating guile expression: %s"
276 (geiser-eval--retort-output ret
))
277 (cdr (assq 'result ret
))))))
279 (defun guix-eval-read (str &optional wrap
)
280 "Evaluate guile expression STR.
281 For the meaning of WRAP, see `guix-eval'.
282 Return elisp expression of the first result value of evaluation."
283 ;; Parsing scheme code with elisp `read' is probably not the best idea.
284 (read (replace-regexp-in-string
285 "#f\\|#<unspecified>" "nil"
286 (replace-regexp-in-string
287 "#t" "t" (car (guix-eval str wrap
))))))
289 (defun guix-eval-in-repl (str)
290 "Switch to Guix REPL and evaluate STR with guile expression there."
291 (let ((repl (guix-get-repl-buffer)))
292 (with-current-buffer repl
293 (delete-region (geiser-repl--last-prompt-end) (point-max))
294 (goto-char (point-max))
296 (geiser-repl--send-input))
297 (geiser-repl--switch-to-buffer repl
)))
299 (provide 'guix-backend
)
301 ;;; guix-backend.el ends here