diff --git a/ci/simple-tests/test-omit-proofs.el b/ci/simple-tests/test-omit-proofs.el index 891343370..2204d0e91 100644 --- a/ci/simple-tests/test-omit-proofs.el +++ b/ci/simple-tests/test-omit-proofs.el @@ -178,7 +178,6 @@ In particular, test that with proof-omit-proofs-option configured: (should (eq (first-overlay-face) 'proof-locked-face))) (ert-deftest omit-proofs-never-omit-hints () - :expected-result :failed "Test that proofs containing Hint are never omitted. This test only checks that the face in the middle of the proof is the normal `proof-locked-face'. diff --git a/coq/coq-syntax.el b/coq/coq-syntax.el index 012081f0f..1f1cb5187 100644 --- a/coq/coq-syntax.el +++ b/coq/coq-syntax.el @@ -1429,6 +1429,11 @@ different." (defconst coq-command-decl-regexp (coq-add-command-prefix coq-keywords-decl)) (defconst coq-command-defn-regexp (coq-add-command-prefix coq-keywords-defn)) +(defconst coq-lowercase-command-regexp "^[a-z]" + "Regular expression matching commands starting with a lowercase letter. +Used in `coq-cmd-prevents-proof-omission' to identify tactics +that only have proof-local effects.") + ;; must match: ;; "with f x y :" (followed by = or not) ;; "with f x y (z:" (not followed by =) diff --git a/coq/coq.el b/coq/coq.el index 5a85bf7b9..f3f02f19e 100644 --- a/coq/coq.el +++ b/coq/coq.el @@ -743,6 +743,18 @@ If locked span already has a state number, then do nothing. Also updates ;; (message "Unknown command, hopes this won't desynchronize ProofGeneral") ;; t)))) +(defun coq-cmd-prevents-proof-omission (cmd) + "Instanciation for `proof-script-cmd-prevents-proof-omission'. +This predicate decides whether a command inside a proof might +have effects outside the proof, which would prohibit omitting the +proof, see `proof-script-omit-proofs'. + +Commands starting lower case are deemed as tactics that have +proof local effect only. Everything else is checked against the +STATECH field in the coq syntax data base, see coq-db.el." + (if (proof-string-match coq-lowercase-command-regexp cmd) + nil + (not (coq-state-preserving-p cmd)))) (defun coq-hide-additional-subgoals-switch () "Function invoked when the user switches option `coq-hide-additional-subgoals'." @@ -1952,7 +1964,8 @@ at `proof-assistant-settings-cmds' evaluation time.") proof-script-proof-start-regexp coq-proof-start-regexp proof-script-proof-end-regexp coq-proof-end-regexp proof-script-definition-end-regexp coq-definition-end-regexp - proof-script-proof-admit-command coq-omit-proof-admit-command) + proof-script-proof-admit-command coq-omit-proof-admit-command + proof-script-cmd-prevents-proof-omission #'coq-cmd-prevents-proof-omission) (setq proof-cannot-reopen-processed-files nil) diff --git a/doc/PG-adapting.texi b/doc/PG-adapting.texi index 26b04d972..2c2a94054 100644 --- a/doc/PG-adapting.texi +++ b/doc/PG-adapting.texi @@ -1187,10 +1187,14 @@ match of @samp{@code{proof-script-proof-end-regexp}}, are omitted (not send to the proof assistant) and replaced by @samp{@code{proof-script-proof-admit-command}}. If a match for @samp{@code{proof-script-definition-end-regexp}} is found while searching -forward for the proof end, the current proof (up to and including -the match of @samp{@code{proof-script-definition-end-regexp}}) is considered -to be not opaque and not omitted, thus all these proof commands -_are_ sent to the proof assistant. +forward for the proof end or if +@samp{@code{proof-script-cmd-prevents-proof-omission}} recognizes a proof +command that prevents proof omission then the current proof (up +to and including the match of +@samp{@code{proof-script-definition-end-regexp}} or +@samp{@code{proof-script-proof-end-regexp}}) is considered to be not opaque +and not omitted, thus all these proof commands _are_ sent to the +proof assistant. The feature does not work for nested proofs. If a match for @samp{@code{proof-script-proof-start-regexp}} is found before the next match diff --git a/generic/proof-config.el b/generic/proof-config.el index 506a0e1fb..81b24cb68 100644 --- a/generic/proof-config.el +++ b/generic/proof-config.el @@ -715,10 +715,14 @@ match of `proof-script-proof-end-regexp', are omitted (not send to the proof assistant) and replaced by `proof-script-proof-admit-command'. If a match for `proof-script-definition-end-regexp' is found while searching -forward for the proof end, the current proof (up to and including -the match of `proof-script-definition-end-regexp') is considered -to be not opaque and not omitted, thus all these proof commands -_are_ sent to the proof assistant. +forward for the proof end or if +`proof-script-cmd-prevents-proof-omission' recognizes a proof +command that prevents proof omission then the current proof (up +to and including the match of +`proof-script-definition-end-regexp' or +`proof-script-proof-end-regexp') is considered to be not opaque +and not omitted, thus all these proof commands _are_ sent to the +proof assistant. The feature does not work for nested proofs. If a match for `proof-script-proof-start-regexp' is found before the next match @@ -759,6 +763,18 @@ See `proof-omit-proofs-configured'." :type 'string :group 'proof-script) +(defcustom proof-script-cmd-prevents-proof-omission nil + "Optional predicate recognizing proof commands that prevent proof omission. +If set, this option should contain a function that takes a proof +command (as string) as argument and returns t or nil. If set, the +function is called on every proof command inside a proof while +scanning for proofs to omit. The predicate should return t if the +command has effects ouside the proof, potentially breaking the +script if the current proof is omitted. If the predicate returns +t, the proof is considered to be not opaque and thus not omitted." + :type 'function + :group 'proof-script) + ;; ;; Proof script indentation diff --git a/generic/proof-script.el b/generic/proof-script.el index be884c115..20f47caf5 100644 --- a/generic/proof-script.el +++ b/generic/proof-script.el @@ -2028,6 +2028,8 @@ start is found inside a proof." maybe-result inside-proof proof-start-span-start proof-start-span-end + ;; t if the proof contains state changing commands and must be kept + proof-must-be-kept ;; the current vanilla item item ;; the command of the current item @@ -2061,7 +2063,12 @@ start is found inside a proof." (line-number-at-pos (span-end (car item)))))) ;; else - no nested proof, but still inside-proof - (if (string-match proof-script-proof-end-regexp cmd) + (if (and (string-match proof-script-proof-end-regexp cmd) + (not proof-must-be-kept)) + ;; End of opaque proof recognized and we didn't + ;; recognize a state changing command inside the + ;; proof that would prohibit throwing the proof + ;; away. (let ;; Reuse the Qed span for the whole proof, ;; including the faked Admitted command. @@ -2102,15 +2109,27 @@ start is found inside a proof." (setq inside-proof nil)) ;; else - no nested proof, no opaque proof, but still inside - (if (string-match proof-script-definition-end-regexp cmd) + (if (or (string-match proof-script-definition-end-regexp cmd) + (and (string-match proof-script-proof-end-regexp cmd) + proof-must-be-kept)) ;; A proof ending in Defined or something similar. + ;; Or a proof containing a state changing command + ;; such that the proof-must-be-kept. ;; Need to keep all commands from the start of the proof. (progn (setq result (cons item (nconc maybe-result result))) (setq maybe-result nil) (setq inside-proof nil)) - ;; normal proof command - maybe it belongs to a + + ;; else - inside proof, no proof termination recognized + ;; Normal proof command - maybe it belongs to a ;; Defined, keep it separate, until we know. + (when (and proof-script-cmd-prevents-proof-omission + (not (eq (span-property (car item) 'type) 'comment)) + (not proof-must-be-kept) + (funcall proof-script-cmd-prevents-proof-omission + cmd)) + (setq proof-must-be-kept t)) (push item maybe-result))))) ;; else - outside proof @@ -2121,7 +2140,8 @@ start is found inside a proof." (push item result) (setq proof-start-span-start (span-start (car item))) (setq proof-start-span-end (span-end (car item))) - (setq inside-proof t)) + (setq inside-proof t) + (setq proof-must-be-kept nil)) ;; outside, no proof start - keep it unmodified (push item result))) (setq vanillas (cdr vanillas)))