| author | nipkow | 
| Mon, 17 Dec 2007 17:01:54 +0100 | |
| changeset 25665 | faabc08af882 | 
| parent 25630 | 98dd706319a1 | 
| child 25749 | 10e7feb4e595 | 
| 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. | |
| 6 | See http://proofgeneral.inf.ed.ac.uk | |
| 7 | *) | |
| 8 | ||
| 9 | signature PROOF_GENERAL = | |
| 10 | sig | |
| 11 | val init: bool -> unit | |
| 24874 | 12 | val init_outer_syntax: unit -> unit | 
| 24289 | 13 | val sendback: string -> Pretty.T list -> unit | 
| 21642 | 14 | end; | 
| 15 | ||
| 16 | structure ProofGeneral: PROOF_GENERAL = | |
| 17 | struct | |
| 18 | ||
| 21945 | 19 | |
| 21642 | 20 | (* print modes *) | 
| 21 | ||
| 22 | val proof_generalN = "ProofGeneralEmacs"; (*token markup (colouring vars, etc.)*) | |
| 23 | val pgasciiN = "PGASCII"; (*plain 7-bit ASCII communication*) | |
| 24 | val thm_depsN = "thm_deps"; (*meta-information about theorem deps*) | |
| 25 | ||
| 26 | fun special oct = | |
| 22590 | 27 | if print_mode_active pgasciiN then chr 1 ^ chr (ord (oct_char oct) - 167) | 
| 21642 | 28 | else oct_char oct; | 
| 29 | ||
| 30 | ||
| 31 | (* text output: print modes for xsymbol *) | |
| 32 | ||
| 33 | local | |
| 34 | ||
| 35 | fun xsym_output "\\" = "\\\\" | |
| 36 | | xsym_output s = if Symbol.is_raw s then Symbol.decode_raw s else s; | |
| 37 | ||
| 38 | fun xsymbols_output s = | |
| 22590 | 39 | if print_mode_active Symbol.xsymbolsN andalso exists_string (equal "\\") s then | 
| 21642 | 40 | let val syms = Symbol.explode s | 
| 23621 | 41 | in (implode (map xsym_output syms), Symbol.length syms) end | 
| 42 | else Output.default_output s; | |
| 21642 | 43 | |
| 44 | in | |
| 45 | ||
| 46 | fun setup_xsymbols_output () = | |
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 47 | Output.add_mode Symbol.xsymbolsN xsymbols_output Symbol.encode_raw; | 
| 21642 | 48 | |
| 49 | end; | |
| 50 | ||
| 51 | ||
| 52 | (* token translations *) | |
| 53 | ||
| 54 | local | |
| 55 | ||
| 56 | fun end_tag () = special "350"; | |
| 57 | val class_tag = ("class", fn () => special "351");
 | |
| 58 | val tfree_tag = ("tfree", fn () => special "352");
 | |
| 59 | val tvar_tag = ("tvar", fn () => special "353");
 | |
| 60 | val free_tag = ("free", fn () => special "354");
 | |
| 61 | val bound_tag = ("bound", fn () => special "355");
 | |
| 62 | val var_tag = ("var", fn () => special "356");
 | |
| 63 | val skolem_tag = ("skolem", fn () => special "357");
 | |
| 64 | ||
| 65 | fun xml_atom kind x = XML.element "atom" [("kind", kind)] [XML.text x];
 | |
| 66 | ||
| 67 | fun tagit (kind, bg_tag) x = | |
| 23621 | 68 | (bg_tag () ^ x ^ end_tag (), Symbol.length (Symbol.explode x)); | 
| 21642 | 69 | |
| 70 | fun free_or_skolem x = | |
| 71 | (case try Name.dest_skolem x of | |
| 72 | NONE => tagit free_tag x | |
| 73 | | SOME x' => tagit skolem_tag x'); | |
| 74 | ||
| 75 | fun var_or_skolem s = | |
| 24244 | 76 | (case Lexicon.read_variable s of | 
| 21642 | 77 | SOME (x, i) => | 
| 78 | (case try Name.dest_skolem x of | |
| 79 | NONE => tagit var_tag s | |
| 80 | | SOME x' => tagit skolem_tag | |
| 22678 | 81 | (setmp show_question_marks true Term.string_of_vname (x', i))) | 
| 21642 | 82 | | NONE => tagit var_tag s); | 
| 83 | ||
| 84 | val proof_general_trans = | |
| 85 | Syntax.tokentrans_mode proof_generalN | |
| 86 |   [("class", tagit class_tag),
 | |
| 87 |    ("tfree", tagit tfree_tag),
 | |
| 88 |    ("tvar", tagit tvar_tag),
 | |
| 89 |    ("free", free_or_skolem),
 | |
| 90 |    ("bound", tagit bound_tag),
 | |
| 91 |    ("var", var_or_skolem)];
 | |
| 92 | ||
| 93 | in | |
| 94 | ||
| 24712 
64ed05609568
proper Sign operations instead of Theory aliases;
 wenzelm parents: 
24555diff
changeset | 95 | val _ = Context.add_setup (Sign.add_tokentrfuns proof_general_trans); | 
| 21642 | 96 | |
| 97 | end; | |
| 98 | ||
| 99 | ||
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 100 | (* common markup *) | 
| 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 101 | |
| 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 102 | fun proof_general_markup (name, props) = | 
| 25630 | 103 | let | 
| 104 | val (bg1, en1) = | |
| 105 |       if name = Markup.promptN then ("", special "372")
 | |
| 106 | else if name = Markup.stateN then (special "366" ^ "\n", "\n" ^ special "367") | |
| 107 | else if name = Markup.sendbackN then (special "376", special "377") | |
| 108 | else if name = Markup.hiliteN then (special "327", special "330") | |
| 109 |       else ("", "");
 | |
| 110 | val (bg2, en2) = | |
| 111 | if name <> Markup.promptN andalso print_mode_active IsabelleProcess.test_markupN | |
| 112 | then IsabelleProcess.test_markup (name, props) | |
| 113 |       else ("", "");
 | |
| 114 | in (bg1 ^ bg2, en2 ^ en1) end; | |
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 115 | |
| 23702 | 116 | val _ = Markup.add_mode proof_generalN proof_general_markup; | 
| 23641 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 117 | |
| 
d6f9d3acffaa
toplevel prompt/print_state: proper markup, removed hooks;
 wenzelm parents: 
23621diff
changeset | 118 | |
| 21642 | 119 | (* messages and notification *) | 
| 120 | ||
| 121 | fun decorate bg en prfx = | |
| 22590 | 122 | Output.writeln_default o enclose bg en o prefix_lines prfx; | 
| 21642 | 123 | |
| 124 | fun setup_messages () = | |
| 23662 | 125 | (Output.writeln_fn := Output.writeln_default; | 
| 22590 | 126 | Output.priority_fn := (fn s => decorate (special "360") (special "361") "" s); | 
| 127 | Output.tracing_fn := (fn s => decorate (special "360" ^ special "375") (special "361") "" s); | |
| 128 | Output.debug_fn := (fn s => decorate (special "362") (special "363") "+++ " s); | |
| 129 | Output.warning_fn := (fn s => decorate (special "362") (special "363") "### " s); | |
| 22699 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 130 | Output.error_fn := (fn s => decorate (special "364") (special "365") "*** " s)); | 
| 21642 | 131 | |
| 22699 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 132 | fun panic s = | 
| 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 133 |   (decorate (special "364") (special "365") "!!! " ("## SYSTEM EXIT ##\n" ^ s); exit 1);
 | 
| 21642 | 134 | |
| 135 | fun emacs_notify s = decorate (special "360") (special "361") "" s; | |
| 136 | ||
| 137 | fun tell_clear_goals () = | |
| 21940 | 138 | emacs_notify "Proof General, please clear the goals buffer."; | 
| 21642 | 139 | |
| 140 | fun tell_clear_response () = | |
| 21940 | 141 | emacs_notify "Proof General, please clear the response buffer."; | 
| 21642 | 142 | |
| 143 | fun tell_file_loaded path = | |
| 21940 | 144 |   emacs_notify ("Proof General, this file is loaded: " ^ quote (File.platform_path path));
 | 
| 21642 | 145 | |
| 146 | fun tell_file_retracted path = | |
| 21940 | 147 |   emacs_notify ("Proof General, you can unlock the file " ^ quote (File.platform_path path));
 | 
| 21642 | 148 | |
| 24289 | 149 | fun sendback heading prts = | 
| 150 | Pretty.writeln (Pretty.big_list heading [Pretty.markup Markup.sendback prts]); | |
| 151 | ||
| 21642 | 152 | |
| 153 | (* theory loader actions *) | |
| 154 | ||
| 155 | local | |
| 156 | ||
| 157 | fun trace_action action name = | |
| 158 | if action = ThyInfo.Update then | |
| 159 | List.app tell_file_loaded (ThyInfo.loaded_files name) | |
| 160 | else if action = ThyInfo.Outdate orelse action = ThyInfo.Remove then | |
| 161 | List.app tell_file_retracted (ThyInfo.loaded_files name) | |
| 162 | else (); | |
| 163 | ||
| 164 | in | |
| 165 | fun setup_thy_loader () = ThyInfo.add_hook trace_action; | |
| 166 | fun sync_thy_loader () = List.app (trace_action ThyInfo.Update) (ThyInfo.names ()); | |
| 167 | end; | |
| 168 | ||
| 169 | ||
| 21948 | 170 | (* get informed about files *) | 
| 21642 | 171 | |
| 25442 | 172 | local | 
| 21642 | 173 | |
| 25442 | 174 | (*liberal low-level version*) | 
| 175 | val thy_name = perhaps (try (unsuffix ".thy")) o List.last o space_explode "/"; | |
| 176 | val thy_path = ThyLoad.thy_path o thy_name; | |
| 177 | ||
| 178 | in | |
| 179 | ||
| 25443 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 wenzelm parents: 
25442diff
changeset | 180 | fun check_thy "" = false | 
| 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 wenzelm parents: 
25442diff
changeset | 181 | | check_thy file = ThyInfo.check_known_thy (thy_name file); | 
| 21642 | 182 | |
| 23913 | 183 | fun proper_inform_file_processed file () = | 
| 25442 | 184 | let | 
| 185 | val name = thy_name file; | |
| 186 | val _ = ThyInfo.touch_child_thys name; | |
| 187 | val _ = ThyInfo.register_thy name handle ERROR msg => | |
| 188 | (warning (cat_lines [msg, "Failed to register theory: " ^ quote name]); | |
| 189 | tell_file_retracted (thy_path file)); | |
| 190 | in () end; | |
| 21642 | 191 | |
| 25442 | 192 | fun vacuous_inform_file_processed file () = tell_file_retracted (thy_path file); | 
| 193 | ||
| 194 | val inform_file_retracted = ThyInfo.if_known_thy ThyInfo.remove_thy o thy_name; | |
| 195 | ||
| 196 | end; | |
| 21642 | 197 | |
| 198 | ||
| 199 | (* restart top-level loop (keeps most state information) *) | |
| 200 | ||
| 201 | val welcome = priority o Session.welcome; | |
| 202 | ||
| 203 | fun restart () = | |
| 21940 | 204 | (sync_thy_loader (); | 
| 205 | tell_clear_goals (); | |
| 206 | tell_clear_response (); | |
| 207 | welcome (); | |
| 208 | raise Toplevel.RESTART); | |
| 21642 | 209 | |
| 210 | ||
| 211 | (* theorem dependency output *) | |
| 212 | ||
| 213 | local | |
| 214 | ||
| 215 | val spaces_quote = space_implode " " o map quote; | |
| 216 | ||
| 217 | fun thm_deps_message (thms, deps) = | |
| 21948 | 218 |   emacs_notify ("Proof General, theorem dependencies of " ^ thms ^ " are " ^ deps);
 | 
| 21642 | 219 | |
| 21968 | 220 | fun tell_thm_deps ths = | 
| 22590 | 221 | if print_mode_active thm_depsN then | 
| 21968 | 222 | let | 
| 22228 
7c27195a4afc
proper use of PureThy.has_name_hint instead of hard-wired string'';
 wenzelm parents: 
22225diff
changeset | 223 | val names = map PureThy.get_name_hint (filter PureThy.has_name_hint ths); | 
| 22225 | 224 | val deps = Symtab.keys (fold Proofterm.thms_of_proof' | 
| 225 | (map Thm.proof_of ths) Symtab.empty); | |
| 21968 | 226 | in | 
| 227 | if null names orelse null deps then () | |
| 228 | else thm_deps_message (spaces_quote names, spaces_quote deps) | |
| 229 | end | |
| 230 | else (); | |
| 21642 | 231 | |
| 232 | in | |
| 233 | ||
| 234 | fun setup_present_hook () = | |
| 235 | Present.add_hook (fn _ => fn res => tell_thm_deps (maps #2 res)); | |
| 236 | ||
| 237 | end; | |
| 238 | ||
| 239 | ||
| 240 | (* additional outer syntax for Isar *) | |
| 241 | ||
| 242 | local structure P = OuterParse and K = OuterKeyword in | |
| 243 | ||
| 25192 
b568f8c5d5ca
made command 'undo' silent ('ProofGeneral.undo' becomes a historical relic);
 wenzelm parents: 
24882diff
changeset | 244 | fun undoP () = (*undo without output -- historical*) | 
| 21642 | 245 | OuterSyntax.improper_command "ProofGeneral.undo" "(internal)" K.control | 
| 246 | (Scan.succeed (Toplevel.no_timing o IsarCmd.undo)); | |
| 247 | ||
| 24867 | 248 | fun restartP () = | 
| 21642 | 249 | OuterSyntax.improper_command "ProofGeneral.restart" "(internal)" K.control | 
| 250 | (P.opt_unit >> (Toplevel.no_timing oo K (Toplevel.imperative restart))); | |
| 251 | ||
| 24867 | 252 | fun kill_proofP () = | 
| 21642 | 253 | OuterSyntax.improper_command "ProofGeneral.kill_proof" "(internal)" K.control | 
| 254 | (Scan.succeed (Toplevel.no_timing o IsarCmd.kill_proof_notify tell_clear_goals)); | |
| 255 | ||
| 24867 | 256 | fun inform_file_processedP () = | 
| 21642 | 257 | OuterSyntax.improper_command "ProofGeneral.inform_file_processed" "(internal)" K.control | 
| 258 | (P.name >> (fn file => Toplevel.no_timing o | |
| 25443 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 wenzelm parents: 
25442diff
changeset | 259 | Toplevel.imperative (fn () => error "Bad file name") o | 
| 
1af14e8f225b
inform_file_processed: made even more robust against bad file specs;
 wenzelm parents: 
25442diff
changeset | 260 | Toplevel.init_empty (fn _ => file <> "") (vacuous_inform_file_processed file) o | 
| 21642 | 261 | Toplevel.kill o | 
| 25442 | 262 | Toplevel.init_empty (fn _ => check_thy file) (proper_inform_file_processed file))); | 
| 21642 | 263 | |
| 24867 | 264 | fun inform_file_retractedP () = | 
| 21642 | 265 | OuterSyntax.improper_command "ProofGeneral.inform_file_retracted" "(internal)" K.control | 
| 266 | (P.name >> (Toplevel.no_timing oo | |
| 267 | (fn file => Toplevel.imperative (fn () => inform_file_retracted file)))); | |
| 268 | ||
| 24867 | 269 | fun process_pgipP () = | 
| 21642 | 270 | OuterSyntax.improper_command "ProofGeneral.process_pgip" "(internal)" K.control | 
| 271 | (P.text >> (Toplevel.no_timing oo | |
| 272 | (fn txt => Toplevel.imperative (fn () => ProofGeneralPgip.process_pgip txt)))); | |
| 273 | ||
| 24867 | 274 | fun init_outer_syntax () = List.app (fn f => f ()) | 
| 21948 | 275 | [undoP, restartP, kill_proofP, inform_file_processedP, inform_file_retractedP, process_pgipP]; | 
| 21642 | 276 | |
| 277 | end; | |
| 278 | ||
| 279 | ||
| 280 | (* init *) | |
| 281 | ||
| 282 | val initialized = ref false; | |
| 283 | ||
| 22699 
938c1011ac94
removed unused Output.panic hook -- internal to PG wrapper;
 wenzelm parents: 
22678diff
changeset | 284 | fun init false = panic "No Proof General interface support for Isabelle/classic mode." | 
| 21940 | 285 | | init true = | 
| 21968 | 286 | (! initialized orelse | 
| 22590 | 287 | (Output.no_warnings init_outer_syntax (); | 
| 21968 | 288 | setup_xsymbols_output (); | 
| 289 | setup_messages (); | |
| 22590 | 290 | ProofGeneralPgip.init_pgip_channel (! Output.priority_fn); | 
| 21968 | 291 | setup_thy_loader (); | 
| 292 | setup_present_hook (); | |
| 293 | set initialized); | |
| 294 | sync_thy_loader (); | |
| 295 | change print_mode (cons proof_generalN o remove (op =) proof_generalN); | |
| 296 | Isar.sync_main ()); | |
| 21642 | 297 | |
| 298 | end; |