Add commands to switch to and from a utop buffer

Those are extremely common in Emacs modes and allow people to
quickly jump to a REPL and back to the last source buffer they
were editing, while using the same keybinding.

I've modeled the implementation here after that of inf-clojure (a similar
mode written by me). I've also implemented a mode menu for
utop-minor-mode, so it's easier for the users to discover its
functionality.
This commit is contained in:
Bozhidar Batsov 2022-07-18 21:40:52 +03:00 committed by Rudi Grinberg
parent 09d26a7509
commit 2a405226f2
2 changed files with 59 additions and 10 deletions

View File

@ -219,12 +219,13 @@ You can start utop inside Emacs with: `M-x utop`.
`utop.el` also ships with a minor mode that has the following key-bindings: `utop.el` also ships with a minor mode that has the following key-bindings:
| key-binding | function | Description | | key-binding | function | Description |
|-------------|-------------------|------------------------------| |-------------|---------------------|------------------------------|
| C-c C-s | utop | Start a utop buffer | | C-c C-s | utop | Start a utop buffer |
| C-x C-e | utop-eval-phrase | Evaluate the current phrase | | C-x C-e | utop-eval-phrase | Evaluate the current phrase |
| C-x C-r | utop-eval-region | Evaluate the selected region | | C-x C-r | utop-eval-region | Evaluate the selected region |
| C-c C-b | utop-eval-buffer | Evaluate the current buffer | | C-c C-b | utop-eval-buffer | Evaluate the current buffer |
| C-c C-k | utop-kill | Kill a running utop process | | C-c C-k | utop-kill | Kill a running utop process |
| C-c C-z | utop-switch-to-repl | Switch to utop process |
You can enable the minor mode using `M-x utop-minor-mode`, or you can You can enable the minor mode using `M-x utop-minor-mode`, or you can
have it enabled by default with the following configuration: have it enabled by default with the following configuration:

View File

@ -25,6 +25,7 @@
(require 'easymenu) (require 'easymenu)
(require 'pcase) (require 'pcase)
(require 'seq)
(require 'tabulated-list) (require 'tabulated-list)
(require 'tuareg) (require 'tuareg)
@ -885,6 +886,32 @@ If ADD-TO-HISTORY is t then the input will be added to history."
(goto-char utop-prompt-max) (goto-char utop-prompt-max)
(move-beginning-of-line 1)))) (move-beginning-of-line 1))))
;; +-----------------------------------------------------------------+
;; | Switch to REPL |
;; +-----------------------------------------------------------------+
(defun utop-switch-to-repl (eob-p)
"Switch to the inferior utop process buffer.
With prefix argument EOB-P, positions cursor at end of buffer."
(interactive "P")
(if (get-buffer-process utop-buffer-name)
(pop-to-buffer utop-buffer-name)
(call-interactively #'utop))
(when eob-p
(push-mark)
(goto-char (point-max))))
(defun utop-switch-to-recent-buffer ()
"Switch to the most recently used `utop-minor-mode' buffer."
(interactive)
(let ((recent-utop-minor-mode-buffer (seq-find (lambda (buf)
(with-current-buffer buf (bound-and-true-p utop-minor-mode)))
(buffer-list))))
(if recent-utop-minor-mode-buffer
(pop-to-buffer recent-utop-minor-mode-buffer)
(message "utop: No recent OCaml buffer found."))))
;; +-----------------------------------------------------------------+ ;; +-----------------------------------------------------------------+
;; | Process control | ;; | Process control |
;; +-----------------------------------------------------------------+ ;; +-----------------------------------------------------------------+
@ -1100,6 +1127,25 @@ See https://github.com/ocaml-community/utop for configuration information."))
(define-key map (kbd "C-x C-r") #'utop-eval-region) (define-key map (kbd "C-x C-r") #'utop-eval-region)
(define-key map (kbd "C-c C-b") #'utop-eval-buffer) (define-key map (kbd "C-c C-b") #'utop-eval-buffer)
(define-key map (kbd "C-c C-k") #'utop-kill) (define-key map (kbd "C-c C-k") #'utop-kill)
(define-key map (kbd "C-c C-z") #'utop-switch-to-repl)
(easy-menu-define
utop-minor-mode-menu map
"utop menu."
'("utop"
["Start utop" utop t]
["Switch to utop" utop-switch-to-repl t]
["Interrupt utop" utop-interrupt :active (utop-is-running)]
["Kill utop" utop-kill :active (utop-is-running)]
["Exit utop gracefully" utop-exit :active (utop-is-running)]
"---"
["Evaluate phrase" utop-eval-phrase :active (and (utop-is-running) (eq utop-state 'edit))]
["Evaluate region" utop-eval-region :active (and (utop-is-running) (eq utop-state 'edit))]
["Evaluate buffer" utop-eval-buffer :active (and (utop-is-running) (eq utop-state 'edit))]
"---"
["Customize utop" (customize-group 'utop) t]
"---"
["About" utop-about t]
["Help" utop-help t]))
map) map)
;; Load local file variables ;; Load local file variables
(add-hook 'hack-local-variables-hook 'utop-hack-local-variables)) (add-hook 'hack-local-variables-hook 'utop-hack-local-variables))
@ -1128,11 +1174,13 @@ See https://github.com/ocaml-community/utop for configuration information."))
utop-menu utop-mode-map utop-menu utop-mode-map
"utop menu." "utop menu."
'("utop" '("utop"
["Start OCaml" utop t] ["Start utop" utop t]
["Interrupt OCaml" utop-interrupt :active (utop-is-running)] ["Interrupt utop" utop-interrupt :active (utop-is-running)]
["Kill OCaml" utop-kill :active (utop-is-running)] ["Kill utop" utop-kill :active (utop-is-running)]
["Exit utop gracefully" utop-exit :active (utop-is-running)] ["Exit utop gracefully" utop-exit :active (utop-is-running)]
"---"
["Evaluate Phrase" utop-eval-input-auto-end :active (and (utop-is-running) (eq utop-state 'edit))] ["Evaluate Phrase" utop-eval-input-auto-end :active (and (utop-is-running) (eq utop-state 'edit))]
["Switch to OCaml source buffer" utop-switch-to-recent-buffer t]
"---" "---"
["Customize utop" (customize-group 'utop) t] ["Customize utop" (customize-group 'utop) t]
"---" "---"