| author | kleing | 
| Wed, 15 Oct 2008 00:18:43 +0200 | |
| changeset 28598 | cb5f98e2e187 | 
| parent 28426 | 5bad734625ef | 
| child 29321 | 6b9ecb3a70ab | 
| 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 | ||
| 27592 
6d81c734f7af
print_mode "test_markup": do not change prompt, otherwise Proof General will not work;
 wenzelm parents: 
27591diff
changeset | 28 | val _ = Markup.add_mode test_markupN (fn (name, props) => | 
| 
6d81c734f7af
print_mode "test_markup": do not change prompt, otherwise Proof General will not work;
 wenzelm parents: 
27591diff
changeset | 29 |   if name = Markup.promptN then ("", "") else XML.output_markup (name, props));
 | 
| 21642 | 30 | |
| 31 | fun special oct = | |
| 26526 | 32 | if print_mode_active pgasciiN then Symbol.SOH ^ chr (ord (oct_char oct) - 167) | 
| 21642 | 33 | else oct_char oct; | 
| 34 | ||
| 35 | ||
| 36 | (* text output: print modes for xsymbol *) | |
| 37 | ||
| 38 | local | |
| 39 | ||
| 40 | fun xsym_output "\\" = "\\\\" | |
| 41 | | xsym_output s = if Symbol.is_raw s then Symbol.decode_raw s else s; | |
| 42 | ||
| 43 | fun xsymbols_output s = | |
| 28375 | 44 | if print_mode_active Symbol.xsymbolsN andalso exists_string (fn c => c = "\\") s then | 
| 21642 | 45 | let val syms = Symbol.explode s | 
| 23621 | 46 | in (implode (map xsym_output syms), Symbol.length syms) end | 
| 47 | else Output.default_output s; | |
| 21642 | 48 | |
| 49 | in | |
| 50 | ||
| 51 | fun setup_xsymbols_output () = | |
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 52 | Output.add_mode Symbol.xsymbolsN xsymbols_output Symbol.encode_raw; | 
| 21642 | 53 | |
| 54 | end; | |
| 55 | ||
| 56 | ||
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 57 | (* common markup *) | 
| 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 58 | |
| 26706 | 59 | val _ = Markup.add_mode proof_generalN (fn (name, props) => | 
| 25630 | 60 | let | 
| 61 | val (bg1, en1) = | |
| 25848 | 62 | if name = Markup.stateN then (special "366" ^ "\n", "\n" ^ special "367") | 
| 25630 | 63 | else if name = Markup.sendbackN then (special "376", special "377") | 
| 64 | else if name = Markup.hiliteN then (special "327", special "330") | |
| 27828 
edafacb690a3
renamed Markup.class to Markup.tclass, to avoid potential conflicts with spacial meaning in markup languages (e.g. HTML);
 wenzelm parents: 
27607diff
changeset | 65 | else if name = Markup.tclassN then (special "351", special "350") | 
| 26706 | 66 | else if name = Markup.tfreeN then (special "352", special "350") | 
| 67 | else if name = Markup.tvarN then (special "353", special "350") | |
| 68 | else if name = Markup.freeN then (special "354", special "350") | |
| 69 | else if name = Markup.boundN then (special "355", special "350") | |
| 70 | else if name = Markup.varN then (special "356", special "350") | |
| 71 | else if name = Markup.skolemN then (special "357", special "350") | |
| 25630 | 72 |       else ("", "");
 | 
| 73 | val (bg2, en2) = | |
| 27592 
6d81c734f7af
print_mode "test_markup": do not change prompt, otherwise Proof General will not work;
 wenzelm parents: 
27591diff
changeset | 74 | if name <> Markup.promptN andalso print_mode_active test_markupN | 
| 26542 | 75 | then XML.output_markup (name, props) | 
| 25630 | 76 |       else ("", "");
 | 
| 26706 | 77 | in (bg1 ^ bg2, en2 ^ en1) end); | 
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 78 | |
| 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 79 | |
| 21642 | 80 | (* messages and notification *) | 
| 81 | ||
| 82 | fun decorate bg en prfx = | |
| 22590 | 83 | Output.writeln_default o enclose bg en o prefix_lines prfx; | 
| 21642 | 84 | |
| 85 | fun setup_messages () = | |
| 23662 | 86 | (Output.writeln_fn := Output.writeln_default; | 
| 27607 
a21271f74bc7
refined Output.status_fn: priority (only visible for non-empty output, e.g. via test_markup print mode);
 wenzelm parents: 
27604diff
changeset | 87 | Output.status_fn := (fn "" => () | s => ! Output.priority_fn s); | 
| 22590 | 88 | Output.priority_fn := (fn s => decorate (special "360") (special "361") "" s); | 
| 89 | Output.tracing_fn := (fn s => decorate (special "360" ^ special "375") (special "361") "" s); | |
| 90 | Output.debug_fn := (fn s => decorate (special "362") (special "363") "+++ " s); | |
| 91 | Output.warning_fn := (fn s => decorate (special "362") (special "363") "### " s); | |
| 25848 | 92 | Output.error_fn := (fn s => decorate (special "364") (special "365") "*** " s); | 
| 93 | Output.prompt_fn := (fn s => Output.std_output (s ^ special "372"))); | |
| 21642 | 94 | |
| 22699 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 95 | fun panic s = | 
| 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 96 |   (decorate (special "364") (special "365") "!!! " ("## SYSTEM EXIT ##\n" ^ s); exit 1);
 | 
| 21642 | 97 | |
| 98 | fun emacs_notify s = decorate (special "360") (special "361") "" s; | |
| 99 | ||
| 100 | fun tell_clear_goals () = | |
| 21940 | 101 | emacs_notify "Proof General, please clear the goals buffer."; | 
| 21642 | 102 | |
| 103 | fun tell_clear_response () = | |
| 21940 | 104 | emacs_notify "Proof General, please clear the response buffer."; | 
| 21642 | 105 | |
| 106 | fun tell_file_loaded path = | |
| 21940 | 107 |   emacs_notify ("Proof General, this file is loaded: " ^ quote (File.platform_path path));
 | 
| 21642 | 108 | |
| 109 | fun tell_file_retracted path = | |
| 21940 | 110 |   emacs_notify ("Proof General, you can unlock the file " ^ quote (File.platform_path path));
 | 
| 21642 | 111 | |
| 24289 | 112 | fun sendback heading prts = | 
| 113 | Pretty.writeln (Pretty.big_list heading [Pretty.markup Markup.sendback prts]); | |
| 114 | ||
| 21642 | 115 | |
| 116 | (* theory loader actions *) | |
| 117 | ||
| 118 | local | |
| 119 | ||
| 120 | fun trace_action action name = | |
| 121 | if action = ThyInfo.Update then | |
| 122 | List.app tell_file_loaded (ThyInfo.loaded_files name) | |
| 123 | else if action = ThyInfo.Outdate orelse action = ThyInfo.Remove then | |
| 124 | List.app tell_file_retracted (ThyInfo.loaded_files name) | |
| 125 | else (); | |
| 126 | ||
| 127 | in | |
| 128 | fun setup_thy_loader () = ThyInfo.add_hook trace_action; | |
| 26613 | 129 | fun sync_thy_loader () = List.app (trace_action ThyInfo.Update) (ThyInfo.get_names ()); | 
| 21642 | 130 | end; | 
| 131 | ||
| 132 | ||
| 21948 | 133 | (* get informed about files *) | 
| 21642 | 134 | |
| 25442 | 135 | (*liberal low-level version*) | 
| 136 | val thy_name = perhaps (try (unsuffix ".thy")) o List.last o space_explode "/"; | |
| 137 | ||
| 138 | val inform_file_retracted = ThyInfo.if_known_thy ThyInfo.remove_thy o thy_name; | |
| 139 | ||
| 27577 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 140 | fun inform_file_processed file = | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 141 | let | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 142 | val name = thy_name file; | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 143 |     val _ = name = "" andalso error ("Bad file name: " ^ quote file);
 | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 144 | val _ = | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 145 | if ThyInfo.check_known_thy name then | 
| 28426 | 146 | (Isar.>> (Toplevel.commit_exit Position.none); | 
| 147 | ThyInfo.touch_child_thys name; ThyInfo.register_thy name) | |
| 27585 
2234ace5b538
inform_file_processed: try harder not to fail, ensure
 wenzelm parents: 
27577diff
changeset | 148 | handle ERROR msg => | 
| 27591 | 149 | (warning (cat_lines ["Failed to register theory: " ^ quote name, msg]); | 
| 27585 
2234ace5b538
inform_file_processed: try harder not to fail, ensure
 wenzelm parents: 
27577diff
changeset | 150 | tell_file_retracted (ThyLoad.thy_path name)) | 
| 27577 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 151 | else (); | 
| 27589 | 152 | val _ = Isar.init_point (); | 
| 27577 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 153 | in () end; | 
| 21642 | 154 | |
| 155 | ||
| 156 | (* restart top-level loop (keeps most state information) *) | |
| 157 | ||
| 158 | val welcome = priority o Session.welcome; | |
| 159 | ||
| 160 | fun restart () = | |
| 21940 | 161 | (sync_thy_loader (); | 
| 162 | tell_clear_goals (); | |
| 163 | tell_clear_response (); | |
| 27537 | 164 | Isar.init_point (); | 
| 27577 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 165 | welcome ()); | 
| 21642 | 166 | |
| 167 | ||
| 168 | (* theorem dependency output *) | |
| 169 | ||
| 170 | local | |
| 171 | ||
| 172 | val spaces_quote = space_implode " " o map quote; | |
| 173 | ||
| 174 | fun thm_deps_message (thms, deps) = | |
| 21948 | 175 |   emacs_notify ("Proof General, theorem dependencies of " ^ thms ^ " are " ^ deps);
 | 
| 21642 | 176 | |
| 28096 
046418f64474
theorem dependency output: Toplevel.add_hook, ProofGeneralPgip.new_thms_deps;
 wenzelm parents: 
28020diff
changeset | 177 | in | 
| 
046418f64474
theorem dependency output: Toplevel.add_hook, ProofGeneralPgip.new_thms_deps;
 wenzelm parents: 
28020diff
changeset | 178 | |
| 28103 
b79e61861f0f
simplified Toplevel.add_hook: cover successful transactions only;
 wenzelm parents: 
28100diff
changeset | 179 | fun setup_present_hook () = Toplevel.add_hook (fn _ => fn state => fn state' => | 
| 28106 | 180 | if print_mode_active thm_depsN andalso | 
| 181 | can Toplevel.theory_of state andalso Toplevel.is_theory state' | |
| 182 | then | |
| 28100 
7650d5e0f8fb
refined theorem dependency output: previous state needs to contain a theory (not empty toplevel);
 wenzelm parents: 
28096diff
changeset | 183 | let val (names, deps) = | 
| 
7650d5e0f8fb
refined theorem dependency output: previous state needs to contain a theory (not empty toplevel);
 wenzelm parents: 
28096diff
changeset | 184 | ProofGeneralPgip.new_thms_deps (Toplevel.theory_of state) (Toplevel.theory_of state') | 
| 
7650d5e0f8fb
refined theorem dependency output: previous state needs to contain a theory (not empty toplevel);
 wenzelm parents: 
28096diff
changeset | 185 | in | 
| 21968 | 186 | if null names orelse null deps then () | 
| 187 | else thm_deps_message (spaces_quote names, spaces_quote deps) | |
| 188 | end | |
| 28096 
046418f64474
theorem dependency output: Toplevel.add_hook, ProofGeneralPgip.new_thms_deps;
 wenzelm parents: 
28020diff
changeset | 189 | else ()); | 
| 21642 | 190 | |
| 191 | end; | |
| 192 | ||
| 193 | ||
| 194 | (* additional outer syntax for Isar *) | |
| 195 | ||
| 196 | local structure P = OuterParse and K = OuterKeyword in | |
| 197 | ||
| 25192 
b568f8c5d5ca
made command 'undo' silent ('ProofGeneral.undo' becomes a historical relic);
 wenzelm parents: 
24882diff
changeset | 198 | fun undoP () = (*undo without output -- historical*) | 
| 21642 | 199 | OuterSyntax.improper_command "ProofGeneral.undo" "(internal)" K.control | 
| 27535 | 200 | (Scan.succeed (Toplevel.no_timing o Toplevel.imperative (fn () => Isar.undo 1))); | 
| 21642 | 201 | |
| 24867 | 202 | fun restartP () = | 
| 21642 | 203 | OuterSyntax.improper_command "ProofGeneral.restart" "(internal)" K.control | 
| 204 | (P.opt_unit >> (Toplevel.no_timing oo K (Toplevel.imperative restart))); | |
| 205 | ||
| 24867 | 206 | fun kill_proofP () = | 
| 21642 | 207 | OuterSyntax.improper_command "ProofGeneral.kill_proof" "(internal)" K.control | 
| 27532 | 208 | (Scan.succeed (Toplevel.no_timing o | 
| 209 | Toplevel.imperative (fn () => (Isar.kill_proof (); tell_clear_goals ())))); | |
| 210 | ||
| 24867 | 211 | fun inform_file_processedP () = | 
| 21642 | 212 | OuterSyntax.improper_command "ProofGeneral.inform_file_processed" "(internal)" K.control | 
| 27577 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 213 | (P.name >> (fn file => | 
| 
7c7a9a343ca5
proper_inform_file_processed: new implementation based on global Isar state (cf. isar.ML);
 wenzelm parents: 
27537diff
changeset | 214 | Toplevel.no_timing o Toplevel.imperative (fn () => inform_file_processed file))); | 
| 21642 | 215 | |
| 24867 | 216 | fun inform_file_retractedP () = | 
| 21642 | 217 | OuterSyntax.improper_command "ProofGeneral.inform_file_retracted" "(internal)" K.control | 
| 218 | (P.name >> (Toplevel.no_timing oo | |
| 219 | (fn file => Toplevel.imperative (fn () => inform_file_retracted file)))); | |
| 220 | ||
| 24867 | 221 | fun process_pgipP () = | 
| 21642 | 222 | OuterSyntax.improper_command "ProofGeneral.process_pgip" "(internal)" K.control | 
| 223 | (P.text >> (Toplevel.no_timing oo | |
| 224 | (fn txt => Toplevel.imperative (fn () => ProofGeneralPgip.process_pgip txt)))); | |
| 225 | ||
| 24867 | 226 | fun init_outer_syntax () = List.app (fn f => f ()) | 
| 27535 | 227 | [undoP, restartP, kill_proofP, inform_file_processedP, inform_file_retractedP, process_pgipP]; | 
| 21642 | 228 | |
| 229 | end; | |
| 230 | ||
| 231 | ||
| 232 | (* init *) | |
| 233 | ||
| 234 | val initialized = ref false; | |
| 235 | ||
| 22699 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 236 | fun init false = panic "No Proof General interface support for Isabelle/classic mode." | 
| 21940 | 237 | | init true = | 
| 21968 | 238 | (! initialized orelse | 
| 22590 | 239 | (Output.no_warnings init_outer_syntax (); | 
| 21968 | 240 | setup_xsymbols_output (); | 
| 241 | setup_messages (); | |
| 22590 | 242 | ProofGeneralPgip.init_pgip_channel (! Output.priority_fn); | 
| 21968 | 243 | setup_thy_loader (); | 
| 244 | setup_present_hook (); | |
| 245 | set initialized); | |
| 246 | sync_thy_loader (); | |
| 25749 | 247 | change print_mode (update (op =) proof_generalN); | 
| 26643 | 248 |        Isar.toplevel_loop {init = true, welcome = true, sync = true, secure = Secure.is_secure ()});
 | 
| 21642 | 249 | |
| 250 | end; |