]> matita.cs.unibo.it Git - helm.git/blob - helm/matita/matitaScript.ml
test branch
[helm.git] / helm / matita / matitaScript.ml
1 (* Copyright (C) 2004-2005, HELM Team.
2  * 
3  * This file is part of HELM, an Hypertextual, Electronic
4  * Library of Mathematics, developed at the Computer Science
5  * Department, University of Bologna, Italy.
6  * 
7  * HELM is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * HELM is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with HELM; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://helm.cs.unibo.it/
24  *)
25
26 (* $Id$ *)
27
28 open Printf
29 open GrafiteTypes
30
31 module TA = GrafiteAst
32
33 let debug = false
34 let debug_print = if debug then prerr_endline else ignore
35
36   (** raised when one of the script margins (top or bottom) is reached *)
37 exception Margin
38 exception NoUnfinishedProof
39
40 let safe_substring s i j =
41   try String.sub s i j with Invalid_argument _ -> assert false
42
43 let heading_nl_RE = Pcre.regexp "^\\s*\n\\s*"
44 let heading_nl_RE' = Pcre.regexp "^(\\s*\n\\s*)((.|\n)*)"
45 let only_dust_RE = Pcre.regexp "^(\\s|\n|%%[^\n]*\n)*$"
46 let multiline_RE = Pcre.regexp "^\n[^\n]+$"
47 let newline_RE = Pcre.regexp "\n"
48  
49 let comment str =
50   if Pcre.pmatch ~rex:multiline_RE str then
51     "\n(** " ^ (Pcre.replace ~rex:newline_RE str) ^ " *)"
52   else
53     "\n(**\n" ^ str ^ "\n*)"
54                      
55 let first_line s =
56   let s = Pcre.replace ~rex:heading_nl_RE s in
57   try
58     let nl_pos = String.index s '\n' in
59     String.sub s 0 nl_pos
60   with Not_found -> s
61
62   (** creates a statement AST for the Goal tactic, e.g. "goal 7" *)
63 let goal_ast n =
64   let module A = GrafiteAst in
65   let loc = HExtlib.dummy_floc in
66   A.Executable (loc, A.Tactical (loc,
67     A.Tactic (loc, A.Goal (loc, n)),
68     Some (A.Dot loc)))
69
70 type guistuff = {
71   mathviewer:MatitaTypes.mathViewer;
72   urichooser: UriManager.uri list -> UriManager.uri list;
73   ask_confirmation: title:string -> message:string -> [`YES | `NO | `CANCEL];
74   develcreator: containing:string option -> unit;
75   mutable filenamedata: string option * MatitamakeLib.development option
76 }
77
78 let eval_with_engine guistuff lexicon_status grafite_status user_goal
79  parsed_text st
80 =
81   let module TAPp = GrafiteAstPp in
82   let parsed_text_length = String.length parsed_text in
83   let initial_space,parsed_text =
84    try
85     let pieces = Pcre.extract ~rex:heading_nl_RE' parsed_text in
86      pieces.(1), pieces.(2)
87    with
88     Not_found -> "", parsed_text in
89   let inital_space,new_grafite_status,new_lexicon_status,new_status_and_text_list' =
90    (* the code commented out adds the "select" command if needed *)
91    initial_space,grafite_status,lexicon_status,[] in
92 (* let loc, ex = 
93     match st with TA.Executable (loc,ex) -> loc, ex | _ -> assert false in
94     match grafite_status.proof_status with
95      | Incomplete_proof { stack = stack }
96       when not (List.mem user_goal (Continuationals.head_goals stack)) ->
97         let grafite_status =
98           MatitaEngine.eval_ast
99             ~do_heavy_checks:true grafite_status (goal_ast user_goal)
100         in
101         let initial_space = if initial_space = "" then "\n" else initial_space
102         in
103         "\n", grafite_status,
104         [ grafite_status,
105           initial_space ^ TAPp.pp_tactical (TA.Select (loc, [user_goal])) ]
106       | _ -> initial_space,grafite_status,[] in *)
107   let enriched_history_fragment =
108    MatitaEngine.eval_ast ~do_heavy_checks:true
109     new_lexicon_status new_grafite_status st
110   in
111   let _,new_text_list_rev = 
112     let module DTE = DisambiguateTypes.Environment in
113     let module UM = UriManager in
114     List.fold_right (
115       fun (_,alias) (initial_space,acc) ->
116        match alias with
117           None -> initial_space,initial_space::acc
118         | Some (k,((v,_) as value)) ->
119            let new_text =
120             let initial_space =
121              if initial_space = "" then "\n" else initial_space
122             in
123              initial_space ^
124               DisambiguatePp.pp_environment
125                (DisambiguateTypes.Environment.add k value
126                  DisambiguateTypes.Environment.empty)
127            in
128             "\n",new_text::acc
129     ) enriched_history_fragment (initial_space,[]) in
130   let new_text_list_rev =
131    match enriched_history_fragment,new_text_list_rev with
132       (_,None)::_, initial_space::tl -> (initial_space ^ parsed_text)::tl
133     | _,_ -> assert false
134   in
135    let res =
136     try
137      List.combine (fst (List.split enriched_history_fragment)) new_text_list_rev
138     with
139      Invalid_argument _ -> assert false
140    in
141     res,parsed_text_length
142
143 let eval_with_engine
144      guistuff lexicon_status grafite_status user_goal parsed_text st
145 =
146   try
147    eval_with_engine guistuff lexicon_status grafite_status user_goal parsed_text
148     st
149   with
150   | DependenciesParser.UnableToInclude what 
151   | GrafiteEngine.IncludedFileNotCompiled what as exc ->
152       let compile_needed_and_go_on d =
153         let target = what in
154         let refresh_cb () = 
155           while Glib.Main.pending () do ignore(Glib.Main.iteration false); done
156         in
157         if not(MatitamakeLib.build_development_in_bg ~target refresh_cb d) then
158           raise exc
159         else
160          eval_with_engine guistuff lexicon_status grafite_status user_goal
161           parsed_text st
162       in
163       let do_nothing () = [], 0 in
164       let handle_with_devel d =
165         let name = MatitamakeLib.name_for_development d in
166         let title = "Unable to include " ^ what in
167         let message = 
168           what ^ " is handled by development <b>" ^ name ^ "</b>.\n\n" ^
169           "<i>Should I compile it and Its dependencies?</i>"
170         in
171         (match guistuff.ask_confirmation ~title ~message with
172         | `YES -> compile_needed_and_go_on d
173         | `NO -> raise exc
174         | `CANCEL -> do_nothing ())
175       in
176       let handle_without_devel filename =
177         let title = "Unable to include " ^ what in
178         let message = 
179          what ^ " is <b>not</b> handled by a development.\n" ^
180          "All dependencies are automatically solved for a development.\n\n" ^
181          "<i>Do you want to set up a development?</i>"
182         in
183         (match guistuff.ask_confirmation ~title ~message with
184         | `YES -> 
185             (match filename with
186             | Some f -> 
187                 guistuff.develcreator ~containing:(Some (Filename.dirname f))
188             | None -> guistuff.develcreator ~containing:None);
189             do_nothing ()
190         | `NO -> raise exc
191         | `CANCEL -> do_nothing())
192       in
193       match guistuff.filenamedata with
194       | None,None -> handle_without_devel None
195       | None,Some d -> handle_with_devel d
196       | Some f,_ ->
197           match MatitamakeLib.development_for_dir (Filename.dirname f) with
198           | None -> handle_without_devel (Some f)
199           | Some d -> handle_with_devel d
200 ;;
201
202 let pp_eager_statement_ast =
203   GrafiteAstPp.pp_statement ~term_pp:CicNotationPp.pp_term
204     ~lazy_term_pp:(fun _ -> assert false) ~obj_pp:(fun _ -> assert false)
205  
206 let rec eval_macro include_paths (buffer : GText.buffer) guistuff lexicon_status grafite_status user_goal unparsed_text parsed_text script mac =
207   let module TAPp = GrafiteAstPp in
208   let module MQ = MetadataQuery in
209   let module MDB = LibraryDb in
210   let module CTC = CicTypeChecker in
211   let module CU = CicUniv in
212   (* no idea why ocaml wants this *)
213   let parsed_text_length = String.length parsed_text in
214   let dbd = LibraryDb.instance () in
215   (* XXX use a real CIC -> string pretty printer *)
216   let pp_macro = TAPp.pp_macro ~term_pp:CicPp.ppterm in
217   match mac with
218   (* WHELP's stuff *)
219   | TA.WMatch (loc, term) -> 
220      let l =  Whelp.match_term ~dbd term in
221      let query_url =
222        MatitaMisc.strip_suffix ~suffix:"."
223          (HExtlib.trim_blanks unparsed_text)
224      in
225      let entry = `Whelp (query_url, l) in
226      guistuff.mathviewer#show_uri_list ~reuse:true ~entry l;
227      [], parsed_text_length
228   | TA.WInstance (loc, term) ->
229      let l = Whelp.instance ~dbd term in
230      let entry = `Whelp (pp_macro (TA.WInstance (loc, term)), l) in
231      guistuff.mathviewer#show_uri_list ~reuse:true ~entry l;
232      [], parsed_text_length
233   | TA.WLocate (loc, s) -> 
234      let l = Whelp.locate ~dbd s in
235      let entry = `Whelp (pp_macro (TA.WLocate (loc, s)), l) in
236      guistuff.mathviewer#show_uri_list ~reuse:true ~entry l;
237      [], parsed_text_length
238   | TA.WElim (loc, term) ->
239      let uri =
240        match term with
241        | Cic.MutInd (uri,n,_) -> UriManager.uri_of_uriref uri n None 
242        | _ -> failwith "Not a MutInd"
243      in
244      let l = Whelp.elim ~dbd uri in
245      let entry = `Whelp (pp_macro (TA.WElim (loc, term)), l) in
246      guistuff.mathviewer#show_uri_list ~reuse:true ~entry l;
247      [], parsed_text_length
248   | TA.WHint (loc, term) ->
249      let s = ((None,[0,[],term], Cic.Meta (0,[]) ,term),0) in
250      let l = List.map fst (MQ.experimental_hint ~dbd s) in
251      let entry = `Whelp (pp_macro (TA.WHint (loc, term)), l) in
252      guistuff.mathviewer#show_uri_list ~reuse:true ~entry l;
253      [], parsed_text_length
254   (* REAL macro *)
255   | TA.Hint loc -> 
256       let user_goal' =
257        match user_goal with
258           Some n -> n
259         | None -> raise NoUnfinishedProof
260       in
261       let proof = GrafiteTypes.get_current_proof grafite_status in
262       let proof_status = proof,user_goal' in
263       let l = List.map fst (MQ.experimental_hint ~dbd proof_status) in
264       let selected = guistuff.urichooser l in
265       (match selected with
266       | [] -> [], parsed_text_length
267       | [uri] -> 
268           let suri = UriManager.string_of_uri uri in
269           let ast loc =
270             TA.Executable (loc, (TA.Tactical (loc,
271               TA.Tactic (loc,
272                 TA.Apply (loc, CicNotationPt.Uri (suri, None))),
273                 Some (TA.Dot loc)))) in
274           let text =
275            comment parsed_text ^ "\n" ^
276             pp_eager_statement_ast (ast HExtlib.dummy_floc) in
277           let text_len = String.length text in
278           let loc = HExtlib.floc_of_loc (0,text_len) in
279           let statement = `Ast (GrafiteParser.LSome (ast loc),text) in
280           let res,_parsed_text_len =
281            eval_statement include_paths buffer guistuff lexicon_status
282             grafite_status user_goal script statement
283           in
284            (* we need to replace all the parsed_text *)
285            res,String.length parsed_text
286       | _ -> 
287           HLog.error 
288             "The result of the urichooser should be only 1 uri, not:\n";
289           List.iter (
290             fun u -> HLog.error (UriManager.string_of_uri u ^ "\n")
291           ) selected;
292           assert false)
293   | TA.Check (_,term) ->
294       let metasenv = GrafiteTypes.get_proof_metasenv grafite_status in
295       let context =
296        match user_goal with
297           None -> []
298         | Some n -> GrafiteTypes.get_proof_context grafite_status n in
299       let ty,_ = CTC.type_of_aux' metasenv context term CicUniv.empty_ugraph in
300       let t_and_ty = Cic.Cast (term,ty) in
301       guistuff.mathviewer#show_entry (`Cic (t_and_ty,metasenv));
302       [], parsed_text_length
303   (* TODO *)
304   | TA.Quit _ -> failwith "not implemented"
305   | TA.Print (_,kind) -> failwith "not implemented"
306   | TA.Search_pat (_, search_kind, str) -> failwith "not implemented"
307   | TA.Search_term (_, search_kind, term) -> failwith "not implemented"
308                                 
309 and eval_executable include_paths (buffer : GText.buffer) guistuff lexicon_status grafite_status user_goal unparsed_text parsed_text script loc ex
310 =
311  let module TAPp = GrafiteAstPp in
312  let module MD = GrafiteDisambiguator in
313  let module ML = MatitaMisc in
314   try
315    begin
316     match ex with
317      | TA.Command (_,TA.Set (_,"baseuri",u)) ->
318         if not (GrafiteMisc.is_empty u) then
319          (match 
320             guistuff.ask_confirmation 
321               ~title:"Baseuri redefinition" 
322               ~message:(
323                 "Baseuri " ^ u ^ " already exists.\n" ^
324                 "Do you want to redefine the corresponding "^
325                 "part of the library?")
326           with
327            | `YES ->
328                let basedir = Helm_registry.get "matita.basedir" in
329                 LibraryClean.clean_baseuris ~basedir [u]
330            | `NO -> ()
331            | `CANCEL -> raise MatitaTypes.Cancel)
332      | _ -> ()
333    end;
334    eval_with_engine
335     guistuff lexicon_status grafite_status user_goal parsed_text
336      (TA.Executable (loc, ex))
337   with
338      MatitaTypes.Cancel -> [], 0
339    | GrafiteEngine.Macro (_loc,lazy_macro) ->
340       let context =
341        match user_goal with
342           None -> []
343         | Some n -> GrafiteTypes.get_proof_context grafite_status n in
344       let grafite_status,macro = lazy_macro context in
345        eval_macro include_paths buffer guistuff lexicon_status grafite_status
346         user_goal unparsed_text parsed_text script macro
347
348 and eval_statement include_paths (buffer : GText.buffer) guistuff lexicon_status
349  grafite_status user_goal script statement
350 =
351   let (lexicon_status,st), unparsed_text =
352     match statement with
353     | `Raw text ->
354         if Pcre.pmatch ~rex:only_dust_RE text then raise Margin;
355         GrafiteParser.parse_statement (Ulexing.from_utf8_string text)
356          ~include_paths lexicon_status, text
357     | `Ast (st, text) -> (lexicon_status, st), text
358   in
359   let text_of_loc loc =
360     let parsed_text_length = snd (HExtlib.loc_of_floc loc) in
361     let parsed_text = safe_substring unparsed_text 0 parsed_text_length in
362     parsed_text, parsed_text_length
363   in
364   match st with
365   | GrafiteParser.LNone loc ->
366       let parsed_text, parsed_text_length = text_of_loc loc in
367        [(grafite_status,lexicon_status),parsed_text],
368         parsed_text_length
369   | GrafiteParser.LSome (GrafiteAst.Comment (loc, _)) -> 
370       let parsed_text, parsed_text_length = text_of_loc loc in
371       let remain_len = String.length unparsed_text - parsed_text_length in
372       let s = String.sub unparsed_text parsed_text_length remain_len in
373       let s,len = 
374        try
375         eval_statement include_paths buffer guistuff lexicon_status
376          grafite_status user_goal script (`Raw s)
377        with
378           HExtlib.Localized (floc, exn) ->
379            HExtlib.raise_localized_exception ~offset:parsed_text_length floc exn
380         | GrafiteDisambiguator.DisambiguationError (offset,errorll) ->
381            raise
382             (GrafiteDisambiguator.DisambiguationError
383               (offset+parsed_text_length, errorll))
384       in
385       (match s with
386       | (statuses,text)::tl ->
387          (statuses,parsed_text ^ text)::tl,parsed_text_length + len
388       | [] -> [], 0)
389   | GrafiteParser.LSome (GrafiteAst.Executable (loc, ex)) ->
390      let parsed_text, parsed_text_length = text_of_loc loc in
391       eval_executable include_paths buffer guistuff lexicon_status
392        grafite_status user_goal unparsed_text parsed_text script loc ex
393   
394 let fresh_script_id =
395   let i = ref 0 in
396   fun () -> incr i; !i
397
398 class script  ~(source_view: GSourceView.source_view)
399               ~(mathviewer: MatitaTypes.mathViewer) 
400               ~set_star
401               ~ask_confirmation
402               ~urichooser 
403               ~develcreator 
404               () =
405 let buffer = source_view#buffer in
406 let source_buffer = source_view#source_buffer in
407 let initial_statuses =
408  (* these include_paths are used only to load the initial notation *)
409  let include_paths =
410   Helm_registry.get_list Helm_registry.string "matita.includes" in
411  let lexicon_status =
412   CicNotation2.load_notation ~include_paths
413    BuildTimeConf.core_notation_script in
414  let grafite_status = GrafiteSync.init () in
415   grafite_status,lexicon_status
416 in
417 object (self)
418   val mutable include_paths =
419    Helm_registry.get_list Helm_registry.string "matita.includes"
420
421   val scriptId = fresh_script_id ()
422   
423   val guistuff = {
424     mathviewer = mathviewer;
425     urichooser = urichooser;
426     ask_confirmation = ask_confirmation;
427     develcreator = develcreator;
428     filenamedata = (None, None)} 
429   
430   method private getFilename =
431     match guistuff.filenamedata with Some f,_ -> f | _ -> assert false
432
433   method filename = self#getFilename
434     
435   method private ppFilename =
436     match guistuff.filenamedata with 
437     | Some f,_ -> f 
438     | None,_ -> sprintf ".unnamed%d.ma" scriptId
439   
440   initializer 
441     ignore (GMain.Timeout.add ~ms:300000 
442        ~callback:(fun _ -> self#_saveToBackupFile ();true));
443     ignore (buffer#connect#modified_changed 
444       (fun _ -> set_star (Filename.basename self#ppFilename) buffer#modified))
445
446   val mutable statements = []    (** executed statements *)
447
448   val mutable history = [ initial_statuses ]
449     (** list of states before having executed statements. Head element of this
450       * list is the current state, last element is the state at the beginning of
451       * the script.
452       * Invariant: this list length is 1 + length of statements *)
453
454   (** goal as seen by the user (i.e. metano corresponding to current tab) *)
455   val mutable userGoal = None
456
457   (** text mark and tag representing locked part of a script *)
458   val locked_mark =
459     buffer#create_mark ~name:"locked" ~left_gravity:true buffer#start_iter
460   val locked_tag = buffer#create_tag [`BACKGROUND "lightblue"; `EDITABLE false]
461   val error_tag = buffer#create_tag [`UNDERLINE `SINGLE; `FOREGROUND "red"]
462
463   method locked_mark = locked_mark
464   method locked_tag = locked_tag
465   method error_tag = error_tag
466
467     (* history can't be empty, the invariant above grant that it contains at
468      * least the init grafite_status *)
469   method grafite_status = match history with (s,_)::_ -> s | _ -> assert false
470   method lexicon_status = match history with (_,ss)::_ -> ss | _ -> assert false
471
472   method private _advance ?statement () =
473    let s = match statement with Some s -> s | None -> self#getFuture in
474    HLog.debug ("evaluating: " ^ first_line s ^ " ...");
475    let (entries, parsed_len) = 
476     try
477      eval_statement include_paths buffer guistuff self#lexicon_status
478       self#grafite_status userGoal self (`Raw s)
479     with End_of_file -> raise Margin
480    in
481    let new_statuses, new_statements =
482      let statuses, texts = List.split entries in
483      statuses, texts
484    in
485    history <- new_statuses @ history;
486    statements <- new_statements @ statements;
487    let start = buffer#get_iter_at_mark (`MARK locked_mark) in
488    let new_text = String.concat "" (List.rev new_statements) in
489    if statement <> None then
490      buffer#insert ~iter:start new_text
491    else begin
492      if new_text <> String.sub s 0 parsed_len then begin
493        buffer#delete ~start ~stop:(start#copy#forward_chars parsed_len);
494        buffer#insert ~iter:start new_text;
495      end;
496    end;
497    self#moveMark (String.length new_text);
498    (* here we need to set the Goal in case we are going to cursor (or to
499       bottom) and we will face a macro *)
500    match self#grafite_status.proof_status with
501       Incomplete_proof p ->
502        userGoal <- Some (Continuationals.Stack.find_goal p.stack)
503     | _ -> userGoal <- None
504
505   method private _retract offset lexicon_status grafite_status new_statements
506    new_history
507   =
508    let cur_grafite_status,cur_lexicon_status =
509     match history with s::_ -> s | [] -> assert false
510    in
511     LexiconSync.time_travel ~present:cur_lexicon_status ~past:lexicon_status;
512     GrafiteSync.time_travel ~present:cur_grafite_status ~past:grafite_status;
513     statements <- new_statements;
514     history <- new_history;
515     self#moveMark (- offset)
516
517   method advance ?statement () =
518     try
519       self#_advance ?statement ();
520       self#notify
521     with 
522     | Margin -> self#notify
523     | exc -> self#notify; raise exc
524
525   method retract () =
526     try
527       let cmp,new_statements,new_history,(grafite_status,lexicon_status) =
528        match statements,history with
529           stat::statements, _::(status::_ as history) ->
530            String.length stat, statements, history, status
531        | [],[_] -> raise Margin
532        | _,_ -> assert false
533       in
534        self#_retract cmp lexicon_status grafite_status new_statements
535         new_history;
536        self#notify
537     with 
538     | Margin -> self#notify
539     | exc -> self#notify; raise exc
540
541   method private getFuture =
542     buffer#get_text ~start:(buffer#get_iter_at_mark (`MARK locked_mark))
543       ~stop:buffer#end_iter ()
544
545       
546   (** @param rel_offset relative offset from current position of locked_mark *)
547   method private moveMark rel_offset =
548     let mark = `MARK locked_mark in
549     let old_insert = buffer#get_iter_at_mark `INSERT in
550     buffer#remove_tag locked_tag ~start:buffer#start_iter ~stop:buffer#end_iter;
551     let current_mark_pos = buffer#get_iter_at_mark mark in
552     let new_mark_pos =
553       match rel_offset with
554       | 0 -> current_mark_pos
555       | n when n > 0 -> current_mark_pos#forward_chars n
556       | n (* when n < 0 *) -> current_mark_pos#backward_chars (abs n)
557     in
558     buffer#move_mark mark ~where:new_mark_pos;
559     buffer#apply_tag locked_tag ~start:buffer#start_iter ~stop:new_mark_pos;
560     buffer#move_mark `INSERT old_insert;
561     let mark_position = buffer#get_iter_at_mark mark in
562     if source_view#move_mark_onscreen mark then
563      begin
564       buffer#move_mark mark mark_position;
565       source_view#scroll_to_mark ~use_align:true ~xalign:1.0 ~yalign:0.1 mark;
566      end;
567     while Glib.Main.pending () do ignore(Glib.Main.iteration false); done
568
569   method clean_dirty_lock =
570     let lock_mark_iter = buffer#get_iter_at_mark (`MARK locked_mark) in
571     buffer#remove_tag locked_tag ~start:buffer#start_iter ~stop:buffer#end_iter;
572     buffer#apply_tag locked_tag ~start:buffer#start_iter ~stop:lock_mark_iter
573
574   val mutable observers = []
575
576   method addObserver (o: LexiconEngine.status -> GrafiteTypes.status -> unit) =
577     observers <- o :: observers
578
579   method private notify =
580     let lexicon_status = self#lexicon_status in
581     let grafite_status = self#grafite_status in
582     List.iter (fun o -> o lexicon_status grafite_status) observers
583
584   method loadFromFile f =
585     buffer#set_text (HExtlib.input_file f);
586     self#reset_buffer;
587     buffer#set_modified false
588     
589   method assignFileName file =
590     let abspath = MatitaMisc.absolute_path file in
591     let dirname = Filename.dirname abspath in
592     let devel = MatitamakeLib.development_for_dir dirname in
593     guistuff.filenamedata <- Some abspath, devel;
594     let include_ = 
595      match MatitamakeLib.development_for_dir dirname with
596         None -> []
597       | Some devel -> [MatitamakeLib.root_for_development devel] in
598     let include_ =
599      include_ @ (Helm_registry.get_list Helm_registry.string "matita.includes")
600     in
601      include_paths <- include_
602     
603   method saveToFile () =
604     let oc = open_out self#getFilename in
605     output_string oc (buffer#get_text ~start:buffer#start_iter
606                         ~stop:buffer#end_iter ());
607     close_out oc;
608     buffer#set_modified false
609   
610   method private _saveToBackupFile () =
611     if buffer#modified then
612       begin
613         let f = self#ppFilename ^ "~" in
614         let oc = open_out f in
615         output_string oc (buffer#get_text ~start:buffer#start_iter
616                             ~stop:buffer#end_iter ());
617         close_out oc;
618         HLog.debug ("backup " ^ f ^ " saved")                    
619       end
620   
621   method private goto_top =
622     let grafite_status,lexicon_status = 
623       let rec last x = function 
624       | [] -> x
625       | hd::tl -> last hd tl
626       in
627       last (self#grafite_status,self#lexicon_status) history
628     in
629     (* FIXME: this is not correct since there is no undo for 
630      * library_objects.set_default... *)
631     GrafiteSync.time_travel ~present:self#grafite_status ~past:grafite_status;
632     LexiconSync.time_travel ~present:self#lexicon_status ~past:lexicon_status
633
634   method private reset_buffer = 
635     statements <- [];
636     history <- [ initial_statuses ];
637     userGoal <- None;
638     self#notify;
639     buffer#remove_tag locked_tag ~start:buffer#start_iter ~stop:buffer#end_iter;
640     buffer#move_mark (`MARK locked_mark) ~where:buffer#start_iter
641
642   method reset () =
643     self#reset_buffer;
644     source_buffer#begin_not_undoable_action ();
645     buffer#delete ~start:buffer#start_iter ~stop:buffer#end_iter;
646     source_buffer#end_not_undoable_action ();
647     buffer#set_modified false;
648   
649   method template () =
650     let template = HExtlib.input_file BuildTimeConf.script_template in 
651     buffer#insert ~iter:(buffer#get_iter `START) template;
652     let development = MatitamakeLib.development_for_dir (Unix.getcwd ()) in
653     guistuff.filenamedata <- (None,development);
654     let include_ = 
655      match development with
656         None -> []
657       | Some devel -> [MatitamakeLib.root_for_development devel ]
658     in
659     let include_ =
660      include_ @ (Helm_registry.get_list Helm_registry.string "matita.includes")
661     in
662      include_paths <- include_ ;
663      buffer#set_modified false;
664      set_star (Filename.basename self#ppFilename) false
665
666   method goto (pos: [`Top | `Bottom | `Cursor]) () =
667     let old_locked_mark =
668      `MARK
669        (buffer#create_mark ~name:"old_locked_mark"
670          ~left_gravity:true (buffer#get_iter_at_mark (`MARK locked_mark))) in
671     let getpos _ = buffer#get_iter_at_mark (`MARK locked_mark) in 
672     let getoldpos _ = buffer#get_iter_at_mark old_locked_mark in 
673     let dispose_old_locked_mark () = buffer#delete_mark old_locked_mark in
674     match pos with
675     | `Top -> 
676         dispose_old_locked_mark (); 
677         self#goto_top; 
678         self#reset_buffer;
679         self#notify
680     | `Bottom ->
681         (try 
682           let rec dowhile () =
683             self#_advance ();
684             let newpos = getpos () in
685             if (getoldpos ())#compare newpos < 0 then
686               begin
687                 buffer#move_mark old_locked_mark newpos;
688                 dowhile ()
689               end
690           in
691           dowhile ();
692           dispose_old_locked_mark ();
693           self#notify 
694         with 
695         | Margin -> dispose_old_locked_mark (); self#notify
696         | exc -> dispose_old_locked_mark (); self#notify; raise exc)
697     | `Cursor ->
698         let locked_iter () = buffer#get_iter_at_mark (`NAME "locked") in
699         let cursor_iter () = buffer#get_iter_at_mark `INSERT in
700         let remember =
701          `MARK
702            (buffer#create_mark ~name:"initial_insert"
703              ~left_gravity:true (cursor_iter ())) in
704         let dispose_remember () = buffer#delete_mark remember in
705         let remember_iter () =
706          buffer#get_iter_at_mark (`NAME "initial_insert") in
707         let cmp () = (locked_iter ())#offset - (remember_iter ())#offset in
708         let icmp = cmp () in
709         let forward_until_cursor () = (* go forward until locked > cursor *)
710           let rec aux () =
711             self#_advance ();
712             if cmp () < 0 && (getoldpos ())#compare (getpos ()) < 0 
713             then
714              begin
715               buffer#move_mark old_locked_mark (getpos ());
716               aux ()
717              end
718           in
719           aux ()
720         in
721         let rec back_until_cursor len = (* go backward until locked < cursor *)
722          function
723             statements, ((grafite_status,lexicon_status)::_ as history)
724             when len <= 0 ->
725              self#_retract (icmp - len) lexicon_status grafite_status statements
726               history
727           | statement::tl1, _::tl2 ->
728              back_until_cursor (len - String.length statement) (tl1,tl2)
729           | _,_ -> assert false
730         in
731         (try
732           begin
733            if icmp < 0 then       (* locked < cursor *)
734              (forward_until_cursor (); self#notify)
735            else if icmp > 0 then  (* locked > cursor *)
736              (back_until_cursor icmp (statements,history); self#notify)
737            else                  (* cursor = locked *)
738                ()
739           end ;
740           dispose_remember ();
741           dispose_old_locked_mark ();
742         with 
743         | Margin -> dispose_remember (); dispose_old_locked_mark (); self#notify
744         | exc -> dispose_remember (); dispose_old_locked_mark ();
745                  self#notify; raise exc)
746               
747   method onGoingProof () =
748     match self#grafite_status.proof_status with
749     | No_proof | Proof _ -> false
750     | Incomplete_proof _ -> true
751     | Intermediate _ -> assert false
752
753 (*   method proofStatus = MatitaTypes.get_proof_status self#status *)
754   method proofMetasenv = GrafiteTypes.get_proof_metasenv self#grafite_status
755
756   method proofContext =
757    match userGoal with
758       None -> []
759     | Some n -> GrafiteTypes.get_proof_context self#grafite_status n
760
761   method proofConclusion =
762    match userGoal with
763       None -> assert false
764     | Some n ->
765        GrafiteTypes.get_proof_conclusion self#grafite_status n
766
767   method stack = GrafiteTypes.get_stack self#grafite_status
768   method setGoal n = userGoal <- n
769   method goal = userGoal
770
771   method eos = 
772     let s = self#getFuture in
773     let rec is_there_and_executable lexicon_status s = 
774       if Pcre.pmatch ~rex:only_dust_RE s then raise Margin;
775       let lexicon_status,st =
776        GrafiteParser.parse_statement (Ulexing.from_utf8_string s)
777         ~include_paths lexicon_status
778       in
779       match st with
780         GrafiteParser.LNone loc
781       | GrafiteParser.LSome (GrafiteAst.Comment (loc,_)) -> 
782           let parsed_text_length = snd (HExtlib.loc_of_floc loc) in
783           let remain_len = String.length s - parsed_text_length in
784           let next = String.sub s parsed_text_length remain_len in
785           is_there_and_executable lexicon_status next
786       | GrafiteParser.LSome (GrafiteAst.Executable (loc, ex)) -> false
787     in
788     try
789       is_there_and_executable self#lexicon_status s
790     with 
791     | CicNotationParser.Parse_error _ -> false
792     | Margin | End_of_file -> true
793
794   (* debug *)
795   method dump () =
796     HLog.debug "script status:";
797     HLog.debug ("history size: " ^ string_of_int (List.length history));
798     HLog.debug (sprintf "%d statements:" (List.length statements));
799     List.iter HLog.debug statements;
800     HLog.debug ("Current file name: " ^
801       (match guistuff.filenamedata with 
802       |None,_ -> "[ no name ]" 
803       | Some f,_ -> f));
804
805 end
806
807 let _script = ref None
808
809 let script ~source_view ~mathviewer ~urichooser ~develcreator ~ask_confirmation ~set_star ()
810 =
811   let s = new script 
812     ~source_view ~mathviewer ~ask_confirmation ~urichooser ~develcreator ~set_star () 
813   in
814   _script := Some s;
815   s
816
817 let current () = match !_script with None -> assert false | Some s -> s
818