Ticket #110: coq.el

File coq.el, 54.2 KB (added by Yves Bertot, 17 years ago)

file from the package sources, with my own corrections

Line 
1;;; coq.el --- Major mode for Coq proof assistant
2;; Copyright (C) 1994 - 1998 LFCS Edinburgh.
3;; Authors: Healfdene Goguen, Pierre Courtieu
4;; License:     GPL (GNU GENERAL PUBLIC LICENSE)
5;; Maintainer: Pierre Courtieu <Pierre.Courtieu@cnam.fr>
6
7;; coq.el,v 8.56 2006/12/13 14:10:36 pier Exp
8
9
10;;; Commentary:
11;;
12
13
14;;; History:
15;;
16
17(require 'proof)
18(require 'holes-load) ; in lib directory
19(require 'local-vars-list) ; in lib directory
20(require 'coq-local-vars) ; in coq directory
21;; coq-syntaxe is required below
22;; ----- coq-shell configuration options
23
24;;; Code:
25;; debugging functions
26;; (defun proofstack () (coq-get-span-proofstack (span-at (point) 'type)))
27;; End debugging
28
29(defcustom coq-prog-name 
30    "coqtop"
31;; On Windows with latest Coq package you might need something like:
32;;  "C:/Program Files/Coq/bin/coqtop.opt.exe"
33;; instead of just "coqtop".  See also coq-prog-env below.
34  "*Name of program to run as Coq. Important: See `proof-prog-name'."
35  :type 'string
36  :group 'coq)
37
38;; List of arguments to pass to Coq process.  Should contain -emacs.
39;; -translate will be added automatically to this list if `coq-translate-to-v8'
40;; is set.
41;; coq-prog-args is set by defpgcustom in proof-config
42(defcustom coq-prog-args '("-emacs") 
43  "The arguments passed to coqtop, important: see `proof-prog-name'.")
44
45(defcustom coq-compile-file-command "coqc %s"
46  "*Command to compile a coq file.
47This is called when `coq-auto-compile-vos' is set, unless a Makefile
48exists in the directory, in which case the `compile' command is run.
49To disable coqc being called (and use only make), set this to nil."
50  :type 'string
51  :group 'coq)
52
53;; Command to initialize the Coq Proof Assistant
54
55(defcustom coq-default-undo-limit 100
56  "Maximum number of Undo's possible when doing a proof."
57  :type 'number
58  :group 'coq)
59
60(defconst coq-shell-init-cmd 
61  (format "Set Undo %s . " coq-default-undo-limit))
62
63;; da 15/02/03: moved setting of coq-version-is-vX to coq-syntax to
64;; fix compilation
65
66(require 'coq-syntax)
67(require 'coq-indent)
68
69(defcustom coq-utf-safe nil 
70  "Should be t if one wants no multibyte characters be used for
71controling coq prompt. Only for coq >= 8.1 (and 8.1 beta)")
72(if (and coq-utf-safe coq-version-is-V8-1) (setq coq-prog-args '("-emacs-U"))
73  (setq coq-prog-args '("-emacs")))
74
75;; List of environment settings d to pass to Coq process.
76;; On Windows you might need something like:
77;; (setq coq-prog-env '("HOME=C:\\Program Files\\Coq\\"))
78(setq coq-prog-env nil)
79
80;; Command to reset the Coq Proof Assistant
81(defconst coq-shell-restart-cmd "Reset Initial.\n ")
82
83
84;; NB: da: PG 3.5: added \n here to account for blank line in Coq output.
85;; Better result for shrinking windows, grabbing shell output.
86;; Pierre added the infos in the prompt, this is new in Coq v8-1 (to be released last
87;; quarter of 2005).
88
89(defvar coq-shell-prompt-pattern 
90  (if coq-version-is-V8-0
91      (concat "\\(?:\n" proof-id " < \\)")
92    (concat "\\(?:\n" proof-id " < [^\n]+\371\\|\n<prompt>[^\n]+</prompt>\\)")
93    )
94    "*The prompt pattern for the inferior shell running coq.")
95;  (concat "^\n?" proof-id " < \\(?:[0-9]+ |\\(?:" proof-id "|?\\)*| " "[0-9]+ < \\)?\\(?:\x6\\|\371\\)")
96
97;; FIXME da: this was disabled (set to nil) -- why?
98;; da: 3.5: add experimetntal
99(defvar coq-shell-cd 
100  "Add LoadPath \"%s\"." ;; fixes unadorned Require (if .vo exists).
101  "*Command of the inferior process to change the directory.")
102
103(defvar coq-shell-abort-goal-regexp "Current goal aborted"
104  "*Regexp indicating that the current goal has been abandoned.")
105
106(defvar coq-shell-proof-completed-regexp "Subtree proved!"
107  "*Regular expression indicating that the proof has been completed.")
108
109(defvar coq-goal-regexp
110  "\\(============================\\)\\|\\(subgoal [0-9]+ is:\\)\n")
111
112
113
114;; Configuration
115
116(setq tags-always-exact t) ; Tags is unusable with Coq library otherwise:
117
118(defun coq-library-directory () 
119  (let ((c (substring (shell-command-to-string "coqtop -where") 0 -1 )))
120    (if (string-match c "not found")
121          "/usr/local/lib/coq"
122      c)))
123
124
125(defcustom coq-tags (concat (coq-library-directory) "/theories/TAGS")
126  "The default TAGS table for the Coq library."
127  :type 'string
128  :group 'coq)
129
130(defconst coq-interrupt-regexp "User Interrupt."
131  "Regexp corresponding to an interrupt.")
132
133;; ----- web documentation
134
135(defcustom coq-www-home-page "http://coq.inria.fr/"
136  "Coq home page URL."
137  :type 'string
138  :group 'coq)
139
140
141
142
143;; ----- outline
144
145(defvar coq-outline-regexp
146  (concat "(\\*\\|" (proof-ids-to-regexp 
147           '(
148"Tactic" "Axiom" "Parameter" "Parameters" "Variable" "Variables" "Syntax" "Grammar" "Syntactic" "Load" "Require" "Import" "Hint" "Hints" "Hypothesis" "Correctness" "Module" "Section" "Chapter" "Goal" "Lemma" "Theorem" "Fact" "Remark" "Record" "Inductive" "Mutual" "Definition" "Fixpoint" "Save" "Qed" "Defined" "End" "Coercion"))))
149
150(defvar coq-outline-heading-end-regexp "\\*\)\n\\|\\.\n")
151
152(defvar coq-shell-outline-regexp coq-goal-regexp)
153(defvar coq-shell-outline-heading-end-regexp coq-goal-regexp)
154
155
156;; all these are to be remove when coq > 8.0
157
158(defconst coq-kill-goal-command "Abort. ")
159(defconst coq-forget-id-command "Reset %s . ")
160(defconst coq-back-n-command "Back %s . ")
161
162;; some of them must kept when v8.1 because they are used by state preserving
163;; check when C-c C-v
164(defconst coq-state-preserving-tactics-regexp 
165  (proof-ids-to-regexp coq-state-preserving-tactics))
166(defconst coq-state-changing-commands-regexp
167  (proof-ids-to-regexp coq-keywords-state-changing-commands))
168(defconst coq-state-preserving-commands-regexp 
169  (proof-ids-to-regexp coq-keywords-state-preserving-commands))
170(defconst coq-commands-regexp 
171  (proof-ids-to-regexp coq-keywords-commands))
172(defvar coq-retractable-instruct-regexp 
173  (proof-ids-to-regexp coq-retractable-instruct))
174(defvar coq-non-retractable-instruct-regexp 
175  (proof-ids-to-regexp coq-non-retractable-instruct))
176
177; delete when no more support for 8.0 ?
178(defvar coq-keywords-section
179  '("Section" "Module\\s-+Type" "Declare\\s-+Module" "Module"))
180
181(defvar coq-section-regexp 
182  (concat "\\(" (proof-ids-to-regexp coq-keywords-section) "\\)")
183;  "\\(\\<Section\\>\\|\\<Module\\>\\s-+\\<Type\\>\\|\\<Module\\>\\)"
184)
185;; End of remove when coq > 8.0
186
187;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
188;;   Derived modes - they're here 'cos they define keymaps 'n stuff ;;
189;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
190
191(eval-and-compile 
192  (define-derived-mode coq-shell-mode proof-shell-mode
193    "coq-shell" nil
194    (coq-shell-mode-config)))
195 
196(eval-and-compile 
197  (define-derived-mode coq-response-mode proof-response-mode
198  "CoqResp" nil
199    (coq-response-config)))
200 
201(eval-and-compile
202  (define-derived-mode coq-mode proof-mode
203   "coq" nil
204   (coq-mode-config)))
205
206(eval-and-compile
207  (define-derived-mode coq-goals-mode proof-goals-mode
208    "CoqGoals" nil
209    (coq-goals-mode-config)))
210
211;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
212;;   Code that's coq specific                                      ;;
213;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
214
215(defun coq-set-undo-limit (undos)
216  (proof-shell-invisible-command (format "Set Undo %s . " undos)))
217
218;; da: have now combined count-undos and find-and-forget, they're the
219;; same now we deal with nested proofs and send general sequence
220;; "Abort. ... Abort. BackTo n. Undo n."
221;; pc: now we do even better: "Backtrack n m p. " based on infos in
222;; the prompt.
223
224;; All this is to be removed when coq > 8.0 (until the "End remove" below)
225
226(defconst coq-keywords-decl-defn-regexp
227  (proof-ids-to-regexp (append coq-keywords-decl coq-keywords-defn))
228  "Declaration and definition regexp.")
229
230(defun coq-proof-mode-p ()
231  "Allow to know if we are currentlly in proof mode.
232Look at the last line of the *coq* buffer to see if the prompt is the
233toplevel \"Coq <\".  Returns nil if yes.  This assumes that no
234\"Resume\" or \"Suspend\" command has been used."
235  (not (string-match "^Coq < " proof-shell-last-prompt)))
236
237
238;; DA: rewrote to combine behaviour with count-undos, to work with
239;; nested proofs.
240
241(defun coq-is-comment-or-proverprocp (span) 
242  (memq (span-property span 'type) '(comment proverproc)))
243(defun coq-is-goalsave-p (span) (eq (span-property span 'type) 'goalsave))
244(defun coq-is-module-equal-p (str) ;;cannot appear vith coq < 7.4
245  (and (proof-string-match "\\`\\(Declare\\s-\\)?\\s-*\\<Module\\>.*:=" str)
246       (not (proof-string-match "\\<with\\>" str))))
247(defun coq-is-def-p (str) 
248  (proof-string-match (concat "\\`Definition\\s-+\\(" proof-id "\\)\\s-*") str))
249(defun coq-is-decl-defn-p (str)
250  (proof-string-match 
251   (concat "\\`\\(" coq-keywords-decl-defn-regexp "\\)\\s-*\\(" 
252           proof-id "\\)\\s-*[\\[,:.]") str))
253
254(defun coq-state-preserving-command-p (str)
255  (proof-string-match (concat "\\`\\(" coq-state-preserving-commands-regexp "\\)") str))
256
257(defun coq-command-p (str)
258  (proof-string-match (concat "\\`\\(" coq-commands-regexp "\\)") str))
259
260(defun coq-state-preserving-tactic-p (str)
261  (proof-string-match 
262   (concat "\\`\\(" 
263           coq-state-preserving-tactics-regexp "\\)") str))
264
265(defun coq-state-changing-tactic-p (str) ; unknown things are considered (state
266                                        ;changing) tactics here
267  (and (not (coq-command-p str)) 
268       (not (coq-state-preserving-tactic-p str)))
269  )
270
271
272(defun coq-state-changing-command-p (str)
273  (proof-string-match (concat "\\`\\(" coq-state-changing-commands-regexp "\\)") str))
274
275; if no second id --> name of the module/section is (match-string 2 str)
276; otherwise it is (match-string 5 str)
277; to know if there is a second id: (match-string 2 str)="Type" ?
278; delete when no more support for 8.0
279(defun coq-section-or-module-start-p (str)
280  (proof-string-match 
281   (concat "\\`" coq-section-regexp 
282           "\\s-+\\(" proof-id "\\)\\(\\s-*\\(" proof-id "\\)\\)?") str)) 
283
284;; End of remove when coq > 8.0
285
286
287;; make this non recursive?
288(defun build-list-id-from-string (s)
289  "Build a list of string from a string S of the form \"id1|id2|...|idn\"."
290  (if (or (not s) (string= s "")) '()
291    (let ((x (string-match (concat "\\(" coq-id-shy "\\)\\(?:|\\|\\'\\)\\(.*\\)") s)))
292      (if (not x) (error "cannot extract list of ids from string")
293        (cons (match-string 1 s)
294              (build-list-id-from-string (match-string 2 s))
295              )))
296    )
297  )
298
299;;New: using the statenumber inside the coq prompte to backtrack more easily
300;; This function returns
301(defun coq-last-prompt-info (s)
302  "Extract informations from the coq prompt S.  See `coq-last-prompt-info-safe'."
303  (let* ((lastprompt (or s (error "no prompt !!?")))
304         (regex 
305          (concat "\\(" coq-id-shy "\\) < \\([0-9]+\\) |\\(\\(?:" coq-id-shy
306                  "|?\\)*\\)| \\([0-9]+\\) < "))
307         (s (string-match regex lastprompt))
308         (lprf (match-string 3 lastprompt))
309         )
310    (cons (string-to-int (match-string 2 lastprompt))
311          (cons (string-to-int (match-string 4 lastprompt))
312                (cons (build-list-id-from-string lprf) nil)))
313    )
314  )
315
316
317(defun coq-last-prompt-info-safe ()
318  "Return a list with all informations from the last prompt.
319The list contains the state number, the proof stack depth, and the names of all
320pending proofs (in a list)."
321
322(coq-last-prompt-info proof-shell-last-prompt) )
323
324;; The state number we want to put in a span is the prompt number given *just before*
325;; the command was sent. This variable remembers this number and will be updated when
326;; used see coq-set-state-number. Initially 1 because Coq initial state has number 1.
327(defvar coq-last-but-one-statenum 1)
328;; same for proof stack depth
329(defvar coq-last-but-one-proofnum 1)
330;;same for pending proofs
331(defvar coq-last-but-one-proofstack '())
332
333(defun coq-get-span-statenum (span)
334  "Return the state number of the SPAN."
335  (span-property span 'statenum)
336  )
337
338(defun coq-get-span-proofnum (span)
339  "Return the proof number of the SPAN."
340  (span-property span 'proofnum)
341  )
342
343(defun coq-get-span-proofstack (span)
344  "Return the proof stack (names of pending proofs) of the SPAN."
345  (span-property span 'proofstack)
346  )
347
348(defun coq-set-span-statenum (span val)
349  "Set the state number of the SPAN to VAL."
350  (set-span-property span 'statenum val)
351  )
352
353(defun coq-get-span-goalcmd (span)
354  "Return the 'goalcmd flag of the SPAN."
355  (span-property span 'goalcmd)
356  )
357
358(defun coq-set-span-goalcmd (span val)
359  "Set the 'goalcmd flag of the SPAN to VAL."
360  (set-span-property span 'goalcmd val)
361  )
362
363(defun coq-set-span-proofnum (span val)
364  "Set the proof number of the SPAN to VAL."
365  (set-span-property span 'proofnum val)
366  )
367
368(defun coq-set-span-proofstack (span val)
369  "Set the proof stack (names of pending proofs) of the SPAN to VAL."
370  (set-span-property span 'proofstack val)
371  )
372
373(defun proof-last-locked-span () 
374  (save-excursion ;; didn't found a way to avoid buffer switching
375    (set-buffer proof-script-buffer)
376    (span-at (- (proof-locked-end) 1) 'type)
377    )
378  )
379
380;; Each time the state changes (hook below), (try to) put the state number in the
381;; last locked span (will fail if there is already a number which should happen when
382;; going back in the script).  The state number we put is not the last one because
383;; the last one has been sent by Coq *after* the change. We use
384;; `coq-last-but-one-statenum' instead and then update it.
385
386;;TODO update docstring and comment
387
388(defun coq-set-state-infos ()
389  "Set the last locked span's state number to the number found last time.
390This number is in the *last but one* prompt (variable `coq-last-but-one-statenum').
391If locked span already has a state number, thne do nothing. Also updates
392`coq-last-but-one-statenum' to the last state number for next time."
393  (if (and proof-shell-last-prompt proof-script-buffer)
394      ;; infos = promt infos of the very last prompt
395      ;; sp = last locked span, which we want to fill with prompt infos
396      (let ((sp    (proof-last-locked-span))
397            (infos (or (coq-last-prompt-info-safe) '(0 0 nil)))
398            )
399        (unless (or (not sp) (coq-get-span-statenum sp))
400          (coq-set-span-statenum sp coq-last-but-one-statenum))
401        (setq coq-last-but-one-statenum (car infos))
402        ;; set goalcmd property if this is a goal start
403        ;; (ie proofstack has changed and not a save cmd)
404        (unless 
405            (or (not sp) (equal (span-property sp 'type) 'goalsave)
406                (<= (length (car (cdr (cdr infos))))
407                    (length coq-last-but-one-proofstack)))
408          (coq-set-span-goalcmd sp t))
409        ;; testing proofstack=nil is not good here because nil is the empty list OR
410        ;; the no value, so we test proofnum as it is always set at the same time.
411        ;; This is why this test is done before the next one (which sets proofnum)
412        (unless (or (not sp) (coq-get-span-proofnum sp))
413          (coq-set-span-proofstack sp coq-last-but-one-proofstack))
414        (setq coq-last-but-one-proofstack (car (cdr (cdr infos))))
415        (unless (or (not sp) (coq-get-span-proofnum sp))
416          (coq-set-span-proofnum sp coq-last-but-one-proofnum))
417        (setq coq-last-but-one-proofnum (car (cdr infos)))
418        )
419    )
420  )
421
422;; This hook seems the one we want (if we are in V8.1 mode only). 
423;; WARNING! It is applied once after each command PLUS once before a group of
424;; commands is started
425(add-hook 'proof-state-change-hook 
426          '(lambda () (if coq-version-is-V8-1 (coq-set-state-infos))))
427
428(defun count-not-intersection (l notin)
429  "Return the number of elts of L that are not in NOTIN."
430 
431  (let ((l1 l) (l2 notin) (res 0))
432    (while l1
433      (if (member (car l1) l2) ()
434        (setq res (+ res 1))) ; else
435      (setq l1 (cdr l1)))
436    res
437    ))
438
439;; Simplified version of backtracking which uses state numbers, proof stack depth and
440;; pending proofs put inside the coq (> v8.1) prompt. It uses the new coq command
441;; "Backtrack". The prompt is like this:
442;;      state                        proof stack
443;;      num                           depth
444;;       __                              _
445;; aux < 12 |aux|SmallStepAntiReflexive| 4 < ù
446;; ^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^     ^
447;; usual           pending proofs           usual
448;;                                          special char
449;; exemple:
450;; to go (back) from 12 |lema1|lema2...|leman| xx
451;; to                8  |lemb1|lemb2...|lembm| 5
452;; we must do "Backtrack 8 5 naborts"
453;; where naborts is the number of lemais that are not lembis
454
455;; Rem: We could deal with suspend and resume with more work. We would need a new coq
456;; command, because it is better to backtrack with *one* command (because
457;; proof-change-hook used above is not exactly called at right times).
458
459(defun  coq-find-and-forget-v81 (span)
460  "Backtrack to SPAN.  Using the \"Backtrack n m p\" coq command."
461  (let* (ans (naborts 0) (nundos 0)
462            (proofdepth (coq-get-span-proofnum span))
463            (proofstack (coq-get-span-proofstack span))
464            (span-staten (coq-get-span-statenum span))
465            (naborts (count-not-intersection coq-last-but-one-proofstack proofstack))
466            )
467    (setq ans
468          (if (and ; this is more efficient as backtrack x y z may be slow
469               (equal coq-last-but-one-proofstack proofstack)
470               (= coq-last-but-one-proofnum proofdepth)
471               (= coq-last-but-one-statenum span-staten))
472              ""
473            (format "Backtrack %s %s %s . " 
474                    (int-to-string span-staten)
475                    (int-to-string proofdepth)
476                    naborts)))
477    (if (string-equal ans "") proof-no-command ; not here because if
478      ;; we backtrack a state preserving command, we must do
479      ;; *nothing*, not even a CR (in > v74, no prompt is returned
480      ;; with "\n")
481      ans)
482    )
483  )
484
485;; to be removed when coq > 8.0
486
487(defun coq-find-and-forget-v80 (span)
488  (let (str ans (naborts 0) (nbacks 0) (nundos 0))
489    (while (and span (not ans))
490      (setq str (span-property span 'cmd))
491      (cond
492       ((coq-is-comment-or-proverprocp span)) ; do nothing
493
494       ;; Module <Type> T ... :=... . inside proof ->  like Definition...:=... 
495       ;; (should actually be disallowed in coq)
496       ; Should go in last cond, but I have a problem trying to avoid next cond to match.
497       ((and (coq-proof-mode-p) (coq-is-module-equal-p str)) (incf nbacks))
498
499       ;; FIXME: combine with coq-keywords-decl-defn-regexp case below?
500       ;; [ Maybe not: Section is being treated as a _goal_ command
501       ;;   now, so this test has to appear before the goalsave ]
502       ((coq-section-or-module-start-p str)
503        (if (equal (match-string 2 str) "Type") ;Module Type id: take the third word
504            (setq ans (format coq-forget-id-command (match-string 5 str)))
505          (setq ans (format coq-forget-id-command (match-string 2 str))))
506        ;; If we're resetting to beginning of a section from inside, need to fix the
507        ;; nesting depth.  FIXME: this is not good enough: the scanning loop exits
508        ;; immediately but Reset has implicit Aborts which are not being counted
509        ;; here.  Really we need to set the "master reset" command which subsumes the
510        ;; others, but still count the depth.
511        (unless (coq-is-goalsave-p span) (decf proof-nesting-depth)))
512
513       ((coq-is-goalsave-p span)
514        ;; da: try using just Back since "Reset" causes loss of proof state.
515        (if (span-property span 'nestedundos)
516            ;; Pierre: more robust when some unknown commands are not well backtracked
517            (if (and (= (span-property span 'nestedundos) 0) (not (coq-proof-mode-p))) 
518                (setq ans (format coq-forget-id-command (span-property span 'name)))
519              (setq nbacks (+ 1 nbacks (span-property span 'nestedundos))))))
520       
521       ;; Unsaved goal commands: each time we hit one of these
522       ;; we need to issue Abort to drop the proof state.
523       ((coq-goal-command-str-p str) (incf naborts)) ; FIX: nundos<-0 ?
524
525       ;; If we are already outside a proof, issue a Reset.  [ improvement would be
526       ;; to see if the undoing will take us outside a proof, and use the first Reset
527       ;; found if so: but this is tricky to co-ordinate with the number of Backs,
528       ;; perhaps? ]
529       ((and (not (coq-proof-mode-p));; (eq proof-nesting-depth 0)
530             (coq-is-decl-defn-p str))
531        (setq ans (format coq-forget-id-command (match-string 2 str))))
532                 
533       ;; Outside a proof: cannot be a tactic, if unknown: do back
534       ;; (we may decide otherwise, it is false anyhow, use elisp
535       ;; vars instead for the perfect thing).
536       ((and (not (coq-proof-mode-p)) (not (coq-state-preserving-command-p str)))
537        (incf nbacks))
538
539       ;; inside a proof: if known command then back if necessary, nothing otherwise,
540       ;; if known "need not undo tactic" then nothing; otherwise : undo (unknown
541       ;; tactics are considered needing undo, use elisp vars for the 1% remaining).
542       ;; no undo if abort
543       ((coq-proof-mode-p)
544        (cond 
545         ((coq-state-changing-command-p str) (incf nbacks))
546         ((and (eq 0 naborts)
547               (not (coq-state-preserving-command-p str))
548               (not (coq-state-preserving-tactic-p str)))
549          (incf nundos)))))
550      (setq span (next-span span 'type)))
551
552    ;; Now adjust proof-nesting depth according to the number of proofs we've passed
553    ;; out of.  FIXME: really this adjustment should be on the successful completion
554    ;; of the Abort commands, as a callback.
555    (setq proof-nesting-depth (- proof-nesting-depth naborts))
556
557    (setq ans
558          (concat
559           (if (stringp ans) ans)
560           (if (> naborts 0)
561               ;; ugly, but blame Coq
562               (let ((aborts "Abort. "))
563                 (while (> (decf naborts) 0)
564                   (setq aborts (concat "Abort. " aborts)))
565                 aborts))
566           (if (> nbacks 0)
567               (concat "Back " (int-to-string nbacks) ". "))
568           (if (> nundos 0) 
569               (concat "Undo " (int-to-string nundos) ". "))))
570
571    (if (null ans) proof-no-command;; FIXME: this is an error really (assert nil);
572      (if (string-equal ans "") proof-no-command ; not here because if
573                                                 ; we backtrack a state preserving command, we must do
574                                                 ; *nothing*, not even a CR (in v74, no prompt is returned
575                                                 ; with "\n")
576      ans))))
577
578;; end of remove when coq > 8.0
579
580;; I don't like this but it make compilation possible
581;; when > 8.0: adapt
582(defun coq-find-and-forget (span)
583  "Go back to SPAN.
584This function calls `coq-find-and-forget-v81' or `coq-find-and-forget-v80'
585depending on the variables `coq-version-is-V8-1' and `coq-version-is-V8-0', if
586none is set, then it default to `coq-find-and-forget-v81' but this should not
587happen since one of them is necessarily set to t in coq-syntax.el."
588 (cond 
589  (coq-version-is-V8-1 (coq-find-and-forget-v81 span))
590  (coq-version-is-V8-0 (coq-find-and-forget-v80 span))
591  (t (coq-find-and-forget-v81 span)) ;; should not happen
592  )
593 ) 
594
595(defvar coq-current-goal 1
596  "Last goal that Emacs looked at.")
597
598(defun coq-goal-hyp ()
599  (cond 
600   ((looking-at "============================\n")
601    (goto-char (match-end 0))
602    (cons 'goal (int-to-string coq-current-goal)))
603   ((looking-at "subgoal \\([0-9]+\\) is:\n")
604    (goto-char (match-end 0))
605    (cons 'goal (match-string 1))
606    (setq coq-current-goal (string-to-int (match-string 1))))
607   ((looking-at proof-shell-assumption-regexp)
608    (cons 'hyp (match-string 1)))
609   (t nil)))
610
611(defun coq-state-preserving-p (cmd)
612;  (or
613   (proof-string-match coq-non-retractable-instruct-regexp cmd)) 
614;   (and
615;    (not (proof-string-match coq-retractable-instruct-regexp cmd))
616;    (or
617;     (message "Unknown command, hopes this won't desynchronize ProofGeneral")
618;     t))))
619
620;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
621;;   Commands specific to coq                                       ;;
622;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
623
624
625
626(defconst notation-print-kinds-table 
627  '(("Print Scope(s)" 0) ("Print Visibility" 1))
628  "Enumerates the different kinds of notation information one can ask to coq.")
629
630(defun coq-PrintScope ()
631  "Show information on notations. Coq specific."
632  (interactive)
633  (let* 
634      ((mods 
635        (completing-read "Infos on notation (tab to see list): " 
636                         notation-print-kinds-table))
637       (s (read-string  "Name (empty for all): "))
638       (all (string-equal s "")))
639    (cond 
640     ((and (string-equal mods "Print Scope(s)") (string-equal s "")) 
641      (proof-shell-invisible-command (format "Print Scopes.")))
642     (t 
643      (proof-shell-invisible-command (format "%s %s ." mods s)))
644     )
645    )
646  )
647
648
649(defun coq-guess-or-ask-for-string (s &optional dontguess)
650  (let ((guess
651         (and (not dontguess)
652         (if (region-exists-p) 
653             (buffer-substring-no-properties (region-beginning) (region-end))
654           (thing-at-point 'word)))))
655    (read-string 
656     (if guess (concat s " (" guess "): ") (concat s " : "))
657     nil 'proof-minibuffer-history guess)))
658
659
660(defun coq-ask-do (ask do &optional dontguess postformatcmd)
661  "Ask for an ident and print the corresponding term."
662  (let* ((cmd) (postform (if (eq postformatcmd nil) 'identity postformatcmd)))
663    (proof-shell-ready-prover) 
664    (setq cmd (coq-guess-or-ask-for-string ask dontguess))
665    (proof-shell-invisible-command
666     (format (concat do " %s . ") (funcall postform cmd)))) 
667  )
668
669(defun coq-SearchIsos ()
670  "Search a term whose type is isomorphic to given type.
671This is specific to `coq-mode'."
672  (interactive)
673  (coq-ask-do "SearchPattern (parenthesis mandatory), ex: (?X1 + _ = _ + ?X1)" "SearchPattern" nil))
674
675(defun coq-SearchConstant ()
676  (interactive)
677  (coq-ask-do "Search constant" "Search" nil))
678
679(defun coq-SearchRewrite ()
680  (interactive)
681  (coq-ask-do "Search Rewrite :" "SearchRewrite" nil))
682
683(defun coq-SearchAbout ()
684  (interactive)
685  (coq-ask-do "Search About :" "SearchAbout" nil))
686
687(defun coq-Print () "Ask for an ident and print the corresponding term."
688  (interactive)
689  (coq-ask-do "Print:" "Print"))
690
691(defun coq-About () "Ask for an ident and print information on it."
692  (interactive)
693  (coq-ask-do "About:" "About"))
694
695(defun coq-LocateConstant ()
696  "Locate a constant.
697This is specific to `coq-mode'."
698  (interactive)
699  (coq-ask-do "Locate :" "Locate"))
700
701(defun coq-LocateLibrary ()
702  "Locate a constant.
703This is specific to `coq-mode'."
704  (interactive)
705  (coq-ask-do "Locate Library" "Locate Library"))
706
707(defun coq-addquotes (s) (concat "\"" s "\""))
708
709(defun coq-LocateNotation ()
710  "Locate a notation.
711This is specific to `coq-mode'."
712  (interactive)
713  (coq-ask-do "Locate Notation (ex: \'exists\' _ , _)" "Locate" 
714              t 'coq-addquotes))
715
716(defun coq-Pwd ()
717  "Locate a notation.
718This is specific to `coq-mode'."
719  (interactive)
720  (proof-shell-invisible-command "Pwd."))
721
722(defun coq-Inspect ()
723  (interactive)
724  (coq-ask-do "Inspect how many objects back?" "Inspect" t))
725
726(defun coq-PrintSection()
727  (interactive)
728  (coq-ask-do "Print Section " "Print Section" t))
729
730(defun coq-Print-implicit ()
731  "Ask for an ident and print the corresponding term."
732  (interactive)
733  (coq-ask-do "Print Implicit: " "Print Implicit"))
734
735(defun coq-Check ()
736  "Ask for a term and print its type."
737  (interactive)
738  (coq-ask-do "Check: " "Check"))
739
740(defun coq-Show ()
741  "Ask for a number i and show the ith goal."
742  (interactive)
743  (coq-ask-do "Show goal number: " "Show" t))
744
745
746(proof-definvisible coq-PrintHint "Print Hint. ")
747
748;; Items on show menu
749(proof-definvisible coq-show-tree "Show Tree.")
750(proof-definvisible coq-show-proof "Show Proof.")
751(proof-definvisible coq-show-conjectures "Show Conjectures.")
752(proof-definvisible coq-show-intros "Show Intros.") ; see coq-insert-intros below
753
754
755(defun coq-PrintHint ()
756  "Print all hints applicable to the current goal."
757  (interactive)
758  (proof-shell-invisible-command "Print Hint. "))
759
760
761
762
763(defun coq-Compile ()
764  "Compiles current buffer."
765  (interactive)
766  (let* ((n (buffer-name))
767         (l (string-match ".v" n)))
768    (compile (concat "make " (substring n 0 l) ".vo"))))
769
770
771
772
773;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
774;;    To guess the command line options   ;;
775;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
776(defun coq-guess-command-line (filename)
777  "Guess the right command line options to compile FILENAME using `make -n'."
778  (let ((dir (file-name-directory filename)))
779    (if (file-exists-p (concat dir "Makefile"))
780        (let* 
781            ((compiled-file (concat (substring 
782                                     filename 0 
783                                     (string-match ".v$" filename)) ".vo"))
784             (command (shell-command-to-string 
785                       (concat  "cd " dir ";"
786                                "gmake -n -W " filename " " compiled-file
787                                "| sed s/coqc/coqtop/"))))
788          (concat 
789           (substring command 0 (string-match " [^ ]*$" command))
790           (if coq-utf-safe '("-emacs-U") '("-emacs"))))
791      coq-prog-name)))
792
793
794;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
795;;   Coq shell startup and exit hooks                               ;;
796;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
797
798(defun coq-pre-shell-start ()
799  (setq proof-prog-name (concat coq-prog-name
800                                (if coq-translate-to-v8 " -translate")))
801  (setq proof-mode-for-shell    'coq-shell-mode)
802  (setq proof-mode-for-goals    'coq-goals-mode)
803  (setq proof-mode-for-response 'coq-response-mode)
804  )
805
806;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
807;;   Configuring proof and pbp mode and setting up various utilities  ;;
808;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
809
810(defun coq-mode-config ()
811  ;; Coq error messages are thrown off by TAB chars.
812  (set (make-local-variable 'indent-tabs-mode) nil)
813  (setq proof-terminal-char ?\.)
814  (setq proof-script-command-end-regexp 
815        "\\(?:[^.]\\|\\(?:\\.\\.\\)\\)\\.\\(\\s-\\|\\'\\)")
816  (setq proof-script-comment-start "(*")
817  (setq proof-script-comment-end "*)")
818  (setq proof-unnamed-theorem-name "Unnamed_thm") ; Coq's default name
819
820  (setq proof-assistant-home-page coq-www-home-page)
821
822  (setq proof-mode-for-script 'coq-mode)
823
824  (setq proof-guess-command-line 'coq-guess-command-line)
825
826  ;; Commands sent to proof engine
827  (setq proof-showproof-command "Show. "
828        proof-context-command "Print All. "
829        proof-goal-command "Goal %s . "
830        proof-save-command "Save %s . "
831        proof-find-theorems-command "Search %s . ")
832;; FIXME da: Does Coq have a help or about command?
833;;      proof-info-command "Help"
834
835;; 3.4: this is no longer used: setting to nil
836;; enforces use of find-and-forget always.
837  (setq proof-kill-goal-command nil)
838
839  (setq proof-goal-command-p 'coq-goal-command-p
840        proof-find-and-forget-fn 'coq-find-and-forget
841        pg-topterm-goalhyp-fn 'coq-goal-hyp
842        proof-state-preserving-p 'coq-state-preserving-p
843        )
844
845  ;; This one to deal with nested comments in xemacs
846  (if (string-match "XEmacs" emacs-version)
847      (setq proof-script-parse-function 'coq-parse-function)
848      )
849
850  (setq proof-save-command-regexp coq-save-command-regexp
851        proof-really-save-command-p 'coq-save-command-p ;pierre:deals with Proof <term>.
852        proof-save-with-hole-regexp coq-save-with-hole-regexp
853        proof-goal-with-hole-regexp coq-goal-with-hole-regexp
854        proof-nested-undo-regexp coq-state-changing-commands-regexp
855  proof-script-imenu-generic-expression coq-generic-expression
856  )
857 
858  (setq 
859;indentation is implemented in coq-indent.el
860;   proof-indent-enclose-offset  (- proof-indent)
861;   proof-indent-enclose-offset 0
862;   proof-indent-close-offset 0
863;   proof-indent-open-offset 0
864   proof-indent-any-regexp      coq-indent-any-regexp
865;   proof-indent-inner-regexp    coq-indent-inner-regexp
866;   proof-indent-enclose-regexp  coq-indent-enclose-regexp
867   proof-indent-open-regexp     coq-indent-open-regexp
868   proof-indent-close-regexp    coq-indent-close-regexp
869   )
870
871  (make-local-variable 'indent-region-function)
872  (setq indent-region-function 'coq-indent-region)
873
874
875  ;; span menu
876  (setq proof-script-span-context-menu-extensions 'coq-create-span-menu)
877
878  (setq proof-shell-start-silent-cmd "Set Silent. "
879        proof-shell-stop-silent-cmd "Unset Silent. ")
880
881  (coq-init-syntax-table)
882  ;(setq comment-quote-nested nil) ;; we can cope with nested comments
883  (set (make-local-variable 'comment-quote-nested) nil) ;; we can cope with nested comments
884
885  ;; font-lock
886  (setq font-lock-keywords coq-font-lock-keywords-1)
887  ;;holes
888  (holes-mode 1)
889
890  (proof-config-done)
891
892  ;; outline
893  (make-local-variable 'outline-regexp)
894  (setq outline-regexp coq-outline-regexp)
895
896  (make-local-variable 'outline-heading-end-regexp)
897  (setq outline-heading-end-regexp coq-outline-heading-end-regexp)
898
899  ;; tags
900  (and (boundp 'tag-table-alist)
901       (setq tag-table-alist
902             (append '(("\\.v$" . coq-tags)
903                       ("coq"  . coq-tags))
904                     tag-table-alist)))
905
906;  (setq blink-matching-paren-dont-ignore-comments t)
907  (set (make-local-variable 'blink-matching-paren-dont-ignore-comments) t)
908
909  ;; multiple file handling
910  (setq proof-cannot-reopen-processed-files t
911        ;; proof-shell-inform-file-retracted-cmd 'coq-retract-file
912        proof-shell-require-command-regexp coq-require-command-regexp
913        proof-done-advancing-require-function 'coq-process-require-command)
914
915  ;; hooks and callbacks
916  (add-hook 'proof-pre-shell-start-hook 'coq-pre-shell-start nil t)
917  ;; FIXME: PG 3.5, next one shouldn't be needed but setting is
918  ;; now lost in define-derived-mode for some reason.
919  (add-hook 'proof-activate-scripting-hook 'proof-cd-sync nil t)
920  (add-hook 'proof-deactivate-scripting-hook 'coq-maybe-compile-buffer nil t))
921 
922
923
924(defun coq-shell-mode-config ()
925  (setq 
926   proof-shell-prompt-pattern coq-shell-prompt-pattern
927   proof-shell-cd-cmd coq-shell-cd
928   proof-shell-filename-escapes '(("\\\\" . "\\\\") ("\""   . "\\\""))
929   proof-shell-abort-goal-regexp coq-shell-abort-goal-regexp
930   proof-shell-proof-completed-regexp coq-shell-proof-completed-regexp
931   proof-shell-error-regexp coq-error-regexp
932   proof-shell-interrupt-regexp coq-interrupt-regexp
933   proof-shell-assumption-regexp coq-id
934   pg-subterm-first-special-char ?\360
935   proof-shell-wakeup-char nil ;?\x6 ; ?\371 ; done: prompt
936   ;; The next three represent path annotation information
937   pg-subterm-start-char ?\372 ; not done
938   pg-subterm-sep-char ?\373 ; not done
939   pg-subterm-end-char ?\374 ; not done
940   pg-topterm-char ?\375 ; done
941   proof-shell-eager-annotation-start "\376\\|\\[Reinterning"
942   proof-shell-eager-annotation-start-length 12
943   proof-shell-eager-annotation-end "\377\\|done\\]" ; done
944   proof-shell-annotated-prompt-regexp proof-shell-prompt-pattern
945   proof-shell-result-start "\372 Pbp result \373"
946   proof-shell-result-end "\372 End Pbp result \373"
947   proof-shell-start-goals-regexp "[0-9]+ subgoals?"
948   proof-shell-end-goals-regexp proof-shell-annotated-prompt-regexp
949   proof-shell-init-cmd  ; (concat
950                         coq-shell-init-cmd
951                         ; Coq has no global settings?
952                         ; (proof-assistant-settings-cmd))
953   proof-shell-restart-cmd coq-shell-restart-cmd
954   pg-subterm-anns-use-stack t)
955 
956  (coq-init-syntax-table)
957  (setq font-lock-keywords coq-font-lock-keywords-1)
958  (holes-mode 1)
959  (proof-shell-config-done))
960
961(defun coq-goals-mode-config ()
962  (setq pg-goals-change-goal "Show %s . ")
963  (setq pg-goals-error-regexp coq-error-regexp)
964  (coq-init-syntax-table)
965  (setq font-lock-keywords coq-font-lock-keywords-1)
966  (holes-mode 1)
967  (proof-goals-config-done))
968
969(defun coq-response-config ()
970   (coq-init-syntax-table)
971   (setq font-lock-keywords coq-font-lock-keywords-1)
972   (holes-mode 1)
973   (proof-response-config-done))
974
975
976;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
977;;
978;; Multiple file handling
979;;
980;; This is an imperfect attempt.  It really needs prover assistance.
981;;
982;; Experimental support for multiple files was first added
983;; for Coq 6.X based on discussions at TYPES 2000 between DA and PC.
984;; Updated and simplified for Coq 8, PG 3.5 (22.04.04) by DA.
985
986;; First note that coq-shell-cd is sent whenever we activate scripting,
987;; it extends the loadpath with the current directory.
988
989;; When scripting is turned off, we compile the file to update the .vo.
990(defun coq-maybe-compile-buffer ()
991  "If the current buffer is completely processed, maybe compile it.
992The attempt is made if `coq-auto-compile-vos' is non-nil.
993This is a value for the `proof-deactivate-scripting-hook'."
994  (if (and coq-auto-compile-vos
995           (proof-locked-region-full-p)
996           buffer-file-name)
997      (progn
998        ;; FIXME: in PG 4, this next step will be done inside
999        ;; proof-register-possibly-new-processed-file.
1000        ;; NB: we might save dependent buffers too!
1001        (ignore-errors 
1002          (proof-save-some-buffers (list buffer)))
1003        ;; Now re-compile.
1004        ;; Coq users like Make, let's see if we have a Makefile
1005        ;; here and choose compile.
1006        (cond
1007         ((and (proof-try-require 'compile) 
1008               compile-command 
1009               (file-exists-p "Makefile"))
1010          ;; NB: compilation is run in the background
1011          ;; in this case!
1012          (let ((compilation-read-command nil))
1013            (call-interactively 'compile)))
1014         (coq-compile-file-command
1015          (message "Compiling %s..." buffer-file-name)
1016          (shell-command 
1017           ;; Could be run in the background here if we
1018           ;; added & to the end of the command.
1019           (format coq-compile-file-command buffer-file-name)))))))
1020
1021
1022;; Dependency management 1: when a buffer is retracted, we also
1023;; need to retract any children buffers.
1024;; To do that, we run coqdep on each of the processed files,
1025;; and see whether the buffer being retracted is an ancestor.
1026
1027(defun coq-ancestors-of (filename)
1028  "Return ancestor .v files of RFILENAME.
1029This is based on the output of coqtop FILENAME.
1030Currently this doesn't take the loadpath into account."
1031  ;; FIXME: is there any way to bring in the load path here in coqdep?
1032  ;; We might use Coq's "Locate File string." command to help.
1033  (let* ((filedir   (file-name-directory filename))
1034         (cdline    (shell-command-to-string 
1035                     (format "coqdep -I %s %s" filedir filename)))
1036         (matchdeps (string-match ": \\(.*\\)$" cdline))
1037         (deps      (and matchdeps
1038                         (split-string (match-string 1 cdline)))))
1039    (mapcar 
1040     ;; normalise to include directories, defaulting
1041     ;; to same dir.  Change .vo -> v
1042     (lambda (file)
1043       (concat 
1044        (if (file-name-directory file) "" filedir)
1045        (file-name-sans-extension file) ".v"))
1046     ;; first dep is vacuous: file itself
1047     (cdr-safe deps))))
1048
1049;; FIXME: memoise here
1050(defun coq-all-ancestors-of (filename)
1051  "Return transitive closure of `coq-ancestors-of'."
1052  (let ((ancs   (coq-ancestors-of filename))
1053        all)
1054    (dolist (anc ancs)
1055      (setq all (union (cons anc
1056                             (coq-all-ancestors-of anc))
1057                       all
1058                       :test 'string-equal)))
1059    all))
1060
1061;; FIXME: add other cases, move to coq-syntax
1062(defconst coq-require-command-regexp 
1063  (concat "Require\\s-+\\(" proof-id "\\)") 
1064  "Regular expression matching Require commands in Coq.
1065Group number 1 matches the name of the library which is required.")
1066
1067(defun coq-process-require-command (span cmd)
1068  "Calculate the ancestors of a loaded file and lock them."
1069  ;; FIXME todo
1070  )
1071
1072(defun coq-included-children (filename)
1073  "Return a list of children of FILENAME on `proof-included-files-list'."
1074  (let (children)
1075    (dolist (incf proof-included-files-list)
1076      ;; Compute all ancestors transitively: could be expensive
1077      ;; if we have a lot of included files with many ancestors.
1078      (let ((ancestors (coq-all-ancestors-of incf)))
1079        (if (member filename ancestors)
1080            (setq children (cons incf children)))))
1081    children))
1082
1083
1084;; Dependency management 2: when a "Require " is executed,
1085;; PG should lock all files whose .vo's are loaded.
1086;; This would be easy if Coq would output some handy
1087;; messages tracking dependencies in .vo's as it loads
1088;; those files.   But it doesn't.
1089;; FIXME: to do this we'll need to watch the
1090;; Require commands ourselves, and then *lock* their
1091;; ancestors. TBD.
1092
1093(defun coq-process-file (rfilename)
1094  "Adjust the value of `proof-included-files-list' when processing RFILENAME."
1095  (if coq-auto-compile-vos
1096      (progn
1097        (add-to-list proof-included-files-list rfilename)
1098        ;; recursively call on ancestors: we hope coqdep doesn't give loop!
1099        (coq-process-file (coq-ancestors-of rfilename)))))
1100
1101;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1102;;
1103;; Flags and other settings for Coq.
1104;; These appear on the Coq -> Setting menu.
1105;;
1106
1107; FIXME da: we should send this command only inside a proof,
1108; otherwise it gives an error message.  It should be on
1109; a different menu command.
1110;(defpacustom print-only-first-subgoal  nil
1111;  "Whether to just print the first subgoal in Coq."
1112;  :type 'boolean
1113;  :setting ("Focus. " . "Unfocus. "))
1114
1115
1116;; FIXME: to handle "printing all" properly, we should change the state
1117;; of the variables that also depend on it.
1118(defpacustom print-fully-explicit nil
1119  "*Print fully explicit terms."
1120  :type 'boolean
1121  :setting ("Set Printing All. " . "Unset Printing All. "))
1122
1123(defpacustom print-implicit nil
1124  "*Print implicit arguments in applications."
1125  :type 'boolean
1126  :setting ("Set Printing Implicit. " . "Unset Printing Implicit. "))
1127
1128(defpacustom print-coercions nil
1129  "*Print coercions."
1130  :type 'boolean
1131  :setting ("Set Printing Coercions. " . "Unset Printing Coercions. "))
1132
1133(defpacustom print-match-wildcards t
1134  "*Print underscores for unused variables in matches."
1135  :type 'boolean
1136  :setting ("Set Printing Wildcard. " . "Unset Printing Wildcard. "))
1137
1138(defpacustom print-elim-types t
1139  "*Print synthesised result type for eliminations."
1140  :type 'boolean
1141  :setting ("Set Printing Synth. " . "Unset Printing Synth. "))
1142
1143(defpacustom printing-depth 50
1144  "*Depth of pretty printer formatting, beyond which dots are displayed."
1145  :type 'integer
1146  :setting "Set Printing Depth %i . ")
1147
1148(defpacustom time-commands nil
1149  "*Whether to display timing information for each command."
1150  :type 'boolean)
1151
1152(defpacustom auto-compile-vos nil
1153  "Whether to automatically compile vos and track dependencies."
1154  :type 'boolean)
1155
1156;; old adjustments:
1157;;  :eval (if coq-auto-compile-vos
1158;            (setq proof-shell-process-file
1159;                  coq-proof-shell-process-file
1160;                  proof-shell-inform-file-retracted-cmd
1161;                  coq-proof-shell-inform-file-retracted-cmd)
1162;          (setq proof-shell-inform-file-processed-cmd nil
1163;                proof-shell-process-file nil
1164;                proof-shell-inform-file-retracted-cmd nil)))
1165
1166;; da: what a shame -translate is a command line flag and not a
1167;; command in Coq. Otherwise we could enable/disable interactively. 
1168;; As it is, this setting will only work between restarts.
1169;; Moreover, if we had collaborated on this we could easily have
1170;; implemented a hook to translate automatically in PG with some
1171;; extra markup.  Scanning the whitespace as formatted presently
1172;; is messy.
1173
1174(defpacustom translate-to-v8 nil
1175  "*Whether to use -translate argument to Coq"
1176  :type 'boolean)
1177
1178;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1179;;
1180;; Pre-processing of input string  (PG 3.5)
1181;;
1182
1183(defun coq-preprocessing () ;; NB: dynamic scoping of `string'
1184  (cond
1185   (coq-time-commands
1186    (setq string (concat "Time " string)))))
1187     
1188(add-hook 'proof-shell-insert-hook 'coq-preprocessing)
1189
1190
1191
1192;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1193;;
1194;; Subterm markup -- it was added to Coq by Healf, but got removed.
1195;;                   Let's try faking something by regexp matching.
1196
1197;; FIXME: not operational yet
1198(defun coq-fake-constant-markup ()
1199  "Markup constants in Coq goal output by matching on regexps.
1200This is a horrible and approximate way of doing subterm markup.
1201\(Code used to be in Coq, but got lost between versions 5 and 7).
1202This is a hook setting for `pg-after-fontify-output-hook' to
1203enable identifiers to be highlighted and allow useful
1204mouse activation."
1205  (goto-char (point-min))
1206  (while (re-search-forward "\(\w+[^\w]\)" nil t)
1207    (replace-match "\372\200\373\\1\374" nil t)))
1208
1209
1210;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1211;;
1212;; Context-senstive in-span menu additions
1213;;
1214
1215;; da: message to Pierre: I just put these in as examples,
1216;; maybe you can suggest some better commands to use on
1217;; `thm'.  (Check maybe not much use since appears before
1218;; in the buffer anyway)
1219
1220(defun coq-create-span-menu (span idiom name)
1221  (if (string-equal idiom "proof")
1222      (let ((thm (span-property span 'name)))
1223        (list (vector
1224               "Check"
1225               `(proof-shell-invisible-command 
1226                 ,(format "Check %s." thm)))
1227              (vector
1228               "Print Proof"
1229               `(proof-shell-invisible-command 
1230                 ,(format "Print Proof %s." thm))))))
1231  (if (string-equal idiom "proof")
1232      (let ((thm (span-property span 'name)))
1233        (list (vector
1234               "Check"
1235               `(proof-shell-invisible-command 
1236                 ,(format "Check %s." thm))))))
1237  )
1238
1239
1240;;;;;;;;;;;;;;;;;;;;;
1241; Some smart insertion function
1242;;;;;;;;;;;;;;;;;;;;;;
1243
1244;; ----- coq specific menu is defined in coq-abbrev.el
1245(require 'coq-abbrev)
1246
1247(defconst module-kinds-table 
1248  '(("Section" 0) ("Module" 1) ("Module Type" 2) ("Declare Module" 3))
1249  "Enumerates the different kinds of modules.")
1250
1251(defconst modtype-kinds-table
1252  '(("" 1) (":" 2) ("<:" 3))
1253  "Enumerates the different kinds of type information for modules.")
1254
1255(defun coq-insert-section-or-module ()
1256  "Insert a module or a section after asking right questions."
1257  (interactive)
1258  (let* 
1259      ((mods (completing-read "kind of module (tab to see list): " module-kinds-table))
1260       (s (read-string  "Name: "))
1261       (typkind (if (string-equal mods "Section")
1262                    "" ;; if not a section
1263                  (completing-read "kind of type (optional, tab to see list): " 
1264                                   modtype-kinds-table)))
1265       (p (point)))
1266    (if (string-equal typkind "")
1267        (progn
1268          (insert mods " " s ".\n#\nEnd " s ".")
1269          (holes-replace-string-by-holes-backward p)
1270          (goto-char p))
1271      (insert mods " " s " " typkind " #.\n#\nEnd " s ".")
1272      (holes-replace-string-by-holes-backward p)
1273      (goto-char p)
1274      (holes-set-point-next-hole-destroy))
1275    )
1276  )
1277
1278(defconst reqkinds-kinds-table
1279  '(("Require" 1) ("Require Export" 2) ("Import" 3))
1280  "Enumerates the different kinds of requiring a module.")
1281
1282
1283(defun coq-insert-requires ()
1284  "Insert requires to modules, iteratively."
1285  (interactive)
1286  (let* ((s) 
1287         (reqkind 
1288          (completing-read "Command (tab to see list, default Require Export) : " 
1289                           reqkinds-kinds-table nil nil nil nil "Require Export"))
1290         )
1291    (setq s (read-string  "Name (empty to stop) : "))
1292    (while (not (string-equal s ""))
1293      (insert (format "%s %s.\n" reqkind s))
1294      (setq s (read-string  "Name (empty to stop) : ")))
1295    )
1296  )
1297
1298
1299(defun coq-end-Section ()
1300  "Ends a Coq section."
1301  (interactive)
1302  (let ((count 1)) ; The number of section already "Ended" + 1
1303    (let ((section 
1304           (save-excursion 
1305             (progn 
1306               (while (and (> count 0) 
1307                           (search-backward-regexp 
1308                            "Chapter\\|Section\\|End" 0 t))
1309                 (if (char-equal (char-after (point)) ?E)
1310                     (setq count (1+ count))
1311                   (setq count (1- count))))
1312               (buffer-substring-no-properties
1313          (progn (beginning-of-line) (forward-word 1) (point)) 
1314          (progn (end-of-line) (point)))))))
1315      (insert (concat "End" section)))))
1316
1317(defun coq-insert-intros ()
1318  "Insert an intros command with names given by Show Intros.
1319Based on idea mentioned in Coq reference manual."
1320  (interactive)
1321  (let* ((shints (proof-shell-invisible-cmd-get-result "Show Intros."))
1322         (intros (replace-in-string shints "^\\([^\n]+\\)\n" "intros \\1.")))
1323    (if (or (< (length shints) 2);; empty response is just NL
1324            (string-match coq-error-regexp shints)) 
1325        (error "Don't know what to intro. ")
1326      (insert intros)
1327      (indent-according-to-mode))))
1328
1329(defun coq-insert-match ()
1330  "Insert a match expression from a type name by Show Intros.
1331Based on idea mentioned in Coq reference manual. Also insert holes at insertion
1332positions."
1333  (interactive)
1334  (proof-shell-ready-prover) 
1335  (let* ((cmd))
1336    (setq cmd (read-string "Build match for type:"))
1337    (let* ((thematch 
1338           (proof-shell-invisible-cmd-get-result (concat "Show Match " cmd ".")))
1339          (match (replace-in-string thematch "=> \n" "=> #\n")))
1340      ;; if error, it will be displayed in response buffer (see def of
1341      ;; proof-shell-invisible-cmd-get-result), otherwise:
1342      (unless (proof-string-match coq-error-regexp match)
1343        (let ((start (point)))
1344          (insert match)
1345          (indent-region start (point) nil)
1346          (let ((n (holes-replace-string-by-holes-backward start)))
1347            (case n
1348        (0 nil)                         ; no hole, stay here.
1349        (1
1350         (goto-char start)
1351         (holes-set-point-next-hole-destroy)) ; if only one hole, go to it.
1352        (t
1353         (goto-char start)
1354         (message 
1355          (substitute-command-keys
1356           "\\[holes-set-point-next-hole-destroy] to jump to active hole.  \\[holes-short-doc] to see holes doc.")))))
1357          )))))
1358
1359
1360
1361(defun coq-insert-tactic ()
1362  "Ask for a tactic name, with completion on a quasi-exhaustive list of coq
1363tactics and insert it at point.  Questions may be asked to the user."
1364  (interactive)
1365  (coq-insert-from-db coq-tactics-db "Tactic"))
1366
1367(defun coq-insert-tactical ()
1368  "Ask for a tactical name, with completion on a quasi-exhaustive list of coq
1369tacticals and insert it at point.  Questions may be asked to the user."
1370  (interactive)
1371  (coq-insert-from-db coq-tacticals-db "Tactical"))
1372
1373(defun coq-insert-command ()
1374  "Ask for a command name, with completion on a quasi-exhaustive list of coq
1375commands and insert it at point.  Questions may be asked to the user."
1376  (interactive)
1377  (coq-insert-from-db coq-commands-db "Command"))
1378
1379(defun coq-insert-term ()
1380  "Ask for a term kind, with completion and insert it at point.  Questions may
1381be asked to the user."
1382  (interactive)
1383  (coq-insert-from-db coq-terms-db "Kind of term"))
1384
1385
1386(define-key coq-keymap [(control ?i)] 'coq-insert-intros)
1387(define-key coq-keymap [(control ?m)] 'coq-insert-match)
1388(define-key coq-keymap [(control ?()] 'coq-insert-section-or-module)
1389(define-key coq-keymap [(control ?))] 'coq-end-Section)
1390(define-key coq-keymap [(control ?s)] 'coq-Show)
1391(define-key coq-keymap [(control ?t)] 'coq-insert-tactic)
1392(define-key coq-keymap [(control ?T)] 'coq-insert-tactical)
1393
1394(define-key coq-keymap [(control space)] 'coq-insert-term)
1395(define-key coq-keymap [(control return)] 'coq-insert-command)
1396
1397(define-key coq-keymap [(control ?r)] 'coq-insert-requires)
1398(define-key coq-keymap [(control ?o)] 'coq-SearchIsos)
1399
1400(define-key coq-keymap [(control ?p)] 'coq-Print)
1401(define-key coq-keymap [(control ?b)] 'coq-About)
1402(define-key coq-keymap [(control ?c)] 'coq-Check)
1403(define-key coq-keymap [(control ?h)] 'coq-PrintHint)
1404(define-key coq-keymap [(control ?l)] 'coq-LocateConstant)
1405(define-key coq-keymap [(control ?n)] 'coq-LocateNotation)
1406;(define-key coq-keymap [?'] 'coq-highlight-error)
1407
1408
1409
1410;;;;;;;;;;;;;;;;;;;;;;;;
1411;; error handling
1412;;;;;;;;;;;;;;;;;;;;;;;;
1413
1414
1415(defvar last-coq-error-location nil
1416  "The last error message seen by `coq-get-last-error-location' and
1417`coq-highlight-error'.")
1418
1419
1420;; I don't use proof-shell-last-ouput here since it is not always set to the
1421;; really last ouptut (specially when a *tactic* gives an error) instead I go
1422;; directly to the response buffer. This allows also to clean the response
1423;; buffer (better to only scroll it?)
1424(defun coq-get-last-error-location (&optional parseresp clean)
1425 "Return location information on last error sent by coq.  Return a three
1426elements list (pos lgth) if successful, nil otherwise.  pos is the number of
1427characters preceding the underlined expression, and lgth is its length.
1428Coq error message must be like this:
1429
1430\"
1431> command with an error here ...
1432>                       ^^^^
1433\"
1434
1435If PARSERESP is nil, don't really parse response buffer but take the value of
1436`last-coq-error-location' instead, otherwise parse response buffer and updates
1437`last-coq-error-location'.
1438
1439If PARSERESP and CLEAN are non-nil, delete the error location from the response
1440buffer."
1441  (if (not parseresp) last-coq-error-location
1442    (save-excursion
1443      ;; proof-shell-handle-error-or-interrupt-hook is called from shell buffer
1444      (set-buffer proof-response-buffer)
1445      (goto-char (point-max))
1446      (let* ((mtch (re-search-backward "\n>[^\n]+\n> \\( *\\)\\(\\^+\\)\n" nil 'dummy))
1447             (pos (and mtch (length (match-string 1))))
1448             (lgth (and (length (match-string 2))))
1449             (res (list pos lgth)))
1450        ;; clean the response buffer from ultra-ugly underlined command line
1451        ;; parsed above. Don't kill the first \n
1452        (when (and clean mtch) (delete-region (+ mtch 1) (match-end 0)))
1453        (when mtch 
1454          (setq last-coq-error-location res)
1455          res)))))
1456
1457(defun coq-highlight-error (&optional parseresp clean)
1458  "Parses the last coq output looking at an error message. Highlight the text
1459pointed by it. Coq error message must be like this:
1460
1461\"
1462> command with an error here ...
1463>                       ^^^^
1464\"
1465
1466If PARSERESP is nil, don't really parse response buffer but take the value of
1467`last-coq-error-location' instead, otherwise parse response buffer and updates
1468`last-coq-error-location'.
1469
1470If PARSERESP and CLEAN are non-nil, delete the error location from the response
1471buffer."
1472  (interactive)
1473  (let ((mtch (coq-get-last-error-location parseresp clean)))
1474    (when mtch 
1475      (let ((pos (car mtch))
1476            (lgth (cadr mtch)))
1477        (set-buffer proof-script-buffer) 
1478        (goto-char (+ (proof-locked-end) 1))
1479        (coq-find-real-start)
1480        (forward-char pos)
1481        (let ((sp (make-span (point) (+ (point) lgth))))
1482          (set-span-face sp 'proof-warning-face)
1483          ;; delete timer if exist
1484          ;;(setq coq-highlight-error-timer (run-with-timer 10 nil 'delete-span sp))
1485          (ignore-errors (sit-for 20)) ; errors here would skip the next delete
1486          (delete-span sp)
1487          )))))
1488
1489
1490(setq coq-allow-highlight-error t)
1491
1492;; if a command is sent to coq without being in the script, then don't
1493;; higilight the error
1494(defun coq-decide-highlight-error ()
1495  (if (eq action 'proof-done-invisible)
1496      (setq coq-allow-highlight-error nil)
1497    (setq coq-allow-highlight-error t)))
1498
1499(defun coq-highlight-error-hook ()
1500  (if coq-allow-highlight-error (save-excursion (coq-highlight-error t t))))
1501
1502;; We need this two hooks to highlight only when scripting
1503(add-hook 'proof-shell-handle-error-or-interrupt-hook 'coq-highlight-error-hook t)
1504(add-hook 'proof-shell-insert-hook 'coq-decide-highlight-error t)
1505
1506(provide 'coq)
1507
1508;;   Local Variables: ***
1509;;   fill-column: 79 ***
1510;;   indent-tabs-mode: nil ***
1511;;   End: ***
1512
1513;;; coq.el ends here