-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy patheval-in-repl.el
341 lines (290 loc) · 12.2 KB
/
eval-in-repl.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
;;; eval-in-repl.el --- Consistent ESS-like eval interface for various REPLs -*- lexical-binding: t; -*-
;; Copyright (C) 2014- Kazuki YOSHIDA
;; Author: Kazuki YOSHIDA <kazukiyoshida@mail.harvard.edu>
;; Keywords: tools, convenience
;; URL: https://github.com/kaz-yos/eval-in-repl
;; Version: 0.9.7
;; Package-Requires: (dash paredit ace-window)
;; This program 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.
;; This program 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:
;; eval-in-repl: Consistent ESS-like eval interface for various REPLs
;;
;; This package does what ESS does for R for various REPLs, including ielm.
;;
;; Emacs Speaks Statistics (ESS) package has a nice function called
;; ess-eval-region-or-line-and-step, which is assigned to C-RET.
;; This function sends a line or a selected region to the corresponding
;; shell (R, Julia, Stata, etc) visibly. It also start up a shell if
;; there is none.
;;
;; This package along with REPL/shell specific packages implement similar
;; work flow for various REPLs.
;;
;; This file alone is not functional. Also require the following depending
;; on your needs.
;;
;; eval-in-repl-ielm.el for Emacs Lisp (via ielm)
;; eval-in-repl-cider.el for Clojure (via cider.el)
;; eval-in-repl-slime.el for Common Lisp (via slime.el)
;; eval-in-repl-geiser.el for Racket/Scheme (via geiser.el)
;; eval-in-repl-racket.el for Racket (via racket-mode.el)
;; eval-in-repl-scheme.el for Scheme (via scheme.el and cmuscheme.el)
;; eval-in-repl-hy.el for Hy (via hy-mode.el and inf-lisp.el)
;;
;; eval-in-repl-python.el for Python (via python.el)
;; eval-in-repl-ruby.el for Ruby (via ruby-mode.el, and inf-ruby.el)
;; eval-in-repl-sml.el for Standard ML (via sml-mode.el)
;; eval-in-repl-ocaml.el for OCaml (via tuareg.el)
;; eval-in-repl-prolog.el for Prolog (via prolog.el)
;; eval-in-repl-javascript.el for Javascript (via js3-mode.el, js2-mode.el, and js-comint.el)
;;
;; eval-in-repl-shell.el for Shell (via native shell support)
;;
;;
;; See the URL below for installation and configuration instructions,
;; known issues, and version history.
;; https://github.com/kaz-yos/eval-in-repl/
;;; Code:
;;;
;;; Require dependencies
(require 'dash)
(require 'paredit)
(require 'ace-window)
;;;
;;; CUSTOMIZATION VARIABLES
;; 14.4 Customization Types
;; http://www.gnu.org/software/emacs/manual/html_node/elisp/Customization-Types.html
;; http://www.gnu.org/software/emacs/manual/html_node/elisp/Simple-Types.html#Simple-Types
;;
;;; If true, jump after evaluation
;; Contributed by Andrea Richiardi (https://github.com/arichiardi)
(defcustom eir-jump-after-eval t
"When t enables jumping to next expression after REPL evaluation.
Jumps to the next expression after REPL evaluation if this option
is not-nil (default), stays where it is otherwise."
:group 'eval-in-repl
:type '(boolean))
;;
;;; If true, delete other windows
;; Contributed by stardiviner (https://github.com/stardiviner)
(defcustom eir-delete-other-windows nil
"When t, deletes all non-script windows at REPL startup.
If t, at REPL startup, all windows other than the current script
window are deleted and two-window REPL/script configuration is used."
:group 'eval-in-repl
:type '(boolean))
;;
;;; If true, always split script window
(defcustom eir-always-split-script-window nil
"When t, always split script window at REPL startup.
If t, at REPL startup, the current script window is split into
two using the eir-repl-placement setting."
:group 'eval-in-repl
:type '(boolean))
;;
;;; How to split window
(defcustom eir-repl-placement 'left
"Where to place REPL when splitting script window.
Give a quoted symbol 'left, 'right, 'above, or 'below."
:group 'eval-in-repl
:type '(symbol)
:options '(left right above below))
(defcustom eir-shell-buffer-name "*shell*"
"The shell buffer name that will be used for the code execution"
:group 'eval-in-repl
:type '(string))
(defcustom eir-shell-type 'shell
"The shell mode to be used for newly spawned shells (ie shell or term)"
:group 'eval-in-repl
:type '(symbol)
:options '(shell term vterm))
(defcustom eir-shell-term-program "/bin/bash"
"The default term program to be used when eir-shell-type is 'term"
:group 'eval-in-repl
:type '(string))
;;;
;;; COMMON ELEMENTS
;;; eir--matching-elements
;; Pure function
(defun eir--matching-elements (regexp lst)
"Return a list of elements matching the REGEXP in the LIST."
;; emacs version of filter (dash.el)
(-filter
;; predicate: non-nil if an element matches the REGEXP
#'(lambda (elt) (string-match regexp elt))
lst))
;;; eir-start-repl
;; A function to start a REPL if not already running
;; https://stat.ethz.ch/pipermail/ess-help/2012-December/008426.html
;; http://t7331.codeinpro.us/q/51502552e8432c0426273040
(defun eir-repl-start (repl-buffer-regexp fun-repl-start &optional exec-in-script)
"Start a REPL if not already available.
Start a REPL using a function specified in FUN-REPL-START,
if a buffer matching REPL-BUFFER-REGEXP is not already available.
If EXEC-IN-SCRIPT is true, run FUN-REPL-START in the script buffer, which
is the intended use for some major modes (e.g., geiser).
Also split the current window when staring a REPL."
(interactive)
;; Create local variables
(let* (window-script
window-repl
name-script-buffer
name-repl-buffer)
;;
;; Execute body only if no REPL is found by name
(when (not (eir--matching-elements
repl-buffer-regexp
(mapcar #'buffer-name (buffer-list))))
;; Make window-script keep the selected window (should be script)
(setq window-script (selected-window))
;; Make name-script-buffer keep the selected buffer (should be script)
(setq name-script-buffer (buffer-name))
;; If requested, delete all other windows to start from scratch
(when eir-delete-other-windows
;; C-x 1 Keep only the window from which this function was called.
(delete-other-windows))
;; Check window count to determine where to put REPL
(cond
;; If always splitting, split
(eir-always-split-script-window
(progn
(setq window-repl (split-window window-script nil eir-repl-placement nil))
;; In order to manipulate buffer list ordering. This is not necessary?
;; (select-window window-repl)
;; (select-window window-script)
))
;; If executing the REPL starter in the script buffer, do nothing
;; Some modes require this:
;; js, ocaml, prolog, ruby, sml, cider
;; This does not allows control over where the REPL shows up.
(exec-in-script nil)
;; If mutiple windows exist, use ace-select-window
;; 2 windows: switch; 3+ windows selection screen
((> (count-windows) 1) (setq window-repl (ace-select-window)))
;; If only 1 window exists, split it.
(t (setq window-repl (split-window window-script nil eir-repl-placement nil))))
;; Shift focus to the newly created REPL window,
;; if not executing REPL starter in script
(when (not exec-in-script)
(select-window window-repl))
;; Activate the REPL (Interactive functions are used)
;; If executing in script, then focus is still in script
(call-interactively fun-repl-start)
;; Select the script window.
(select-window window-script))))
(cl-defgeneric eir-insert (string)
"Default implementation of eir-insert is just to insert a string into the apropriate buffer.
Other REPLs however might need other implementations (see for example eval-in-repl-shell.el
for term-mode).
Define your own with cl-defmethod"
(insert string))
;;; eir-send-to-repl
(defun eir-send-to-repl (fun-change-to-repl fun-execute region-string)
"Send a string to a REPL buffer for execution.
Given a REGION-STRING, switch to the REPL buffer by FUN-CHANGE-TO-REPL,
and execute by FUN-EXECUTE."
(let* (;; Assign the current buffer
(script-window (selected-window)))
;; Change other window to REPL
(funcall fun-change-to-repl)
;; Move to end of buffer
(goto-char (point-max))
;; Insert the string
(eir-insert region-string)
;; Execute
(funcall fun-execute)
;; Come back to the script
(select-window script-window)
;; Deactivate selection explicitly (necessary in Emacs 25)
(deactivate-mark)
;; Return nil (this is a void function)
nil))
;;;
;;; COMMON ELEMENTS FOR LISP LANGUAGES
;;; eir-eval-in-repl-lisp (used as a skeleton)
(defun eir-eval-in-repl-lisp (repl-buffer-regexp
fun-repl-start
fun-repl-send
defun-string
&optional exec-in-script)
"eval-in-repl function for lisp languages.
Evaluate expression using a REPL specified by REPL-BUFFER-REGEXP.
If not present, a REPL is started using FUN-REPL-START.
Send expression using a function specified in FUN-REPL-SEND.
A function definition is detected by a string specified in DEFUN-STRING
and handled accordingly.
EXEC-IN-SCRIPT determines if FUN-REPL-START should be run in the script."
(interactive)
(let* (;; Save current point
(initial-point (point)))
;; Check for the presence of a REPL buffer
(eir-repl-start repl-buffer-regexp fun-repl-start exec-in-script)
;; Check if selection is present
(if (and transient-mark-mode mark-active)
;; If there is a selected region, send it to the REPL
(funcall fun-repl-send (buffer-substring-no-properties (point) (mark)))
;; If not selected, do all the following
;; Move to the beginning of line
(beginning-of-line)
;; Check if the first word is def (function def)
(if (looking-at defun-string)
;; Use eval-defun if on defun
(progn
;; Set a mark there
(set-mark (line-beginning-position))
;; Go to the end
(forward-sexp)
;; Send to REPL
(funcall fun-repl-send (buffer-substring-no-properties (point) (mark)))
;; Go to the next expression
(forward-sexp))
;; If it is not def, do all the following
;; Go to the previous position
(goto-char initial-point)
;; Go back one S-exp. (paredit dependency)
(paredit-backward)
;; Loop until at a top-level "(" at column 0
(while (not (equal (current-column) 0))
;; Go back one S-exp. (paredit dependency)
(paredit-backward))
;; Set a mark there
(set-mark (line-beginning-position))
;; Go to the end of the S-exp starting there
(forward-sexp)
;; Send to REPL
(funcall fun-repl-send (buffer-substring-no-properties (point) (mark))))
;; Go to the next expression if configured so
;; Contributed by Andrea Richiardi (https://github.com/arichiardi)
(if eir-jump-after-eval
(forward-sexp)
;; Go back to the initial position otherwise
(goto-char initial-point)))))
;;; COMMON ELEMENT FOR NON-LISP LANGUAGES
;;; eir-next-code-line (taken from essh.el)
(defun eir-next-code-line (&optional arg)
"Move ARG lines of code forward (backward if ARG is negative).
Skips past all empty and comment lines. Default for ARG is 1.
On success, return 0. Otherwise, go as far as possible and return -1."
(interactive "p")
(or arg (setq arg 1))
(beginning-of-line)
(let ((n 0)
(inc (if (> arg 0) 1 -1)))
(while (and (/= arg 0) (= n 0))
(setq n (forward-line inc)); n=0 is success
(while (and (= n 0)
(looking-at "\\s-*\\($\\|\\s<\\)"))
(setq n (forward-line inc)))
(setq arg (- arg inc)))
n))
(provide 'eval-in-repl)
;;; eval-in-repl.el ends here