summaryrefslogtreecommitdiff
path: root/ide/nanoPG.ml
diff options
context:
space:
mode:
authorGravatar Enrico Tassi <gareuselesinge@debian.org>2015-01-25 14:42:51 +0100
committerGravatar Enrico Tassi <gareuselesinge@debian.org>2015-01-25 14:42:51 +0100
commit7cfc4e5146be5666419451bdd516f1f3f264d24a (patch)
treee4197645da03dc3c7cc84e434cc31d0a0cca7056 /ide/nanoPG.ml
parent420f78b2caeaaddc6fe484565b2d0e49c66888e5 (diff)
Imported Upstream version 8.5~beta1+dfsg
Diffstat (limited to 'ide/nanoPG.ml')
-rw-r--r--ide/nanoPG.ml321
1 files changed, 321 insertions, 0 deletions
diff --git a/ide/nanoPG.ml b/ide/nanoPG.ml
new file mode 100644
index 00000000..805ace93
--- /dev/null
+++ b/ide/nanoPG.ml
@@ -0,0 +1,321 @@
+(************************************************************************)
+(* v * The Coq Proof Assistant / The Coq Development Team *)
+(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2015 *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(************************************************************************)
+
+open Ideutils
+open Session
+open Preferences
+open GdkKeysyms
+open Printf
+
+let eprintf x =
+ if !Flags.debug then Printf.eprintf x else Printf.ifprintf stderr x
+
+type gui = {
+ notebook : session Wg_Notebook.typed_notebook;
+ action_groups : GAction.action_group list;
+}
+
+let actiong gui name = List.find (fun ag -> ag#name = name) gui.action_groups
+let ct gui = gui.notebook#current_term
+
+let get_sel b = b#selection_bounds
+let sel_nonempty b = let i, j = get_sel b in not (i#equal j)
+let get_sel_txt b = let i, j = get_sel b in i#get_text ~stop:j
+
+type status = { move : int option; kill : (string * bool) option; sel: bool }
+
+let pr_status { move; kill; sel } =
+ let move = Option.cata (fun i -> string_of_int i) "" move in
+ let kill = Option.cata (fun (s,b) -> sprintf "kill(%b) %S" b s) "" kill in
+ let sel = string_of_bool sel in
+ Printf.sprintf "{ move: %s; kill: %s; sel: %s }" move kill sel
+let pr_key t =
+ let kv = GdkEvent.Key.keyval t in
+ let str = GdkEvent.Key.string t in
+ let str_of_mod = function
+ | `SHIFT -> "SHIFT" | `LOCK -> "LOCK" | `CONTROL -> "CONTROL"
+ | `MOD1 -> "MOD1" | `MOD2 -> "MOD2" | `MOD3 -> "MOD3" | `MOD4 -> "MOD4"
+ | `MOD5 -> "MOD5" | `BUTTON1 -> "BUTTON1" | `BUTTON2 -> "BUTTON2"
+ | `BUTTON3 -> "BUTTON3" | `BUTTON4 -> "BUTTON4" | `BUTTON5 -> "BUTTON5"
+ | `SUPER -> "SUPER" | `HYPER -> "HYPER" | `META -> "META"
+ | `RELEASE -> "RELEASE" in
+ let mods = String.concat " " (List.map str_of_mod (GdkEvent.Key.state t)) in
+ Printf.sprintf "'%s' (%d, %s)" str kv mods
+
+type action =
+ | Action of string * string
+ | Callback of (gui -> unit)
+ | Edit of (status -> GSourceView2.source_buffer -> GText.iter ->
+ (string -> string -> unit) -> status)
+ | Motion of (status -> GText.iter -> GText.iter * status)
+
+type 'c entry = {
+ mods : Gdk.Tags.modifier list;
+ key : Gdk.keysym;
+ keyname : string;
+ doc : string;
+ contents : 'c
+}
+
+let mC = [`CONTROL]
+let mM = [`MOD1]
+
+let mod_of t x = List.for_all (fun m -> List.mem m (GdkEvent.Key.state t)) x
+
+let pr_keymod l =
+ if l = mC then "C-"
+ else if l = mM then "M-"
+ else ""
+
+let mkE ?(mods=mC) key keyname doc ?(alias=[]) contents =
+ List.map (fun (mods, key, keyname) -> { mods; key; keyname; doc; contents })
+ ((mods, key, keyname)::alias)
+
+type keypaths = Step of action entry list * keypaths entry list
+
+let print_keypaths kps =
+ let rec aux prefix (Step (l, konts)) =
+ String.concat "\n" (
+ (List.map (fun x ->
+ prefix ^ pr_keymod x.mods ^ x.keyname ^ " " ^ x.doc ) l) @
+ (List.map (fun x ->
+ aux (prefix^pr_keymod x.mods^x.keyname^" ") x.contents) konts)) in
+ aux " " kps
+
+let empty = Step([],[])
+
+let frontier (Step(l1,l2)) =
+ List.map (fun x -> pr_keymod x.mods ^ x.keyname) l1 @
+ List.map (fun x -> pr_keymod x.mods ^ x.keyname) l2
+
+let insert kps name enter_syms bindings =
+ let rec aux kps enter_syms =
+ match enter_syms, kps with
+ | [], Step (el, konts) -> Step (List.flatten bindings @ el, konts)
+ | (mods, key, keyname)::gs, Step (el, konts) ->
+ if List.exists (fun { key = k; mods = m } -> key = k && mods = m) konts
+ then
+ let konts =
+ List.map
+ (fun ({ key = k; contents } as x) ->
+ if key <> k then x else { x with contents = aux contents gs })
+ konts in
+ Step(el,konts)
+ else
+ let kont =
+ { mods; key; keyname; doc = name; contents = aux empty gs } in
+ Step(el, kont::konts) in
+ aux kps enter_syms
+
+let run_action gui group name =
+ ((actiong gui group)#get_action name)#activate ()
+
+let run key gui action status =
+ match action with
+ | Callback f -> f gui; status
+ | Action(group, name) -> run_action gui group name; status
+ | Edit f ->
+ let b = (ct gui).script#source_buffer in
+ let i = b#get_iter_at_mark `INSERT in
+ let status = f status b i (run_action gui) in
+ if not status.sel then
+ b#place_cursor ~where:(b#get_iter_at_mark `SEL_BOUND);
+ status
+ | Motion f ->
+ let b, script = (ct gui).script#source_buffer, (ct gui).script in
+ let sel_mode = status.sel || List.mem `SHIFT (GdkEvent.Key.state key) in
+ let i =
+ if sel_mode then b#get_iter_at_mark `SEL_BOUND
+ else b#get_iter_at_mark `INSERT in
+ let where, status = f status i in
+ let sel_mode = status.sel || List.mem `SHIFT (GdkEvent.Key.state key) in
+ if sel_mode then (b#move_mark `SEL_BOUND ~where; script#scroll_mark_onscreen `SEL_BOUND)
+ else (b#place_cursor ~where; script#scroll_mark_onscreen `INSERT);
+ status
+
+let emacs = empty
+
+let emacs = insert emacs "Emacs" [] [
+ (* motion *)
+ mkE _e "e" "Move to end of line" (Motion(fun s i ->
+ (if not i#ends_line then i#forward_to_line_end else i),
+ { s with move = None }));
+ mkE _a "a" "Move to beginning of line" (Motion(fun s i ->
+ (i#set_line_offset 0), { s with move = None }));
+ mkE ~mods:mM _e "e" "Move to end of sentence" (Motion(fun s i ->
+ i#forward_sentence_end, { s with move = None }));
+ mkE ~mods:mM _a "a" "Move to beginning of sentence" (Motion(fun s i ->
+ i#backward_sentence_start, { s with move = None }));
+ mkE _n "n" "Move to next line" ~alias:[[],_Down,"DOWN"] (Motion(fun s i ->
+ let orig_off = Option.default i#line_offset s.move in
+ let i = i#forward_line in
+ let new_off = min (i#chars_in_line - 1) orig_off in
+ (if new_off > 0 then i#set_line_offset new_off else i),
+ { s with move = Some orig_off }));
+ mkE _p "p" "Move to previous line" ~alias:[[],_Up,"UP"] (Motion(fun s i ->
+ let orig_off = Option.default i#line_offset s.move in
+ let i = i#backward_line in
+ let new_off = min (i#chars_in_line - 1) orig_off in
+ (if new_off > 0 then i#set_line_offset new_off else i),
+ { s with move = Some orig_off }));
+ mkE _f "f" "Forward char" ~alias:[[],_Right,"RIGHT"]
+ (Motion(fun s i -> i#forward_char, { s with move = None }));
+ mkE _b "b" "Backward char" ~alias:[[],_Left,"LEFT"]
+ (Motion(fun s i -> i#backward_char, { s with move = None }));
+ mkE ~mods:mM _f "f" "Forward word" ~alias:[mC,_Right,"RIGHT"]
+ (Motion(fun s i -> i#forward_word_end, { s with move = None }));
+ mkE ~mods:mM _b "b" "Backward word" ~alias:[mC,_Left,"LEFT"]
+ (Motion(fun s i -> i#backward_word_start, { s with move = None }));
+ mkE _space "SPC" "Set mark" ~alias:[mC,_at,"@"] (Motion(fun s i ->
+ if s.sel = false then i, { s with sel = true }
+ else i, { s with sel = false } ));
+ (* edits *)
+ mkE ~mods:mM _w "w" "Copy selected region" (Edit(fun s b i run ->
+ if sel_nonempty b then
+ let txt = get_sel_txt b in
+ run "Edit" "Copy";
+ { s with kill = Some(txt,false); sel = false }
+ else s));
+ mkE _w "w" "Kill selected region" (Edit(fun s b i run ->
+ if sel_nonempty b then
+ let txt = get_sel_txt b in
+ run "Edit" "Cut";
+ { s with kill = Some(txt,false); sel = false }
+ else s));
+ mkE _k "k" "Kill untill the end of line" (Edit(fun s b i _ ->
+ let already_killed = match s.kill with Some (k,true) -> k | _ -> "" in
+ let k =
+ if i#ends_line then begin
+ b#delete ~start:i ~stop:i#forward_char; "\n"
+ end else begin
+ let k = b#get_text ~start:i ~stop:i#forward_to_line_end () in
+ b#delete ~start:i ~stop:i#forward_to_line_end; k
+ end in
+ { s with kill = Some (already_killed ^ k,true) }));
+ mkE ~mods:mM _d "d" "Kill next word" (Edit(fun s b i _ ->
+ let already_killed = match s.kill with Some (k,true) -> k | _ -> "" in
+ let k =
+ let k = b#get_text ~start:i ~stop:i#forward_word_end () in
+ b#delete ~start:i ~stop:i#forward_word_end; k in
+ { s with kill = Some (already_killed ^ k,true) }));
+ mkE ~mods:mM _k "k" "Kill until sentence end" (Edit(fun s b i _ ->
+ let already_killed = match s.kill with Some (k,true) -> k | _ -> "" in
+ let k =
+ let k = b#get_text ~start:i ~stop:i#forward_sentence_end () in
+ b#delete ~start:i ~stop:i#forward_sentence_end; k in
+ { s with kill = Some (already_killed ^ k,true) }));
+ mkE ~mods:mM _BackSpace "DELBACK" "Kill word before cursor"
+ (Edit(fun s b i _ ->
+ let already_killed = match s.kill with Some (k,true) -> k | _ -> "" in
+ let k =
+ let k = b#get_text ~start:i ~stop:i#backward_word_start () in
+ b#delete ~start:i ~stop:i#backward_word_start; k in
+ { s with kill = Some (already_killed ^ k,true) }));
+ mkE _d "d" "Delete next character" (Edit(fun s b i _ ->
+ b#delete ~start:i ~stop:i#forward_char; s));
+ mkE _y "y" "Yank killed text back " (Edit(fun s b i _ ->
+ let k, s = match s.kill with
+ | Some (k,_) -> k, { s with kill = Some (k,false) }
+ | _ -> "", s in
+ b#insert ~iter:i k;
+ s));
+ (* misc *)
+ mkE _underscore "_" "Undo" (Action("Edit", "Undo"));
+ mkE _g "g" "Esc" (Callback(fun gui -> (ct gui).finder#hide ()));
+ mkE _s "s" "Search" (Callback(fun gui ->
+ if (ct gui).finder#coerce#misc#visible
+ then run_action gui "Edit" "Find Next"
+ else run_action gui "Edit" "Find"));
+ mkE _s "r" "Search backward" (Callback(fun gui ->
+ if (ct gui).finder#coerce#misc#visible
+ then run_action gui "Edit" "Find Previous"
+ else run_action gui "Edit" "Find"));
+ ]
+
+let emacs = insert emacs "Emacs" [mC,_x,"x"] [
+ mkE _s "s" "Save" (Action("File", "Save"));
+ mkE _c "c" "Quit" (Action("File", "Quit"));
+ mkE _f "f" "Open" (Action("File", "Open"));
+ mkE ~mods:[] _u "u" "Undo" (Action("Edit", "Undo"));
+ ]
+
+let pg = insert emacs "Proof General" [mC,_c,"c"] [
+ mkE _Return "RET" "Go to" (Action("Navigation", "Go to"));
+ mkE _n "n" "Advance 1 sentence" (Action("Navigation", "Forward"));
+ mkE _u "u" "Retract 1 sentence" (Action("Navigation", "Backward"));
+ mkE _b "b" "Advance" (Action("Navigation", "End"));
+ mkE _r "r" "Restart" (Action("Navigation", "Start"));
+ mkE _c "c" "Stop" (Action("Navigation", "Interrupt"));
+ ]
+
+let command gui c =
+ let command = (ct gui).command in
+ let script = (ct gui).script in
+ let term =
+ let i, j = script#source_buffer#selection_bounds in
+ if i#equal j then None
+ else Some (script#buffer#get_text ~start:i ~stop:j ()) in
+ command#show;
+ command#new_query ~command:c ?term ()
+
+let pg = insert pg "Proof General" [mC,_c,"c"; mC,_a,"a"] [
+ mkE _p "p" "Print" (Callback (fun gui -> command gui "Print"));
+ mkE _c "c" "Check" (Callback (fun gui -> command gui "Check"));
+ mkE _b "b" "About" (Callback (fun gui -> command gui "About"));
+ mkE _a "a" "Search About" (Callback (fun gui -> command gui "SearchAbout"));
+ mkE _o "o" "Search Pattern" (Callback (fun gui->command gui "SearchPattern"));
+ mkE _l "l" "Locate" (Callback (fun gui -> command gui "Locate"));
+ mkE _Return "RET" "match template" (Action("Templates","match"));
+ ]
+
+let empty = { sel = false; kill = None; move = None }
+
+let find gui (Step(here,konts)) t =
+ (* hack: ^c does copy in clipboard *)
+ let sel_nonempty () = sel_nonempty (ct gui).script#source_buffer in
+ let k = GdkEvent.Key.keyval t in
+ if k = _x && mod_of t mC && sel_nonempty () then
+ ignore(run t gui (Action("Edit","Cut")) empty)
+ else
+ if k = _c && mod_of t mC && sel_nonempty () then
+ ignore(run t gui (Action("Edit","Copy")) empty);
+ let cmp { key; mods } = key = k && mod_of t mods in
+ try `Do (List.find cmp here) with Not_found ->
+ try `Cont (List.find cmp konts).contents with Not_found -> `NotFound
+
+let init w nb ags =
+ let gui = { notebook = nb; action_groups = ags } in
+ let cur = ref pg in
+ let status = ref empty in
+ let reset () = eprintf "reset\n%!"; cur := pg in
+ ignore(w#event#connect#key_press ~callback:(fun t ->
+ let on_current_term f =
+ let term = try Some nb#current_term with Invalid_argument _ -> None in
+ match term with None -> false | Some t -> f t
+ in
+ on_current_term (fun x ->
+ if x.script#misc#get_property "has-focus" <> `BOOL true
+ then false
+ else begin
+ eprintf "got key %s\n%!" (pr_key t);
+ if current.nanoPG then begin
+ match find gui !cur t with
+ | `Do e ->
+ eprintf "run (%s) %s on %s\n%!" e.keyname e.doc (pr_status !status);
+ status := run t gui e.contents !status; reset (); true
+ | `Cont c ->
+ flash_info ("Waiting one of " ^ String.concat " " (frontier c));
+ cur := c; true
+ | `NotFound -> reset (); false
+ end else false
+ end)));
+ ignore(w#event#connect#button_press ~callback:(fun t -> reset (); false))
+
+
+
+let get_documentation () = print_keypaths pg