src/Pure/ProofGeneral/proof_general_emacs.ML
changeset 52006 9402221f77dd
parent 51996 26aecb553c74
child 52007 0b1183012a3c
equal deleted inserted replaced
51996:26aecb553c74 52006:9402221f77dd
     1 (*  Title:      Pure/ProofGeneral/proof_general_emacs.ML
       
     2     Author:     David Aspinall and Markus Wenzel
       
     3 
       
     4 Isabelle/Isar configuration for Emacs Proof General.
       
     5 See also http://proofgeneral.inf.ed.ac.uk
       
     6 *)
       
     7 
       
     8 signature PROOF_GENERAL =
       
     9 sig
       
    10   val init: unit -> unit
       
    11   structure ThyLoad: sig val add_path: string -> unit end
       
    12 end;
       
    13 
       
    14 structure ProofGeneral: PROOF_GENERAL =
       
    15 struct
       
    16 
       
    17 (* render markup *)
       
    18 
       
    19 fun special ch = chr 1 ^ ch;
       
    20 
       
    21 local
       
    22 
       
    23 fun render_trees ts = fold render_tree ts
       
    24 and render_tree t =
       
    25   (case XML.unwrap_elem t of
       
    26     SOME (_, ts) => render_trees ts
       
    27   | NONE =>
       
    28       (case t of
       
    29         XML.Text s => Buffer.add s
       
    30       | XML.Elem ((name, props), ts) =>
       
    31           let
       
    32             val (bg, en) =
       
    33               if null ts then Markup.no_output
       
    34               else if name = Markup.stateN then (special "O" ^ "\n", "\n" ^ special "P")
       
    35               else if name = Markup.sendbackN then (special "W", special "X")
       
    36               else if name = Markup.intensifyN then (special "0", special "1")
       
    37               else if name = Markup.tfreeN then (special "C", special "A")
       
    38               else if name = Markup.tvarN then (special "D", special "A")
       
    39               else if name = Markup.freeN then (special "E", special "A")
       
    40               else if name = Markup.boundN then (special "F", special "A")
       
    41               else if name = Markup.varN then (special "G", special "A")
       
    42               else if name = Markup.skolemN then (special "H", special "A")
       
    43               else
       
    44                 (case Markup.get_entity_kind (name, props) of
       
    45                   SOME kind =>
       
    46                     if kind = Markup.classN then (special "B", special "A")
       
    47                     else Markup.no_output
       
    48                 | NONE => Markup.no_output);
       
    49           in Buffer.add bg #> render_trees ts #> Buffer.add en end));
       
    50 
       
    51 in
       
    52 
       
    53 fun render text =
       
    54   Buffer.content (render_trees (YXML.parse_body text) Buffer.empty);
       
    55 
       
    56 end;
       
    57 
       
    58 
       
    59 (* messages *)
       
    60 
       
    61 fun message bg en prfx text =
       
    62   (case render text of
       
    63     "" => ()
       
    64   | s => Output.physical_writeln (enclose bg en (prefix_lines prfx s)));
       
    65 
       
    66 fun setup_messages () =
       
    67  (Output.Private_Hooks.writeln_fn := message "" "" "";
       
    68   Output.Private_Hooks.status_fn := (fn _ => ());
       
    69   Output.Private_Hooks.report_fn := (fn _ => ());
       
    70   Output.Private_Hooks.urgent_message_fn := message (special "I") (special "J") "";
       
    71   Output.Private_Hooks.tracing_fn := message (special "I" ^ special "V") (special "J") "";
       
    72   Output.Private_Hooks.warning_fn := message (special "K") (special "L") "### ";
       
    73   Output.Private_Hooks.error_fn := (fn (_, s) => message (special "M") (special "N") "*** " s);
       
    74   Output.Private_Hooks.prompt_fn := (fn s => Output.physical_stdout (render s ^ special "S")));
       
    75 
       
    76 
       
    77 (* notification *)
       
    78 
       
    79 val emacs_notify = message (special "I") (special "J") "";
       
    80 
       
    81 fun tell_clear_goals () =
       
    82   emacs_notify "Proof General, please clear the goals buffer.";
       
    83 
       
    84 fun tell_clear_response () =
       
    85   emacs_notify "Proof General, please clear the response buffer.";
       
    86 
       
    87 fun tell_file_loaded path =
       
    88   emacs_notify ("Proof General, this file is loaded: " ^ quote (File.platform_path path));
       
    89 
       
    90 fun tell_file_retracted path =
       
    91   emacs_notify ("Proof General, you can unlock the file " ^ quote (File.platform_path path));
       
    92 
       
    93 
       
    94 (* theory loader actions *)
       
    95 
       
    96 local
       
    97 
       
    98 fun trace_action action name =
       
    99   if action = Thy_Info.Update then
       
   100     List.app tell_file_loaded (Thy_Info.loaded_files name)
       
   101   else if action = Thy_Info.Remove then
       
   102     List.app tell_file_retracted (Thy_Info.loaded_files name)
       
   103   else ();
       
   104 
       
   105 in
       
   106   fun setup_thy_loader () = Thy_Info.add_hook trace_action;
       
   107   fun sync_thy_loader () = List.app (trace_action Thy_Info.Update) (Thy_Info.get_names ());
       
   108 end;
       
   109 
       
   110 
       
   111 (* get informed about files *)
       
   112 
       
   113 (*liberal low-level version*)
       
   114 val thy_name = perhaps (try (unsuffix ".thy")) o List.last o space_explode "/";
       
   115 
       
   116 val inform_file_retracted = Thy_Info.kill_thy o thy_name;
       
   117 
       
   118 fun inform_file_processed file =
       
   119   let
       
   120     val name = thy_name file;
       
   121     val _ = name = "" andalso error ("Bad file name: " ^ quote file);
       
   122     val _ =
       
   123       Thy_Info.register_thy (Toplevel.end_theory Position.none (Isar.state ()))
       
   124         handle ERROR msg =>
       
   125           (warning (cat_lines ["Failed to register theory: " ^ quote name, msg]);
       
   126             tell_file_retracted (Thy_Load.thy_path (Path.basic name)))
       
   127     val _ = Isar.init ();
       
   128   in () end;
       
   129 
       
   130 
       
   131 (* restart top-level loop (keeps most state information) *)
       
   132 
       
   133 val welcome = Output.urgent_message o Session.welcome;
       
   134 
       
   135 fun restart () =
       
   136  (sync_thy_loader ();
       
   137   tell_clear_goals ();
       
   138   tell_clear_response ();
       
   139   Isar.init ();
       
   140   welcome ());
       
   141 
       
   142 
       
   143 (* theorem dependencies *)
       
   144 
       
   145 local
       
   146 
       
   147 fun add_proof_body (PBody {thms, ...}) =
       
   148   thms |> fold (fn (_, (name, _, _)) => name <> "" ? Symtab.update (name, ()));
       
   149 
       
   150 fun add_thm th =
       
   151   (case Thm.proof_body_of th of
       
   152     PBody {proof = PThm (_, ((name, _, _), body)), ...} =>
       
   153       if Thm.has_name_hint th andalso Thm.get_name_hint th = name
       
   154       then add_proof_body (Future.join body)
       
   155       else I
       
   156   | body => add_proof_body body);
       
   157 
       
   158 in
       
   159 
       
   160 fun thm_deps ths =
       
   161   let
       
   162     (* FIXME proper derivation names!? *)
       
   163     val names = map Thm.get_name_hint (filter Thm.has_name_hint ths);
       
   164     val deps = Symtab.keys (fold add_thm ths Symtab.empty);
       
   165   in (names, deps) end;
       
   166 
       
   167 end;
       
   168 
       
   169 
       
   170 (* report theorem dependencies *)
       
   171 
       
   172 local
       
   173 
       
   174 val spaces_quote = space_implode " " o map quote;
       
   175 
       
   176 fun thm_deps_message (thms, deps) =
       
   177   emacs_notify ("Proof General, theorem dependencies of " ^ thms ^ " are " ^ deps);
       
   178 
       
   179 in
       
   180 
       
   181 fun setup_present_hook () = Toplevel.add_hook (fn _ => fn state => fn state' =>
       
   182   if print_mode_active Preferences.thm_depsN andalso
       
   183     can Toplevel.theory_of state andalso Toplevel.is_theory state'
       
   184   then
       
   185     let
       
   186       val prev_facts = Global_Theory.facts_of (Toplevel.theory_of state);
       
   187       val facts = Global_Theory.facts_of (Toplevel.theory_of state');
       
   188       val (names, deps) = thm_deps (maps #2 (Facts.dest_static [prev_facts] facts));
       
   189     in
       
   190       if null names orelse null deps then ()
       
   191       else thm_deps_message (spaces_quote names, spaces_quote deps)
       
   192     end
       
   193   else ());
       
   194 
       
   195 end;
       
   196 
       
   197 
       
   198 (* additional outer syntax for Isar *)
       
   199 
       
   200 val _ =
       
   201   Outer_Syntax.improper_command
       
   202     (("ProofGeneral.pr", Keyword.diag), Position.none) "print state (internal)"
       
   203     (Scan.succeed (Toplevel.keep (fn state =>
       
   204       if Toplevel.is_toplevel state orelse Toplevel.is_theory state then tell_clear_goals ()
       
   205       else (Toplevel.quiet := false; Toplevel.print_state true state))));
       
   206 
       
   207 val _ = (*undo without output -- historical*)
       
   208   Outer_Syntax.improper_command
       
   209     (("ProofGeneral.undo", Keyword.control), Position.none) "(internal)"
       
   210     (Scan.succeed (Toplevel.imperative (fn () => Isar.undo 1)));
       
   211 
       
   212 val _ =
       
   213   Outer_Syntax.improper_command
       
   214     (("ProofGeneral.restart", Keyword.control), Position.none) "(internal)"
       
   215     (Parse.opt_unit >> (K (Toplevel.imperative restart)));
       
   216 
       
   217 val _ =
       
   218   Outer_Syntax.improper_command
       
   219     (("ProofGeneral.kill_proof", Keyword.control), Position.none) "(internal)"
       
   220     (Scan.succeed (Toplevel.imperative (fn () => (Isar.kill_proof (); tell_clear_goals ()))));
       
   221 
       
   222 val _ =
       
   223   Outer_Syntax.improper_command
       
   224     (("ProofGeneral.inform_file_processed", Keyword.control), Position.none) "(internal)"
       
   225     (Parse.name >> (fn file => Toplevel.imperative (fn () => inform_file_processed file)));
       
   226 
       
   227 val _ =
       
   228   Outer_Syntax.improper_command
       
   229     (("ProofGeneral.inform_file_retracted", Keyword.control), Position.none) "(internal)"
       
   230     (Parse.name >> (fn file => Toplevel.imperative (fn () => inform_file_retracted file)));
       
   231 
       
   232 
       
   233 (* init *)
       
   234 
       
   235 val proof_general_emacsN = "ProofGeneralEmacs";
       
   236 
       
   237 val initialized = Unsynchronized.ref false;
       
   238 
       
   239 fun init () =
       
   240   (if ! initialized then ()
       
   241    else
       
   242     (Output.add_mode Symbol.xsymbolsN Symbol.output Output.default_escape;
       
   243      Output.add_mode proof_general_emacsN
       
   244       Output.default_output Output.default_escape;
       
   245      Markup.add_mode proof_general_emacsN YXML.output_markup;
       
   246      setup_messages ();
       
   247      setup_thy_loader ();
       
   248      setup_present_hook ();
       
   249      initialized := true);
       
   250    sync_thy_loader ();
       
   251    Unsynchronized.change print_mode (update (op =) proof_general_emacsN);
       
   252    Secure.PG_setup ();
       
   253    Isar.toplevel_loop TextIO.stdIn
       
   254     {init = true, welcome = true, sync = true, secure = Secure.is_secure ()});
       
   255 
       
   256 
       
   257 (* fake old ThyLoad -- with new semantics *)
       
   258 
       
   259 structure ThyLoad =
       
   260 struct
       
   261   val add_path = Thy_Load.set_master_path o Path.explode;
       
   262 end;
       
   263 
       
   264 end;