diff options
Diffstat (limited to 'ide')
-rw-r--r-- | ide/coq.ml | 33 | ||||
-rw-r--r-- | ide/coq.mli | 10 | ||||
-rw-r--r-- | ide/coqOps.ml | 133 | ||||
-rw-r--r-- | ide/coqOps.mli | 1 | ||||
-rw-r--r-- | ide/session.ml | 4 | ||||
-rw-r--r-- | ide/wg_Command.ml | 5 |
6 files changed, 133 insertions, 53 deletions
diff --git a/ide/coq.ml b/ide/coq.ml index 121981110..baff54d62 100644 --- a/ide/coq.ml +++ b/ide/coq.ml @@ -275,6 +275,8 @@ type coqtop = { sup_args : string list; (* called whenever coqtop dies *) mutable reset_handler : reset_kind -> unit task; + (* called whenever coqtop sends a feedback message *) + mutable feedback_handler : Interface.feedback -> unit; (* actual coqtop process and its status *) mutable handle : handle; mutable status : status; @@ -360,6 +362,11 @@ let handle_intermediate_message logger xml = let content = message.Interface.message_content in logger level content +let handle_feedback feedback_processor xml = + let () = Minilib.log "Handling some feedback" in + let feedback = Serialize.to_feedback xml in + feedback_processor feedback + let handle_final_answer handle ccb xml = let () = Minilib.log "Handling coqtop answer" in let () = handle.waiting_for <- None in @@ -370,7 +377,7 @@ type input_state = { mutable lexerror : int option; } -let unsafe_handle_input handle state conds = +let unsafe_handle_input handle feedback_processor state conds = let chan = Glib.Io.channel_of_descr handle.cout in let () = check_errors conds in let s = io_read_all chan in @@ -392,6 +399,12 @@ let unsafe_handle_input handle state conds = let () = state.lexerror <- None in let () = handle_intermediate_message logger xml in loop () + else if Serialize.is_feedback xml then + let remaining = String.sub s l_end (String.length s - l_end) in + let () = state.fragment <- remaining in + let () = state.lexerror <- None in + let () = handle_feedback feedback_processor xml in + loop () else (* We should have finished decoding s *) let () = if not (is_blank s l_end) then raise AnswerWithoutRequest in @@ -413,7 +426,7 @@ let unsafe_handle_input handle state conds = let () = state.lexerror <- Some l_end in () -let install_input_watch handle respawner = +let install_input_watch handle respawner feedback_processor = let io_chan = Glib.Io.channel_of_descr handle.cout in let all_conds = [`ERR; `HUP; `IN; `NVAL; `PRI] in (* all except `OUT *) let state = { @@ -429,7 +442,7 @@ let install_input_watch handle respawner = if not handle.alive then false (* coqtop already terminated *) else try - let () = unsafe_handle_input handle state conds in + let () = unsafe_handle_input handle feedback_processor state conds in true with e -> let () = Minilib.log ("Coqtop reader failed, resetting: "^print_exception e) in @@ -472,7 +485,9 @@ let rec respawn_coqtop ?(why=Unexpected) coqtop = If not, there isn't much we can do ... *) assert (coqtop.handle.alive = true); coqtop.status <- New; - install_input_watch coqtop.handle (fun () -> respawn_coqtop coqtop); + install_input_watch coqtop.handle + (fun () -> respawn_coqtop coqtop) + (fun msg -> coqtop.feedback_handler msg); ignore (coqtop.reset_handler why coqtop.handle (mkready coqtop)) let spawn_coqtop sup_args = @@ -481,14 +496,18 @@ let spawn_coqtop sup_args = handle = spawn_handle sup_args; sup_args = sup_args; reset_handler = (fun _ _ k -> k ()); + feedback_handler = (fun _ -> ()); status = New; } in - install_input_watch ct.handle (fun () -> respawn_coqtop ct); + install_input_watch ct.handle + (fun () -> respawn_coqtop ct) (fun msg -> ct.feedback_handler msg); ct let set_reset_handler coqtop hook = coqtop.reset_handler <- hook +let set_feedback_handler coqtop hook = coqtop.feedback_handler <- hook + let is_computing coqtop = (coqtop.status = Busy) (* For closing a coqtop, we don't try to send it a Quit call anymore, @@ -540,8 +559,8 @@ let eval_call ?(logger=default_logger) call handle k = Minilib.log "End eval_call"; Void -let interp ?(logger=default_logger) ?(raw=false) ?(verbose=true) s h k = - eval_call ~logger (Serialize.interp (raw,verbose,s)) h +let interp ?(logger=default_logger) ?(raw=false) ?(verbose=true) i s h k = + eval_call ~logger (Serialize.interp (i,raw,verbose,s)) h (function | Interface.Good (Util.Inl v) -> k (Interface.Good v) | Interface.Good (Util.Inr v) -> k (Interface.Unsafe v) diff --git a/ide/coq.mli b/ide/coq.mli index 0210d0b41..84ef466d7 100644 --- a/ide/coq.mli +++ b/ide/coq.mli @@ -63,6 +63,9 @@ val spawn_coqtop : string list -> coqtop val set_reset_handler : coqtop -> (reset_kind -> unit task) -> unit (** Register a handler called when a coqtop dies (badly or on purpose) *) +val set_feedback_handler : coqtop -> (Interface.feedback -> unit) -> unit +(** Register a handler called when coqtop sends a feedback message *) + val init_coqtop : coqtop -> unit task -> unit (** Finish initializing a freshly spawned coqtop, by running a first task on it. The task should run its inner continuation at the end. *) @@ -113,8 +116,11 @@ val try_grab : coqtop -> unit task -> (unit -> unit) -> unit type 'a query = 'a Interface.value task (** A type abbreviation for coqtop specific answers *) -val interp : ?logger:Ideutils.logger -> ?raw:bool -> ?verbose:bool -> - string -> string query +val interp : + ?logger:Ideutils.logger -> + ?raw:bool -> + ?verbose:bool -> + int -> string -> string query val rewind : int -> int query val status : Interface.status query val goals : Interface.goals option query diff --git a/ide/coqOps.ml b/ide/coqOps.ml index 3e76b94b2..2647f923c 100644 --- a/ide/coqOps.ml +++ b/ide/coqOps.ml @@ -11,11 +11,40 @@ open Ideutils type flag = [ `COMMENT | `UNSAFE ] -type ide_info = { - start : GText.mark; - stop : GText.mark; - flags : flag list; -} +module SentenceId : sig + + type sentence = private { + start : GText.mark; + stop : GText.mark; + mutable flags : flag list; + id : int; + } + + val mk_sentence : + start:GText.mark -> stop:GText.mark -> flag list -> sentence + val set_flags : sentence -> flag list -> unit + +end = struct + + type sentence = { + start : GText.mark; + stop : GText.mark; + mutable flags : flag list; + id : int; + } + + let id = ref 0 + let mk_sentence ~start ~stop flags = decr id; { + start = start; + stop = stop; + flags = flags; + id = !id; + } + + let set_flags s f = s.flags <- f + +end +open SentenceId let prefs = Preferences.current @@ -39,6 +68,7 @@ class coqops (_script:Wg_ScriptView.script_view) (_pv:Wg_ProofView.proof_view) (_mv:Wg_MessageView.message_view) + (_ct:Coq.coqtop) get_filename = object(self) val script = _script @@ -48,6 +78,9 @@ object(self) val cmd_stack = Stack.create () + initializer + Coq.set_feedback_handler _ct self#process_feedback + method private get_start_of_input = buffer#get_iter_at_mark (`NAME "start_of_input") @@ -81,7 +114,8 @@ object(self) flash_info "This error is so nasty that I can't even display it." else messages#add s; in - let query = Coq.interp ~logger:messages#push ~raw:true ~verbose:false phrase in + let query = + Coq.interp ~logger:messages#push ~raw:true ~verbose:false 0 phrase in let next = function | Interface.Fail (_, err) -> display_error err; Coq.return () | Interface.Good msg | Interface.Unsafe msg -> @@ -103,12 +137,12 @@ object(self) let is_comment = stop#backward_char#has_tag Tags.Script.comment_sentence in - let payload = { - start = `MARK (buffer#create_mark start); - stop = `MARK (buffer#create_mark stop); - flags = if is_comment then [`COMMENT] else []; - } in - Queue.push payload queue; + let sentence = + mk_sentence + ~start:(`MARK (buffer#create_mark start)) + ~stop:(`MARK (buffer#create_mark stop)) + (if is_comment then [`COMMENT] else []) in + Queue.push sentence queue; if not stop#is_end then loop (succ len) stop in try loop 0 self#get_start_of_input with Exit -> () @@ -123,22 +157,37 @@ object(self) buffer#delete_mark sentence.stop; done - method private commit_queue_transaction queue sentence newflags = + method private mark_as_needed sentence = + let start = buffer#get_iter_at_mark sentence.start in + let stop = buffer#get_iter_at_mark sentence.stop in + let tag = + if List.mem `UNSAFE sentence.flags then Tags.Script.unjustified + else Tags.Script.processed in + buffer#apply_tag tag ~start ~stop + + method private process_feedback msg = + let id = msg.Interface.edit_id in + if id = 0 || Stack.is_empty cmd_stack then () else + let sentence = + let last_sentence = Stack.top cmd_stack in + if last_sentence.id = id then Some last_sentence else None in + match msg.Interface.content, sentence with + | Interface.AddedAxiom, Some sentence -> + set_flags sentence (CList.add_set `UNSAFE sentence.flags); + self#mark_as_needed sentence + | Interface.Processed, Some sentence -> self#mark_as_needed sentence + | _, None -> Minilib.log "Coqide/Coq is really asynchronous now!" + (* In this case we shoud look for (exec_)id into cmd_stack *) + + method private commit_queue_transaction sentence = (* A queued command has been successfully done, we push it to [cmd_stack]. We reget the iters here because Gtk is unable to warranty that they were not modified meanwhile. Not really necessary but who knows... *) + self#mark_as_needed sentence; let start = buffer#get_iter_at_mark sentence.start in let stop = buffer#get_iter_at_mark sentence.stop in - let sentence = { sentence with flags = newflags @ sentence.flags } in - let tag = - if List.mem `UNSAFE newflags then Tags.Script.unjustified - else Tags.Script.processed - in buffer#move_mark ~where:stop (`NAME "start_of_input"); - buffer#apply_tag tag ~start ~stop; - buffer#remove_tag Tags.Script.to_process ~start ~stop; - ignore (Queue.pop queue); - Stack.push sentence cmd_stack + buffer#remove_tag Tags.Script.to_process ~start ~stop method private process_error queue phrase loc msg = Coq.bind (Coq.return ()) (function () -> @@ -153,6 +202,10 @@ object(self) buffer#apply_tag Tags.Script.error ~start ~stop; buffer#place_cursor ~where:start in + let sentence = Stack.pop cmd_stack in + let start = buffer#get_iter_at_mark sentence.start in + let stop = buffer#get_iter_at_mark sentence.stop in + buffer#remove_tag Tags.Script.to_process ~start ~stop; self#discard_command_queue queue; pop_info (); position_error loc; @@ -183,9 +236,10 @@ object(self) let () = script#recenter_insert in self#show_goals else - let sentence = Queue.peek queue in + let sentence = Queue.pop queue in + Stack.push sentence cmd_stack; if List.mem `COMMENT sentence.flags then - (self#commit_queue_transaction queue sentence []; loop ()) + (self#commit_queue_transaction sentence; loop ()) else (* If the line is not a comment, we interpret it. *) let phrase = @@ -193,17 +247,18 @@ object(self) let stop = buffer#get_iter_at_mark sentence.stop in start#get_slice ~stop in - let commit_and_continue msg flags = + let commit_and_continue msg = push_msg Interface.Notice msg; - self#commit_queue_transaction queue sentence flags; + self#commit_queue_transaction sentence; loop () in - let query = Coq.interp ~logger:push_msg ~verbose phrase in + let query = Coq.interp ~logger:push_msg ~verbose sentence.id phrase in let next = function - | Interface.Good msg -> commit_and_continue msg [] - | Interface.Unsafe msg -> commit_and_continue msg [`UNSAFE] - | Interface.Fail (loc, msg) -> - self#process_error queue phrase loc msg + | Interface.Good msg -> commit_and_continue msg + | Interface.Unsafe msg -> + set_flags sentence (CList.add_set `UNSAFE sentence.flags); + commit_and_continue msg + | Interface.Fail (loc, msg) -> self#process_error queue phrase loc msg in Coq.bind query next in @@ -329,12 +384,12 @@ object(self) buffer#apply_tag tag ~start ~stop; if self#get_insert#compare stop <= 0 then buffer#place_cursor ~where:stop; - let ide_payload = { - start = `MARK (buffer#create_mark start); - stop = `MARK (buffer#create_mark stop); - flags = []; - } in - Stack.push ide_payload cmd_stack; + let sentence = + mk_sentence + ~start:(`MARK (buffer#create_mark start)) + ~stop:(`MARK (buffer#create_mark stop)) + [] in + Stack.push sentence cmd_stack; messages#clear; self#show_goals in @@ -345,7 +400,7 @@ object(self) in let try_phrase phrase stop more = let action = log "Sending to coq now" in - let query = Coq.interp ~verbose:false phrase in + let query = Coq.interp ~verbose:false 0 phrase in let next = function | Interface.Fail (l, str) -> display_error (l, str); @@ -400,7 +455,7 @@ object(self) | Interface.Good true | Interface.Unsafe true -> Coq.return () | Interface.Good false | Interface.Unsafe false -> let cmd = Printf.sprintf "Add LoadPath \"%s\". " dir in - let cmd = Coq.interp cmd in + let cmd = Coq.interp 0 cmd in let next = function | Interface.Fail (l, str) -> messages#set ("Couln't add loadpath:\n"^str); diff --git a/ide/coqOps.mli b/ide/coqOps.mli index 7e47ca23f..48a07f48b 100644 --- a/ide/coqOps.mli +++ b/ide/coqOps.mli @@ -25,5 +25,6 @@ class coqops : Wg_ScriptView.script_view -> Wg_ProofView.proof_view -> Wg_MessageView.message_view -> + coqtop -> (unit -> string option) -> ops diff --git a/ide/session.ml b/ide/session.ml index 8eeb3c6f9..4e95fefca 100644 --- a/ide/session.ml +++ b/ide/session.ml @@ -155,8 +155,8 @@ let create file coqtop_args = let finder = new Wg_Find.finder (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 (fun () -> fops#filename) - in + let cops = + new CoqOps.coqops script proof messages coqtop (fun () -> fops#filename) in let _ = Coq.set_reset_handler coqtop cops#handle_reset_initial in let _ = Coq.init_coqtop coqtop cops#initialize in { diff --git a/ide/wg_Command.ml b/ide/wg_Command.ml index e0a742779..dd8896cba 100644 --- a/ide/wg_Command.ml +++ b/ide/wg_Command.ml @@ -111,10 +111,9 @@ object(self) if String.get com (String.length com - 1) = '.' then com ^ " " else com ^ " " ^ entry#text ^" . " in - let log level message = result#buffer#insert (message^"\n") - in + let log level message = result#buffer#insert (message^"\n") in let process = - Coq.bind (Coq.interp ~logger:log ~raw:true phrase) (function + Coq.bind (Coq.interp ~logger:log ~raw:true 0 phrase) (function | Interface.Fail (l,str) -> result#buffer#insert ("Error while interpreting "^phrase^":\n"^str); Coq.return () |