products/sources/formale Sprachen/Coq/lib image not shown  

Quellcode-Bibliothek

© Kompilation durch diese Firma

[Weder Korrektheit noch Funktionsfähigkeit der Software werden zugesichert.]

Datei: root.prf   Sprache: SML

Original von: Coq©

(************************************************************************)
(*         *   The Coq Proof Assistant / The Coq Development Team       *)
(*  v      *   INRIA, CNRS and contributors - Copyright 1999-2018       *)
(* <O___,, *       (see CREDITS file for the list of authors)           *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

open Preferences

(** A session is a script buffer + proof + messages,
    interacting with a coqtop, and a few other elements around *)


class type ['a] page =
  object
    inherit GObj.widget
    method update : 'a -> unit
    method on_update : callback:('a -> unit) -> unit
    method data : 'a
  end

class type control =
  object
    method detach : unit -> unit
  end

type errpage = (int * stringlist page
type jobpage = string CString.Map.t page

type session = {
  buffer : GText.buffer;
  script : Wg_ScriptView.script_view;
  proof : Wg_ProofView.proof_view;
  messages : Wg_RoutedMessageViews.message_views_router;
  segment : Wg_Segment.segment;
  fileops : FileOps.ops;
  coqops : CoqOps.ops;
  coqtop : Coq.coqtop;
  command : Wg_Command.command_window;
  finder : Wg_Find.finder;
  tab_label : GMisc.label;
  errpage : errpage;
  jobpage : jobpage;
  mutable control : control;
}

let create_buffer () =
  let buffer = GSourceView3.source_buffer
    ~tag_table:Tags.Script.table
    ~highlight_matching_brackets:true
    ?language:(lang_manager#language source_language#get)
    ?style_scheme:(style_manager#style_scheme source_style#get)
    ()
  in
  let _ = buffer#create_mark ~name:"start_of_input" buffer#start_iter in
  let _ = buffer#create_mark
    ~left_gravity:false ~name:"stop_of_input" buffer#end_iter in
  let _ = buffer#create_mark ~name:"prev_insert" buffer#start_iter in
  let _ = buffer#place_cursor ~where:buffer#start_iter in
  let _ = buffer#add_selection_clipboard Ideutils.cb in
  buffer

let create_script coqtop source_buffer =
  let script = Wg_ScriptView.script_view coqtop ~source_buffer
    ~show_line_numbers:true ~wrap_mode:`NONE ()
  in
  let _ = script#misc#set_name "ScriptWindow"
  in
  script

(** NB: Events during text edition:

    - [begin_user_action]
    - [insert_text] (or [delete_range] when deleting)
    - [changed]
    - [end_user_action]

   When pasting a text containing tags (e.g. the sentence terminators),
   there is actually many [insert_text] and [changed]. For instance,
   for "a. b.":

    - [begin_user_action]
    - [insert_text] (for "a")
    - [changed]
    - [insert_text] (for ".")
    - [changed]
    - [apply_tag] (for the tag of ".")
    - [insert_text] (for " b")
    - [changed]
    - [insert_text] (for ".")
    - [changed]
    - [apply_tag] (for the tag of ".")
    - [end_user_action]

  Since these copy-pasted tags may interact badly with the retag mechanism,
  we now don't monitor the "changed" event, but rather the "begin_user_action"
  and "end_user_action". We begin by setting a mark at the initial cursor
  point. At the end, the zone between the mark and the cursor is to be
  untagged and then retagged. *)


let set_buffer_handlers
  (buffer : GText.buffer) script (coqops : CoqOps.ops) coqtop
=
  let action_was_cancelled = ref true in
  let no_coq_action_required = ref true in
  let cur_action = ref 0 in
  let new_action_id =
    let id = ref 0 in
    fun () -> incr id; !id in
  let running_action = ref None in
  let cancel_signal ?(stop_emit=true) reason =
    Minilib.log ("user_action cancelled: "^reason);
    action_was_cancelled := true;
    if stop_emit then GtkSignal.stop_emit () in
  let del_mark () =
    try buffer#delete_mark (`NAME "target")
    with GText.No_such_mark _ -> () in
  let add_mark it = del_mark (); buffer#create_mark ~name:"target" it in
  let call_coq_or_cancel_action f =
    no_coq_action_required := false;
    let action = !cur_action in
    let action, fallback =
      Coq.seq (Coq.lift (fun () -> running_action := Some action)) f,
      fun () -> (* If Coq is busy due to the current action, we don't cancel *)
        match !running_action with
        | Some aid when aid = action -> ()
        | _ -> cancel_signal ~stop_emit:false "Coq busy" in
    Coq.try_grab coqtop action fallback in
  let get_start () = buffer#get_iter_at_mark (`NAME "start_of_input"in
  let get_stop () = buffer#get_iter_at_mark (`NAME "stop_of_input"in
  let ensure_marks_exist () =
    try ignore(buffer#get_mark (`NAME "stop_of_input"))
    with GText.No_such_mark _ -> assert false in
  let get_insert () = buffer#get_iter_at_mark `INSERT in
  let update_prev it =
    let prev = buffer#get_iter_at_mark (`NAME "prev_insert"in
    if it#offset < prev#offset then
      buffer#move_mark (`NAME "prev_insert") ~where:it
  in
  let debug_edit_zone () = if false (*!Minilib.debug*) then begin
    buffer#remove_tag Tags.Script.edit_zone
      ~start:buffer#start_iter ~stop:buffer#end_iter;
    buffer#apply_tag Tags.Script.edit_zone
      ~start:(get_start()) ~stop:(get_stop())
    end in
  let processed_sentence_just_before_error it =
    let rec aux old it =
      if it#is_start then None
      else if it#has_tag Tags.Script.processed then Some old
      else if it#has_tag Tags.Script.error_bg then aux it it#backward_char 
      else None in
    aux it it in
  let insert_cb it s = if String.length s = 0 then () else begin
    Minilib.log ("insert_cb " ^ string_of_int it#offset);
    let text_mark = add_mark it in
    let () = update_prev it in
    if it#has_tag Tags.Script.to_process then
      cancel_signal "Altering the script being processed in not implemented"
    else if it#has_tag Tags.Script.incomplete then
      cancel_signal "Altering the script being processed in not implemented"
    else if it#has_tag Tags.Script.processed then
      call_coq_or_cancel_action (coqops#go_to_mark (`MARK text_mark))
    else if it#has_tag Tags.Script.error_bg then begin
      match processed_sentence_just_before_error it with
      | None -> ()
      | Some prev_sentence_end ->
          let text_mark = add_mark prev_sentence_end in
          call_coq_or_cancel_action (coqops#go_to_mark (`MARK text_mark))
    end end in
  let delete_cb ~start ~stop =
    Minilib.log (Printf.sprintf "delete_cb %d %d" start#offset stop#offset);
    let min_iter, max_iter =
      if start#compare stop < 0 then start, stop else stop, start in
    let () = update_prev min_iter in
    let text_mark = add_mark min_iter in
    let rec aux min_iter =
      if min_iter#equal max_iter then ()
      else if min_iter#has_tag Tags.Script.incomplete then
        cancel_signal "Altering the script being processed in not implemented"
      else if min_iter#has_tag Tags.Script.to_process then
        cancel_signal "Altering the script being processed in not implemented"
      else if min_iter#has_tag Tags.Script.processed then
        call_coq_or_cancel_action (coqops#go_to_mark (`MARK text_mark))
      else if min_iter#has_tag Tags.Script.error_bg then
        match processed_sentence_just_before_error min_iter with
        | None -> ()
        | Some prev_sentence_end ->
            let text_mark = add_mark prev_sentence_end in
            call_coq_or_cancel_action (coqops#go_to_mark (`MARK text_mark))
      else aux min_iter#forward_char in
    aux min_iter in
  let begin_action_cb () =
    Minilib.log "begin_action_cb";
    action_was_cancelled := false;
    no_coq_action_required := true;
    cur_action := new_action_id ();
    let where = get_insert () in
    buffer#move_mark (`NAME "prev_insert") ~where in
  let end_action_cb () =
    Minilib.log "end_action_cb";
    ensure_marks_exist ();
    if not !action_was_cancelled then begin
      (* If coq was asked to backtrack, the clenup must be done by the
         backtrack_until function, since it may move the stop_of_input
         to a point indicated by coq. *)

      if !no_coq_action_required then begin
        let start, stop = get_start (), get_stop () in
 List.iter (fun tag -> buffer#remove_tag tag ~start ~stop)
    Tags.Script.ephemere;
        Sentence.tag_on_insert buffer
      end;
    end in
  let mark_deleted_cb m =
    match GtkText.Mark.get_name m with
    | Some "insert" -> ()
    | Some s -> Minilib.log (s^" deleted")
    | None -> ()
  in
  let mark_set_cb it m =
    debug_edit_zone ();
    let ins = get_insert () in
    let () = Ideutils.display_location ins in
    match GtkText.Mark.get_name m with
    | Some "insert" -> ()
    | Some s -> Minilib.log (s^" moved")
    | None -> ()
  in
  (* Pluging callbacks *)
  let _ = buffer#connect#insert_text ~callback:insert_cb in
  let _ = buffer#connect#delete_range ~callback:delete_cb in
  let _ = buffer#connect#begin_user_action ~callback:begin_action_cb in
  let _ = buffer#connect#end_user_action ~callback:end_action_cb in
  let _ = buffer#connect#after#mark_set ~callback:mark_set_cb in
  let _ = buffer#connect#after#mark_deleted ~callback:mark_deleted_cb in
  ()

let find_int_col s l =
  match List.assoc s l with `IntC c -> c | _ -> assert false

let find_string_col s l =
  match List.assoc s l with `StringC c -> c | _ -> assert false

let make_table_widget ?sort cd cb =
  let frame = GBin.scrolled_window ~hpolicy:`NEVER ~vpolicy:`AUTOMATIC () in
  let columns, store =
    let cols = new GTree.column_list in
    let columns = List.map (function
      | (`Int,n,_)    -> n, `IntC (cols#add Gobject.Data.int)
      | (`String,n,_) -> n, `StringC (cols#add Gobject.Data.string))
    cd in
    columns, GTree.list_store cols in
  let data = GTree.view
      ~vadjustment:frame#vadjustment ~hadjustment:frame#hadjustment
      ~rules_hint:true ~headers_visible:false
      ~model:store ~packing:frame#add () in
  let () = data#set_headers_visible true in
  let () = data#set_headers_clickable true in
(* FIXME: handle this using CSS *)
(*   let refresh clr = data#misc#modify_bg [`NORMAL, `NAME clr] in *)
(*   let _ = background_color#connect#changed ~callback:refresh in *)
(*   let _ = data#misc#connect#realize ~callback:(fun () -> refresh background_color#get) in *)
  let mk_rend c = GTree.cell_renderer_text [], ["text",c] in
  let cols =
    List.map2 (fun (_,c) (_,n,v) ->
      let c = match c with
        | `IntC c -> GTree.view_column ~renderer:(mk_rend c) ()
        | `StringC c -> GTree.view_column ~renderer:(mk_rend c) () in
      c#set_title n;
      c#set_visible v;
      c#set_sizing `AUTOSIZE;
      c)
    columns cd in
  let make_sorting i (_, c) =
    let sort (store : GTree.model) it1 it2 = match c with
    | `IntC c ->
      Pervasives.compare (store#get ~row:it1 ~column:c) (store#get ~row:it2 ~column:c)
    | `StringC c ->
      Pervasives.compare (store#get ~row:it1 ~column:c) (store#get ~row:it2 ~column:c)
    in
    store#set_sort_func i sort
  in
  CList.iteri make_sorting columns;
  CList.iteri (fun i c -> c#set_sort_column_id i) cols;
  List.iter (fun c -> ignore(data#append_column c)) cols;
  ignore(
    data#connect#row_activated ~callback:(fun tp vc -> cb columns store tp vc)
  );
  let () = match sort with None -> () | Some (i, t) -> store#set_sort_column_id i t in
  frame, (fun f -> f columns store)

let create_errpage (script : Wg_ScriptView.script_view) : errpage =
  let table, access =
    make_table_widget ~sort:(0, `ASCENDING)
      [`Int,"Line",true; `String,"Error message",true]
      (fun columns store tp vc ->
        let row = store#get_iter tp in
        let lno = store#get ~row ~column:(find_int_col "Line" columns) in
        let where = script#buffer#get_iter (`LINE (lno-1)) in
        script#buffer#place_cursor ~where;
        script#misc#grab_focus ();
        ignore (script#scroll_to_iter
    ~use_align:false ~yalign:0.75 ~within_margin:0.25 where)) in
  let tip = GMisc.label ~text:"Double click to jump to error line" () in
  let box = GPack.vbox ~homogeneous:false () in
  let () = box#pack ~expand:true table#coerce in
  let () = box#pack ~expand:false ~padding:2 tip#coerce in
  let last_update = ref [] in
  let callback = ref (fun _ -> ()) in
  object (self)
    inherit GObj.widget box#as_widget
    method update errs =
      if !last_update = errs then ()
      else begin
 last_update := errs;
 access (fun _ store -> store#clear ());
        !callback errs;
 List.iter (fun (lno, msg) -> access (fun columns store ->
          let line = store#append () in
          store#set ~row:line ~column:(find_int_col "Line" columns) lno;
          store#set ~row:line ~column:(find_string_col "Error message" columns) msg))
          errs
      end
    method on_update ~callback:cb = callback := cb
    method data = !last_update
  end

let create_jobpage coqtop coqops : jobpage =
  let table, access =
    make_table_widget ~sort:(0, `ASCENDING)
      [`String,"Worker",true; `String,"Job name",true]
      (fun columns store tp vc ->
        let row = store#get_iter tp in
        let w = store#get ~row ~column:(find_string_col "Worker" columns) in
 let info () = Minilib.log ("Coq busy, discarding query"in
 Coq.try_grab coqtop (coqops#stop_worker w) info
      ) in
  let tip = GMisc.label ~text:"Double click to interrupt worker" () in
  let box = GPack.vbox ~homogeneous:false () in
  let () = box#pack ~expand:true table#coerce in
  let () = box#pack ~expand:false ~padding:2 tip#coerce in
  let last_update = ref CString.Map.empty in
  let callback = ref (fun _ -> ()) in
  object (self)
    inherit GObj.widget box#as_widget
    method update jobs =
      if !last_update = jobs then ()
      else begin
 last_update := jobs;
 access (fun _ store -> store#clear ());
        !callback jobs;
 CString.Map.iter (fun id job -> access (fun columns store ->
          let column = find_string_col "Worker" columns in
          if job = "Dead" then
            store#foreach (fun _ row ->
              if store#get ~row ~column = id then store#remove row || true
              else false)
          else
            let line = store#append () in
            store#set ~row:line ~column id;
            store#set ~row:line ~column:(find_string_col "Job name" columns) job))
          jobs
      end
    method on_update ~callback:cb = callback := cb
    method data = !last_update
  end

let create_proof () =
  let proof = Wg_ProofView.proof_view () in
  let _ = proof#misc#set_can_focus true in
  let _ = GtkBase.Widget.add_events proof#as_widget
    [`ENTER_NOTIFY;`POINTER_MOTION]
  in
  proof

let create_messages () =
  let messages = Wg_MessageView.message_view () in
  let _ = messages#misc#set_can_focus true in
  Wg_RoutedMessageViews.message_views ~route_0:messages

let dummy_control : control =
  object
    method detach () = ()
  end

let create file coqtop_args =
  let basename = match file with
    |None -> "*scratch*"
    |Some f -> Glib.Convert.filename_to_utf8 (Filename.basename f)
  in
  let coqtop = Coq.spawn_coqtop coqtop_args in
  let reset () = Coq.reset_coqtop coqtop in
  let buffer = create_buffer () in
  let script = create_script coqtop buffer in
  let proof = create_proof () in
  let messages = create_messages () in
  let segment = new Wg_Segment.segment () in
  let finder = new Wg_Find.finder basename (script :> GText.view) in
  let fops = new FileOps.fileops (buffer :> GText.buffer) file reset in
  let _ = fops#update_stats in
  let cops =
    new CoqOps.coqops script proof messages segment coqtop (fun () -> fops#filename) in
  let command = new Wg_Command.command_window basename coqtop cops messages in
  let errpage = create_errpage script in
  let jobpage = create_jobpage coqtop cops in
  let _ = set_buffer_handlers (buffer :> GText.buffer) script cops coqtop in
  let _ = Coq.set_reset_handler coqtop cops#handle_reset_initial in
  let _ = Coq.init_coqtop coqtop cops#initialize in
  {
    buffer = (buffer :> GText.buffer);
    script=script;
    proof=proof;
    messages=messages;
    segment=segment;
    fileops=fops;
    coqops=cops;
    coqtop=coqtop;
    command=command;
    finder=finder;
    tab_label= GMisc.label ~text:basename ();
    errpage=errpage;
    jobpage=jobpage;
    control = dummy_control;
  }

let kill (sn:session) =
  (* To close the detached views of this script, we call manually
     [destroy] on it, triggering some callbacks in [detach_view].
     In a more modern lablgtk, rather use the page-removed signal ? *)

  sn.coqops#destroy ();
  sn.script#destroy ();
  Coq.close_coqtop sn.coqtop

let build_layout (sn:session) =
  let session_paned = GPack.paned `VERTICAL () in
  let session_box =
    GPack.vbox ~packing:(session_paned#pack1 ~shrink:false ~resize:true) ()
  in

  (* Right part of the window. *)

  let eval_paned = GPack.paned `HORIZONTAL ~border_width:5
    ~packing:(session_box#pack ~expand:true) () in
  let script_frame = GBin.frame ~shadow_type:`IN
    ~packing:(eval_paned#pack1 ~shrink:false) () in
  let script_scroll = GBin.scrolled_window
    ~vpolicy:`AUTOMATIC ~hpolicy:`AUTOMATIC ~packing:script_frame#add () in
  let state_paned = GPack.paned `VERTICAL
    ~packing:(eval_paned#pack2 ~shrink:true) () in

  (* Proof buffer. *)

  let title = Printf.sprintf "Proof (%s)" sn.tab_label#text in
  let proof_detachable = Wg_Detachable.detachable ~title () in
  let () = proof_detachable#button#misc#hide () in
  let () = proof_detachable#frame#set_shadow_type `IN in
  let () = state_paned#pack1 ~shrink:true proof_detachable#coerce in
  let proof_scroll = GBin.scrolled_window
    ~vpolicy:`AUTOMATIC ~hpolicy:`AUTOMATIC ~packing:proof_detachable#pack () in
  let callback _ = proof_detachable#show;
                   proof_scroll#coerce#misc#set_size_request ~width:0 ~height:0 ()
  in
  let () = proof_detachable#connect#attached ~callback in
  let callback _ =
    proof_scroll#coerce#misc#set_size_request ~width:500 ~height:400 ()
  in
  let () = proof_detachable#connect#detached ~callback in

  (* Message buffer. *)

  let message_frame = GPack.notebook ~packing:(state_paned#pack2 ~shrink:true) () in
  let add_msg_page pos name text (w : GObj.widget) =
    let detachable =
      Wg_Detachable.detachable ~title:(text^" ("^name^")") () in
    detachable#add w#coerce;
    let label = GPack.hbox ~spacing:5 () in
    let lbl = GMisc.label ~text ~packing:label#add () in
    let but = GButton.button () in
    but#add (GMisc.label ~markup:"" ())#coerce;
    label#add but#coerce;
    ignore(message_frame#insert_page ~pos
      ~tab_label:label#coerce detachable#coerce);
    ignore(but#connect#clicked ~callback:(fun _ ->
      message_frame#remove_page (message_frame#page_num detachable#coerce);
      detachable#button#clicked ()));
    detachable#connect#detached ~callback:(fun _ ->
      if message_frame#all_children = [] then message_frame#misc#hide ();
      w#misc#set_size_request  ~width:500 ~height:400 ());
    detachable#connect#attached ~callback:(fun _ ->
      ignore(message_frame#insert_page ~pos
        ~tab_label:label#coerce detachable#coerce);
      message_frame#misc#show ();
      detachable#show);
    detachable#button#misc#hide ();
    detachable, lbl in
  let session_tab = GPack.hbox ~homogeneous:false () in
  let img = GMisc.image ~icon_size:`SMALL_TOOLBAR
    ~packing:session_tab#pack () in
  let _ =
    sn.buffer#connect#modified_changed
      ~callback:(fun () -> if sn.buffer#modified
        then img#set_stock `SAVE
        else img#set_stock `YES) in
  (* There was an issue in the previous implementation for setting the
     position of the handle. It was using the size_allocate event but
     there is an issue with size_allocate. G. Melquiond analyzed that
     at starting time, the size_allocate event is only issued in
     Layout phase of the gtk loop so that it is actually processed
     only in the next iteration of the event-update-layout-paint loop,
     after the user does something and trigger an effective new event
     (see #10578). So we preventively enforce an estimated position
     for the handles to be used in the very first initializing
     iteration of the loop *)

  let () =
    (* 14 is the estimated size for vertical borders *)
    let estimated_vertical_handle_position = (window_width#get - 14) / 2 in
    (* 169 is the estimated size for menus, command line, horizontal border *)
    let estimated_horizontal_handle_position = (window_height#get - 169) / 2 in
    if estimated_vertical_handle_position > 0 then
      eval_paned#set_position estimated_vertical_handle_position;
    if estimated_horizontal_handle_position > 0 then
      state_paned#set_position estimated_horizontal_handle_position in
  session_box#pack sn.finder#coerce;
  session_box#pack sn.segment#coerce;
  sn.command#pack_in (session_paned#pack2 ~shrink:false ~resize:false);
  script_scroll#add sn.script#coerce;
  proof_scroll#add sn.proof#coerce;
  let detach, _ = add_msg_page 0 sn.tab_label#text "Messages" sn.messages#default_route#coerce in
  let _, label = add_msg_page 1 sn.tab_label#text "Errors" sn.errpage#coerce in
  let _, _ = add_msg_page 2 sn.tab_label#text "Jobs" sn.jobpage#coerce in
  (* When a message is received, focus on the message pane *)
  let _ =
    sn.messages#default_route#connect#pushed ~callback:(fun _ _ ->
      let num = message_frame#page_num detach#coerce in
      if 0 <= num then message_frame#goto_page num
    )
  in
  (* When an error occurs, paint the error label in red *)
  let txt = label#text in
  let red s = "#FF0000\">" ^ s ^ "" in
  sn.errpage#on_update ~callback:(fun l ->
    if l = [] then (label#set_use_markup false; label#set_text txt)
    else (label#set_text (red txt);label#set_use_markup true));
  session_tab#pack sn.tab_label#coerce;
  img#set_stock `YES;
  let control =
    object
      method detach () = proof_detachable#detach ()
    end
  in
  let () = sn.control <- control in
  (Some session_tab#coerce,None,session_paned#coerce)

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet)  ¤





Druckansicht
unsichere Verbindung
Druckansicht
sprechenden Kalenders

in der Quellcodebibliothek suchen




Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.


Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.


Bot Zugriff