(************************************************************************) (* * The Coq Proof Assistant / The Coq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* 0 then d else compare s1 s2 end module Proposals = Set.Make(StringOrd) (** Retrieve completion proposals in the buffer *) let get_syntactic_completion (buffer : GText.buffer) pattern accu = let rec get_aux accu (iter : GText.iter) = match iter#forward_search pattern with | None -> accu | Some (start, stop) -> if Gtk_parsing.starts_word start then let ne = Gtk_parsing.find_word_end stop in if ne#compare stop = 0 then get_aux accu stop else let proposal = buffer#get_text ~start ~stop:ne () in let accu = Proposals.add proposal accu in get_aux accu stop else get_aux accu stop in get_aux accu buffer#start_iter (** Retrieve completion proposals in Coq libraries *) let get_semantic_completion pattern accu = let flags = [Interface.Name_Pattern ("^" ^ pattern), true] in (* Only get the last part of the qualified name *) let rec last accu = function | [] -> accu | [basename] -> Proposals.add basename accu | _ :: l -> last accu l in let next = function | Interface.Good l -> let fold accu elt = last accu elt.Interface.coq_object_qualid in let ans = List.fold_left fold accu l in Coq.return ans | _ -> Coq.return accu in Coq.bind (Coq.search flags) next let is_substring s1 s2 = let s1 = Glib.Utf8.to_unistring s1 in let s2 = Glib.Utf8.to_unistring s2 in let break = ref true in let i = ref 0 in let len1 = Array.length s1 in let len2 = Array.length s2 in while !break && !i < len1 && !i < len2 do break := s1.(!i) = s2.(!i); incr i; done; if !break then len2 - len1 else -1 class completion_provider buffer coqtop = let self_provider = ref None in let active = ref true in let provider = object (self) val mutable auto_complete_length = 3 val mutable cache = (-1, "", Proposals.empty) val mutable insert_offset = -1 method name = "" method icon = None method private update_proposals pref = let (_, _, props) = cache in let filter prop = 0 <= is_substring pref prop in let props = Proposals.filter filter props in props method private add_proposals ctx props = let mk text = let item = GSourceView3.source_completion_item ~text ~label:text () in (item :> GSourceView3.source_completion_proposal) in let props = List.map mk (Proposals.elements props) in ctx#add_proposals (Option.get !self_provider) props true method populate ctx = let iter = buffer#get_iter_at_mark `INSERT in let () = insert_offset <- iter#offset in let () = Minilib.log (Printf.sprintf "Completion at offset: %i" insert_offset) in let buffer = new GText.buffer iter#buffer in if not (Gtk_parsing.ends_word iter#backward_char) then self#add_proposals ctx Proposals.empty else let start = Gtk_parsing.find_word_start iter in if iter#offset - start#offset < auto_complete_length then self#add_proposals ctx Proposals.empty else let w = start#get_text ~stop:iter in let () = Minilib.log ("Completion of prefix: '" ^ w ^ "'") in let (off, prefix, props) = cache in let start_offset = start#offset in (* check whether we have the last request in cache *) if (start_offset = off) && (0 <= is_substring prefix w) then let props = self#update_proposals w in self#add_proposals ctx props else let cancel = ref false in let _ = ctx#connect#cancelled ~callback:(fun () -> cancel := true) in let update props = let () = cache <- (start_offset, w, props) in if not !cancel then self#add_proposals ctx props in (* If not in the cache, we recompute it: first syntactic *) let synt = get_syntactic_completion buffer w Proposals.empty in (* Then semantic *) let next props = update props; Coq.return () in let query = Coq.bind (get_semantic_completion w synt) next in (* If coqtop is computing, do the syntactic completion altogether *) let occupied () = update synt in Coq.try_grab coqtop query occupied method matched ctx = !active method activation = [`INTERACTIVE; `USER_REQUESTED] method info_widget proposal = None method update_info proposal info = () method start_iter ctx proposal iter = false method activate_proposal proposal iter = false method interactive_delay = (-1) method priority = 0 end in let provider = GSourceView3.source_completion_provider provider in object (self) inherit GSourceView3.source_completion_provider provider#as_source_completion_provider method active = !active method set_active b = active := b initializer self_provider := Some (self :> GSourceView3.source_completion_provider) end