why3ide.ml 14.3 KB
Newer Older
1
2
3
4

open Format
open Why3
open Gconfig
5
6
7
open Stdlib
open Session_itp
open Controller_itp
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

let debug = Debug.lookup_flag "ide_info"

(************************)
(* parsing command line *)
(************************)

let files = Queue.create ()
let opt_parser = ref None

let spec = Arg.align [
  "-F", Arg.String (fun s -> opt_parser := Some s),
      "<format> select input format (default: \"why\")";
  "--format", Arg.String (fun s -> opt_parser := Some s),
      " same as -F";
(*
  "-f",
   Arg.String (fun s -> input_files := s :: !input_files),
   "<file> add file to the project (ignored if it is already there)";
*)
  Termcode.arg_extra_expl_prefix
]

let usage_str = sprintf
  "Usage: %s [options] [<file.why>|<project directory>]..."
  (Filename.basename Sys.argv.(0))

let gconfig = try
  let config, base_config, env =
    Whyconf.Args.initialize spec (fun f -> Queue.add f files) usage_str in
  if Queue.is_empty files then Whyconf.Args.exit_with_usage spec usage_str;
  Gconfig.load_config config base_config env;
  Gconfig.config ()

  with e when not (Debug.test_flag Debug.stack_trace) ->
    eprintf "%a@." Exn_printer.exn_printer e;
    exit 1

46
47
48
49
50
51
52
53
let task_driver =
  let main = Whyconf.get_main gconfig.config in
  let d = Filename.concat (Whyconf.datadir main)
                          (Filename.concat "drivers" "why3_itp.drv")
  in
  Driver.load_driver gconfig.env d []


MARCHE Claude's avatar
MARCHE Claude committed
54
55
56
57
58
59
let provers : Whyconf.config_prover Whyconf.Mprover.t =
  Whyconf.get_provers gconfig.config

let cont =
  Session_user_interface.cont_from_files spec usage_str gconfig.env files provers

60
61
62
63
64
65
let () =
  Debug.dprintf debug "[GUI] Init the GTK interface...@?";
  ignore (GtkMain.Main.init ());
  Debug.dprintf debug " done.@.";
  Gconfig.init ()

MARCHE Claude's avatar
MARCHE Claude committed
66

67
68
69
(**********************)
(* Graphical elements *)
(**********************)
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

let main_window =
  let w = GWindow.window
            ~allow_grow:true ~allow_shrink:true
            ~width:gconfig.window_width
            ~height:gconfig.window_height
            ~title:"Why3 Interactive Proof Session" ()
  in
  (* callback to record the new size of the main window when changed, so
   that on restart the window size is the same as the last session *)
  let (_ : GtkSignal.id) =
    w#misc#connect#size_allocate
      ~callback:
      (fun {Gtk.width=w;Gtk.height=h} ->
       gconfig.window_height <- h;
       gconfig.window_width <- w)
  in w

(* the main window contains a vertical box, containing:
   1. the menu
   2. an horizontal box
 *)

let vbox = GPack.vbox ~packing:main_window#add ()

let menubar = GMenu.menu_bar
  ~packing:(vbox#pack ?from:None ?expand:None ?fill:None ?padding:None)
  ()

let hb = GPack.hbox ~packing:vbox#add ()

(* 1. Menu *)

let factory = new GMenu.factory menubar

let accel_group = factory#accel_group

let file_menu = factory#add_submenu "_File"
let file_factory = new GMenu.factory file_menu ~accel_group

let exit_function ~destroy () =
  ignore(destroy); GMain.quit ()

let (_ : GtkSignal.id) = main_window#connect#destroy
  ~callback:(exit_function ~destroy:true)

let (_ : GMenu.menu_item) =
  file_factory#add_item ~key:GdkKeysyms._Q "_Quit"
    ~callback:(exit_function ~destroy:false)

(* 2. horizontal box contains:
   2.1 TODO: a tool box ?
   2.2 a horizontal paned containing:
     2.2.1 a scrolled window to hold the tree view of the session
Clément Fumex's avatar
Clément Fumex committed
124
     2.2.2 a vertical paned containing
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
*)

let hp = GPack.paned `HORIZONTAL ~packing:hb#add ()

let scrollview =
  let sv =
    GBin.scrolled_window
      ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC
      ~width:gconfig.tree_width ~shadow_type:`ETCHED_OUT
      ~packing:hp#add ()
  in
  let (_ : GtkSignal.id) =
    sv#misc#connect#size_allocate
      ~callback:
      (fun {Gtk.width=w;Gtk.height=_h} ->
       gconfig.tree_width <- w)
  in sv

Clément Fumex's avatar
Clément Fumex committed
143
let vpan222 = GPack.paned `VERTICAL ~packing:hp#add ()
144
145
146
147
148
149

(*  the scrolled window 2.2.1 contains a GTK tree

*)


MARCHE Claude's avatar
MARCHE Claude committed
150
151
152
153
154
155
156
157
(* view for the session tree *)
let scrolled_session_view =
  GBin.scrolled_window
    ~hpolicy: `AUTOMATIC ~vpolicy: `AUTOMATIC
    ~shadow_type:`ETCHED_OUT
    ~packing:scrollview#add
    ()

158
159
160
let cols = new GTree.column_list
let name_column = cols#add Gobject.Data.string
let index_column = cols#add Gobject.Data.int
MARCHE Claude's avatar
MARCHE Claude committed
161

162
163
164
165
166
let name_renderer = GTree.cell_renderer_text [`XALIGN 0.]
let view_name_column = GTree.view_column ~title:"Theories/Goals" ()
let () =
  view_name_column#pack name_renderer;
  view_name_column#add_attribute name_renderer "text" name_column
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
let goals_model,goals_view =
  Debug.dprintf debug "[GUI] Creating tree model...@?";
  let model = GTree.tree_store cols in
  let view = GTree.view ~model ~packing:scrolled_session_view#add () in
(*
  let () = view#selection#set_mode (* `SINGLE *) `MULTIPLE in
  let () = view#set_rules_hint true in
 *)
  ignore (view#append_column view_name_column);
(*
  ignore (view#append_column view_status_column);
  ignore (view#append_column view_time_column);
*)
  Debug.dprintf debug " done@.";
  model,view
183

Clément Fumex's avatar
Clément Fumex committed
184
(* vpan222 contains:
185
   2.2.2.1 a view of the current task
Clément Fumex's avatar
Clément Fumex committed
186
187
188
   2.2.2.2 a vertiacal pan which contains
     2.2.2.2.1 the input field to type commands
     2.2.2.2.2 the output of the commands
189
190
191
192
193
194
 *)

let scrolled_task_view =
  GBin.scrolled_window
    ~hpolicy: `AUTOMATIC ~vpolicy: `AUTOMATIC
    ~shadow_type:`ETCHED_OUT
Clément Fumex's avatar
Clément Fumex committed
195
    ~packing:vpan222#add ()
196
197
198
199

let task_view =
  GSourceView2.source_view
    ~editable:false
Clément Fumex's avatar
Clément Fumex committed
200
    ~cursor_visible:false
201
202
203
204
    ~show_line_numbers:true
    ~packing:scrolled_task_view#add
    ()

Clément Fumex's avatar
Clément Fumex committed
205
let vbox2222 = GPack.vbox ~packing:vpan222#add  ()
206

Clément Fumex's avatar
Clément Fumex committed
207
208
let command_entry =
  GEdit.entry ~packing:(vbox2222#pack ?from:None ?expand:None ?fill:None ?padding:None) ()
MARCHE Claude's avatar
MARCHE Claude committed
209
210
let message_zone =
  GText.view ~editable:false ~cursor_visible:false
Clément Fumex's avatar
Clément Fumex committed
211
             ~packing:(vbox2222#pack ?from:None ~expand:true ~fill:true ?padding:None) ()
MARCHE Claude's avatar
MARCHE Claude committed
212

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
(********************************************)
(* controller instance on the GTK scheduler *)
(********************************************)


module S = struct
    let idle ~prio f =
      let (_ : GMain.Idle.id) = GMain.Idle.add ~prio f in ()

    let timeout ~ms f =
      let (_ : GMain.Timeout.id) = GMain.Timeout.add ~ms ~callback:f in
      ()
end

module C = Controller_itp.Make(S)


(***********************************)
(* Mapping session to the GTK tree *)
(***********************************)

type index =
  | Inone
236
  | IproofAttempt of proofAttemptID
237
  | IproofNode of proofNodeID
238
  | Itransformation  of transID
239
240
241
242
  | Ifile of file
  | Itheory of theory

let model_index : index Hint.t = Stdlib.Hint.create 17
243
244
245
246
(* To each proofnodeid we have the corresponding row_reference *)
let pn_id_to_gtree : GTree.row_reference Hpn.t = Hpn.create 17
let pan_id_to_gtree : GTree.row_reference Hpan.t = Hpan.create 17

247
248
249

let new_node =
  let cpt = ref (-1) in
250
  fun ?parent ?(collapse=false) name ind ->
251
252
253
254
255
256
  incr cpt;
  Hint.add model_index !cpt ind;
  let parent = Opt.map (fun x -> x#iter) parent in
  let iter = goals_model#append ?parent () in
  goals_model#set ~row:iter ~column:name_column name;
  goals_model#set ~row:iter ~column:index_column !cpt;
257
  let new_ref = goals_model#get_row_reference (goals_model#get_path iter) in
258
259
  (* By default expand_path when creating a new node *)
  if not collapse then goals_view#expand_to_path (goals_model#get_path iter);
260
261
262
263
264
265
266
267
268
  begin
    match ind with
    | IproofAttempt panid ->
       Hpan.add pan_id_to_gtree panid new_ref
    | IproofNode pnid ->
       Hpn.add pn_id_to_gtree pnid new_ref
    | _ -> ()
  end;
  new_ref
269

270
let build_subtree_proof_attempt_from_goal ses row_ref id =
271
272
273
274
275
276
  Whyconf.Hprover.iter
    (fun pa panid ->
     let name = Pp.string_of Whyconf.print_prover pa in
      ignore(new_node ~parent:row_ref name
                 (IproofAttempt panid)))
    (get_proof_attempt_ids ses id)
277
278

let rec build_subtree_from_goal ses th_row_reference id =
279
  let name = get_proof_name ses id in
280
  let row_ref =
281
282
    new_node ~parent:th_row_reference name.Ident.id_string
             (IproofNode id)
MARCHE Claude's avatar
MARCHE Claude committed
283
  in
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  List.iter
    (fun trans_id ->
      build_subtree_from_trans ses row_ref trans_id)
    (get_transformations ses id);
  build_subtree_proof_attempt_from_goal ses row_ref id

and build_subtree_from_trans ses goal_row_reference trans_id =
  let name = get_transf_name ses trans_id in
  let row_ref =
    new_node ~parent:goal_row_reference name (Itransformation trans_id) in
  List.iter
    (fun goal_id ->
      (build_subtree_from_goal ses row_ref goal_id))
    (get_sub_tasks ses trans_id)
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318

let build_tree_from_session ses =
  let files = get_files ses in
  Stdlib.Hstr.iter
    (fun _ file ->
       let file_row_reference = new_node file.file_name (Ifile file) in
       List.iter (fun th ->
                  let th_row_reference =
                    new_node ~parent:file_row_reference
                             (theory_name th).Ident.id_string
                             (Itheory th)
                  in
                  List.iter (build_subtree_from_goal ses th_row_reference)
                            (theory_goals th))
                 file.file_theories)
    files

(******************)
(*    actions     *)
(******************)

319
320
(* TODO We currently use this for transformations etc... With strategies, we sure
   do not want to move the current index with the computing of strategy. *)
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
let current_selected_index = ref Inone

let run_strategy_on_task s =
  match !current_selected_index with
  | IproofNode id ->
     let l = Session_user_interface.strategies
               cont.controller_env gconfig.config
     in
     let st = List.filter (fun (_,c,_,_) -> c=s) l in
     begin
       match st with
       | [(n,_,_,st)] ->
          printf "running strategy '%s'@." n;
          let callback sts =
            printf "Strategy status: %a@." print_strategy_status sts
          in
          C.run_strategy_on_goal cont id st ~callback
    | _ -> printf "Strategy '%s' not found@." s
     end
  | _ -> ()


343
344
(* TODO maybe an other file for callbacks *)
(* Callback of a transformation *)
345
let callback_update_tree_transform ses row_reference = fun status ->
346
347
  match status with
  | TSdone trans_id ->
348
349
350
351
352
353
      build_subtree_from_trans ses row_reference trans_id;
      (match Session_itp.get_sub_tasks ses trans_id with
      | first_goal :: _ ->
          (* Put the selection on the first goal *)
          goals_view#selection#select_iter (Hpn.find pn_id_to_gtree first_goal)#iter
      | [] -> ())
354
355
  | _ -> ()

356
let apply_transform ses t args =
357
358
  match !current_selected_index with
  | IproofNode id ->
359
    let row_ref = Hpn.find pn_id_to_gtree id in (* TODO exception *)
360
    let callback =
361
         callback_update_tree_transform ses row_ref
362
       in
363
       C.schedule_transformation cont id t args ~callback
364
365
366
367
368
369
370
371
372
    | _ -> printf "Error: Give the name of the transformation@."


let remove_children iter =
  if (goals_model#iter_has_child iter) then
    ignore (goals_model#remove (goals_model#iter_children (Some iter)))
  else ()

(* Callback of a proof_attempt *)
373
374
let callback_update_tree_proof _ses row_ref _id name =
  fun panid pa_status ->
375
  match pa_status with
376
377
378
379
380
381
382
383
    | Scheduled ->
       begin
       try
         let _new_row_ref = Hpan.find pan_id_to_gtree panid in
         () (* TODO: set icon to 'pause' *)
       with Not_found ->
            ignore(new_node ~parent:row_ref (name ^ " scheduled") (IproofAttempt panid))
       end
384
  | Done _pr ->
385
386
387
388
389
390
391
392
       begin
       try
         let r = Hpan.find pan_id_to_gtree panid in
         goals_model#set ~row:r#iter ~column:name_column (name ^ " done")
       with Not_found -> assert false
       end
  | Running -> () (* TODO: set icon to 'play' *)
  | _ ->  () (* TODO ? *)
393

394
let test_schedule_proof_attempt ses (p: Whyconf.config_prover) limit =
395
396
  match !current_selected_index with
  | IproofNode id ->
397
    let row_ref = Hpn.find pn_id_to_gtree id in
398
399
400
401
402
403
404
    let prover = p.Whyconf.prover in
    let printing = prover.Whyconf.prover_name ^ "(" ^ prover.Whyconf.prover_version ^ ")" in
    let callback = callback_update_tree_proof ses row_ref id printing in
        C.schedule_proof_attempt
          cont id prover
          ~limit ~callback
  | _ -> message_zone#buffer#set_text ("Must be on a proof node to use a prover.")
405
406

let clear_command_entry () = command_entry#set_text ""
MARCHE Claude's avatar
MARCHE Claude committed
407

408
open Session_user_interface
MARCHE Claude's avatar
MARCHE Claude committed
409
410

let interp cmd =
411
412
413
414
415
416
  match interp cont.controller_env cmd with
    | Transform(s,_t,args) ->
       clear_command_entry ();
       apply_transform cont.controller_session s args
    | Query s ->
       message_zone#buffer#set_text s
417
418
419
420
421
422
423
424
425
426
427
428
    | Other(s,args) ->
      begin
        match parse_prover_name gconfig.config s args with
        | Some (prover_config, limit) ->
          clear_command_entry ();
          test_schedule_proof_attempt cont.controller_session prover_config limit
        | None ->
          match s with
          | "s" -> clear_command_entry ();
                   run_strategy_on_task "1"
          | _ -> message_zone#buffer#set_text ("unknown command '"^s^"'")
      end
MARCHE Claude's avatar
MARCHE Claude committed
429
430
431
432

let (_ : GtkSignal.id) =
  command_entry#connect#activate
    ~callback:(fun () -> interp command_entry#text)
433
434


435
436
437
438
439
440
441
442
let get_selected_row_references () =
  List.map
    (fun path -> goals_model#get_row_reference path)
    goals_view#selection#get_selected_rows

let on_selected_row r =
  let index = goals_model#get ~row:r#iter ~column:index_column in
  try
443
444
    let session_element = Hint.find model_index index in
    let () = match session_element with
445
             | IproofNode id -> Hpn.add pn_id_to_gtree id r (* TODO maybe not the good place to fill
446
447
448
449
                                                                    this table *)
             | _ -> () in
    current_selected_index := session_element;
    match session_element with
450
451
452
453
454
    | IproofNode id ->
       let task = get_task cont.controller_session id in
       let s = Pp.string_of
                 (Driver.print_task ~cntexample:false task_driver)
                 task
Clément Fumex's avatar
Clément Fumex committed
455
456
457
       in task_view#source_buffer#set_text s;
       (* scroll to end of text *)
       task_view#scroll_to_mark `INSERT
458
459
460
461
462
463
464
465
466
467
468
469
    | _ -> task_view#source_buffer#set_text ""
  with
    | Not_found -> task_view#source_buffer#set_text ""

let (_ : GtkSignal.id) =
  goals_view#selection#connect#after#changed ~callback:
    (fun () ->
      match get_selected_row_references () with
        | [r] -> on_selected_row r
        | _ -> ())

(***********************)
470
(* start the interface *)
471
(***********************)
472
473

let () =
474
475
  build_tree_from_session cont.controller_session;
  (* temporary *)
Clément Fumex's avatar
Clément Fumex committed
476
  vpan222#set_position 500;
477
  goals_view#expand_all ();
478
479
480
481
  main_window#add_accel_group accel_group;
  main_window#set_icon (Some !Gconfig.why_icon);
  main_window#show ();
  GMain.main ()