aboutsummaryrefslogtreecommitdiff
path: root/generic/proof-script.el
diff options
context:
space:
mode:
Diffstat (limited to 'generic/proof-script.el')
-rw-r--r--generic/proof-script.el2805
1 files changed, 2805 insertions, 0 deletions
diff --git a/generic/proof-script.el b/generic/proof-script.el
new file mode 100644
index 00000000..8c5421c2
--- /dev/null
+++ b/generic/proof-script.el
@@ -0,0 +1,2805 @@
+;; proof-script.el Major mode for proof assistant script files.
+;;
+;; Copyright (C) 1994-2001 LFCS Edinburgh.
+;; Authors: David Aspinall, Yves Bertot, Healfdene Goguen,
+;; Thomas Kleymann and Dilip Sequeira
+;; License: GPL (GNU GENERAL PUBLIC LICENSE)
+;;
+;; $Id$
+;;
+
+(require 'proof) ; loader
+(require 'proof-syntax) ; utils for manipulating syntax
+(require 'span) ; abstraction of overlays/extents
+(require 'pg-user) ; user-level commands
+(require 'proof-menu) ; menus for script mode
+
+
+
+;; Nuke some byte-compiler warnings
+;; NB: eval-when (compile) is different to eval-when-compile!!
+(eval-when (compile)
+ (proof-try-require 'func-menu)
+ (require 'comint))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; PRIVATE VARIABLES
+;;
+;; Local variables used by proof-script-mode
+;;
+
+;; Scripting variables
+
+(defvar proof-last-theorem-dependencies nil
+ "Contains the dependencies of the last theorem. A list of strings.
+Set in `proof-shell-process-urgent-message'.")
+
+(defvar proof-nesting-depth 0
+ "Current depth of a nested proof.
+Zero means outside a proof, 1 means inside a top-level proof, etc.
+
+This variable is maintained in proof-done-advancing; it is zeroed
+in proof-shell-clear-state.")
+
+(defvar proof-element-counters nil
+ "Table of (name . count) pairs, counting elements in scripting buffer.")
+
+
+;; Buffer-local variables
+
+(deflocal proof-active-buffer-fake-minor-mode nil
+ "An indication in the modeline that this is the *active* script buffer")
+
+(deflocal proof-script-buffer-file-name nil
+ ;; NB: if buffer-file-name is nil for some other reason, this may break.
+ "A copied value of buffer-file-name to cope with `find-alternative-file'.
+The `find-alternative-file' function has a nasty habit of setting the
+buffer file name to nil before running kill buffer, which breaks PG's
+kill buffer hook. This variable is used when buffer-file-name is nil.")
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Counting and naming proof elements
+;;
+
+(defun proof-next-element-count (idiom)
+ "Return count for next element of type IDIOM.
+This uses and updates `proof-element-counters'."
+ (let ((next (1+ (or (cdr-safe (assq idiom proof-element-counters)) 0))))
+ (setq proof-element-counters
+ (cons (cons idiom next)
+ (remassq idiom proof-element-counters)))
+ next))
+
+(defun proof-element-id (idiom number)
+ "Return a string identifier composed from symbol IDIOM and NUMBER."
+ (concat (symbol-name idiom) "-" (int-to-string number)))
+
+(defun proof-next-element-id (idiom)
+ (proof-element-id idiom (proof-next-element-count idiom)))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Configuration of function-menu (aka "fume")
+;;
+;; FIXME: we would like this code only enabled if the user loads
+;; func-menu into Emacs.
+;;
+
+(deflocal proof-script-last-entity nil
+ "Record of last entity found.
+A hack for entities that are named in two places, so that `find-next-entity'
+doesn't return the same values twice.")
+
+;; FIXME mmw: maybe handle comments/strings by using
+;; proof-looking-at-syntactic-context
+(defun proof-script-find-next-entity (buffer)
+ "Find the next entity for function menu in a proof script.
+A value for `fume-find-function-name-method-alist' for proof scripts.
+Uses `fume-function-name-regexp', which is initialised from
+`proof-script-next-entity-regexps', which see."
+ ;; Hopefully this function is fast enough.
+ (set-buffer buffer)
+ ;; could as well use next-entity-regexps directly since this is
+ ;; not really meant to be used as a general function.
+ (let ((anyentity (car fume-function-name-regexp)))
+ (if (proof-re-search-forward anyentity nil t)
+ ;; We've found some interesting entity, but have to find out
+ ;; which one, and where it begins.
+ (let ((entity (buffer-substring (match-beginning 0) (match-end 0)))
+ (start (match-beginning 0))
+ (discriminators (cdr fume-function-name-regexp))
+ (p (point))
+ disc res)
+ (while (and (not res) (setq disc (car-safe discriminators)))
+ (if (proof-string-match (car disc) entity)
+ (let*
+ ((items (nth 1 disc))
+ (items (if (numberp items) (list items) items))
+ (name ""))
+ (dolist (item items)
+ (setq name
+ (concat name
+ (substring entity
+ (match-beginning item)
+ (match-end item))
+ " ")))
+ (cond
+ ((eq (nth 2 disc) 'backward)
+ (setq start
+ (or (proof-re-search-backward (nth 3 disc) nil t)
+ start))
+ (goto-char p))
+ ((eq (nth 2 disc) 'forward)
+ (proof-re-search-forward (nth 3 disc))))
+ (setq res (cons name start)))
+ (setq discriminators (cdr discriminators))))
+ res))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Basic functions for handling the locked and queue regions
+;;
+;; --------------------------------------------------------------
+;;
+;; Notes on regions in the scripting buffer. (da)
+;;
+;; The locked region is covered by a collection of non-overlaping
+;; spans (our abstraction of extents/overlays).
+;;
+;; For an unfinished proof, there is one extent for each command
+;; or comment outside a command. For a finished proof, there
+;; is one extent for the whole proof.
+;;
+;; Each command span has a 'type property, one of:
+;;
+;; 'goalsave A goal..savegoal region in the buffer, a completed proof.
+;; 'vanilla Initialised in proof-semis-to-vanillas, for
+;; 'comment A comment outside a command.
+;; 'proverproc A region closed by the prover, processed outwith PG
+;; 'pbp A PBP command inserted automatically into the script
+;;
+;; All spans except those of type 'comment have a 'cmd property,
+;; which is set to a string of its command. This is the
+;; text in the buffer stripped of leading whitespace and any comments.
+;;
+
+;; ** Variables
+
+(deflocal proof-locked-span nil
+ "The locked span of the buffer.
+Each script buffer has its own locked span, which may be detached
+from the buffer.
+Proof General allows buffers in other modes also to be locked;
+these also have a non-nil value for this variable.")
+
+(deflocal proof-queue-span nil
+ "The queue span of the buffer. May be detached if inactive or empty.
+Each script buffer has its own queue span, although only the active
+scripting buffer may have an active queue span.")
+;; da: reason for buffer local queue span is because initialisation
+;; in proof-init-segmentation can happen when a file is visited.
+;; So nasty things might happen if a locked file is visited whilst
+;; another buffer has a non-empty queue region being processed.
+
+
+;; ** Getters and setters
+
+(defun proof-span-read-only (span)
+ "Make span be read-only, if proof-strict-read-only is non-nil.
+Otherwise make span give a warning message on edits."
+ ;; Note: perhaps the queue region should always be locked strictly.
+ (if proof-strict-read-only
+ (span-read-only span)
+ (span-write-warning span)))
+
+;; not implemented yet: presently must toggle via restarting scripting
+;; (defun proof-toggle-strict-read-only ()
+;; "Toggle proof-strict-read-only, changing current spans."
+;; (interactive)
+;; map-spans blah
+;; )
+
+(defsubst proof-set-queue-endpoints (start end)
+ "Set the queue span to be START, END."
+ (set-span-endpoints proof-queue-span start end))
+
+(defsubst proof-set-locked-endpoints (start end)
+ "Set the locked span to be START, END."
+ (set-span-endpoints proof-locked-span start end))
+
+(defsubst proof-detach-queue ()
+ "Remove the span for the queue region."
+ (and proof-queue-span (detach-span proof-queue-span)))
+
+(defsubst proof-detach-locked ()
+ "Remove the span for the locked region."
+ (and proof-locked-span (detach-span proof-locked-span)))
+
+(defsubst proof-set-queue-start (start)
+ "Set the queue span to begin at START."
+ (set-span-start proof-queue-span start))
+
+(defsubst proof-set-locked-end (end)
+ "Set the end of the locked region to be END.
+If END is at or before (point-min), remove the locked region.
+Otherwise set the locked region to be from (point-min) to END."
+ ;; FIXME: is it really needed to detach the span here?
+ (if (>= (point-min) end)
+ (proof-detach-locked)
+ (set-span-endpoints
+ proof-locked-span
+ (point-min)
+ (min (point-max) end) ;; safety: sometimes called with end>point-max(?)
+ )))
+
+;; Reimplemented this to mirror above because of remaining
+;; span problen
+(defsubst proof-set-queue-end (end)
+ "Set the queue span to end at END."
+ (if (or (>= (point-min) end)
+ (<= end (span-start proof-queue-span)))
+ (proof-detach-queue)
+ (set-span-end proof-queue-span end)))
+
+
+;; ** Initialise spans for a buffer
+
+(defun proof-init-segmentation ()
+ "Initialise the queue and locked spans in a proof script buffer.
+Allocate spans if need be. The spans are detached from the
+buffer, so the regions are made empty by this function.
+Also clear list of script portions."
+ ;; Initialise queue span, remove it from buffer.
+ (unless proof-queue-span
+ (setq proof-queue-span (make-span 1 1))
+ ;; FIXME: span-raise is an GNU hack to make locked span appear.
+ ;; overlays still don't work as well as they did/should pre 99.
+ (span-raise proof-queue-span))
+ (set-span-property proof-queue-span 'start-closed t)
+ (set-span-property proof-queue-span 'end-open t)
+ (proof-span-read-only proof-queue-span)
+ (set-span-property proof-queue-span 'face 'proof-queue-face)
+ (detach-span proof-queue-span)
+ ;; Initialise locked span, remove it from buffer
+ (unless proof-locked-span
+ (setq proof-locked-span (make-span 1 1))
+ (span-raise proof-locked-span))
+ (set-span-property proof-locked-span 'start-closed t)
+ (set-span-property proof-locked-span 'end-open t)
+ (proof-span-read-only proof-locked-span)
+ (set-span-property proof-locked-span 'face 'proof-locked-face)
+ (detach-span proof-locked-span)
+ (setq proof-last-theorem-dependencies nil)
+ (setq proof-element-counters nil)
+ (pg-clear-script-portions))
+
+
+;; ** Restarting and clearing spans
+
+(defun proof-restart-buffers (buffers)
+ "Remove all extents in BUFFERS and maybe reset `proof-script-buffer'.
+No effect on a buffer which is nil or killed. If one of the buffers
+is the current scripting buffer, then proof-script-buffer
+will deactivated."
+ (mapcar
+ (lambda (buffer)
+ (save-excursion
+ (if (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (if proof-active-buffer-fake-minor-mode
+ (setq proof-active-buffer-fake-minor-mode nil))
+ (delete-spans (point-min) (point-max) 'type) ;; remove top-level spans
+ (delete-spans (point-min) (point-max) 'idiom) ;; and embedded spans
+ (setq pg-script-portions nil) ;; also the record of them
+ (proof-detach-queue) ;; remove queue and locked
+ (proof-detach-locked)
+ (proof-init-segmentation)))
+ (if (eq buffer proof-script-buffer)
+ (setq proof-script-buffer nil))))
+ buffers))
+
+(defun proof-script-buffers-with-spans ()
+ "Return a list of all buffers with spans.
+This is calculated by finding all the buffers with a non-nil
+value of proof-locked span."
+ (let ((bufs-left (buffer-list))
+ bufs-got)
+ (dolist (buf bufs-left bufs-got)
+ (if (with-current-buffer buf proof-locked-span)
+ (setq bufs-got (cons buf bufs-got))))))
+
+(defun proof-script-remove-all-spans-and-deactivate ()
+ "Remove all spans from scripting buffers via proof-restart-buffers."
+ (proof-restart-buffers (proof-script-buffers-with-spans)))
+
+(defun proof-script-clear-queue-spans ()
+ "If there is an active scripting buffer, remove the queue span from it.
+This is a subroutine used in proof-shell-handle-{error,interrupt}."
+ (if proof-script-buffer
+ (with-current-buffer proof-script-buffer
+ (proof-detach-queue)
+ ;; FIXME da: point-max seems a bit excessive here,
+ ;; proof-queue-or-locked-end should be enough.
+ (delete-spans (proof-locked-end) (point-max) 'type))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Buffer position functions
+;;
+
+(defun proof-unprocessed-begin ()
+ "Return end of locked region in current buffer or (point-min) otherwise.
+The position is actually one beyond the last locked character."
+ (or
+ (and proof-locked-span
+ (span-end proof-locked-span))
+ (point-min)))
+
+(defun proof-script-end ()
+ "Return the character beyond the last non-whitespace character.
+This is the same position proof-locked-end ends up at when asserting
+the script. Works for any kind of buffer."
+ (save-excursion
+ (goto-char (point-max))
+ (skip-chars-backward " \t\n")
+ (point)))
+
+(defun proof-queue-or-locked-end ()
+ "Return the end of the queue region, or locked region, or (point-min).
+This position should be the first writable position in the buffer.
+An appropriate point to move point to (or make sure is displayed)
+when a queue of commands is being processed."
+ (or
+ ;; span-end returns nil if span is detatched
+ (and proof-queue-span (span-end proof-queue-span))
+ (and proof-locked-span (span-end proof-locked-span))
+ (point-min)))
+
+;; FIXME: get rid of/rework this function. Some places expect this to
+;; return nil if locked region is empty. Moreover, it confusingly
+;; returns the point past the end of the locked region.
+(defun proof-locked-end ()
+ "Return end of the locked region of the current buffer.
+Only call this from a scripting buffer."
+ (proof-unprocessed-begin))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Predicates for locked region.
+;;
+;; These work on any buffer, so that non-script buffers can be locked
+;; (as processed files) too.
+;;
+
+(defun proof-locked-region-full-p ()
+ "Non-nil if the locked region covers all the buffer's non-whitespace.
+Works on any buffer."
+ (save-excursion
+ (goto-char (point-max))
+ (skip-chars-backward " \t\n")
+ (>= (proof-unprocessed-begin) (point))))
+
+(defun proof-locked-region-empty-p ()
+ "Non-nil if the locked region is empty. Works on any buffer."
+ (eq (proof-unprocessed-begin) (point-min)))
+
+(defun proof-only-whitespace-to-locked-region-p ()
+ "Non-nil if only whitespace separates char after point from end of locked region.
+Point should be after the locked region.
+NB: If nil, point is left at first non-whitespace character found.
+If non-nil, point is left where it was."
+ ;; NB: this function doesn't quite do what you'd expect, but fixing it
+ ;; breaks proof-assert-until-point and electric-terminator which
+ ;; rely on the side effect. So careful!
+ ;; (unless (eobp)
+ ;; (forward-char))
+ ;; (save-excursion -- no, side effect is expected!
+ (not (proof-re-search-backward "\\S-" (proof-unprocessed-begin) t)))
+
+(defun proof-in-locked-region-p ()
+ "Non-nil if point is in locked region. Assumes proof script buffer current."
+ (< (point) (proof-unprocessed-begin)))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Misc movement functions
+;;
+
+(defun proof-goto-end-of-locked (&optional switch)
+ "Jump to the end of the locked region, maybe switching to script buffer.
+If interactive or SWITCH is non-nil, switch to script buffer first."
+ (interactive)
+ (proof-with-script-buffer
+ (if (and (not (get-buffer-window proof-script-buffer))
+ (or switch (interactive-p)))
+ (switch-to-buffer proof-script-buffer)
+ (goto-char (proof-unprocessed-begin)))))
+
+;; Careful: movement can happen when the user is typing, not very nice!
+(defun proof-goto-end-of-locked-if-pos-not-visible-in-window ()
+ "If the end of the locked region is not visible, jump to the end of it.
+A possible hook function for proof-shell-handle-error-or-interrupt-hook.
+Does nothing if there is no active scripting buffer, or if
+`proof-follow-mode' is set to 'ignore."
+ (interactive)
+ (if (and proof-script-buffer
+ (not (eq proof-follow-mode 'ignore)))
+ (let ((pos (with-current-buffer proof-script-buffer
+ (proof-locked-end)))
+ (win (get-buffer-window proof-script-buffer t)))
+ (unless (and win (pos-visible-in-window-p pos))
+ (proof-goto-end-of-locked t)))))
+
+;; Simplified version of above for toolbar follow mode -- which wouldn't
+;; work with abouve because of proof-shell-handle-error-or-interrupt-hook[?]
+(defun proof-goto-end-of-queue-or-locked-if-not-visible ()
+ "Jump to the end of the queue region or locked region if it isn't visible.
+Assumes script buffer is current"
+ (unless (pos-visible-in-window-p
+ (proof-queue-or-locked-end)
+ (get-buffer-window (current-buffer) t))
+ (goto-char (proof-queue-or-locked-end))))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Names of proofs (and other elements) in a script
+;;
+;; Each kind of part ("idiom") in a proof script has its own name space.
+;; Visibility within a script is then handled with buffer-invisibility-spec
+;; controlling appearance of each idiom.
+;;
+
+(defvar pg-idioms '(proof)
+ "Vector of script element kinds PG is aware of for this prover.")
+
+(defvar pg-visibility-specs nil
+ "Cache of visibility spec symbols used by PG.")
+
+
+(deflocal pg-script-portions nil
+ "Table of lists of symbols naming script portions which have been processed so far.")
+
+(defun pg-clear-script-portions ()
+ "Clear record of script portion names and types from internal list.
+Also clear all visibility specifications."
+ (setq pg-script-portions nil)
+ (setq buffer-invisibility-spec nil))
+
+(defun pg-add-script-element (elt)
+ (add-to-list pg-script-portions elt))
+
+(defun pg-remove-script-element (ns id)
+ (let* ((elts (cdr-safe (assq ns pg-script-portions)))
+ (newelts (remq id elts)))
+ (setq pg-script-portions
+ (if newelts
+ (cons (cons ns newelts) (remassq ns pg-script-portions))
+ (remassq ns pg-script-portions)))))
+
+(defsubst pg-visname (namespace id)
+ "Return a unique symbol made from strings NAMESPACE and unique ID."
+ (intern (concat namespace ":" id)))
+
+(defun pg-add-element (idiom id span &optional name)
+ "Add element of type IDIOM with identifier ID, referred to by SPAN.
+This records the element in `pg-script-portions' and sets span
+properties accordingly.
+IDIOM, ID, and optional NAME are all strings.
+Identifiers must be unique for a given idiom; the optional NAME
+will be recorded as a textual name used instead of ID for users;
+NAME does not need to be unique."
+ (let* ((idiomsym (intern idiom))
+ (idsym (intern id))
+ (name (or name id))
+ (visname (pg-visname idiom id))
+ (delfn `(lambda () (pg-remove-element (quote ,idiomsym) (quote ,idsym))))
+ (elts (cdr-safe (assq idiomsym pg-script-portions))))
+ (if elts
+ (if (memq id elts)
+ (proof-debug "Element named " name " (type " idiom ") already in buffer.")
+ (nconc elts (list idsym)))
+ (setq pg-script-portions (cons (cons idiomsym (list idsym))
+ pg-script-portions)))
+ ;; Idiom and ID are stored in the span as symbols; name as a string.
+ (set-span-property span 'idiom idiomsym)
+ (set-span-property span 'id idsym)
+ (set-span-property span 'name name)
+ (set-span-property span 'span-delete-action delfn)
+ (set-span-property span 'invisible visname)
+ ;; Bad behaviour if span gets copied: unique ID shouldn't be duplicated.
+ (set-span-property span 'duplicable nil) ;; NB: not supported in Emacs
+ ;; Nice behaviour in with isearch: open invisible regions temporarily.
+ (set-span-property span 'isearch-open-invisible
+ 'pg-open-invisible-span)
+ (set-span-property span 'isearch-open-invisible-temporary
+ 'pg-open-invisible-span)))
+
+(defun pg-open-invisible-span (span &optional invisible)
+ "Function for `isearch-open-invisible' property."
+ (let ((idiom (span-property span 'idiom))
+ (id (span-property span 'id)))
+ (and idiom id
+ (if invisible
+ (pg-make-element-invisible
+ (symbol-name idiom) id)
+ (pg-make-element-visible
+ (symbol-name idiom) (symbol-name id))))))
+
+(defun pg-remove-element (ns idsym)
+ (pg-remove-script-element ns idsym)
+ ;; We could leave the visibility note, but that may
+ ;; be counterintuitive, so lets remove it.
+ (pg-make-element-visible (symbol-name ns) (symbol-name idsym))
+ (pg-redisplay-for-gnuemacs))
+
+(defun pg-make-element-invisible (idiom id)
+ "Make element ID of type IDIOM invisible, with ellipsis."
+ (add-to-list 'buffer-invisibility-spec
+ (cons (pg-visname idiom id) t)))
+
+(defun pg-make-element-visible (idiom id)
+ "Make element ID of type IDIOM visible."
+ (setq buffer-invisibility-spec
+ (remassq (pg-visname idiom id) buffer-invisibility-spec)))
+
+(defun pg-toggle-element-visibility (idiom id)
+ "Toggle visibility of script element of type IDIOM, named ID."
+ (if (and (listp buffer-invisibility-spec)
+ (assq (pg-visname idiom id) buffer-invisibility-spec))
+ (pg-make-element-visible idiom id)
+ (pg-make-element-invisible idiom id))
+ (pg-redisplay-for-gnuemacs))
+
+(defun pg-redisplay-for-gnuemacs ()
+ "GNU Emacs requires redisplay for changes in buffer-invisibility-spec."
+ (if proof-running-on-Emacs21
+ ;; GNU Emacs requires redisplay here to see result
+ ;; (sit-for 0) not enough
+ (redraw-frame (selected-frame))))
+
+(defun pg-show-all-portions (idiom &optional hide)
+ "Show or hide all portions of kind IDIOM"
+ (interactive
+ (list
+ (completing-read
+ (concat "Make " (if current-prefix-arg "in" "") "visible all regions of: ")
+ (apply 'vector pg-idioms) nil t)
+ current-prefix-arg))
+ (let ((elts (cdr-safe (assq (intern idiom) pg-script-portions)))
+ (alterfn (if hide
+ (lambda (arg) (pg-make-element-invisible idiom
+ (symbol-name arg)))
+ (lambda (arg) (pg-make-element-visible idiom
+ (symbol-name arg))))))
+ (mapcar alterfn elts))
+ (pg-redisplay-for-gnuemacs))
+
+;; Next two could be in pg-user.el. No key-bindings for these.
+(defun pg-show-all-proofs ()
+ "Display all completed proofs in the buffer."
+ (interactive)
+ (pg-show-all-portions "proof"))
+
+(defun pg-hide-all-proofs ()
+ "Hide all completed proofs in the buffer."
+ (interactive)
+ (pg-show-all-portions "proof" 'hide))
+
+(defun pg-add-proof-element (name span controlspan)
+ "Add a nested span proof element."
+ (let ((proofid (proof-next-element-id 'proof)))
+ (pg-add-element "proof" proofid span name)
+ ;; Set id in controlspan
+ (set-span-property controlspan 'id (intern proofid))
+ ;; Make a navigable link between the two spans.
+ (set-span-property span 'controlspan controlspan)
+ (set-span-property controlspan 'children
+ (cons span (span-property controlspan 'children)))
+ (pg-set-span-helphighlights span 'nohighlight)
+ (if proof-disappearing-proofs
+ (pg-make-element-invisible "proof" proofid))))
+
+(defun pg-span-name (span)
+ "Return a user-level name for SPAN."
+ (let ((type (span-property span 'type))
+ (idiom (span-property span 'idiom))
+ (name (span-property span 'name)))
+ (cond
+ (idiom
+ (concat (upcase-initials (symbol-name idiom))
+ (if name (concat ": " name))))
+ ((or (eq type 'proof) (eq type 'goalsave))
+ (concat "Proof"
+ (let ((name (span-property span 'name)))
+ (if name (concat " of " name)))))
+ ((eq type 'comment) "Comment")
+ ((eq type 'vanilla) "Command")
+ ((eq type 'pbp) "PBP command")
+ ((eq type 'proverproc)
+ "Prover-processed region"))))
+
+(defun pg-set-span-helphighlights (span &optional nohighlight)
+ "Set the help echo message, default highlight, and keymap for SPAN."
+ (let ((helpmsg (pg-span-name span)))
+ (set-span-property span 'balloon-help helpmsg)
+ (set-span-property span 'help-echo helpmsg)
+ (set-span-property span 'keymap pg-span-context-menu-keymap)
+ (unless nohighlight
+ (set-span-property span 'mouse-face 'proof-mouse-highlight-face))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Multiple file handling
+;;
+(defun proof-complete-buffer-atomic (buffer)
+ "Make sure BUFFER is marked as completely processed, completing with a single step.
+
+If buffer already contains a locked region, only the remainder of the
+buffer is closed off atomically.
+
+This works for buffers which are not in proof scripting mode too,
+to allow other files loaded by proof assistants to be marked read-only."
+;; NB: this isn't quite right, because not all of the structure in the
+;; locked region will be preserved when processing across several
+;; files. In particular, the span for a currently open goal should be
+;; removed. Keeping the structure is an approximation to make up for
+;; the fact that that no structure is created by loading files via the
+;; proof assistant. Future ideas: proof assistant could ask Proof
+;; General to do the loading, to alleviate file handling there;
+;; we could cache meta-data resulting from processing files;
+;; or even, could include parsing inside PG.
+ (save-excursion
+ (set-buffer buffer)
+ (if (< (proof-unprocessed-begin) (proof-script-end))
+ (let ((span (make-span (proof-unprocessed-begin)
+ (proof-script-end)))
+ cmd)
+ (if (eq proof-buffer-type 'script)
+ ;; For a script buffer
+ (progn
+ (goto-char (point-min))
+ (proof-goto-command-end)
+ (let ((cmd-list (member-if
+ (lambda (entry) (equal (car entry) 'cmd))
+ (proof-segment-up-to (point)))))
+ ;; Reset queue and locked regions.
+ (proof-init-segmentation)
+ (if cmd-list
+ (progn
+ ;; FIXME 3.3 da: this can be simplified now,
+ ;; we don't need to set cmd for proverproc
+ (setq cmd (second (car cmd-list)))
+ (set-span-property span 'type 'proverproc)
+ (set-span-property span 'cmd cmd))
+ ;; If there was no command in the buffer, atomic span
+ ;; becomes a comment. This isn't quite right because
+ ;; the first ACS in a buffer could also be a goal-save
+ ;; span. We don't worry about this in the current
+ ;; implementation. This case should not happen in a
+ ;; LEGO module (because we assume that the first
+ ;; command is a module declaration). It should have no
+ ;; impact in Isabelle either (because there is no real
+ ;; retraction).
+ (set-span-property span 'type 'comment))))
+ ;; For a non-script buffer
+ (proof-init-segmentation)
+ (set-span-property span 'type 'comment))
+ ;; End of locked region is always end of buffer
+ (proof-set-locked-end (proof-script-end))))))
+
+
+
+
+
+;; FIXME da: cleanup of odd asymmetry here: we have a nice setting for
+;; proof-register-possibly-new-processed-file but something much more
+;; complicated for retracting, because we allow a hook function
+;; to calculate the new included files list.
+
+(defun proof-register-possibly-new-processed-file (file &optional informprover noquestions)
+ "Register a possibly new FILE as having been processed by the prover.
+
+If INFORMPROVER is non-nil, the proof assistant will be told about this,
+to co-ordinate with its internal file-management. (Otherwise we assume
+that it is a message from the proof assistant which triggers this call).
+In this case, the user will be queried to save some buffers, unless
+NOQUESTIONS is non-nil.
+
+No action is taken if the file is already registered.
+
+A warning message is issued if the register request came from the
+proof assistant and Emacs has a modified buffer visiting the file."
+ (let* ((cfile (file-truename file))
+ (buffer (proof-file-to-buffer cfile)))
+ (proof-debug (concat "Registering file " cfile
+ (if (member cfile proof-included-files-list)
+ " (already registered, no action)." ".")))
+ (unless (member cfile proof-included-files-list)
+ (and buffer
+ (not informprover)
+ (buffer-modified-p buffer)
+ (proof-warning (concat "Changes to "
+ (buffer-name buffer)
+ " have not been saved!")))
+ ;; Add the new file onto the front of the list
+ (setq proof-included-files-list
+ (cons cfile proof-included-files-list))
+ ;; If the file is loaded into a buffer, make sure it is completely locked
+ (if buffer
+ (proof-complete-buffer-atomic buffer))
+ ;; Tell the proof assistant, if we should and if we can
+ (if (and informprover proof-shell-inform-file-processed-cmd)
+ (progn
+ ;; Markus suggests we should ask if the user wants to save
+ ;; the file now (presumably because the proof assistant
+ ;; might examine the file timestamp, or attempt to visit
+ ;; the file later??).
+ ;; Presumably it would be enough to ask about this file,
+ ;; not all files?
+ (if (and
+ proof-query-file-save-when-activating-scripting
+ (not noquestions))
+ (unwind-protect
+ (save-some-buffers)))
+ ;; Tell the prover
+ (proof-shell-invisible-command
+ (proof-format-filename proof-shell-inform-file-processed-cmd
+ cfile)
+ 'wait))))))
+
+(defun proof-inform-prover-file-retracted (rfile)
+ (if proof-shell-inform-file-retracted-cmd
+ (proof-shell-invisible-command
+ (proof-format-filename proof-shell-inform-file-retracted-cmd
+ rfile)
+ 'wait)))
+
+(defun proof-auto-retract-dependencies (cfile &optional informprover)
+ "Perhaps automatically retract the (linear) dependencies of CFILE.
+If proof-auto-multiple-files is nil, no action is taken.
+If CFILE does not appear on proof-included-files-list, no action taken.
+
+Any buffers which are visiting files in proof-included-files-list
+before CFILE are retracted using proof-protected-process-or-retract.
+They are retracted in reverse order.
+
+Since the proof-included-files-list is examined, we expect scripting
+to be turned off before calling here (because turning it off could
+otherwise change proof-included-files-list).
+
+If INFORMPROVER is non-nil, the proof assistant will be told about this,
+using proof-shell-inform-file-retracted-cmd, to co-ordinate with its
+internal file-management.
+
+Files which are not visited by any buffer are not retracted, on the
+basis that we may not have the information necessary to retract them
+-- spans that cover the buffer with definition/declaration
+information. A warning message is given for these cases, since it
+could cause inconsistency problems.
+
+NB! Retraction can cause recursive calls of this function.
+This is a subroutine for proof-unregister-buffer-file-name."
+ (if proof-auto-multiple-files
+ (let ((depfiles (reverse
+ (cdr-safe
+ (member cfile (reverse proof-included-files-list)))))
+ rfile rbuf)
+ (while (setq rfile (car-safe depfiles))
+ ;; If there's a buffer visiting a dependent file, retract it.
+ ;; We test that the file to retract hasn't been retracted
+ ;; already by a recursive call here. (But since we do retraction
+ ;; in reverse order, this shouldn't happen...)
+ (if (and (member rfile proof-included-files-list)
+ (setq rbuf (proof-file-to-buffer rfile)))
+ (progn
+ (proof-debug "Automatically retracting " rfile)
+ (proof-protected-process-or-retract 'retract rbuf)
+ (setq proof-included-files-list
+ (delete rfile proof-included-files-list))
+ ;; Tell the proof assistant, if we should and we can.
+ ;; This may be useful if we synchronise the *prover* with
+ ;; PG's management of multiple files. If the *prover*
+ ;; informs PG (better case), then we hope the prover will
+ ;; retract dependent files and we shouldn't use this
+ ;; degenerate (linear dependency) code.
+ (if informprover
+ (proof-inform-prover-file-retracted rfile)))
+ ;; If no buffer available, issue a warning that nothing was done
+ (proof-warning "Not retracting unvisited file " rfile))
+ (setq depfiles (cdr depfiles))))))
+
+(defun proof-unregister-buffer-file-name (&optional informprover)
+ "Remove current buffer's filename from the list of included files.
+No effect if the current buffer has no file name.
+If INFORMPROVER is non-nil, the proof assistant will be told about this,
+using proof-shell-inform-file-retracted-cmd, to co-ordinate with its
+internal file-management.
+
+If proof-auto-multiple-files is non-nil, any buffers on
+proof-included-files-list before this one will be automatically
+retracted using proof-auto-retract-dependencies."
+ (if buffer-file-name
+ (let ((cfile (file-truename
+ (or buffer-file-name
+ proof-script-buffer-file-name))))
+ (proof-debug (concat "Unregistering file " cfile
+ (if (not (member cfile
+ proof-included-files-list))
+ " (not registered, no action)." ".")))
+ (if (member cfile proof-included-files-list)
+ (progn
+ (proof-auto-retract-dependencies cfile informprover)
+ (setq proof-included-files-list
+ (delete cfile proof-included-files-list))
+ ;; Tell the proof assistant, if we should and we can.
+ ;; This case may be useful if there is a combined
+ ;; management of multiple files between PG and prover.
+ (if informprover
+ (proof-inform-prover-file-retracted cfile)))))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Activating and Deactivating Scripting
+;;
+;; The notion of "active scripting buffer" clarifies how
+;; scripting across multiple files is handled. Only one
+;; script buffer is allowed to be active, and actions are
+;; taken when scripting is turned off/on.
+;;
+
+(defun proof-protected-process-or-retract (action &optional buffer)
+ "If ACTION='process, process, If ACTION='retract, retract.
+Process or retract the current buffer, which should be the active
+scripting buffer, according to ACTION.
+Retract buffer BUFFER if set, otherwise use the current buffer.
+Gives a message in the minibuffer and busy-waits for the retraction
+or processing to complete. If it fails for some reason,
+an error is signalled here."
+ (let ((fn (cond ((eq action 'process) 'proof-process-buffer)
+ ((eq action 'retract) 'proof-retract-buffer)))
+ (name (cond ((eq action 'process) "Processing")
+ ((eq action 'retract) "Retracting")))
+ (buf (or buffer (current-buffer))))
+ (if fn
+ (unwind-protect
+ (with-current-buffer buf
+ (message "%s buffer %s..." name buf)
+ (funcall fn)
+ (while proof-shell-busy ; busy wait
+ (sit-for 1))
+ (message "%s buffer %s...done." name buf)
+ (sit-for 0))
+ ;; Test to see if action was successful
+ (with-current-buffer buf
+ (or (and (eq action 'retract) (proof-locked-region-empty-p))
+ (and (eq action 'process) (proof-locked-region-full-p))
+ (error "%s of %s failed!" name buf)))))))
+
+(defun proof-deactivate-scripting-auto ()
+ "Deactivate scripting without asking questions or raising errors.
+If the locked region is full, register the file as processed.
+Otherwise retract it. Errors are ignored"
+ (ignore-errors
+ (proof-deactivate-scripting
+ (proof-with-script-buffer
+ (if (proof-locked-region-full-p) 'process 'retract)))))
+
+(defun proof-deactivate-scripting (&optional forcedaction)
+ "Deactivate scripting for the active scripting buffer.
+
+Set proof-script-buffer to nil and turn off the modeline indicator.
+No action if there is no active scripting buffer.
+
+We make sure that the active scripting buffer either has no locked
+region or a full locked region (everything in it has been processed).
+If this is not already the case, we question the user whether to
+retract or assert, or automatically take the action indicated in the
+user option `proof-auto-action-when-deactivating-scripting.'
+
+If the scripting buffer is (or has become) fully processed, and it is
+associated with a file, it is registered on
+`proof-included-files-list'. Conversely, if it is (or has become)
+empty, we make sure that it is *not* registered. This is to be
+certain that the included files list behaves as we might expect with
+respect to the active scripting buffer, in an attempt to harmonize
+mixed scripting and file reading in the prover.
+
+This function either succeeds, fails because the user refused to
+process or retract a partly finished buffer, or gives an error message
+because retraction or processing failed. If this function succeeds,
+then proof-script-buffer is nil afterwards.
+
+The optional argument FORCEDACTION overrides the user option
+`proof-auto-action-when-deactivating-scripting' and prevents
+questioning the user. It is used to make a value for
+the kill-buffer-hook for scripting buffers, so that when
+a scripting buffer is killed it is always retracted."
+ (interactive)
+ (if proof-script-buffer
+ (with-current-buffer proof-script-buffer
+ ;; Examine buffer.
+
+ ;; We must ensure that the locked region is either
+ ;; empty or full, to make sense for multiple-file
+ ;; scripting. (A proof assistant won't be able to
+ ;; process just part of a file typically; moreover
+ ;; switching between buffers during a proof makes
+ ;; no sense.)
+ (if (or (proof-locked-region-empty-p)
+ (proof-locked-region-full-p)
+ ;; Buffer is partly-processed
+ (let*
+ ((action
+ (or
+ forcedaction
+ proof-auto-action-when-deactivating-scripting
+ (progn
+ (save-window-excursion
+ (unless
+ ;; Test to see whether to display the
+ ;; buffer or not.
+ ;; Could have user option here to avoid switching
+ ;; or maybe borrow similar standard setting
+ ;; save-some-buffers-query-display-buffer
+ (or
+ (eq (current-buffer)
+ (window-buffer (selected-window)))
+ (eq (selected-window) (minibuffer-window)))
+ (progn
+ (unless (one-window-p)
+ (delete-other-windows))
+ (switch-to-buffer proof-script-buffer t)))
+ ;; Would be nicer to ask a single question, but
+ ;; a nuisance to define our own dialogue since it
+ ;; doesn't really fit with one of the standard ones.
+ (cond
+ ((y-or-n-p
+ (format
+ "Scripting incomplete in buffer %s, retract? "
+ proof-script-buffer))
+ 'retract)
+ ((y-or-n-p
+ (format
+ "Completely process buffer %s instead? "
+ proof-script-buffer))
+ 'process)))))))
+ ;; Take the required action
+ (if action
+ (proof-protected-process-or-retract action)
+ ;; Give an acknowledgement to user's choice
+ ;; neither to assert or retract.
+ (message "Scripting still active in %s"
+ proof-script-buffer)
+ ;; Delay because this can be followed by an error
+ ;; message in proof-activate-scripting when trying
+ ;; to switch to another scripting buffer.
+ (sit-for 1)
+ nil)))
+
+ ;; If we get here, then the locked region is (now) either
+ ;; completely empty or completely full.
+ (progn
+ ;; We can immediately indicate that there is no active
+ ;; scripting buffer
+ (setq proof-previous-script-buffer proof-script-buffer)
+ (setq proof-script-buffer nil)
+
+ (if (proof-locked-region-full-p)
+ ;; If locked region is full, make sure that this buffer
+ ;; is registered on the included files list, and
+ ;; let the prover know it can consider it processed.
+ (if (or buffer-file-name proof-script-buffer-file-name)
+ (proof-register-possibly-new-processed-file
+ (or buffer-file-name proof-script-buffer-file-name)
+ 'tell-the-prover
+ forcedaction)))
+
+ (if (proof-locked-region-empty-p)
+ ;; If locked region is empty, make sure this buffer is
+ ;; *off* the included files list.
+ ;; FIXME: probably this isn't necessary: the
+ ;; file should be unregistered by the retract
+ ;; action, or in any case since it was only
+ ;; partly processed.
+ ;; FIXME 2: be careful about automatic
+ ;; multiple file handling here, since it calls
+ ;; for activating scripting elsewhere.
+ ;; We move the onus on unregistering now to
+ ;; the activate-scripting action.
+ (proof-unregister-buffer-file-name))
+
+ ;; Turn off Scripting indicator here.
+ (setq proof-active-buffer-fake-minor-mode nil)
+
+ ;; Make status of inactive scripting buffer show up
+ ;; FIXME da:
+ ;; not really necessary when called by kill buffer, at least.
+ (if (fboundp 'redraw-modeline)
+ (redraw-modeline)
+ (force-mode-line-update)))))))
+
+(defun proof-activate-scripting (&optional nosaves queuemode)
+ "Ready prover and activate scripting for the current script buffer.
+
+The current buffer is prepared for scripting. No changes are
+necessary if it is already in Scripting minor mode. Otherwise, it
+will become the new active scripting buffer, provided scripting
+can be switched off in the previous active scripting buffer
+with `proof-deactivate-scripting'.
+
+Activating a new script buffer may be a good time to ask if the
+user wants to save some buffers; this is done if the user
+option `proof-query-file-save-when-activating-scripting' is set
+and provided the optional argument NOSAVES is non-nil.
+
+The optional argument QUEUEMODE relaxes the test for a
+busy proof shell to allow one which has mode QUEUEMODE.
+In all other cases, a proof shell busy error is given.
+
+Finally, the hooks `proof-activate-scripting-hook' are run.
+This can be a useful place to configure the proof assistant for
+scripting in a particular file, for example, loading the
+correct theory, or whatever. If the hooks issue commands
+to the proof assistant (via `proof-shell-invisible-command')
+which result in an error, the activation is considered to
+have failed and an error is given."
+ (interactive)
+ ;; FIXME: the scope of this save-excursion is rather wide.
+ ;; Problems without it however: Use button behaves oddly
+ ;; when process is started already.
+ ;; Where is save-excursion needed?
+ ;; First experiment shows that it's the hooks that cause
+ ;; problem, maybe even the use of proof-cd-sync (can't see why).
+ (save-excursion
+ ;; FIXME: proof-shell-ready-prover here s
+ (proof-shell-ready-prover queuemode)
+ (cond
+ ((not (eq proof-buffer-type 'script))
+ (error "Must be running in a script buffer!"))
+
+ ;; If the current buffer is the active one there's nothing to do.
+ ((equal (current-buffer) proof-script-buffer))
+
+ ;; Otherwise we need to activate a new Scripting buffer.
+ (t
+ ;; If there's another buffer currently active, we need to
+ ;; deactivate it (also fixing up the included files list).
+ (if proof-script-buffer
+ (progn
+ (proof-deactivate-scripting)
+ ;; Test whether deactivation worked
+ (if proof-script-buffer
+ (error
+ "You cannot have more than one active scripting buffer!"))))
+
+ ;; Now make sure that this buffer is off the included files
+ ;; list. In case we re-activate scripting in an already
+ ;; completed buffer, it may be that the proof assistant
+ ;; needs to retract some of this buffer's dependencies.
+ (proof-unregister-buffer-file-name 'tell-the-prover)
+
+ ;; If automatic retraction happened in the above step, we may
+ ;; have inadvertently activated scripting somewhere else.
+ ;; Better turn it off again. This should succeed trivially.
+ ;; NB: it seems that we could move the first test for an already
+ ;; active buffer here, but it is more subtle: the first
+ ;; deactivation can extend the proof-included-files list, which
+ ;; would affect what retraction was done in
+ ;; proof-unregister-buffer-file-name.
+ (if proof-script-buffer
+ (proof-deactivate-scripting))
+ (assert (null proof-script-buffer)
+ "Bug in proof-activate-scripting: deactivate failed.")
+
+ ;; Set the active scripting buffer, and initialise the
+ ;; queue and locked regions if necessary.
+ (setq proof-script-buffer (current-buffer))
+ (if (proof-locked-region-empty-p)
+ ;; This removes any locked region that was there, but
+ ;; sometimes we switch on scripting in "full" buffers,
+ ;; so mustn't do this.
+ (proof-init-segmentation))
+
+ ;; Turn on the minor mode, make it show up.
+ (setq proof-active-buffer-fake-minor-mode t)
+ (if (fboundp 'redraw-modeline)
+ (redraw-modeline)
+ (force-mode-line-update))
+
+ ;; This may be a good time to ask if the user wants to save some
+ ;; buffers. On the other hand, it's jolly annoying to be
+ ;; queried on the active scripting buffer if we've started
+ ;; writing in it. So pretend that one is unmodified, at least
+ ;; (we certainly don't expect the proof assitant to load it)
+ (if (and
+ proof-query-file-save-when-activating-scripting
+ (not nosaves))
+ (let ((modified (buffer-modified-p)))
+ (set-buffer-modified-p nil)
+ (unwind-protect
+ (save-some-buffers)
+ (set-buffer-modified-p modified))))
+
+ ;; Run hooks with a variable which suggests whether or not
+ ;; to block. NB: The hook function may send commands to the
+ ;; process which will re-enter this function, but should exit
+ ;; immediately because scripting has been turned on now.
+ (if proof-activate-scripting-hook
+ (let
+ ((activated-interactively (interactive-p)))
+ ;; Clear flag in case no hooks run shell commands
+ (setq proof-shell-error-or-interrupt-seen nil)
+ (run-hooks 'proof-activate-scripting-hook)
+ ;; In case the activate scripting functions
+ ;; caused an error in the proof assistant, we'll
+ ;; consider activating scripting to have failed,
+ ;; and raise an error here.
+ ;; (Since this behaviour is intimate with the hook functions,
+ ;; it could be removed and left to implementors of
+ ;; specific instances of PG).
+ ;; FIXME: we could consider simply running the hooks
+ ;; as the last step before turning on scripting properly,
+ ;; provided the hooks don't inspect proof-script-buffer.
+ (if proof-shell-error-or-interrupt-seen
+ (progn
+ (proof-deactivate-scripting) ;; turn it off again!
+ ;; Give an error to prevent further actions.
+ (error "Proof General: Scripting not activated because of error or interrupt.")))))))))
+
+
+(defun proof-toggle-active-scripting (&optional arg)
+ "Toggle active scripting mode in the current buffer.
+With ARG, turn on scripting iff ARG is positive."
+ (interactive "P")
+ ;; A little less obvious than it may seem: toggling scripting in the
+ ;; current buffer may involve turning it off in some other buffer
+ ;; first!
+ (if (if (null arg)
+ (not (eq proof-script-buffer (current-buffer)))
+ (> (prefix-numeric-value arg) 0))
+ (progn
+ (if proof-script-buffer
+ (call-interactively 'proof-deactivate-scripting))
+ (call-interactively 'proof-activate-scripting))
+ (call-interactively 'proof-deactivate-scripting)))
+
+;;
+;; End of activating and deactivating scripting section
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Processing the script management queue -- PART 1: "advancing"
+;;
+;; The proof-action-list contains a list of (span,command,action)
+;; triples. The loop looks like: Execute the command, and if it's
+;; successful, do action on span. If the command's not successful, we
+;; bounce the rest of the queue and do some error processing.
+;;
+;; When a span has been processed, it is classified as
+;; 'comment, 'goalsave, 'vanilla, etc.
+;;
+;; The main function for dealing with processed spans is
+;; `proof-done-advancing'
+
+(defun proof-done-advancing (span)
+ "The callback function for assert-until-point."
+ ;; FIXME da: if the buffer dies, this function breaks horribly.
+ (if (not (span-live-p span))
+ ;; NB: Sometimes this function is called with a destroyed
+ ;; extent as argument. Seems odd.
+ (proof-debug
+ "Proof General idiosyncrasy: proof-done-advancing called with a dead span!")
+ ;;
+ (let ((end (span-end span))
+ (cmd (span-property span 'cmd)))
+ ;; State of spans after advancing:
+ (proof-set-locked-end end)
+ ;; FIXME: buglet here, can sometimes arrive with queue span already detached.
+ ;; (I think when complete file process is requested during scripting)
+ (if (span-live-p proof-queue-span)
+ (proof-set-queue-start end))
+
+ (cond
+ ;; CASE 1: Comments just get highlighted
+ ((eq (span-property span 'type) 'comment)
+ (proof-done-advancing-comment span))
+
+ ;; CASE 2: Save command seen, now we may amalgamate spans.
+ ((and proof-save-command-regexp
+ (proof-string-match proof-save-command-regexp cmd)
+ ;; FIXME: would like to get rid of proof-really-save-command-p
+ ;; and use nested goals mechanism instead.
+ (funcall proof-really-save-command-p span cmd)
+ (decf proof-nesting-depth) ;; [always non-nil/true]
+ (if proof-nested-goals-history-p
+ ;; For now, we only support this nesting behaviour:
+ ;; don't amalgamate unless the nesting depth is 0,
+ ;; i.e. we're in a top-level proof.
+ ;; This assumes prover keeps history for nested proofs.
+ ;; (True for Isabelle/Isar).
+ (eq proof-nesting-depth 0)
+ t))
+ (proof-done-advancing-save span))
+
+ ;; CASE 3: Proof completed one step or more ago, non-save
+ ;; command seen, no nested goals allowed.
+ ;;
+ ;; We make a fake goal-save from any previous
+ ;; goal to the command before the present one.
+ ;;
+ ;; This allows smooth undoing in proofs which have no "qed"
+ ;; statements. If your proof assistant doesn't allow these
+ ;; "unclosed" proofs, then you can safely set
+ ;; proof-completed-proof-behaviour.
+ ((and
+ proof-shell-proof-completed
+ (or (and (eq proof-completed-proof-behaviour 'extend)
+ (>= proof-shell-proof-completed 0))
+ (and (eq proof-completed-proof-behaviour 'closeany)
+ (> proof-shell-proof-completed 0))
+ (and (eq proof-completed-proof-behaviour 'closegoal)
+ (funcall proof-goal-command-p cmd))))
+ (proof-done-advancing-autosave span))
+
+ ;; CASE 4: Some other kind of command (or a nested goal).
+ (t
+ (proof-done-advancing-other span))))
+
+ ;; Finally: state of scripting may have changed now, run hooks.
+ (run-hooks 'proof-state-change-hook)))
+
+
+
+(defun proof-done-advancing-comment (span)
+ "A subroutine of `proof-done-advancing'"
+ ;; Add an element for a new span, which should span
+ ;; the text of the comment.
+ ;; FIXME: might be better to use regexps for matching
+ ;; start/end of comments, rather than comment-start-skip
+ (let ((bodyspan (make-span
+ (+ (length comment-start) (span-start span))
+ (- (span-end span) (length comment-end))))
+ (id (proof-next-element-id 'comment)))
+ (pg-add-element "comment" id bodyspan)
+ (set-span-property span 'id (intern id))
+ (set-span-property span 'idiom 'comment)
+ (pg-set-span-helphighlights span)))
+
+
+(defun proof-done-advancing-save (span)
+ "A subroutine of `proof-done-advancing'"
+ (unless (eq proof-shell-proof-completed 1)
+ ;; We expect saves to succeed only for recently completed proofs.
+ ;; Give a hint to the proof assistant implementor in case something
+ ;; odd is going on. (NB: this is normal for nested proofs, though).
+ (proof-debug
+ (format
+ "PG: save command with proof-shell-proof-completed=%s, proof-nesting-depth=%s"
+ proof-shell-proof-completed proof-nesting-depth)))
+
+ (setq proof-shell-proof-completed nil)
+
+ ;; FIXME: need subroutine here:
+ (let ((gspan span) ; putative goal span
+ (savestart (span-start span))
+ (saveend (span-end span))
+ (cmd (span-property span 'cmd))
+ lev nestedundos nam next ncmd)
+
+ ;; Try to set the name of the theorem from the save
+ ;; (message "%s" cmd) 3.4: remove this message.
+
+ (and proof-save-with-hole-regexp
+ (proof-string-match proof-save-with-hole-regexp cmd)
+ ;; Give a message of a name if one can be determined
+ (message "%s"
+ (setq nam
+ (if (stringp proof-save-with-hole-result)
+ (replace-match proof-save-with-hole-result nil nil cmd)
+ (match-string proof-save-with-hole-result cmd)))))
+
+ ;; Search backwards for matching goal command, deleting spans
+ ;; along the way: they will be amalgamated into a single
+ ;; goal-save region, which corresponds to the prover
+ ;; discarding the proof history.
+ ;; Provers without flat history yet nested proofs (i.e. Coq)
+ ;; need to keep a record of the undo count for nested goalsaves.
+ ;; FIXME: should also remove nested 'idiom spans, perhaps.
+ (setq lev 1)
+ (setq nestedundos 0)
+ (while (and gspan (> lev 0))
+ (setq next (prev-span gspan 'type))
+ (delete-span gspan)
+ (setq gspan next)
+ (if gspan
+ (progn
+ (setq cmd (span-property gspan 'cmd))
+ (cond
+ ;; Ignore comments [may have null cmd setting]
+ ((eq (span-property gspan 'type) 'comment))
+ ;; Nested goal saves: add in any nestedcmds
+ ((eq (span-property gspan 'type) 'goalsave)
+ (setq nestedundos
+ (+ nestedundos 1
+ (or (span-property gspan 'nestedundos) 0))))
+ ;; Increment depth for a nested save, in case
+ ;; prover supports history of nested proofs
+ ((and proof-nested-goals-history-p
+ proof-save-command-regexp
+ (proof-string-match proof-save-command-regexp cmd))
+ (incf lev))
+ ;; Decrement depth when a goal is hit
+ ((funcall proof-goal-command-p cmd)
+ (decf lev))
+ ;; Remainder cases: see if command matches something we
+ ;; should count for a global undo
+ ((and proof-nested-undo-regexp
+ (proof-string-match proof-nested-undo-regexp cmd))
+ (incf nestedundos))
+ ))))
+
+ (if (not gspan)
+ ;; No goal span found! Issue a warning and do nothing more.
+ (proof-warning
+ "Proof General: script management confused, couldn't find goal span for save.")
+
+ ;; If the name isn't set, try to set it from the goal,
+ ;; or as a final desparate attempt, set the name to
+ ;; proof-unnamed-theorem-name (Coq actually uses a default
+ ;; name for unnamed theorems, believe it or not, and issues
+ ;; a name-binding error for two unnamed theorems in a row!).
+ (setq nam (or nam
+ (proof-get-name-from-goal gspan)
+ proof-unnamed-theorem-name))
+
+ (proof-make-goalsave gspan (span-end gspan)
+ savestart saveend nam nestedundos)
+
+ ;; *** Theorem dependencies ***
+ (if proof-last-theorem-dependencies
+ (proof-depends-process-dependencies nam gspan)))))
+
+(defun proof-make-goalsave (gspan goalend savestart saveend nam &optional nestedundos)
+ "Make new goal-save span, using GSPAN. Subroutine of `proof-done-advancing-save'"
+ (set-span-end gspan saveend)
+ (set-span-property gspan 'type 'goalsave)
+ (set-span-property gspan 'idiom 'proof);; links to nested proof element
+ (set-span-property gspan 'name nam)
+ (and nestedundos (set-span-property gspan 'nestedundos nestedundos))
+ (pg-set-span-helphighlights gspan)
+ ;; Now make a nested span covering the purported body of the
+ ;; proof, and add to buffer-local list of elements.
+ (let ((proofbodyspan
+ (make-span goalend (if proof-script-integral-proofs
+ saveend savestart))))
+ (pg-add-proof-element nam proofbodyspan gspan)))
+
+(defun proof-get-name-from-goal (gspan)
+ "Try to return a goal name from GSPAN. Subroutine of `proof-done-advancing-save'"
+ (let ((cmdstr (span-property gspan 'cmd)))
+ (and proof-goal-with-hole-regexp
+ (proof-string-match proof-goal-with-hole-regexp cmdstr)
+ (if (stringp proof-goal-with-hole-result)
+ (replace-match proof-goal-with-hole-result nil nil cmdstr)
+ (match-string proof-goal-with-hole-result cmdstr)))))
+
+
+;; FIXME: this next function should be more like proof-done-advancing-save,
+;; perhaps simplifying the proof-completed-proof-behaviour functionality,
+;; which isn't seriously used in any prover. At the moment the behaviour
+;; here is incomplete compared with proof-done-advancing-save.
+;; NB: in this function we assume non-nested proofs, which explains
+;; some of the logic. There is no attempt to fix up proof-nesting-depth.
+;; NB: 'extend behaviour is not currently compatible with appearance of
+;; save commands, since proof-done-advancing-save allow for goalspan
+;; already existing.
+(defun proof-done-advancing-autosave (span)
+ "A subroutine of `proof-done-advancing'"
+
+ ;; In the extend case, the context of proof grows until hit a save
+ ;; or new goal.
+ (if (eq proof-completed-proof-behaviour 'extend)
+ (incf proof-shell-proof-completed)
+ (setq proof-shell-proof-completed nil))
+
+ (let* ((swallow (eq proof-completed-proof-behaviour 'extend))
+ (gspan (if swallow span (prev-span span 'type)))
+ (newend (if swallow (span-end span) (span-start span)))
+ (cmd (span-property span 'cmd))
+ (newgoal (funcall proof-goal-command-p cmd))
+ nam hitsave dels ncmd)
+ ;; Search backwards to see if we can find a previous goal
+ ;; before a save or the start of the buffer.
+ ;; FIXME: this should really do the work done in
+ ;; proof-done-advancing-save above, too, with nested undos, etc.
+ (while ;; YUK!
+ (and
+ gspan
+ (or
+ (eq (span-property gspan 'type) 'comment)
+ (and
+ (setq ncmd (span-property gspan 'cmd))
+ (not (funcall proof-goal-command-p (setq cmd ncmd)))
+ (not
+ (and proof-save-command-regexp
+ (proof-string-match proof-save-command-regexp cmd)
+ (funcall proof-really-save-command-p span cmd)
+ (setq hitsave t))))))
+ (setq dels (cons gspan dels))
+ (setq gspan (prev-span gspan 'type)))
+ (cond
+ ((or hitsave (null gspan))
+ (proof-debug
+ "Proof General strangeness: unclosed proof completed, but couldn't find its start!")
+ (pg-set-span-helphighlights span))
+ ((and swallow newgoal)
+ ;; If extending the region, goalsave already there; just highlight new region
+ (setq proof-shell-proof-completed nil)
+ (pg-set-span-helphighlights span))
+ (t
+ ;; If, search back through spans, we haven't hit a save or the
+ ;; start of the buffer, we make a fake goal-save region.
+
+ ;; Delete spans between the previous goal and new command
+ (mapcar 'delete-span dels)
+
+ ;; Try to set the name from the goal... [as above]
+ (setq nam (or (proof-get-name-from-goal gspan)
+ proof-unnamed-theorem-name))
+
+ ;; NB: if extending an already closed region, ought to delete
+ ;; the body and extend that too: currently we make multiple nested
+ ;; bodies, a bit messy.
+ ;; (NB: savestart used for nested region: here use saveend)
+ (proof-make-goalsave gspan
+ (+ (span-start gspan)
+ (length (or (span-property-safe gspan 'cmd))))
+ newend newend nam)))))
+
+(defun proof-done-advancing-other (span)
+ ;; We might add hidable regions also for commands: the problem
+ ;; is that they have no natural surrounding region, so makes
+ ;; it difficult to define a region for revealing again.
+ ;; [ best solution would be to support clicking on ellipsis]
+ (if (funcall proof-goal-command-p (span-property span 'cmd))
+ (incf proof-nesting-depth))
+
+ (if proof-shell-proof-completed
+ (incf proof-shell-proof-completed))
+
+ (pg-set-span-helphighlights span))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Parsing functions for parsing commands in script
+;;
+;; Command parsing is suprisingly subtle with various possibilities of
+;; command syntax (terminated, not terminated, or lisp-style); whether
+;; or not PG silently ignores comments, etc.
+
+;; FIXME: currently there's several sets of functions. We need to be
+;; more general and a bit more clear, but the latest versions are a
+;; much better attempt.
+
+(defun proof-segment-up-to-parser (pos &optional next-command-end)
+ "Parse the script buffer from end of locked to POS.
+Return a list of (type, string, int) tuples (in reverse order).
+
+Each tuple denotes the command and the position of its final character,
+type is one of 'comment, or 'cmd.
+
+The behaviour around comments is set by
+`proof-script-fly-past-comments', which see.
+
+This version is used when `proof-script-parse-function' is set,
+to the function which parses the script segment by segment."
+ (save-excursion
+ (let* ((start (goto-char (proof-queue-or-locked-end)))
+ (cur (1- start))
+ (seg t)
+ prevtype realstart cmdseen segs)
+ ;; Keep parsing until:
+ ;; - we fail to find a segment (seg = nil)
+ ;; - we go beyond the stop point (cur >= end)
+ ;; - unless we're flying past comments, in which case
+ ;; wait for a command (cmdseen<>nil)
+ (while (and seg
+ (or (< cur pos)
+ (and proof-script-fly-past-comments
+ (not cmdseen))))
+ ;; Skip whitespace before this element
+ (skip-chars-forward " \t\n")
+ (setq realstart (point))
+ (let* ((type (funcall proof-script-parse-function)))
+ (setq seg nil)
+ (cond
+ ((eq type 'comment)
+ (setq seg (list 'comment "" (point))))
+ ((eq type 'cmd)
+ (setq cmdseen t)
+ (setq seg (list
+ 'cmd
+ (buffer-substring realstart (point))
+ (point))))
+ ((null type)) ; nothing left in buffer
+ (t
+ (error
+ "proof-segment-up-to-parser: bad TYPE value from proof-script-parse-function.")))
+ ;;
+ (if seg
+ (progn
+ ;; Add the new segment, coalescing comments if
+ ;; the user likes it that way. I first made
+ ;; coalescing a separate configuration option, but
+ ;; it works well used in tandem with the fly-past
+ ;; behaviour.
+ (if (and proof-script-fly-past-comments
+ (eq type 'comment)
+ (eq prevtype 'comment))
+ (setq segs (cons seg (cdr segs)))
+ (setq segs (cons seg segs)))
+ ;; Update state
+ (setq cur (point))
+ (setq prevtype type)))))
+ ;; Return segment list
+ segs)))
+
+(defun proof-script-generic-parse-find-comment-end ()
+ "Find the end of the comment point is at the start of. Nil if not found."
+ (let ((notout t))
+ ;; Find end of comment (NB: doesn't undertand nested comments)
+ (while (and notout (re-search-forward
+ proof-script-comment-end-regexp nil 'movetolimit))
+ (setq notout (proof-buffer-syntactic-context)))
+ (not (proof-buffer-syntactic-context))))
+
+(defun proof-script-generic-parse-cmdend ()
+ "Used for proof-script-parse-function if proof-script-command-end-regexp is set."
+ (if (looking-at proof-script-comment-start-regexp)
+ ;; Handle comments
+ (if (proof-script-generic-parse-find-comment-end) 'comment)
+ ;; Handle non-comments: assumed to be commands
+ (let (foundend)
+ ;; Find end of command
+ (while (and (setq foundend
+ (re-search-forward proof-script-command-end-regexp nil t))
+ (proof-buffer-syntactic-context))
+ ;; inside a string or comment before the command end
+ )
+ (if (and foundend
+ (not (proof-buffer-syntactic-context)))
+ ;; Found command end outside string/comment
+ 'cmd
+ ;; Didn't find command end
+ nil))))
+
+(defun proof-script-generic-parse-cmdstart ()
+ "For proof-script-parse-function if proof-script-command-start-regexp is set."
+ ;; This was added for the fine-grained command structure of Isar
+ ;;
+ ;; It's is a lot more involved than the case of just scanning for
+ ;; the command end; we have to find two successive command starts
+ ;; and go backwards from the second. This coalesces comments
+ ;; following commands with commands themselves, and sends them to
+ ;; the prover (only case where it does). It's needed particularly
+ ;; for Isar's text command (text {* foo *}) so we can define the
+ ;; buffer syntax for text as a comment.
+ ;;
+ ;; To avoid doing that, we would need to scan also for comments but
+ ;; it would be difficult to distinguish between:
+ ;; complete command (* that's it *)
+ ;; and
+ ;; complete (* almost *) command
+ ;;
+ ;; Maybe the second case should be disallowed in command-start regexp case?
+ ;;
+ ;; Another improvement idea might be to take into account both
+ ;; command starts *and* ends, but let's leave that for another day.
+ ;;
+ ;; NB: proof-script-comment-start-regexp doesn't need to be the same
+ ;; as (reqexp-quote comment-start).
+ (if (looking-at proof-script-comment-start-regexp)
+ ;; Find end of comment
+ (if (proof-script-generic-parse-find-comment-end) 'comment)
+ ;; Handle non-comments: assumed to be commands
+ (if (looking-at proof-script-command-start-regexp)
+ (progn
+ ;; We've got at least the beginnings of a command, skip past it
+ ;(re-search-forward proof-script-command-start-regexp nil t)
+ (goto-char (match-end 0))
+ (let (foundstart)
+ ;; Find next command start
+ (while (and (setq
+ foundstart
+ (and
+ (re-search-forward proof-script-command-start-regexp
+ nil 'movetolimit)
+ (match-beginning 0)))
+ (proof-buffer-syntactic-context))
+ ;; inside a string or comment before the next command start
+ )
+ (if (not (proof-buffer-syntactic-context)) ; not inside a comment/string
+ (if foundstart ; found a second command start
+ (progn
+ (goto-char foundstart) ; beginning of command start
+ (skip-chars-backward " \t\n") ; end of previous command
+ 'cmd)
+ (if (eq (point) (point-max)) ; At the end of the buffer
+ (progn
+ (skip-chars-backward " \t\n") ; benefit of the doubt, let
+ 'cmd)))) ; the PA moan if it's incomplete
+ ;; Return nil in other cases, no complete command found
+ )))))
+
+
+(defun proof-script-generic-parse-sexp ()
+ "Used for proof-script-parse-function if proof-script-sexp-commands is set."
+ ;; Usual treatment of comments
+ (if (looking-at proof-script-comment-start-regexp)
+ ;; Find end of comment
+ (if (proof-script-generic-parse-find-comment-end) 'comment)
+ (let* ((parse-sexp-ignore-comments t) ; gobble comments into commands
+ (end (scan-sexps (point) 1)))
+ (if end
+ (progn (goto-char end) 'cmd)))))
+
+
+;; Parsing functions new in v3.2
+;;
+;; NB: compared with old code,
+;; (1) this doesn't treat comments quite so well, but parsing
+;; should be rather more efficient.
+;; (2) comments are treated less like commands, and are only
+;; coloured blue "in passing" when commands are sent.
+;; However, undo still process comments step-by-step.
+
+(defun proof-segment-up-to-cmdstart (pos &optional next-command-end)
+ "Parse the script buffer from end of locked to POS.
+Return a list of (type, string, int) tuples.
+
+Each tuple denotes the command and the position of its terminator,
+type is one of 'comment, or 'cmd.
+
+If optional NEXT-COMMAND-END is non-nil, we include the command
+which continues past POS, if any. (NOT IMPLEMENTED IN THIS VERSION).
+
+This version is used when `proof-script-command-start-regexp' is set."
+ (save-excursion
+ (let* ((commentre (concat "[ \t\n]*" proof-script-comment-start-regexp))
+ (commentend (concat proof-script-comment-end-regexp "[ \t\n]*" ))
+ (add-segment-for-cmd ; local function: advances "prev"
+ (lambda ()
+ (setq tmp (point))
+ ;; Find end of previous command...
+ (goto-char comstart)
+ ;; Special hack: allow terminal char to be included
+ ;; in a command, if it's set.
+ (if (and proof-terminal-char
+ (looking-at
+ (regexp-quote (char-to-string proof-terminal-char))))
+ (goto-char (1+ (point)))
+ (skip-chars-backward " \t\n"))
+ (let* ((prev-no-blanks
+ (save-excursion
+ (goto-char prev)
+ (skip-chars-forward " \t\n")
+ (point)))
+ (comend (point))
+ (bufstr (buffer-substring prev-no-blanks comend))
+ (type (save-excursion
+ ;; The behaviour here is a bit odd: this
+ ;; is a half-hearted attempt to strip comments
+ ;; as we send text to the proof assistant,
+ ;; but it breaks when we have certain bad
+ ;; input: (* foo *) blah (* bar *) cmd
+ ;; We check for the case
+ ;; of a comment spanning the *whole*
+ ;; substring, otherwise send the
+ ;; (defective) text as if it were a
+ ;; proper command anyway.
+ ;; This shouldn't cause problems: the
+ ;; proof assistant is certainly capable
+ ;; of skipping comments itself, and
+ ;; the situation should cause an error.
+ ;; (If it were accepted it could upset the
+ ;; undo behaviour)
+ (goto-char prev-no-blanks)
+ ;; Update: PG 3.4: try to deal with sequences
+ ;; of comments as well, since previous behaviour
+ ;; breaks Isar, in fact, since repeated
+ ;; comments get categorized as commands,
+ ;; breaking sync.
+ (if (and
+ (looking-at commentre)
+ (re-search-forward proof-script-comment-end-regexp)
+ (progn
+ (while (looking-at commentre)
+ (re-search-forward proof-script-comment-end-regexp))
+ (>= (point) comend)))
+ 'comment 'cmd)))
+ (string (if (eq type 'comment) "" bufstr)))
+ (setq prev (point))
+ (goto-char tmp)
+ ;; NB: Command string excludes whitespace, span includes it.
+ (setq alist (cons (list type string prev) alist)))))
+ alist prev cmdfnd startpos comstart tmp)
+ (goto-char (proof-queue-or-locked-end))
+ (setq prev (point))
+ (skip-chars-forward " \t\n")
+ (setq startpos (point))
+ (while
+ (and
+ (proof-re-search-forward proof-script-command-start-regexp
+ nil t) ; search for next command
+ (setq comstart (match-beginning 0)); save command start
+ (or (save-excursion
+ (goto-char comstart)
+ ;; continue if inside (or at start of) comment/string
+ (proof-looking-at-syntactic-context))
+ (progn ; or, if found command...
+ (setq cmdfnd
+ (> comstart startpos)); ignore first match
+ (<= prev pos))))
+ (if cmdfnd (progn
+ (funcall add-segment-for-cmd)
+ (setq cmdfnd nil))))
+ ;; End of parse; see if we found some text at the end of the
+ ;; buffer which could be a command. (NB: With a regexp for
+ ;; start of commands only, we can't be sure it is a complete
+ ;; command).
+ (if (and comstart ; previous command was found
+ (<= prev pos) ; last command within range
+ (goto-char (point-max))
+ (setq comstart (point)) ; pretend there's another cmd here
+ (not (proof-buffer-syntactic-context))) ; buffer ends well
+ (funcall add-segment-for-cmd))
+ ; (if (and cmdfnd next-command-end)
+ ; (funcall add-segment-for-cmd))
+ ;; Return resulting list
+ alist)))
+
+
+;; FIXME: this needs fixing to include final comment in buffer
+;; if there is one!!
+
+(defun proof-segment-up-to-cmdend (pos &optional next-command-end)
+ "Parse the script buffer from end of locked to POS.
+Return a list of (type, string, int) tuples.
+
+Each tuple denotes the command and the position of its terminator,
+type is one of 'comment, or 'cmd. 'unclosed-comment may be consed onto
+the start if the segment finishes with an unclosed comment.
+
+If optional NEXT-COMMAND-END is non-nil, we include the command
+which continues past POS, if any.
+
+This version is used when `proof-script-command-end-regexp' is set."
+ (save-excursion
+ (let*
+ ((commentre (concat "[ \t\n]*" proof-script-comment-start-regexp))
+ (add-segment-for-cmd ; local function: advances "prev"
+ (lambda ()
+ (let ((cmdend (point)) start)
+ (goto-char prev)
+ ;; String may start with comments, let's strip them off
+ (while
+ (and
+ (setq start (point))
+ (proof-re-search-forward commentre cmdend t)
+ (or (eq (match-beginning 0) start)
+ ;; In case a comment inside a command was found, make
+ ;; sure we're at the start of the cmd before exiting
+ (progn (goto-char start) nil)))
+ ;; Look for the end of the comment
+ ;; (FIXME: ignore nested comments here, we should
+ ;; have a consistent policy!)
+ (unless
+ (if (fboundp 'comment-forward)
+ (progn
+ (goto-char (or (match-end 1) (match-beginning 0)))
+ (comment-forward))
+ (proof-re-search-forward
+ proof-script-comment-end-regexp cmdend t))
+ (error
+ "PG error: proof-segment-up-to-cmd-end didn't find comment end"))
+ (setq alist (cons (list 'comment "" (point)) alist)))
+ ;; There should be something left: a command.
+ (skip-chars-forward " \t\n")
+ (setq alist (cons (list 'cmd
+ (buffer-substring
+ (point) cmdend)
+ cmdend) alist))
+ (setq prev cmdend)
+ (goto-char cmdend))))
+ alist prev cmdfnd startpos tmp)
+ (goto-char (proof-queue-or-locked-end))
+ (setq prev (point))
+ (skip-chars-forward " \t\n")
+ (setq startpos (point))
+ (while
+ (and
+ (proof-re-search-forward proof-script-command-end-regexp
+ nil t) ; search for next command
+ (or (proof-buffer-syntactic-context) ; continue if inside comment/string
+ (progn ; or, if found command...
+ (setq cmdfnd t)
+ (<= (point) pos))))
+ (if cmdfnd (progn
+ (funcall add-segment-for-cmd)
+ (setq cmdfnd nil))))
+ ;; End of parse; if we found a command past POS maybe add it.
+ ;; FIXME: also, if we found a *comment* maybe add it!
+ (if cmdfnd ; (and cmdfnd next-command-end)
+ (funcall add-segment-for-cmd))
+ ;; Return resulting list
+ alist)))
+
+(defun proof-semis-to-vanillas (semis &optional callback-fn)
+ "Convert a sequence of terminator positions to a set of vanilla extents.
+Proof terminator positions SEMIS has the form returned by
+the function proof-segment-up-to.
+Set the callback to CALLBACK-FN or 'proof-done-advancing by default."
+ (let ((ct (proof-queue-or-locked-end)) span alist semi)
+ (while (not (null semis))
+ (setq semi (car semis)
+ span (make-span ct (nth 2 semi))
+ ct (nth 2 semi))
+ (if (eq (car (car semis)) 'cmd)
+ (progn
+ (set-span-property span 'type 'vanilla)
+ (set-span-property span 'cmd (nth 1 semi))
+ (setq alist (cons (list span (nth 1 semi)
+ (or callback-fn 'proof-done-advancing))
+ alist)))
+ (set-span-property span 'type 'comment)
+ (setq alist (cons (list span proof-no-command 'proof-done-advancing)
+ alist)))
+ (setq semis (cdr semis)))
+ (nreverse alist)))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Assert-until-point and friends.
+;;
+;; These functions parse some region of the script buffer into
+;; commands, and add the commands into the queue.
+;;
+
+
+;; First: two commands for moving forwards in proof scripts. Moving
+;; forward for a "new" command may insert spaces or new lines. Moving
+;; forward for the "next" command does not.
+
+(defun proof-script-new-command-advance ()
+ "Move point to a nice position for a new command.
+Assumes that point is at the end of a command."
+ (interactive)
+; FIXME: pending improvement.2, needs a fix here.
+; (if (eq (proof-locked-end) (point))
+; ;; A hack to fix problem that the locked span
+; ;; is [ ) so sometimes inserting at the end
+; ;; tries to extend it, giving "read only" error.
+; (if (> (point-max) (proof-locked-end))
+; (progn
+; (goto-char (1+ (proof-locked-end)))
+; (backward-char))))
+ (if proof-one-command-per-line
+ ;; One command per line: move to next new line, creating one if
+ ;; at end of buffer or at the start of a blank line. (This has
+ ;; the pleasing effect that blank regions of the buffer are
+ ;; automatically extended when inserting new commands).
+; unfortunately if we're not at the end of a line to start,
+; it skips past stuff to the end of the line! don't want
+; that.
+; (cond
+; ((eq (forward-line) 1)
+; (newline))
+; ((eolp)
+; (newline)
+; (forward-line -1)))
+ (newline)
+ ;; Multiple commands per line: skip spaces at point, and insert
+ ;; the 1/0 number of spaces that were skipped in front of point
+ ;; (at least one). This has the pleasing effect that the spacing
+ ;; policy of the current line is copied: e.g. <command>;
+ ;; <command>; Tab columns don't work properly, however. Instead
+ ;; of proof-one-command-per-line we could introduce a
+ ;; "proof-command-separator" to improve this.
+ (let ((newspace (max (skip-chars-forward " \t") 1))
+ (p (point)))
+ (if proof-script-command-separator
+ (insert proof-script-command-separator)
+ (insert-char ?\ newspace)
+ (goto-char p)))))
+
+(defun proof-script-next-command-advance ()
+ "Move point to the beginning of the next command if it's nearby.
+Assumes that point is at the end of a command."
+ (interactive)
+ ;; skip whitespace on this line
+ (skip-chars-forward " \t")
+ (if (and proof-one-command-per-line (eolp))
+ ;; go to the next line if we have one command per line
+ (forward-line)))
+
+
+;; NB: the "interactive" variant is so that we get a simple docstring.
+(defun proof-assert-until-point-interactive ()
+ "Process the region from the end of the locked-region until point.
+Default action if inside a comment is just process as far as the start of
+the comment."
+ (interactive)
+ (proof-assert-until-point))
+
+
+;; Assert until point - We actually use this to implement the
+;; assert-until-point, electric terminator keypress, and
+;; goto-command-end. In different cases we want different things, but
+;; usually the information (e.g. are we inside a comment) isn't
+;; available until we've actually run proof-segment-up-to (point),
+;; hence all the different options when we've done so.
+
+;; FIXME da: this command doesn't behave as the doc string says when
+;; inside comments. Also is unhelpful at the start of commands, and
+;; in the locked region. I prefer the new version below.
+
+;; FIXME: get rid of duplication between proof-assert-next-command and
+;; proof-assert-until-point. Get rid of ignore process nonsense.
+
+;; FIXME: get rid of unclosed-comment-fun nonsense. It's used
+;; in the electric terminator function, but we should probably
+;; use something else for that!
+
+(defun proof-assert-until-point (&optional unclosed-comment-fun
+ ignore-proof-process-p)
+ "Process the region from the end of the locked-region until point.
+Default action if inside a comment is just process as far as the start of
+the comment.
+
+If you want something different, put it inside
+UNCLOSED-COMMENT-FUN. If IGNORE-PROOF-PROCESS-P is set, no commands
+will be added to the queue and the buffer will not be activated for
+scripting."
+ (unless ignore-proof-process-p
+ (proof-activate-scripting nil 'advancing))
+ (let ((semis))
+ (save-excursion
+ ;; Give error if no non-whitespace between point and end of
+ ;; locked region.
+ (if (proof-only-whitespace-to-locked-region-p)
+ (error "At the end of the locked region already, there's nothing to do to!"))
+ ;; NB: (point) has now been moved backwards to first non-whitespace char.
+ (setq semis (proof-segment-up-to (point))))
+ (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis)))
+ (funcall unclosed-comment-fun)
+ (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis)))
+ (if (and (not ignore-proof-process-p) (null semis))
+ ;; This is another case that there's nothing to do: maybe
+ ;; because inside a string or something.
+ (if (eq unclosed-comment-fun 'proof-electric-term-incomment-fn)
+ ;; With electric terminator, we shouldn't give an error, but
+ ;; should still insert and pretend it was as if a comment.
+ (proof-electric-term-incomment-fn)
+ (error "I can't find any complete commands to process!"))
+ (goto-char (nth 2 (car semis)))
+ (and (not ignore-proof-process-p)
+ (let ((vanillas (proof-semis-to-vanillas (nreverse semis))))
+ (proof-extend-queue (point) vanillas)))))))
+
+
+;; da: This is my alternative version of the above.
+;; It works from the locked region too.
+;; I find it more convenient to assert up to the current command (command
+;; point is inside), and move to the next command.
+;; This means proofs can be easily replayed with point at the start
+;; of lines. Above function gives stupid "nothing to do error." when
+;; point is on the start of line or in the locked region.
+
+;; FIXME: behaviour inside comments may be odd at the moment. (it
+;; doesn't behave as docstring suggests, same prob as
+;; proof-assert-until-point)
+;; FIXME: polish the undo behaviour and quit behaviour of this
+;; command (should inhibit quit somewhere or other).
+
+
+
+(defun proof-assert-next-command
+ (&optional unclosed-comment-fun ignore-proof-process-p
+ dont-move-forward for-new-command)
+ "Process until the end of the next unprocessed command after point.
+If inside a comment, just process until the start of the comment.
+
+If you want something different, put it inside UNCLOSED-COMMENT-FUN.
+If IGNORE-PROOF-PROCESS-P is set, no commands will be added to the queue.
+Afterwards, move forward to near the next command afterwards, unless
+DONT-MOVE-FORWARD is non-nil. If FOR-NEW-COMMAND is non-nil,
+a space or newline will be inserted automatically."
+ (interactive)
+ (unless ignore-proof-process-p
+ (proof-activate-scripting nil 'advancing))
+ (or ignore-proof-process-p
+ (if (proof-locked-region-full-p)
+ (error "Locked region is full, no more commands to do!")))
+ (let ((semis))
+ (save-excursion
+ ;; CHANGE from old proof-assert-until-point: don't bother check
+ ;; for non-whitespace between locked region and point.
+ ;; CHANGE: ask proof-segment-up-to to scan until command end
+ ;; (which it used to do anyway, except in the case of a comment)
+ (setq semis (proof-segment-up-to (point) t)))
+ ;; old code:
+ ;;(if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t))
+ ;; (progn (goto-char pt)
+ ;; (error "I don't know what I should be doing in this buffer!")))
+ ;; (setq semis (proof-segment-up-to (point))))
+ (if (and unclosed-comment-fun (eq 'unclosed-comment (car-safe semis)))
+ (funcall unclosed-comment-fun)
+ (if (eq 'unclosed-comment (car-safe semis))
+ (setq semis (cdr semis)))
+ (if (and (not ignore-proof-process-p) (null semis))
+ (error "I can't see any complete commands to process!"))
+ (if (nth 2 (car semis))
+ (goto-char (nth 2 (car semis))))
+ (if (not ignore-proof-process-p)
+ (let ((vanillas (proof-semis-to-vanillas (nreverse semis))))
+ (proof-extend-queue (point) vanillas)))
+ ;; This is done after the queuing to be polite: it means the
+ ;; spacing policy enforced here is not put into the locked
+ ;; region so the user can re-edit.
+ (if (not dont-move-forward)
+ (if for-new-command
+ (proof-script-new-command-advance)
+ (proof-script-next-command-advance))))))
+
+(defun proof-goto-point ()
+ "Assert or retract to the command at current position.
+Calls proof-assert-until-point or proof-retract-until-point as
+appropriate."
+ (interactive)
+ (if (<= (proof-queue-or-locked-end) (point))
+ ;; This asserts only until the next command before point and
+ ;; does nothing if whitespace between point and locked region.
+ (proof-assert-until-point)
+ (proof-retract-until-point)))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; PBP call-backs
+;;
+(defun proof-insert-pbp-command (cmd)
+ "Insert CMD into the proof queue."
+ (proof-activate-scripting)
+ (let (span)
+ (proof-goto-end-of-locked)
+ (insert cmd)
+ (setq span (make-span (proof-locked-end) (point)))
+ (set-span-property span 'type 'pbp)
+ (set-span-property span 'cmd cmd)
+ (proof-start-queue (proof-unprocessed-begin) (point)
+ (list (list span cmd 'proof-done-advancing)))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Processing the script management queue -- PART 2: retracting
+;;
+
+;; Most of the hard work (computing the commands to do the retraction)
+;; is implemented in the customisation module (lego.el or coq.el), so
+;; code here is fairly straightforward.
+
+
+;; FIXME: we need to adjust proof-nesting-depth appropriately here.
+;; It would help to know the type of retraction which has just
+;; occurred: a kill-proof may be assumed to set nesting depth
+;; to zero; an undo sequence may alter it some other way.
+;; FIXME FIXME: at the moment, the adjustment is made in
+;; the wrong place!!
+
+(defun proof-done-retracting (span)
+ "Callback for proof-retract-until-point.
+We update display after proof process has reset its state.
+See also the documentation for `proof-retract-until-point'.
+Optionally delete the region corresponding to the proof sequence.
+After an undo, we clear the proof completed flag. The rationale
+is that undoing never leaves prover in a \"proof just completed\"
+state, which is true for some proof assistants (but probably not
+others)."
+ ;; FIXME: need to fixup proof-nesting-depth
+ (setq proof-shell-proof-completed nil)
+ (if (span-live-p span)
+ (let ((start (span-start span))
+ (end (span-end span))
+ (kill (span-property span 'delete-me)))
+ ;; da: check for empty region seems odd here?
+ ;; [prevents regions from being detached in set-locked-end]
+ (unless (proof-locked-region-empty-p)
+ (proof-set-locked-end start)
+ (proof-set-queue-end start))
+ (delete-spans start end 'type)
+ (delete-spans start end 'idiom)
+ (delete-span span)
+ (if kill (kill-region start end))))
+ ;; State of scripting may have changed now
+ (run-hooks 'proof-state-change-hook))
+
+(defun proof-setup-retract-action (start end proof-command delete-region)
+ "Make span from START to END which corresponds to retraction.
+Returns retraction action destined for proof shell queue, and make span.
+Action holds PROOF-COMMAND and `proof-done-retracting' callback.
+Span deletion property set to flag DELETE-REGION."
+ (let ((span (make-span start end)))
+ (set-span-property span 'delete-me delete-region)
+ (list (list span proof-command 'proof-done-retracting))))
+
+
+(defun proof-last-goal-or-goalsave ()
+ (save-excursion
+ (let ((span (span-at-before (proof-locked-end) 'type)))
+ (while (and span
+ (not (eq (span-property span 'type) 'goalsave))
+ (or (eq (span-property span 'type) 'proof)
+ (eq (span-property span 'type) 'comment)
+ (not (funcall proof-goal-command-p
+ (span-property span 'cmd)))))
+ (setq span (prev-span span 'type)))
+ span)))
+
+;;
+;; NB: Should carefully explain/document this behaviour somewhere.
+;; The undo is three-phase:
+;; undo-cmd - ... - undo-cmd within proof
+;; kill proof exit proof
+;; forget-to-declaration forget target span
+;;
+;; It turns out that this behaviour is not quite right for Coq.
+;; It might be simpler to just use a single undo/forget
+;; command, which is called in all cases.
+;;
+(defun proof-retract-target (target delete-region)
+ "Retract the span TARGET and delete it if DELETE-REGION is non-nil.
+Notice that this necessitates retracting any spans following TARGET,
+up to the end of the locked region."
+ (let ((end (proof-unprocessed-begin))
+ (start (span-start target))
+ (span (proof-last-goal-or-goalsave))
+ actions)
+
+ ;; NB: first section only entered if proof-kill-goal-command is
+ ;; non-nill. Otherwise we expect proof-find-and-forget-fn to do
+ ;; all relevent work for arbitrary retractions. FIXME: clean up
+
+ ;; Examine the last span in the locked region.
+
+ ;; If the last goal or save span is not a proof or
+ ;; prover processed file, we examine to see how to remove it.
+ (if (and span proof-kill-goal-command
+ (not (or
+ (memq (span-property span 'type)
+ '(goalsave proverproc)))))
+ ;; If the goal or goalsave span ends before the target span,
+ ;; then we are retracting within the last unclosed proof,
+ ;; and the retraction just amounts to a number of undo
+ ;; steps.
+ ;; FIXME: really, there shouldn't be more work to do: so
+ ;; why call proof-find-and-forget-fn later?
+ (if (< (span-end span) (span-end target))
+ (progn
+ ;; Skip comment spans at and immediately following target
+ (setq span target)
+ (while (and span (eq (span-property span 'type) 'comment))
+ (setq span (next-span span 'type)))
+ ;; Calculate undos for the current open segment
+ ;; of proof commands
+ (setq actions (proof-setup-retract-action
+ start end
+ (if (null span) proof-no-command
+ (funcall proof-count-undos-fn span))
+ delete-region)
+ end start))
+ ;; Otherwise, start the retraction by killing off the
+ ;; currently active goal.
+ ;; FIXME: and couldn't we move the end upwards?
+ ;; FIXME: hack proof-nesting-depth here. This is
+ ;; in the wrong place: it should be done *after* the
+ ;; retraction has succeeded.
+ (setq proof-nesting-depth (1- proof-nesting-depth))
+ (setq actions
+ (proof-setup-retract-action (span-start span) end
+ proof-kill-goal-command
+ delete-region)
+ end (span-start span))))
+ ;; Check the start of the target span lies before the end
+ ;; of the locked region (should always be true since we don't
+ ;; make spans outside the locked region at the moment)...
+ ;; But end may have moved backwards above: this just checks whether
+ ;; there is more retraction to be done.
+ (if (> end start)
+ (setq actions
+ ;; Append a retract action to clear the entire
+ ;; start-end region. Rely on proof-find-and-forget-fn
+ ;; to calculate a command which "forgets" back to
+ ;; the first definition, declaration, or whatever
+ ;; that comes after the target span.
+ ;; FIXME: originally this assumed a linear context,
+ ;; and that forgetting the first thing forgets all
+ ;; subsequent ones. it might be more general to
+ ;; allow *several* commands, and even queue these
+ ;; separately for each of the spans following target
+ ;; which are concerned.
+ (nconc actions (proof-setup-retract-action
+ start end
+ (funcall proof-find-and-forget-fn target)
+ delete-region))))
+
+ (proof-start-queue (min start end) (proof-locked-end) actions)))
+
+;; FIXME da: I would rather that this function moved point to
+;; the start of the region retracted?
+
+;; FIXME da: Maybe retraction to the start of
+;; a file should remove it from the list of included files?
+;; NB: the "interactive" variant is so that we get a simple docstring.
+(defun proof-retract-until-point-interactive (&optional delete-region)
+ "Tell the proof process to retract until point.
+If invoked outside a locked region, undo the last successfully processed
+command. If called with a prefix argument (DELETE-REGION non-nil), also
+delete the retracted region from the proof-script."
+ (interactive "P")
+ (proof-retract-until-point delete-region))
+
+(defun proof-retract-until-point (&optional delete-region)
+ "Set up the proof process for retracting until point.
+In particular, set a flag for the filter process to call
+`proof-done-retracting' after the proof process has successfully
+reset its state.
+If DELETE-REGION is non-nil, delete the region in the proof script
+corresponding to the proof command sequence.
+If invoked outside a locked region, undo the last successfully processed
+command."
+ (if (proof-locked-region-empty-p)
+ (error "No locked region")
+ ;; Make sure we're ready: either not busy, or already advancing/retracting.
+ ;;(proof-activate-scripting nil '(advancing retracting))
+ (proof-activate-scripting)
+ (let ((span (span-at (point) 'type)))
+ ;; If no span at point, retracts the last span in the buffer.
+ (unless span
+ (proof-goto-end-of-locked)
+ (backward-char)
+ (setq span (span-at (point) 'type)))
+ (proof-retract-target span delete-region))))
+
+
+
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Proof General scripting mode definition, part 1.
+;;
+
+(eval-and-compile ; to define vars
+;;; NB: autoload tag below doesn't work for d-d-m, autoload is in proof.el
+;;;###autoload
+(define-derived-mode proof-mode fundamental-mode
+ proof-general-name
+ "Proof General major mode class for proof scripts.
+\\{proof-mode-map}"
+
+ (setq proof-buffer-type 'script)
+
+ ;; font-lock-keywords isn't automatically buffer-local in Emacs 21.2
+ (make-local-variable 'font-lock-keywords)
+
+ ;; During write-file it can happen that we re-set the mode for
+ ;; the currently active scripting buffer. The user might also
+ ;; do this for some reason. We could maybe let
+ ;; this pass through, but it seems safest to treat it as
+ ;; a kill buffer operation (retract and clear spans).
+ ;; (NB: other situations seem to cause double successive calls to
+ ;; proof-mode).
+ (if (eq (current-buffer) proof-script-buffer)
+ (proof-script-kill-buffer-fn))
+
+ ;; We set hook functions here rather than in proof-config-done so
+ ;; that they can be adjusted by prover specific code if need be.
+ (proof-script-set-buffer-hooks)
+
+ (make-local-hook 'after-set-visited-file-name-hooks)
+ (add-hook 'after-set-visited-file-name-hooks 'proof-script-set-visited-file-name))
+
+ (make-local-hook 'proof-activate-scripting-hook)
+ (add-hook 'proof-activate-scripting-hook 'proof-cd-sync nil t))
+
+;; NB: proof-mode-map declared by define-derived-mode above
+(proof-menu-define-keys proof-mode-map)
+
+(defun proof-script-set-visited-file-name ()
+ "Called when visited file name is changed.
+
+This is a hook function for `after-set-visited-file-name-hooks'.
+
+For some provers, the file from which script commands are being
+processed may be important, and if it is changed with C-x C-w, for
+example, we might have to retract the contents or inform the proof
+assistant of the new name. This should be done by adding
+additional functions to `after-set-visited-file-name-hooks'.
+
+At the least, we need to set the buffer local hooks again
+with `proof-script-set-buffer-hooks' which is what this function does,
+as well as setting `proof-script-buffer-file-name' (which see).
+
+This hook also gives a warning in case this is the active scripting buffer."
+ (setq proof-script-buffer-file-true buffer-file-name)
+ (if (eq (current-buffer) proof-script-buffer)
+ (proof-warning
+"Active scripting buffer changed name; synchronization risked if prover tracks filenames!"))
+ (proof-script-set-buffer-hooks))
+
+
+
+(defun proof-script-set-buffer-hooks ()
+ "Set the hooks for a proof script buffer.
+The hooks set here are cleared by write-file, so we use this function
+to restore them using `after-set-visited-file-name-hooks'."
+ (make-local-hook 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'proof-script-kill-buffer-fn t t)
+ ;; Reverting buffer is same as killing it as far as PG is concerned
+ (make-local-hook 'before-revert-hook)
+ (add-hook 'before-revert-hook 'proof-script-kill-buffer-fn t t))
+
+(defun proof-script-kill-buffer-fn ()
+ "Value of kill-buffer-hook for proof script buffers.
+Clean up before a script buffer is killed.
+If killing the active scripting buffer, run proof-deactivate-scripting-auto.
+Otherwise just do proof-restart-buffers to delete some spans from memory."
+ ;; Deactivate scripting in the current buffer if need be, forcing
+ ;; automatic retraction if the buffer is not fully processed.
+ (if (eq (current-buffer) proof-script-buffer)
+ (proof-deactivate-scripting-auto))
+ (proof-restart-buffers (list (current-buffer)))
+ ;; Hide away goals, response, and tracing. This is a hack because
+ ;; otherwise we can lead the user to frustration with the
+ ;; dedicated windows nonsense.
+ (proof-map-buffers
+ (list proof-goals-buffer proof-response-buffer proof-trace-buffer)
+ (bury-buffer (current-buffer))))
+
+
+;; Notes about how to deal with killing/reverting/renaming buffers:
+;; (As of XEmacs 21.1.9, see files.el)
+;;
+;; Killing: easy, set kill-buffer-hook
+;; Reverting: ditto, set before-revert-hook to do same as kill.
+;; Renaming (write-file): much tricker. Code for write-file does
+;; several odd things. It kills off local hook functions, calls
+;; `after-set-visited-file-name-hooks' right at the end to give the
+;; chance to restore them, but then tends to automatically (re-)set
+;; the mode anyway.
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Proof General scripting mode definition - part 2
+;;
+
+;; The functions proof-config-done[-related] are called after the
+;; derived mode has made its settings.
+
+;; The callback *-config-done mechanism is an irritating hack - there
+;; should be some elegant mechanism for computing constants after the
+;; child has configured. Should petition the author of "derived-mode"
+;; about this!
+
+(defun proof-config-done-related ()
+ "Finish setup of Proof General scripting and related modes.
+This is a subroutine of `proof-config-done'.
+
+This is intended for proof assistant buffers which are similar to
+script buffers, but for which scripting is not enabled. In
+particular, we: lock the buffer if it appears on
+`proof-included-files-list'; configure font-lock support from
+`font-lock-keywords'; maybe turn on X-Symbol minor mode.
+
+This is used for Isabelle theory files, which share some scripting
+mode features, but are only ever processed atomically by the proof
+assistant."
+ (setq proof-script-buffer-file-name buffer-file-name)
+
+ ;; Has buffer already been processed?
+ ;; NB: call to file-truename is needed for GNU Emacs which
+ ;; chooses to make buffer-file-truename abbreviate-file-name
+ ;; form of file-truename.
+ (and buffer-file-truename
+ (member (file-truename buffer-file-truename)
+ proof-included-files-list)
+ (proof-complete-buffer-atomic (current-buffer)))
+
+ ;; calculate some strings and regexps for searching
+ (setq proof-terminal-string
+ (if proof-terminal-char
+ (char-to-string proof-terminal-char)
+ ""))
+
+ (make-local-variable 'comment-start)
+ (setq comment-start (concat proof-script-comment-start " "))
+ (make-local-variable 'comment-end)
+ (setq comment-end (concat " " proof-script-comment-end))
+
+ (unless proof-script-comment-start-regexp
+ (setq proof-script-comment-start-regexp (regexp-quote proof-script-comment-start)))
+ (unless proof-script-comment-end-regexp
+ (setq proof-script-comment-end-regexp (regexp-quote proof-script-comment-end)))
+
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip
+ (concat proof-script-comment-start-regexp "+\\s_?"))
+
+ ;;
+ ;; Fontlock support.
+ ;;
+ ;; Assume font-lock case folding follows proof-case-fold-search
+ (proof-font-lock-configure-defaults 'autofontify proof-case-fold-search)
+
+ ;; Maybe turn on x-symbol mode.
+ (proof-x-symbol-mode))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Generic defaults for hooks, based on regexps.
+;;
+
+;; The next step is to use proof-stringfn-match scheme more widely, to
+;; allow settings which are string or fn, so we don't need both regexp
+;; and function hooks, and so that the other hooks can be functions too.
+
+(defun proof-generic-goal-command-p (str)
+ "Is STR a goal? Decide by matching with proof-goal-command-regexp."
+ (proof-string-match-safe proof-goal-command-regexp str))
+
+(defun proof-generic-state-preserving-p (cmd)
+ "Is CMD state preserving? Match on proof-non-undoables-regexp."
+ ;; FIXME: logic here is not quite finished: proof-non-undoables are
+ ;; certainly not state preserving, but so are a bunch more things,
+ ;; i.e. ordinary proof commands which may appear in proof scripts.
+ ;; Might be better to add positive and negative regexps for
+ ;; state-preserving tests (only one of which needs to be set).
+ (not (proof-string-match-safe proof-non-undoables-regexp cmd)))
+
+(defun proof-generic-count-undos (span)
+ "Count number of undos in a span, return command needed to undo that far.
+Command is set using `proof-undo-n-times-cmd'.
+
+A default value for `proof-count-undos-fn'.
+
+For this function to work properly, you must configure
+`proof-undo-n-times-cmd' and `proof-ignore-for-undo-count'."
+ (let
+ ((case-fold-search proof-case-fold-search)
+ (ct 0) str i)
+ (while span
+ (setq str (span-property span 'cmd))
+ (cond ((eq (span-property span 'type) 'vanilla)
+ (unless (proof-stringfn-match proof-ignore-for-undo-count str)
+ (incf ct)))
+ ((eq (span-property span 'type) 'pbp)
+ (setq i 0)
+ (while (< i (length str))
+ (if (= (aref str i) proof-terminal-char) (incf ct))
+ (incf i))))
+ (setq span (next-span span 'type)))
+ (if (= ct 0)
+ proof-no-command
+ (cond ((stringp proof-undo-n-times-cmd)
+ (format proof-undo-n-times-cmd ct))
+ ((functionp proof-undo-n-times-cmd)
+ (funcall proof-undo-n-times-cmd ct))))))
+
+(defun proof-generic-find-and-forget (span)
+ "Calculate a forget/undo command to forget back to SPAN.
+This is a long-range forget: we know that there is no
+open goal at the moment, so forgetting involves unbinding
+declarations, etc, rather than undoing proof steps.
+
+This generic implementation assumes it is enough to find the
+nearest following span with a `name' property, and retract
+that using `proof-forget-id-command' with the given name.
+
+If this behaviour is not correct, you must customize the function
+with something different."
+ ;; Modelled on Isar's find-and-forget function, but less
+ ;; general at the moment: will only issue one und command.
+ ;; FIXME: would be much cleaner to wrap up the undo behaviour
+ ;; also within proofs in this function.
+ (let (str ans typ name answers)
+ (while span
+ (setq ans nil)
+ (setq str (span-property span 'cmd))
+ (setq typ (span-property span 'type))
+ (cond
+ ;; comment, diagnostic, nested proof command: skip
+ ((or (eq typ 'comment)
+ (eq typ 'proof)
+ (and proof-ignore-for-undo-count cmd
+ (proof-string-match proof-ignore-for-undo-count cmd))))
+ ;; some named element: use generic forget-id function; finish.
+ ((setq name (span-property span 'name))
+ (setq ans (format proof-forget-id-command name))
+ (setq span nil)))
+ (if ans (setq answers (cons ans answers)))
+ (if span (setq span (next-span span 'type))))
+ (if (null answers) proof-no-command (apply 'concat answers))))
+
+;;
+;; End of new generic functions
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+;;
+;; Sanity checks on important settings
+;;
+
+(defconst proof-script-important-settings
+ '(proof-script-comment-start ;
+ proof-script-comment-end
+ proof-save-command-regexp
+; proof-goal-command-regexp ; not needed if proof-goal-command-p is set
+; proof-goal-with-hole-regexp ; non-essential?
+; proof-save-with-hole-regexp ; non-essential?
+; proof-showproof-command ; non-essential
+; proof-goal-command ; non-essential
+; proof-save-command ; do
+; proof-kill-goal-command ; do
+ ))
+
+(defun proof-config-done ()
+ "Finish setup of Proof General scripting mode.
+Call this function in the derived mode for the proof assistant to
+finish setup which depends on specific proof assistant configuration."
+
+ (proof-config-done-related)
+
+ ;; Following group of settings only relevant if the current
+ ;; buffer is really a scripting buffer. Isabelle Proof General
+ ;; has theory file buffers which share some aspects, they
+ ;; just use proof-config-done-related.
+
+ ;; Preamble: make this mode class "pg-sticky" so that renaming file
+ ;; to something different doesn't change the mode, no matter what
+ ;; the filename. This is a hack so that write-file will work:
+ ;; otherwise Emacs insists (as of XEmacs 21.1.9 at least) on
+ ;; re-setting the mode, which leads to problems with synchronization
+ ;; and losing extents. (Attempt to catch this in proof-mode by
+ ;; looking for active scripting buffer fails; perhaps because of
+ ;; kill buffer function)
+ (put major-mode 'mode-class 'pg-sticky)
+
+ ;; First, define some values if they aren't defined already.
+ (unless proof-mode-for-script
+ (setq proof-mode-for-script major-mode))
+
+ (if (and proof-non-undoables-regexp
+ (not proof-ignore-for-undo-count))
+ (setq proof-ignore-for-undo-count
+ proof-non-undoables-regexp))
+
+ ;; Give warnings if some crucial settings haven't been made
+ (dolist (sym proof-script-important-settings)
+ (proof-warn-if-unset "proof-config-done" sym))
+
+ ;; Additional key definitions which depend on configuration for
+ ;; specific proof assistant.
+ ;; FIXME da: generalize here. Might have electric terminator for
+ ;; other parsing mechanisms too, using new proof-script-parse-function
+ ;; Could use a default terminal char
+ (if proof-terminal-char
+ (progn
+ (define-key proof-mode-map
+ (vconcat [(control c)] (vector proof-terminal-char))
+ 'proof-electric-terminator-toggle)
+ (define-key proof-mode-map (vector proof-terminal-char)
+ 'proof-electric-terminator)))
+ ;; It's ugly, but every script buffer has a local copy changed in
+ ;; sync by the fn proof-electric-terminator-enable
+ (setq proof-electric-terminator proof-electric-terminator-enable)
+
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'proof-indent-line)
+
+ ;; Toolbar and scripting menu
+ ;; NB: autloads proof-toolbar, which defines proof-toolbar-scripting-menu.
+ (proof-toolbar-setup)
+
+ ;; Menus: the Proof-General and the specific menu
+ (proof-menu-define-main)
+ (proof-menu-define-specific)
+ (easy-menu-add proof-mode-menu proof-mode-map)
+ (easy-menu-add proof-assistant-menu proof-mode-map)
+
+ ;; Choose parsing mechanism according to different kinds of script syntax.
+ ;; Choice of function depends on configuration setting.
+ (unless (fboundp 'proof-segment-up-to)
+ (if proof-script-use-old-parser
+ ;; Configuration for using old parsing mechanism.
+ (cond
+ (proof-script-parse-function ;; still allowed to override in 3.2
+ (defalias 'proof-segment-up-to 'proof-segment-up-to-parser))
+ ;; 3.2 mechanism here
+ (proof-script-command-start-regexp
+ (defalias 'proof-segment-up-to 'proof-segment-up-to-cmdstart))
+ (t
+ (defalias 'proof-segment-up-to 'proof-segment-up-to-cmdend)
+ (unless proof-script-command-end-regexp
+ (proof-warn-if-unset "proof-config-done" 'proof-terminal-char)
+ (setq proof-script-command-end-regexp
+ (if proof-terminal-char
+ (regexp-quote (char-to-string proof-terminal-char))
+ "$")))))
+ ;; Configuration for using new parsing (3.3 and later; default in 3.5)
+ (progn
+ (defalias 'proof-segment-up-to 'proof-segment-up-to-parser)
+ (cond
+ (proof-script-parse-function
+ ;; already set, nothing to do
+ )
+ (proof-script-sexp-commands
+ (setq proof-script-parse-function 'proof-script-generic-parse-sexp))
+ (proof-script-command-start-regexp
+ (setq proof-script-parse-function 'proof-script-generic-parse-cmdstart))
+ ((or proof-script-command-end-regexp proof-terminal-char)
+ (setq proof-script-parse-function 'proof-script-generic-parse-cmdend)
+ (unless proof-script-command-end-regexp
+ (proof-warn-if-unset "probof-config-done" 'proof-terminal-char)
+ (setq proof-script-command-end-regexp
+ (if proof-terminal-char
+ (regexp-quote (char-to-string proof-terminal-char))
+ "$"))))
+ (t
+ (error "Configuration error: must set `proof-terminal-char' or one of its friends"))))
+ )) ; default if nothing set is EOL.
+
+ ;; Setup a default for imenu.
+ (unless (and (boundp 'imenu-generic-expression)
+ imenu-generic-expression)
+ (set (make-local-variable 'imenu-generic-expression)
+ (delq nil
+ (list
+ (if proof-goal-with-hole-regexp
+ (list nil proof-goal-with-hole-regexp
+ proof-goal-with-hole-result))
+ (if proof-save-with-hole-regexp
+ (list "Saves" proof-save-with-hole-regexp
+ proof-save-with-hole-result))))))
+
+ ;; Make sure func menu is configured. (NB: Ideal place for this and
+ ;; similar stuff would be in something evaluated at top level after
+ ;; defining the derived mode: normally we wouldn't repeat this
+ ;; each time the mode function is run, so we wouldn't need "pushnew").
+
+ (cond ((proof-try-require 'func-menu)
+ ;; FIXME: we'd like to let the library load later, but
+ ;; it's a bit tricky: this stuff doesn't seem to work
+ ;; in an eval-after-load body (local vars?).
+ (unless proof-script-next-entity-regexps ; unless already set
+ ;; Try to calculate a useful default value.
+ ;; FIXME: this is rather complicated! The use of the regexp
+ ;; variables needs sorting out.
+ (customize-set-variable
+ 'proof-script-next-entity-regexps
+ (let ((goal-discrim
+ ;; Goal discriminator searches forward for matching
+ ;; save if the regexp is set.
+ (if proof-goal-with-hole-regexp
+ (if proof-save-command-regexp
+ (list
+ proof-goal-with-hole-regexp 2
+ 'forward proof-save-command-regexp)
+ (list proof-goal-with-hole-regexp 2))))
+ ;; Save discriminator searches backward for matching
+ ;; goal if the regexp is set.
+ (save-discrim
+ (if proof-save-with-hole-regexp
+ (if proof-goal-command-regexp
+ (list
+ proof-save-with-hole-regexp 2
+ 'backward proof-goal-command-regexp)
+ (list proof-save-with-hole-regexp 2)))))
+ (cond
+ ((and proof-goal-with-hole-regexp proof-save-with-hole-regexp)
+ (list
+ (proof-regexp-alt
+ proof-goal-with-hole-regexp
+ proof-save-with-hole-regexp) goal-discrim save-discrim))
+
+ (proof-goal-with-hole-regexp
+ (list proof-goal-with-hole-regexp goal-discrim))
+
+ (proof-save-with-hole-regexp
+ (list proof-save-with-hole-regexp save-discrim))))))
+
+ (if proof-script-next-entity-regexps
+ ;; Enable func-menu for this mode if regexps set now
+ (progn
+ (pushnew
+ (cons major-mode 'proof-script-next-entity-regexps)
+ fume-function-name-regexp-alist)
+ (pushnew
+ (cons major-mode proof-script-find-next-entity-fn)
+ fume-find-function-name-method-alist)))))
+
+ ;; Offer to save script mode buffers which have no files,
+ ;; in case Emacs is exited accidently.
+ (or (buffer-file-name)
+ (setq buffer-offer-save t))
+
+ ;; Localise the invisibility glyph (XEmacs only):
+ (let ((img (proof-get-image "hiddenproof" t nil)))
+ (cond
+ ((and img proof-running-on-XEmacs)
+ (set-glyph-image invisible-text-glyph img (current-buffer)))))
+
+ ;; To begin with, make everything visible in the buffer
+ ;; (FIXME: this is done via proof-init-segmentation, now,
+ ;; when is that called?)
+ (setq buffer-invisibility-spec nil)
+
+ ;; Finally, make sure the user has been welcomed!
+ ;; [NB: this doesn't work well, gets zapped by loading messages]
+ (proof-splash-message))
+
+
+(provide 'proof-script)
+;; proof-script.el ends here.