| author | wenzelm | 
| Thu, 17 Apr 2008 16:30:52 +0200 | |
| changeset 26706 | 4ea64590d28b | 
| parent 26643 | 99f5407c05ef | 
| child 27532 | f3d92b5dcd45 | 
| permissions | -rw-r--r-- | 
| 21642 | 1  | 
(* Title: Pure/ProofGeneral/proof_general_emacs.ML  | 
2  | 
ID: $Id$  | 
|
3  | 
Author: David Aspinall and Markus Wenzel  | 
|
4  | 
||
5  | 
Isabelle/Isar configuration for Emacs Proof General.  | 
|
| 26549 | 6  | 
See also http://proofgeneral.inf.ed.ac.uk  | 
| 21642 | 7  | 
*)  | 
8  | 
||
9  | 
signature PROOF_GENERAL =  | 
|
10  | 
sig  | 
|
| 26549 | 11  | 
val test_markupN: string  | 
| 21642 | 12  | 
val init: bool -> unit  | 
| 24874 | 13  | 
val init_outer_syntax: unit -> unit  | 
| 24289 | 14  | 
val sendback: string -> Pretty.T list -> unit  | 
| 21642 | 15  | 
end;  | 
16  | 
||
17  | 
structure ProofGeneral: PROOF_GENERAL =  | 
|
18  | 
struct  | 
|
19  | 
||
| 21945 | 20  | 
|
| 21642 | 21  | 
(* print modes *)  | 
22  | 
||
23  | 
val proof_generalN = "ProofGeneralEmacs"; (*token markup (colouring vars, etc.)*)  | 
|
24  | 
val pgasciiN = "PGASCII"; (*plain 7-bit ASCII communication*)  | 
|
25  | 
val thm_depsN = "thm_deps"; (*meta-information about theorem deps*)  | 
|
| 26549 | 26  | 
val test_markupN = "test_markup"; (*XML markup for everything*)  | 
27  | 
||
28  | 
val _ = Markup.add_mode test_markupN XML.output_markup;  | 
|
| 21642 | 29  | 
|
30  | 
fun special oct =  | 
|
| 26526 | 31  | 
if print_mode_active pgasciiN then Symbol.SOH ^ chr (ord (oct_char oct) - 167)  | 
| 21642 | 32  | 
else oct_char oct;  | 
33  | 
||
34  | 
||
35  | 
(* text output: print modes for xsymbol *)  | 
|
36  | 
||
37  | 
local  | 
|
38  | 
||
39  | 
fun xsym_output "\\" = "\\\\"  | 
|
40  | 
| xsym_output s = if Symbol.is_raw s then Symbol.decode_raw s else s;  | 
|
41  | 
||
42  | 
fun xsymbols_output s =  | 
|
| 22590 | 43  | 
if print_mode_active Symbol.xsymbolsN andalso exists_string (equal "\\") s then  | 
| 21642 | 44  | 
let val syms = Symbol.explode s  | 
| 23621 | 45  | 
in (implode (map xsym_output syms), Symbol.length syms) end  | 
46  | 
else Output.default_output s;  | 
|
| 21642 | 47  | 
|
48  | 
in  | 
|
49  | 
||
50  | 
fun setup_xsymbols_output () =  | 
|
| 
23641
 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 
wenzelm 
parents: 
23621 
diff
changeset
 | 
51  | 
Output.add_mode Symbol.xsymbolsN xsymbols_output Symbol.encode_raw;  | 
| 21642 | 52  | 
|
53  | 
end;  | 
|
54  | 
||
55  | 
||
| 
23641
 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 
wenzelm 
parents: 
23621 
diff
changeset
 | 
56  | 
(* common markup *)  | 
| 
 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 
wenzelm 
parents: 
23621 
diff
changeset
 | 
57  | 
|
| 26706 | 58  | 
val _ = Markup.add_mode proof_generalN (fn (name, props) =>  | 
| 25630 | 59  | 
let  | 
60  | 
val (bg1, en1) =  | 
|
| 25848 | 61  | 
if name = Markup.stateN then (special "366" ^ "\n", "\n" ^ special "367")  | 
| 25630 | 62  | 
else if name = Markup.sendbackN then (special "376", special "377")  | 
63  | 
else if name = Markup.hiliteN then (special "327", special "330")  | 
|
| 26706 | 64  | 
else if name = Markup.classN then (special "351", special "350")  | 
65  | 
else if name = Markup.tfreeN then (special "352", special "350")  | 
|
66  | 
else if name = Markup.tvarN then (special "353", special "350")  | 
|
67  | 
else if name = Markup.freeN then (special "354", special "350")  | 
|
68  | 
else if name = Markup.boundN then (special "355", special "350")  | 
|
69  | 
else if name = Markup.varN then (special "356", special "350")  | 
|
70  | 
else if name = Markup.skolemN then (special "357", special "350")  | 
|
| 25630 | 71  | 
      else ("", "");
 | 
72  | 
val (bg2, en2) =  | 
|
| 26549 | 73  | 
if print_mode_active test_markupN  | 
| 26542 | 74  | 
then XML.output_markup (name, props)  | 
| 25630 | 75  | 
      else ("", "");
 | 
| 26706 | 76  | 
in (bg1 ^ bg2, en2 ^ en1) end);  | 
| 
23641
 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 
wenzelm 
parents: 
23621 
diff
changeset
 | 
77  | 
|
| 
 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 
wenzelm 
parents: 
23621 
diff
changeset
 | 
78  | 
|
| 21642 | 79  | 
(* messages and notification *)  | 
80  | 
||
81  | 
fun decorate bg en prfx =  | 
|
| 22590 | 82  | 
Output.writeln_default o enclose bg en o prefix_lines prfx;  | 
| 21642 | 83  | 
|
84  | 
fun setup_messages () =  | 
|
| 23662 | 85  | 
(Output.writeln_fn := Output.writeln_default;  | 
| 22590 | 86  | 
Output.priority_fn := (fn s => decorate (special "360") (special "361") "" s);  | 
87  | 
Output.tracing_fn := (fn s => decorate (special "360" ^ special "375") (special "361") "" s);  | 
|
88  | 
Output.debug_fn := (fn s => decorate (special "362") (special "363") "+++ " s);  | 
|
89  | 
Output.warning_fn := (fn s => decorate (special "362") (special "363") "### " s);  | 
|
| 25848 | 90  | 
Output.error_fn := (fn s => decorate (special "364") (special "365") "*** " s);  | 
91  | 
Output.prompt_fn := (fn s => Output.std_output (s ^ special "372")));  | 
|
| 21642 | 92  | 
|
| 
22699
 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 
wenzelm 
parents: 
22678 
diff
changeset
 | 
93  | 
fun panic s =  | 
| 
 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 
wenzelm 
parents: 
22678 
diff
changeset
 | 
94  | 
  (decorate (special "364") (special "365") "!!! " ("## SYSTEM EXIT ##\n" ^ s); exit 1);
 | 
| 21642 | 95  | 
|
96  | 
fun emacs_notify s = decorate (special "360") (special "361") "" s;  | 
|
97  | 
||
98  | 
fun tell_clear_goals () =  | 
|
| 21940 | 99  | 
emacs_notify "Proof General, please clear the goals buffer.";  | 
| 21642 | 100  | 
|
101  | 
fun tell_clear_response () =  | 
|
| 21940 | 102  | 
emacs_notify "Proof General, please clear the response buffer.";  | 
| 21642 | 103  | 
|
104  | 
fun tell_file_loaded path =  | 
|
| 21940 | 105  | 
  emacs_notify ("Proof General, this file is loaded: " ^ quote (File.platform_path path));
 | 
| 21642 | 106  | 
|
107  | 
fun tell_file_retracted path =  | 
|
| 21940 | 108  | 
  emacs_notify ("Proof General, you can unlock the file " ^ quote (File.platform_path path));
 | 
| 21642 | 109  | 
|
| 24289 | 110  | 
fun sendback heading prts =  | 
111  | 
Pretty.writeln (Pretty.big_list heading [Pretty.markup Markup.sendback prts]);  | 
|
112  | 
||
| 21642 | 113  | 
|
114  | 
(* theory loader actions *)  | 
|
115  | 
||
116  | 
local  | 
|
117  | 
||
118  | 
fun trace_action action name =  | 
|
119  | 
if action = ThyInfo.Update then  | 
|
120  | 
List.app tell_file_loaded (ThyInfo.loaded_files name)  | 
|
121  | 
else if action = ThyInfo.Outdate orelse action = ThyInfo.Remove then  | 
|
122  | 
List.app tell_file_retracted (ThyInfo.loaded_files name)  | 
|
123  | 
else ();  | 
|
124  | 
||
125  | 
in  | 
|
126  | 
fun setup_thy_loader () = ThyInfo.add_hook trace_action;  | 
|
| 26613 | 127  | 
fun sync_thy_loader () = List.app (trace_action ThyInfo.Update) (ThyInfo.get_names ());  | 
| 21642 | 128  | 
end;  | 
129  | 
||
130  | 
||
| 21948 | 131  | 
(* get informed about files *)  | 
| 21642 | 132  | 
|
| 25442 | 133  | 
local  | 
| 21642 | 134  | 
|
| 25442 | 135  | 
(*liberal low-level version*)  | 
136  | 
val thy_name = perhaps (try (unsuffix ".thy")) o List.last o space_explode "/";  | 
|
137  | 
val thy_path = ThyLoad.thy_path o thy_name;  | 
|
138  | 
||
139  | 
in  | 
|
140  | 
||
| 
25443
 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 
wenzelm 
parents: 
25442 
diff
changeset
 | 
141  | 
fun check_thy "" = false  | 
| 
 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 
wenzelm 
parents: 
25442 
diff
changeset
 | 
142  | 
| check_thy file = ThyInfo.check_known_thy (thy_name file);  | 
| 21642 | 143  | 
|
| 23913 | 144  | 
fun proper_inform_file_processed file () =  | 
| 25442 | 145  | 
let  | 
146  | 
val name = thy_name file;  | 
|
147  | 
val _ = ThyInfo.touch_child_thys name;  | 
|
148  | 
val _ = ThyInfo.register_thy name handle ERROR msg =>  | 
|
149  | 
(warning (cat_lines [msg, "Failed to register theory: " ^ quote name]);  | 
|
150  | 
tell_file_retracted (thy_path file));  | 
|
151  | 
in () end;  | 
|
| 21642 | 152  | 
|
| 25442 | 153  | 
fun vacuous_inform_file_processed file () = tell_file_retracted (thy_path file);  | 
154  | 
||
155  | 
val inform_file_retracted = ThyInfo.if_known_thy ThyInfo.remove_thy o thy_name;  | 
|
156  | 
||
157  | 
end;  | 
|
| 21642 | 158  | 
|
159  | 
||
160  | 
(* restart top-level loop (keeps most state information) *)  | 
|
161  | 
||
162  | 
val welcome = priority o Session.welcome;  | 
|
163  | 
||
164  | 
fun restart () =  | 
|
| 21940 | 165  | 
(sync_thy_loader ();  | 
166  | 
tell_clear_goals ();  | 
|
167  | 
tell_clear_response ();  | 
|
168  | 
welcome ();  | 
|
169  | 
raise Toplevel.RESTART);  | 
|
| 21642 | 170  | 
|
171  | 
||
172  | 
(* theorem dependency output *)  | 
|
173  | 
||
174  | 
local  | 
|
175  | 
||
176  | 
val spaces_quote = space_implode " " o map quote;  | 
|
177  | 
||
178  | 
fun thm_deps_message (thms, deps) =  | 
|
| 21948 | 179  | 
  emacs_notify ("Proof General, theorem dependencies of " ^ thms ^ " are " ^ deps);
 | 
| 21642 | 180  | 
|
| 21968 | 181  | 
fun tell_thm_deps ths =  | 
| 22590 | 182  | 
if print_mode_active thm_depsN then  | 
| 21968 | 183  | 
let  | 
| 
22228
 
7c27195a4afc
proper use of PureThy.has_name_hint instead of hard-wired string'';
 
wenzelm 
parents: 
22225 
diff
changeset
 | 
184  | 
val names = map PureThy.get_name_hint (filter PureThy.has_name_hint ths);  | 
| 22225 | 185  | 
val deps = Symtab.keys (fold Proofterm.thms_of_proof'  | 
186  | 
(map Thm.proof_of ths) Symtab.empty);  | 
|
| 21968 | 187  | 
in  | 
188  | 
if null names orelse null deps then ()  | 
|
189  | 
else thm_deps_message (spaces_quote names, spaces_quote deps)  | 
|
190  | 
end  | 
|
191  | 
else ();  | 
|
| 21642 | 192  | 
|
193  | 
in  | 
|
194  | 
||
195  | 
fun setup_present_hook () =  | 
|
196  | 
Present.add_hook (fn _ => fn res => tell_thm_deps (maps #2 res));  | 
|
197  | 
||
198  | 
end;  | 
|
199  | 
||
200  | 
||
201  | 
(* additional outer syntax for Isar *)  | 
|
202  | 
||
203  | 
local structure P = OuterParse and K = OuterKeyword in  | 
|
204  | 
||
| 
25192
 
b568f8c5d5ca
made command 'undo' silent ('ProofGeneral.undo' becomes a historical relic);
 
wenzelm 
parents: 
24882 
diff
changeset
 | 
205  | 
fun undoP () = (*undo without output -- historical*)  | 
| 21642 | 206  | 
OuterSyntax.improper_command "ProofGeneral.undo" "(internal)" K.control  | 
207  | 
(Scan.succeed (Toplevel.no_timing o IsarCmd.undo));  | 
|
208  | 
||
| 24867 | 209  | 
fun restartP () =  | 
| 21642 | 210  | 
OuterSyntax.improper_command "ProofGeneral.restart" "(internal)" K.control  | 
211  | 
(P.opt_unit >> (Toplevel.no_timing oo K (Toplevel.imperative restart)));  | 
|
212  | 
||
| 24867 | 213  | 
fun kill_proofP () =  | 
| 21642 | 214  | 
OuterSyntax.improper_command "ProofGeneral.kill_proof" "(internal)" K.control  | 
215  | 
(Scan.succeed (Toplevel.no_timing o IsarCmd.kill_proof_notify tell_clear_goals));  | 
|
216  | 
||
| 24867 | 217  | 
fun inform_file_processedP () =  | 
| 21642 | 218  | 
OuterSyntax.improper_command "ProofGeneral.inform_file_processed" "(internal)" K.control  | 
219  | 
(P.name >> (fn file => Toplevel.no_timing o  | 
|
| 
25443
 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 
wenzelm 
parents: 
25442 
diff
changeset
 | 
220  | 
Toplevel.imperative (fn () => error "Bad file name") o  | 
| 
 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 
wenzelm 
parents: 
25442 
diff
changeset
 | 
221  | 
Toplevel.init_empty (fn _ => file <> "") (vacuous_inform_file_processed file) o  | 
| 21642 | 222  | 
Toplevel.kill o  | 
| 25442 | 223  | 
Toplevel.init_empty (fn _ => check_thy file) (proper_inform_file_processed file)));  | 
| 21642 | 224  | 
|
| 24867 | 225  | 
fun inform_file_retractedP () =  | 
| 21642 | 226  | 
OuterSyntax.improper_command "ProofGeneral.inform_file_retracted" "(internal)" K.control  | 
227  | 
(P.name >> (Toplevel.no_timing oo  | 
|
228  | 
(fn file => Toplevel.imperative (fn () => inform_file_retracted file))));  | 
|
229  | 
||
| 24867 | 230  | 
fun process_pgipP () =  | 
| 21642 | 231  | 
OuterSyntax.improper_command "ProofGeneral.process_pgip" "(internal)" K.control  | 
232  | 
(P.text >> (Toplevel.no_timing oo  | 
|
233  | 
(fn txt => Toplevel.imperative (fn () => ProofGeneralPgip.process_pgip txt))));  | 
|
234  | 
||
| 24867 | 235  | 
fun init_outer_syntax () = List.app (fn f => f ())  | 
| 21948 | 236  | 
[undoP, restartP, kill_proofP, inform_file_processedP, inform_file_retractedP, process_pgipP];  | 
| 21642 | 237  | 
|
238  | 
end;  | 
|
239  | 
||
240  | 
||
241  | 
(* init *)  | 
|
242  | 
||
243  | 
val initialized = ref false;  | 
|
244  | 
||
| 
22699
 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 
wenzelm 
parents: 
22678 
diff
changeset
 | 
245  | 
fun init false = panic "No Proof General interface support for Isabelle/classic mode."  | 
| 21940 | 246  | 
| init true =  | 
| 21968 | 247  | 
(! initialized orelse  | 
| 22590 | 248  | 
(Output.no_warnings init_outer_syntax ();  | 
| 21968 | 249  | 
setup_xsymbols_output ();  | 
250  | 
setup_messages ();  | 
|
| 22590 | 251  | 
ProofGeneralPgip.init_pgip_channel (! Output.priority_fn);  | 
| 21968 | 252  | 
setup_thy_loader ();  | 
253  | 
setup_present_hook ();  | 
|
254  | 
set initialized);  | 
|
255  | 
sync_thy_loader ();  | 
|
| 25749 | 256  | 
change print_mode (update (op =) proof_generalN);  | 
| 26643 | 257  | 
       Isar.toplevel_loop {init = true, welcome = true, sync = true, secure = Secure.is_secure ()});
 | 
| 21642 | 258  | 
|
259  | 
end;  |