|
| 1 | +;;; inferior-coq.el --- Run an inferior Coq process. |
| 2 | +;;; |
| 3 | +;;; Copyright (C) Marco Maggesi <[email protected]> |
| 4 | +;;; Time-stamp: "2002-02-28 12:15:04 maggesi" |
| 5 | + |
| 6 | + |
| 7 | +;; Emacs Lisp Archive Entry |
| 8 | +;; Filename: inferior-coq.el |
| 9 | +;; Version: 1.0 |
| 10 | +;; Keywords: process coq |
| 11 | +;; Author: Marco Maggesi <[email protected]> |
| 12 | +;; Maintainer: Marco Maggesi <[email protected]> |
| 13 | +;; Description: Run an inferior Coq process. |
| 14 | +;; URL: http://www.math.unifi.it/~maggesi/ |
| 15 | +;; Compatibility: Emacs20, Emacs21, XEmacs21 |
| 16 | + |
| 17 | +;; This is free software; you can redistribute it and/or modify it under |
| 18 | +;; the terms of the GNU General Public License as published by the Free |
| 19 | +;; Software Foundation; either version 2, or (at your option) any later |
| 20 | +;; version. |
| 21 | +;; |
| 22 | +;; This is distributed in the hope that it will be useful, but WITHOUT |
| 23 | +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 24 | +;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 25 | +;; for more details. |
| 26 | +;; |
| 27 | +;; You should have received a copy of the GNU General Public License |
| 28 | +;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 29 | +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
| 30 | +;; MA 02111-1307, USA. |
| 31 | + |
| 32 | +;;; Commentary: |
| 33 | + |
| 34 | +;; Coq is a proof assistant (http://coq.inria.fr/). This code run an |
| 35 | +;; inferior Coq process and defines functions to send bits of code |
| 36 | +;; from other buffers to the inferior process. This is a |
| 37 | +;; customisation of comint-mode (see comint.el). For a more complex |
| 38 | +;; and full featured Coq interface under Emacs look at Proof General |
| 39 | +;; (http://zermelo.dcs.ed.ac.uk/~proofgen/). |
| 40 | +;; |
| 41 | +;; Written by Marco Maggesi <[email protected]> with code heavly |
| 42 | +;; borrowed from emacs cmuscheme.el |
| 43 | +;; |
| 44 | +;; Please send me bug reports, bug fixes, and extensions, so that I can |
| 45 | +;; merge them into the master source. |
| 46 | + |
| 47 | +;;; Installation: |
| 48 | + |
| 49 | +;; You need to have coq.el already installed (it comes with the |
| 50 | +;; standard Coq distribution) in order to use this code. Put this |
| 51 | +;; file somewhere in you load-path and add the following lines in your |
| 52 | +;; "~/.emacs": |
| 53 | +;; |
| 54 | +;; (setq auto-mode-alist (cons '("\\.v$" . coq-mode) auto-mode-alist)) |
| 55 | +;; (autoload 'coq-mode "coq" "Major mode for editing Coq vernacular." t) |
| 56 | +;; (autoload 'run-coq "inferior-coq" "Run an inferior Coq process." t) |
| 57 | +;; (autoload 'run-coq-other-window "inferior-coq" |
| 58 | +;; "Run an inferior Coq process in a new window." t) |
| 59 | +;; (autoload 'run-coq-other-frame "inferior-coq" |
| 60 | +;; "Run an inferior Coq process in a new frame." t) |
| 61 | + |
| 62 | +;;; Usage: |
| 63 | + |
| 64 | +;; Call `M-x "run-coq'. |
| 65 | +;; |
| 66 | +;; Functions and key bindings (Learn more keys with `C-c C-h' or `C-h m'): |
| 67 | +;; C-return ('M-x coq-send-line) send the current line. |
| 68 | +;; C-c C-r (`M-x coq-send-region') send the current region. |
| 69 | +;; C-c C-a (`M-x coq-send-abort') send the command "Abort". |
| 70 | +;; C-c C-t (`M-x coq-send-restart') send the command "Restart". |
| 71 | +;; C-c C-s (`M-x coq-send-show') send the command "Show". |
| 72 | +;; C-c C-u (`M-x coq-send-undo') send the command "Undo". |
| 73 | +;; C-c C-v (`M-x coq-check-region') run command "Check" on region. |
| 74 | +;; C-c . (`M-x coq-come-here') Restart and send until current point. |
| 75 | + |
| 76 | +;;; Change Log: |
| 77 | + |
| 78 | +;; From -0.0 to 1.0 brought into existence. |
| 79 | + |
| 80 | + |
| 81 | +(require 'coq) |
| 82 | +(require 'comint) |
| 83 | + |
| 84 | +(setq coq-program-name "coqtop") |
| 85 | + |
| 86 | +(defgroup inferior-coq nil |
| 87 | + "Run a coq process in a buffer." |
| 88 | + :group 'coq) |
| 89 | + |
| 90 | +(defcustom inferior-coq-mode-hook nil |
| 91 | + "*Hook for customising inferior-coq mode." |
| 92 | + :type 'hook |
| 93 | + :group 'coq) |
| 94 | + |
| 95 | +(defvar inferior-coq-mode-map |
| 96 | + (let ((m (make-sparse-keymap))) |
| 97 | + (define-key m "\C-c\C-r" 'coq-send-region) |
| 98 | + (define-key m "\C-c\C-a" 'coq-send-abort) |
| 99 | + (define-key m "\C-c\C-t" 'coq-send-restart) |
| 100 | + (define-key m "\C-c\C-s" 'coq-send-show) |
| 101 | + (define-key m "\C-c\C-u" 'coq-send-undo) |
| 102 | + (define-key m "\C-c\C-v" 'coq-check-region) |
| 103 | + m)) |
| 104 | + |
| 105 | +;; Install the process communication commands in the coq-mode keymap. |
| 106 | +(define-key coq-mode-map [(control return)] 'coq-send-line) |
| 107 | +(define-key coq-mode-map "\C-c\C-r" 'coq-send-region) |
| 108 | +(define-key coq-mode-map "\C-c\C-a" 'coq-send-abort) |
| 109 | +(define-key coq-mode-map "\C-c\C-t" 'coq-send-restart) |
| 110 | +(define-key coq-mode-map "\C-c\C-s" 'coq-send-show) |
| 111 | +(define-key coq-mode-map "\C-c\C-u" 'coq-send-undo) |
| 112 | +(define-key coq-mode-map "\C-c\C-v" 'coq-check-region) |
| 113 | +(define-key coq-mode-map "\C-c." 'coq-come-here) |
| 114 | + |
| 115 | +(defvar coq-buffer) |
| 116 | + |
| 117 | +(define-derived-mode inferior-coq-mode comint-mode "Inferior Coq" |
| 118 | + "\ |
| 119 | +Major mode for interacting with an inferior Coq process. |
| 120 | +
|
| 121 | +The following commands are available: |
| 122 | +\\{inferior-coq-mode-map} |
| 123 | +
|
| 124 | +A Coq process can be fired up with M-x run-coq. |
| 125 | +
|
| 126 | +Customisation: Entry to this mode runs the hooks on comint-mode-hook |
| 127 | +and inferior-coq-mode-hook (in that order). |
| 128 | +
|
| 129 | +You can send text to the inferior Coq process from other buffers |
| 130 | +containing Coq source. |
| 131 | +
|
| 132 | +Functions and key bindings (Learn more keys with `C-c C-h'): |
| 133 | + C-return ('M-x coq-send-line) send the current line. |
| 134 | + C-c C-r (`M-x coq-send-region') send the current region. |
| 135 | + C-c C-a (`M-x coq-send-abort') send the command \"Abort\". |
| 136 | + C-c C-t (`M-x coq-send-restart') send the command \"Restart\". |
| 137 | + C-c C-s (`M-x coq-send-show') send the command \"Show\". |
| 138 | + C-c C-u (`M-x coq-send-undo') send the command \"Undo\". |
| 139 | + C-c C-v (`M-x coq-check-region') run command \"Check\" on region. |
| 140 | + C-c . (`M-x coq-come-here') Restart and send until current point. |
| 141 | +" |
| 142 | + ;; Customise in inferior-coq-mode-hook |
| 143 | + (setq comint-prompt-regexp "^[^<]* < *") |
| 144 | + (coq-mode-variables) |
| 145 | + (setq mode-line-process '(":%s")) |
| 146 | + (setq comint-input-filter (function coq-input-filter)) |
| 147 | + (setq comint-get-old-input (function coq-get-old-input))) |
| 148 | + |
| 149 | +(defcustom inferior-coq-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'" |
| 150 | + "*Input matching this regexp are not saved on the history list. |
| 151 | +Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters." |
| 152 | + :type 'regexp |
| 153 | + :group 'inferior-coq) |
| 154 | + |
| 155 | +(defun coq-input-filter (str) |
| 156 | + "Don't save anything matching `inferior-coq-filter-regexp'." |
| 157 | + (not (string-match inferior-coq-filter-regexp str))) |
| 158 | + |
| 159 | +(defun coq-get-old-input () |
| 160 | + "Snarf the sexp ending at point." |
| 161 | + (save-excursion |
| 162 | + (let ((end (point))) |
| 163 | + (backward-sexp) |
| 164 | + (buffer-substring (point) end)))) |
| 165 | + |
| 166 | +(defun coq-args-to-list (string) |
| 167 | + (let ((where (string-match "[ \t]" string))) |
| 168 | + (cond ((null where) (list string)) |
| 169 | + ((not (= where 0)) |
| 170 | + (cons (substring string 0 where) |
| 171 | + (coq-args-to-list (substring string (+ 1 where) |
| 172 | + (length string))))) |
| 173 | + (t (let ((pos (string-match "[^ \t]" string))) |
| 174 | + (if (null pos) |
| 175 | + nil |
| 176 | + (coq-args-to-list (substring string pos |
| 177 | + (length string))))))))) |
| 178 | + |
| 179 | +;;;###autoload |
| 180 | +(defun run-coq (cmd) |
| 181 | + "Run an inferior Coq process, input and output via buffer *coq*. |
| 182 | +If there is a process already running in `*coq*', switch to that buffer. |
| 183 | +With argument, allows you to edit the command line (default is value |
| 184 | +of `coq-program-name'). Runs the hooks `inferior-coq-mode-hook' |
| 185 | +\(after the `comint-mode-hook' is run). |
| 186 | +\(Type \\[describe-mode] in the process buffer for a list of commands.)" |
| 187 | + |
| 188 | + (interactive (list (if current-prefix-arg |
| 189 | + (read-string "Run Coq: " coq-program-name) |
| 190 | + coq-program-name))) |
| 191 | + (if (not (comint-check-proc "*coq*")) |
| 192 | + (let ((cmdlist (coq-args-to-list cmd))) |
| 193 | + (set-buffer (apply 'make-comint "coq" (car cmdlist) |
| 194 | + nil (cdr cmdlist))) |
| 195 | + (inferior-coq-mode))) |
| 196 | + (setq coq-program-name cmd) |
| 197 | + (setq coq-buffer "*coq*") |
| 198 | + (switch-to-buffer "*coq*")) |
| 199 | +;;;###autoload (add-hook 'same-window-buffer-names "*coq*") |
| 200 | + |
| 201 | +;;;###autoload |
| 202 | +(defun run-coq-other-window (cmd) |
| 203 | + "Run an inferior Coq process, input and output via buffer *coq*. |
| 204 | +If there is a process already running in `*coq*', switch to that buffer. |
| 205 | +With argument, allows you to edit the command line (default is value |
| 206 | +of `coq-program-name'). Runs the hooks `inferior-coq-mode-hook' |
| 207 | +\(after the `comint-mode-hook' is run). |
| 208 | +\(Type \\[describe-mode] in the process buffer for a list of commands.)" |
| 209 | + |
| 210 | + (interactive (list (if current-prefix-arg |
| 211 | + (read-string "Run Coq: " coq-program-name) |
| 212 | + coq-program-name))) |
| 213 | + (if (not (comint-check-proc "*coq*")) |
| 214 | + (let ((cmdlist (coq-args-to-list cmd))) |
| 215 | + (set-buffer (apply 'make-comint "coq" (car cmdlist) |
| 216 | + nil (cdr cmdlist))) |
| 217 | + (inferior-coq-mode))) |
| 218 | + (setq coq-program-name cmd) |
| 219 | + (setq coq-buffer "*coq*") |
| 220 | + (pop-to-buffer "*coq*")) |
| 221 | +;;;###autoload (add-hook 'same-window-buffer-names "*coq*") |
| 222 | + |
| 223 | +(defun run-coq-other-frame (cmd) |
| 224 | + "Run an inferior Coq process, input and output via buffer *coq*. |
| 225 | +If there is a process already running in `*coq*', switch to that buffer. |
| 226 | +With argument, allows you to edit the command line (default is value |
| 227 | +of `coq-program-name'). Runs the hooks `inferior-coq-mode-hook' |
| 228 | +\(after the `comint-mode-hook' is run). |
| 229 | +\(Type \\[describe-mode] in the process buffer for a list of commands.)" |
| 230 | + |
| 231 | + (interactive (list (if current-prefix-arg |
| 232 | + (read-string "Run Coq: " coq-program-name) |
| 233 | + coq-program-name))) |
| 234 | + (if (not (comint-check-proc "*coq*")) |
| 235 | + (let ((cmdlist (coq-args-to-list cmd))) |
| 236 | + (set-buffer (apply 'make-comint "coq" (car cmdlist) |
| 237 | + nil (cdr cmdlist))) |
| 238 | + (inferior-coq-mode))) |
| 239 | + (setq coq-program-name cmd) |
| 240 | + (setq coq-buffer "*coq*") |
| 241 | + (switch-to-buffer-other-frame "*coq*")) |
| 242 | + |
| 243 | +(defun switch-to-coq (eob-p) |
| 244 | + "Switch to the coq process buffer. |
| 245 | +With argument, position cursor at end of buffer." |
| 246 | + (interactive "P") |
| 247 | + (if (get-buffer coq-buffer) |
| 248 | + (pop-to-buffer coq-buffer) |
| 249 | + (error "No current process buffer. See variable `coq-buffer'")) |
| 250 | + (cond (eob-p |
| 251 | + (push-mark) |
| 252 | + (goto-char (point-max))))) |
| 253 | + |
| 254 | +(defun coq-send-region (start end) |
| 255 | + "Send the current region to the inferior Coq process." |
| 256 | + (interactive "r") |
| 257 | + (comint-send-region (coq-proc) start end) |
| 258 | + (comint-send-string (coq-proc) "\n")) |
| 259 | + |
| 260 | +(defun coq-send-line () |
| 261 | + "Send the current line to the Coq process." |
| 262 | + (interactive) |
| 263 | + (save-excursion |
| 264 | + (end-of-line) |
| 265 | + (let ((end (point))) |
| 266 | + (beginning-of-line) |
| 267 | + (coq-send-region (point) end))) |
| 268 | + (next-line 1)) |
| 269 | + |
| 270 | +(defun coq-send-abort () |
| 271 | + "Send the command \"Abort.\" to the inferior Coq process." |
| 272 | + (interactive) |
| 273 | + (comint-send-string (coq-proc) "Abort.\n")) |
| 274 | + |
| 275 | +(defun coq-send-restart () |
| 276 | + "Send the command \"Restart.\" to the inferior Coq process." |
| 277 | + (interactive) |
| 278 | + (comint-send-string (coq-proc) "Restart.\n")) |
| 279 | + |
| 280 | +(defun coq-send-undo () |
| 281 | + "Reset coq to the initial state and send the region between the |
| 282 | + beginning of file and the point." |
| 283 | + (interactive) |
| 284 | + (comint-send-string (coq-proc) "Undo.\n")) |
| 285 | + |
| 286 | +(defun coq-check-region (start end) |
| 287 | + "Run the commmand \"Check\" on the current region." |
| 288 | + (interactive "r") |
| 289 | + (comint-proc-query (coq-proc) |
| 290 | + (concat "Check " |
| 291 | + (buffer-substring start end) |
| 292 | + ".\n"))) |
| 293 | + |
| 294 | +(defun coq-send-show () |
| 295 | + "Send the command \"Show.\" to the inferior Coq process." |
| 296 | + (interactive) |
| 297 | + (comint-send-string (coq-proc) "Show.\n")) |
| 298 | + |
| 299 | +(defun coq-come-here () |
| 300 | + "Reset coq to the initial state and send the region between the |
| 301 | + beginning of file and the point." |
| 302 | + (interactive) |
| 303 | + (comint-send-string (coq-proc) "Reset Initial.\n") |
| 304 | + (coq-send-region 1 (point))) |
| 305 | + |
| 306 | +(defvar coq-buffer nil "*The current coq process buffer.") |
| 307 | + |
| 308 | +(defun coq-proc () |
| 309 | + "Return the current coq process. See variable `coq-buffer'." |
| 310 | + (let ((proc (get-buffer-process (if (eq major-mode 'inferior-coq-mode) |
| 311 | + (current-buffer) |
| 312 | + coq-buffer)))) |
| 313 | + (or proc |
| 314 | + (error "No current process. See variable `coq-buffer'")))) |
| 315 | + |
| 316 | +(defcustom inferior-coq-load-hook nil |
| 317 | + "This hook is run when inferior-coq is loaded in. |
| 318 | +This is a good place to put keybindings." |
| 319 | + :type 'hook |
| 320 | + :group 'inferior-coq) |
| 321 | + |
| 322 | +(run-hooks 'inferior-coq-load-hook) |
| 323 | + |
| 324 | +(provide 'inferior-coq) |
0 commit comments