aboutsummaryrefslogtreecommitdiffhomepage
path: root/generic/proof-depends.el
blob: 74332c714a15c2a8be435c73103f725a32c9b512 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
;;; proof-depends.el --- Theorem-theorem and theorem-definition dependencies

;; This file is part of Proof General.

;; Portions © Copyright 1994-2012  David Aspinall and University of Edinburgh
;; Portions © Copyright 2003, 2012, 2014  Free Software Foundation, Inc.
;; Portions © Copyright 2001-2017  Pierre Courtieu
;; Portions © Copyright 2010, 2016  Erik Martin-Dorel
;; Portions © Copyright 2011-2013, 2016-2017  Hendrik Tews
;; Portions © Copyright 2015-2017  Clément Pit-Claudel

;; Authors:      David Aspinall <David.Aspinall@ed.ac.uk>
;;	         Earlier version by Fiona McNeil.

;; License:      GPL (GNU GENERAL PUBLIC LICENSE)

;;; Commentary:
;;
;; Status:       Experimental code
;;
;; Based on Fiona McNeill's MSc project on analysing dependencies
;; within proofs.  Code rewritten by David Aspinall.
;;

;;; Code:
(require 'cl)
(require 'span)
(require 'pg-vars)
(require 'proof-config)
(require 'proof-autoloads)

(defvar proof-thm-names-of-files nil
  "A list of file and theorems contained within.
A list of lists; the first element of each list is a file-name, the
second element a list of all the thm names in that file.
i.e.: ((file-name-1 (thm1 thm2 thm3)) (file-name-2 (thm1 thm2 thm3)))")

(defvar proof-def-names-of-files nil
  "A list of files and defs contained within.
A list of lists; the first element of each list is a file-name, the
second element a list of all the def names in that file.
i.e.: ((file-name-1 (def1 def2 def3)) (file-name-2 (def1 def2 def3)))")

;; Utility functions

(defun proof-depends-module-name-for-buffer ()
  "Return a module name for the current buffer.
This is a name that the prover prefixes all item names with.
For example, in isabelle, a file Stuff.ML contains theorems with
fully qualified names of the form Stuff.theorem1, etc.
For other provers, this function may need modifying."
  (if buffer-file-name
      (file-name-nondirectory
       (file-name-sans-extension buffer-file-name)) ""))

(defun proof-depends-module-of (name)
  "Return a pair of a module name and base name for given item NAME.
Assumes module name is given by dotted prefix."
  (let ((dot (string-match "\\." name)))
    (if dot
	(cons (substring name 0 dot) (substring name (+ dot 1)))
      (cons "" name))))

(defun proof-depends-names-in-same-file (names)
  "Return subset of list NAMES which are guessed to occur in same file.
This is done using `proof-depends-module-name-for-buffer' and
`proof-depends-module-of'."
  (let ((filemod  (proof-depends-module-name-for-buffer))
	samefile)
    (while names
      (let ((splitname (proof-depends-module-of (car names))))
	(if (equal filemod (car splitname))
	    (setq samefile (cons (cdr splitname) samefile))))
      (setq names (cdr names)))
    ;; NB: reversed order
    samefile))


;;
;; proof-depends-process-dependencies: the main entry point.
;;
;;;###autoload
(defun proof-depends-process-dependencies (name gspan)
  "Process dependencies reported by prover, for NAME in span GSPAN.
Called from `proof-done-advancing' when a save is processed and
`proof-last-theorem-dependencies' is set."

  (span-set-property gspan 'dependencies
		     ;; Ancestors of NAME are in the second component.
		     ;; FIXME: for now we ignore the first component:
		     ;; NAME may not be enough [Isar allows proof regions
		     ;; with multiple names, which are reported in dep'c'y
		     ;; output].
		     (cdr proof-last-theorem-dependencies))

  (let* ((samefilenames    (proof-depends-names-in-same-file
			    (cdr proof-last-theorem-dependencies)))

	 ;; Find goalsave spans earlier in this file which this
	 ;; one depends on; update their list of dependents,
	 ;; and return resulting list paired up with names.
	 (depspans
	  (apply 'append
		 (span-mapcar-spans
		  (lambda (depspan)
		    (let ((dname (span-property depspan 'name)))
		      (if (and
			   (eq (span-property depspan 'type) 'goalsave)
			   (member dname samefilenames))
			  (let ((forwarddeps
				 (span-property depspan 'dependents)))
			    (span-set-property depspan 'dependents
					       (cons
						(list name gspan) forwarddeps))
			    ;; return list of args for menu fun: name and span
			    (list (list dname depspan))))))
		  (point-min)
		  (span-start gspan)
		  'type))))

    (span-set-property gspan 'dependencies-within-file depspans)
    (setq proof-last-theorem-dependencies nil)))




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Menu Functions
;;
;; The following functions set up the menus which are the key way in
;; which the dependency information is used.


;;;###autoload
(defun proof-dependency-in-span-context-menu (span)
  "Make some menu entries showing proof dependencies of SPAN."
  ;; FIXME: might only activate this for dependency-relevant spans.
  (list
   "-------------"
   (proof-dep-make-submenu "Local Dependency..."
			   (lambda (namespan) (car namespan))
			   'proof-goto-dependency
			   (span-property span 'dependencies-within-file))
   (proof-make-highlight-depts-menu "Highlight Dependencies"
				    'proof-highlight-depcs
				    span 'dependencies-within-file)
   (proof-dep-make-submenu "Local Dependents..."
			   (lambda (namepos) (car namepos))
			   'proof-goto-dependency
			   (span-property span 'dependents))
   (proof-make-highlight-depts-menu "Highlight Dependents"
				    'proof-highlight-depts
				    span 'dependents)
   ["Unhighlight all" proof-dep-unhighlight t]
   "-------------"
   (proof-dep-alldeps-menu span)))

(defun proof-dep-alldeps-menu (span)
  (or (span-property span 'dependencies-menu) ;; cached value
      (span-set-property span 'dependencies-menu
			 (proof-dep-make-alldeps-menu
			  (span-property span 'dependencies)))))

(defun proof-dep-make-alldeps-menu (deps)
  (let ((menuname "All Dependencies...")
	(showdep  'proof-show-dependency))
    (if deps
	(let ((nestedtop   (proof-dep-split-deps deps)))
	  (cons menuname
		(append
		 (mapcar (lambda (l)
			   (vector l (list showdep l) t))
			 (cdr nestedtop))
		 (mapcar (lambda (sm)
			   (proof-dep-make-submenu (car sm)
						   'car
						   'proof-show-dependency
						   (mapcar 'list (cdr sm))))
			 (car nestedtop)))))
      (vector menuname nil nil))))

(defun proof-dep-split-deps (deps)
  "Split dependencies into named nested lists according to dotted prefixes."
  ;; NB: could handle deeper nesting here, but just do one level for now.
  (let (nested toplevel)
    ;; Add each name into a nested list or toplevel list
    (dolist (name deps)
      (let* ((period   (string-match "\\." name))
	     (ns       (and period (substring name 0 period)))
	     (subitems (and ns (assoc ns nested))))
	(cond
	 ((and ns subitems)
	  (setcdr subitems (adjoin name (cdr subitems))))
	 (ns
	  (setq nested
		(cons (cons ns (list name)) nested)))
	 (t
	  (setq toplevel (adjoin name  toplevel))))))
    (cons nested toplevel)))

(defun proof-dep-make-submenu (name namefn appfn list)
  "Make menu items for a submenu NAME, using APPFN applied to each elt in LIST.
If LIST is empty, return a disabled menu item with NAME.
NAMEFN is applied to each element of LIST to make the names."
  (if list
      (cons name
	    (mapcar `(lambda (l)
		       (vector (,namefn l)
			       (cons (quote ,appfn) l) t)) list))
    (vector name nil nil)))

(defun proof-make-highlight-depts-menu (name fn span prop)
  "Return a menu item that for highlighting dependents/depencies of SPAN."
  (let ((deps (span-property span prop)))
    (vector name `(,fn ,(span-property span 'name) (quote ,deps))
	    (not (not deps)))))


;;
;; Functions triggered by menus
;;

(defun proof-goto-dependency (name span)
  "Go to the start of SPAN."
  ;; FIXME: check buffer is right one.  Later we'll allow switching buffer
  ;; here and jumping to different files.
  (goto-char (span-start span))
  (skip-chars-forward " \t\n"))

(defun proof-show-dependency (thm)
  "Show dependency THM using `proof-show-dependency-cmd'.
This is simply to display the dependency somehow."
  (if proof-shell-show-dependency-cmd ;; robustness
      (proof-shell-invisible-command
       (format proof-shell-show-dependency-cmd thm))))

(defconst pg-dep-span-priority 500)
(defconst pg-ordinary-span-priority 100)

(defun proof-highlight-depcs (name nmspans)
  (let ((helpmsg  (concat "This item is a dependency (ancestor) of " name)))
    (while nmspans
      (let ((span (cadar nmspans)))
	(proof-depends-save-old-face span)
	(span-set-property span 'face 'proof-highlight-dependency-face)
	;; (span-set-property span 'priority pg-dep-span-priority)
	(span-set-property span 'mouse-highlight nil)
	(span-set-property span 'help-echo helpmsg))
      (setq nmspans (cdr nmspans)))))

(defun proof-highlight-depts (name nmspans)
  (let ((helpmsg  (concat "This item depends on (is a child of) " name)))
    (while nmspans
      (let ((span (cadar nmspans)))
	(proof-depends-save-old-face span)
	(span-set-property span 'face 'proof-highlight-dependent-face)
	;; (span-set-property span 'priority pg-dep-span-priority)
	(span-set-property span 'mouse-highlight nil)
	(span-set-property span 'help-echo helpmsg)
	(span-set-property span 'balloon-help helpmsg))
      (setq nmspans (cdr nmspans)))))

(defun proof-depends-save-old-face (span)
  (unless (span-property span 'depends-old-face)
    (span-set-property span 'depends-old-face
		       (span-property span 'face))))

(defun proof-depends-restore-old-face (span)
  (when (span-property span 'depends-old-face)
    (span-set-property span 'face
		       (span-property span 'depends-old-face))
    (span-set-property span 'depends-old-face nil)))

(defun proof-dep-unhighlight ()
  "Remove additional highlighting on all spans in file to their default."
  (interactive)
  (save-excursion
    (let ((span (span-at (point-min) 'type)))
      ;; FIXME: covers too many spans!
      (while span
	(pg-set-span-helphighlights span 'nohighlight)
	(proof-depends-restore-old-face span)
	;; (span-set-property span 'priority pg-ordinary-span-priority)
	(setq span (next-span span 'type))))))




(provide 'proof-depends)
;; proof-depends.el ends here

(provide 'proof-depends)

;;; proof-depends.el ends here