|
32 | 32 |
|
33 | 33 | (require 'comint)
|
34 | 34 | (require 'subr-x)
|
| 35 | +(require 'map) |
35 | 36 |
|
36 | 37 |
|
37 | 38 | ;;; Customization
|
@@ -69,11 +70,25 @@ NOTE: Changing this variable will not affect running REPLs."
|
69 | 70 | :type 'string
|
70 | 71 | :group 'inf-elixir)
|
71 | 72 |
|
| 73 | +(defcustom inf-elixir-repl-buffer nil |
| 74 | + "Override for what REPL buffer code snippets should be sent to. |
| 75 | +
|
| 76 | +If this variable is set and the corresponding REPL buffer exists |
| 77 | +and has a living process, all `inf-elixir-send-*' commands will |
| 78 | +send to it. If this variable is unset (the default) or the |
| 79 | +indicated buffer is dead or has a dead process, a warning will be |
| 80 | +printed instead." |
| 81 | + :type 'buffer |
| 82 | + :group 'inf-elixir) |
| 83 | + |
72 | 84 |
|
73 | 85 | ;;; Mode definitions and configuration
|
74 | 86 | (defvar inf-elixir-project-buffers (make-hash-table :test 'equal)
|
75 | 87 | "A mapping of projects to buffers with running Elixir REPL subprocesses.")
|
76 | 88 |
|
| 89 | +(defvar inf-elixir-unaffiliated-buffers '() |
| 90 | + "A list of Elixir REPL buffers unaffiliated with any project.") |
| 91 | + |
77 | 92 | ;;;###autoload
|
78 | 93 | (define-minor-mode inf-elixir-minor-mode
|
79 | 94 | "Minor mode for Elixir buffers that allows interacting with the REPL.")
|
@@ -154,18 +169,66 @@ Always returns a REPL buffer for DIR."
|
154 | 169 | (with-current-buffer
|
155 | 170 | (apply #'make-comint-in-buffer buf-name nil (car cmd) nil (cdr cmd))
|
156 | 171 | (inf-elixir-mode)
|
157 |
| - (when dir (inf-elixir--set-project-buffer dir (current-buffer))) |
| 172 | + (if dir |
| 173 | + (inf-elixir--set-project-buffer dir (current-buffer)) |
| 174 | + (add-to-list 'inf-elixir-unaffiliated-buffers (current-buffer))) |
158 | 175 | (current-buffer)))))
|
159 | 176 |
|
160 |
| -(defun inf-elixir--send (command) |
161 |
| - "Send COMMAND to the REPL process in BUF." |
162 |
| - (let* ((proj-dir (inf-elixir--find-project-root)) |
163 |
| - (proj-buf (inf-elixir--get-project-buffer proj-dir))) |
164 |
| - (if (process-live-p (get-buffer-process proj-buf)) |
165 |
| - (with-current-buffer proj-buf |
166 |
| - (comint-add-to-input-history command) |
167 |
| - (comint-send-string proj-buf (concat command "\n"))) |
168 |
| - (message (concat "No REPL running in " (inf-elixir--project-name proj-dir)))))) |
| 177 | +(defun inf-elixir--send (cmd) |
| 178 | + "Determine where to send CMD and send it." |
| 179 | + (when-let ((buf (inf-elixir--determine-repl-buf))) |
| 180 | + (with-current-buffer buf |
| 181 | + (comint-add-to-input-history cmd) |
| 182 | + (comint-send-string buf (concat cmd "\n"))) |
| 183 | + (pop-to-buffer buf))) |
| 184 | + |
| 185 | +(defun inf-elixir--determine-repl-buf () |
| 186 | + "Determines where to send a cmd when `inf-elixir-send-*' are used." |
| 187 | + (if inf-elixir-repl-buffer |
| 188 | + (if (process-live-p (get-buffer-process inf-elixir-repl-buffer)) |
| 189 | + inf-elixir-repl-buffer |
| 190 | + (inf-elixir--prompt-repl-buffers "`inf-elixir-repl-buffer' is dead, please choose another REPL buffer: ")) |
| 191 | + (if-let ((proj-dir (inf-elixir--find-project-root))) |
| 192 | + (inf-elixir--determine-project-repl-buf proj-dir) |
| 193 | + (inf-elixir--prompt-repl-buffers)))) |
| 194 | + |
| 195 | +(defun inf-elixir--determine-project-repl-buf (proj-dir) |
| 196 | + "Determines where to send a cmd when `inf-elixir-send-*' are used inside the PROJ-DIR Mix project." |
| 197 | + (if-let ((proj-buf (inf-elixir--get-project-buffer proj-dir))) |
| 198 | + (if (process-live-p (get-buffer-process proj-buf)) |
| 199 | + proj-buf |
| 200 | + (if (y-or-n-p "A project REPL buffer exists, but the process is dead. Start a new one? ") |
| 201 | + (inf-elixir-project) |
| 202 | + (inf-elixir--prompt-repl-buffers))) |
| 203 | + (if (y-or-n-p "No REPL exists for this project yet. Start one? ") |
| 204 | + (inf-elixir-project) |
| 205 | + (inf-elixir--prompt-repl-buffers)))) |
| 206 | + |
| 207 | +(defun inf-elixir--prompt-repl-buffers (&optional prompt) |
| 208 | + "Prompt the user to select an inf-elixir REPL buffers or create an new one. |
| 209 | +
|
| 210 | +Returns the select buffer (as a buffer object). |
| 211 | +
|
| 212 | +If PROMPT is supplied, it is used as the prompt for a REPL buffer. |
| 213 | +
|
| 214 | +When the user selects a REPL, it is set as `inf-elixir-repl-buffer' locally in |
| 215 | +the buffer so that the choice is remembered for that buffer." |
| 216 | + ;; Cleanup |
| 217 | + (setq inf-elixir-unaffiliated-buffers (seq-filter 'buffer-live-p inf-elixir-unaffiliated-buffers)) |
| 218 | + (setq inf-elixir-project-buffers |
| 219 | + (map-into |
| 220 | + (map-filter (lambda (_dir buf) (buffer-live-p buf)) inf-elixir-project-buffers) |
| 221 | + '(hash-table :test equal))) |
| 222 | + ;; Actual functionality |
| 223 | + (let* ((repl-buffers (append |
| 224 | + '("Create new") |
| 225 | + (mapcar (lambda (buf) `(,(buffer-name buf) . buf)) inf-elixir-unaffiliated-buffers) |
| 226 | + (mapcar (lambda (buf) `(,(buffer-name buf) . buf)) (hash-table-values inf-elixir-project-buffers)))) |
| 227 | + (prompt (or prompt "Which REPL?")) |
| 228 | + (selected-buf (completing-read prompt repl-buffers (lambda (_) t) t))) |
| 229 | + (setq-local inf-elixir-repl-buffer (if (equal selected-buf "Create new") |
| 230 | + (inf-elixir) |
| 231 | + selected-buf)))) |
169 | 232 |
|
170 | 233 |
|
171 | 234 | ;;; Public functions
|
@@ -222,6 +285,11 @@ be prompted for the REPL command. The default is provided by
|
222 | 285 | (inf-elixir-run-cmd default-directory cmd))
|
223 | 286 | (message "Could not find project root! Try `inf-elixir' instead.")))
|
224 | 287 |
|
| 288 | +(defun inf-elixir-set-repl () |
| 289 | + "Select which REPL to use for this buffer." |
| 290 | + (interactive) |
| 291 | + (inf-elixir--prompt-repl-buffers)) |
| 292 | + |
225 | 293 | (defun inf-elixir-send-line ()
|
226 | 294 | "Send the region to the REPL buffer and run it."
|
227 | 295 | (interactive)
|
|
0 commit comments