|
Packit |
bd2e5d |
(*************************************************************************)
|
|
Packit |
bd2e5d |
(* *)
|
|
Packit |
bd2e5d |
(* OCaml LablTk library *)
|
|
Packit |
bd2e5d |
(* *)
|
|
Packit |
bd2e5d |
(* Jacques Garrigue, Kyoto University RIMS *)
|
|
Packit |
bd2e5d |
(* *)
|
|
Packit |
bd2e5d |
(* Copyright 1999 Institut National de Recherche en Informatique et *)
|
|
Packit |
bd2e5d |
(* en Automatique and Kyoto University. All rights reserved. *)
|
|
Packit |
bd2e5d |
(* This file is distributed under the terms of the GNU Library *)
|
|
Packit |
bd2e5d |
(* General Public License, with the special exception on linking *)
|
|
Packit |
bd2e5d |
(* described in file ../../../LICENSE. *)
|
|
Packit |
bd2e5d |
(* *)
|
|
Packit |
bd2e5d |
(*************************************************************************)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* $Id$ *)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
open StdLabels
|
|
Packit |
bd2e5d |
module Unix = UnixLabels
|
|
Packit |
bd2e5d |
open Tk
|
|
Packit |
bd2e5d |
open Jg_tk
|
|
Packit |
bd2e5d |
open Dummy
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* Here again, memoize regexps *)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let (~!) = Jg_memo.fast ~f:Str.regexp
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* Nice history class. May reuse *)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
class ['a] history () = object
|
|
Packit |
bd2e5d |
val mutable history = ([] : 'a list)
|
|
Packit |
bd2e5d |
val mutable count = 0
|
|
Packit |
bd2e5d |
method empty = history = []
|
|
Packit |
bd2e5d |
method add s = count <- 0; history <- s :: history
|
|
Packit |
bd2e5d |
method previous =
|
|
Packit |
bd2e5d |
let s = List.nth history count in
|
|
Packit |
bd2e5d |
count <- (count + 1) mod List.length history;
|
|
Packit |
bd2e5d |
s
|
|
Packit |
bd2e5d |
method next =
|
|
Packit |
bd2e5d |
let l = List.length history in
|
|
Packit |
bd2e5d |
count <- (l + count - 1) mod l;
|
|
Packit |
bd2e5d |
List.nth history ((l + count - 1) mod l)
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let dump_handle (h : Unix.file_descr) =
|
|
Packit |
bd2e5d |
let obj = Obj.repr h in
|
|
Packit |
bd2e5d |
if Obj.is_int obj || Obj.tag obj <> Obj.custom_tag then
|
|
Packit |
bd2e5d |
invalid_arg "Shell.dump_handle";
|
|
Packit |
bd2e5d |
Nativeint.format "%x" (Obj.obj obj)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* The shell class. Now encapsulated *)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let protect f x = try f x with _ -> ()
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let is_win32 = Sys.os_type = "Win32"
|
|
Packit |
bd2e5d |
let use_threads = is_win32
|
|
Packit |
bd2e5d |
let use_sigpipe = is_win32
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
class shell ~textw ~prog ~args ~env ~history =
|
|
Packit |
bd2e5d |
let (in2,out1) = Unix.pipe ()
|
|
Packit |
bd2e5d |
and (in1,out2) = Unix.pipe ()
|
|
Packit |
bd2e5d |
and (err1,err2) = Unix.pipe ()
|
|
Packit |
bd2e5d |
and (sig2,sig1) = Unix.pipe () in
|
|
Packit |
bd2e5d |
object (self)
|
|
Packit |
bd2e5d |
val pid =
|
|
Packit |
bd2e5d |
let env =
|
|
Packit |
bd2e5d |
if use_sigpipe then
|
|
Packit |
bd2e5d |
let sigdef = "CAMLSIGPIPE=" ^ dump_handle sig2 in
|
|
Packit |
bd2e5d |
Array.append env [|sigdef|]
|
|
Packit |
bd2e5d |
else env
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
Unix.create_process_env ~prog ~args ~env
|
|
Packit |
bd2e5d |
~stdin:in2 ~stdout:out2 ~stderr:err2
|
|
Packit |
bd2e5d |
val out = Unix.out_channel_of_descr out1
|
|
Packit |
bd2e5d |
val h : _ history = history
|
|
Packit |
bd2e5d |
val mutable alive = true
|
|
Packit |
bd2e5d |
val mutable reading = false
|
|
Packit |
bd2e5d |
val ibuffer = Buffer.create 1024
|
|
Packit |
bd2e5d |
val imutex = Mutex.create ()
|
|
Packit |
bd2e5d |
val mutable ithreads = []
|
|
Packit |
bd2e5d |
method alive = alive
|
|
Packit |
bd2e5d |
method kill =
|
|
Packit |
bd2e5d |
if Winfo.exists textw then Text.configure textw ~state:`Disabled;
|
|
Packit |
bd2e5d |
if alive then begin
|
|
Packit |
bd2e5d |
alive <- false;
|
|
Packit |
bd2e5d |
protect close_out out;
|
|
Packit |
bd2e5d |
try
|
|
Packit |
bd2e5d |
if use_sigpipe then
|
|
Packit |
bd2e5d |
ignore (Unix.write sig1 ~buf:(Bytes.make 1 'T') ~pos:0 ~len:1);
|
|
Packit |
bd2e5d |
List.iter ~f:(protect Unix.close) [in1; err1; sig1; sig2];
|
|
Packit |
bd2e5d |
if not use_threads then begin
|
|
Packit |
bd2e5d |
Fileevent.remove_fileinput ~fd:in1;
|
|
Packit |
bd2e5d |
Fileevent.remove_fileinput ~fd:err1;
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
if not use_sigpipe then begin
|
|
Packit |
bd2e5d |
Unix.kill ~pid ~signal:Sys.sigkill;
|
|
Packit |
bd2e5d |
ignore (Unix.waitpid ~mode:[] pid)
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
with _ -> ()
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
method interrupt =
|
|
Packit |
bd2e5d |
if alive then try
|
|
Packit |
bd2e5d |
reading <- false;
|
|
Packit |
bd2e5d |
if use_sigpipe then begin
|
|
Packit |
bd2e5d |
ignore (Unix.write sig1 ~buf:(Bytes.make 1 'C') ~pos:0 ~len:1);
|
|
Packit |
bd2e5d |
self#send " "
|
|
Packit |
bd2e5d |
end else
|
|
Packit |
bd2e5d |
Unix.kill ~pid ~signal:Sys.sigint
|
|
Packit |
bd2e5d |
with Unix.Unix_error _ -> ()
|
|
Packit |
bd2e5d |
method send s =
|
|
Packit |
bd2e5d |
if alive then try
|
|
Packit |
bd2e5d |
output_string out s;
|
|
Packit |
bd2e5d |
flush out
|
|
Packit |
bd2e5d |
with Sys_error _ -> ()
|
|
Packit |
bd2e5d |
method private read ~fd ~len =
|
|
Packit |
bd2e5d |
begin try
|
|
Packit |
bd2e5d |
let buf = Bytes.create len in
|
|
Packit |
bd2e5d |
let len = Unix.read fd ~buf ~pos:0 ~len in
|
|
Packit |
bd2e5d |
if len > 0 then begin
|
|
Packit |
bd2e5d |
self#insert (Bytes.sub_string buf ~pos:0 ~len);
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"input" ~index:(`Mark"insert",[`Char(-1)])
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
len
|
|
Packit |
bd2e5d |
with Unix.Unix_error _ -> 0
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
method history (dir : [`Next|`Previous]) =
|
|
Packit |
bd2e5d |
if not h#empty then begin
|
|
Packit |
bd2e5d |
if reading then begin
|
|
Packit |
bd2e5d |
Text.delete textw ~start:(`Mark"input",[`Char 1])
|
|
Packit |
bd2e5d |
~stop:(`Mark"insert",[])
|
|
Packit |
bd2e5d |
end else begin
|
|
Packit |
bd2e5d |
reading <- true;
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"input"
|
|
Packit |
bd2e5d |
~index:(`Mark"insert",[`Char(-1)])
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
self#insert (if dir = `Previous then h#previous else h#next)
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
method private lex ?(start = `Mark"insert",[`Linestart])
|
|
Packit |
bd2e5d |
?(stop = `Mark"insert",[`Lineend]) () =
|
|
Packit |
bd2e5d |
Lexical.tag textw ~start ~stop
|
|
Packit |
bd2e5d |
method insert text =
|
|
Packit |
bd2e5d |
let idx = Text.index textw
|
|
Packit |
bd2e5d |
~index:(`Mark"insert",[`Char(-1);`Linestart]) in
|
|
Packit |
bd2e5d |
Text.insert textw ~text ~index:(`Mark"insert",[]);
|
|
Packit |
bd2e5d |
self#lex ~start:(idx,[`Linestart]) ();
|
|
Packit |
bd2e5d |
Text.see textw ~index:(`Mark"insert",[])
|
|
Packit |
bd2e5d |
method private keypress c =
|
|
Packit |
bd2e5d |
if not reading && c > " " then begin
|
|
Packit |
bd2e5d |
reading <- true;
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"input" ~index:(`Mark"insert",[`Char(-1)])
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
method private keyrelease c = if c <> "" then self#lex ()
|
|
Packit |
bd2e5d |
method private return =
|
|
Packit |
bd2e5d |
if reading then reading <- false
|
|
Packit |
bd2e5d |
else Text.mark_set textw ~mark:"input"
|
|
Packit |
bd2e5d |
~index:(`Mark"insert",[`Linestart;`Char 1]);
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"insert" ~index:(`Mark"insert",[`Lineend]);
|
|
Packit |
bd2e5d |
self#lex ~start:(`Mark"input",[`Linestart]) ();
|
|
Packit |
bd2e5d |
let s =
|
|
Packit |
bd2e5d |
(* input is one character before real input *)
|
|
Packit |
bd2e5d |
Text.get textw ~start:(`Mark"input",[`Char 1])
|
|
Packit |
bd2e5d |
~stop:(`Mark"insert",[]) in
|
|
Packit |
bd2e5d |
h#add s;
|
|
Packit |
bd2e5d |
Text.insert textw ~index:(`Mark"insert",[]) ~text:"\n";
|
|
Packit |
bd2e5d |
Text.yview_index textw ~index:(`Mark"insert",[]);
|
|
Packit |
bd2e5d |
self#send s;
|
|
Packit |
bd2e5d |
self#send "\n"
|
|
Packit |
bd2e5d |
method private paste ev =
|
|
Packit |
bd2e5d |
if not reading then begin
|
|
Packit |
bd2e5d |
reading <- true;
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"input"
|
|
Packit |
bd2e5d |
~index:(`Atxy(ev.ev_MouseX, ev.ev_MouseY),[`Char(-1)])
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
initializer
|
|
Packit |
bd2e5d |
Lexical.init_tags textw;
|
|
Packit |
bd2e5d |
let rec bindings =
|
|
Packit |
bd2e5d |
[ ([], `KeyPress, [`Char], fun ev -> self#keypress ev.ev_Char);
|
|
Packit |
bd2e5d |
([], `KeyRelease, [`Char], fun ev -> self#keyrelease ev.ev_Char);
|
|
Packit |
bd2e5d |
(* [], `KeyPressDetail"Return", [], fun _ -> self#return; *)
|
|
Packit |
bd2e5d |
([], `ButtonPressDetail 2, [`MouseX; `MouseY], self#paste);
|
|
Packit |
bd2e5d |
([`Alt], `KeyPressDetail"p", [], fun _ -> self#history `Previous);
|
|
Packit |
bd2e5d |
([`Alt], `KeyPressDetail"n", [], fun _ -> self#history `Next);
|
|
Packit |
bd2e5d |
([`Meta], `KeyPressDetail"p", [], fun _ -> self#history `Previous);
|
|
Packit |
bd2e5d |
([`Meta], `KeyPressDetail"n", [], fun _ -> self#history `Next);
|
|
Packit |
bd2e5d |
([`Control], `KeyPressDetail"c", [], fun _ -> self#interrupt);
|
|
Packit |
bd2e5d |
([], `Destroy, [], fun _ -> self#kill) ]
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
List.iter bindings ~f:
|
|
Packit |
bd2e5d |
begin fun (modif,event,fields,action) ->
|
|
Packit |
bd2e5d |
bind textw ~events:[`Modified(modif,event)] ~fields ~action
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
bind textw ~events:[`KeyPressDetail"Return"] ~breakable:true
|
|
Packit |
bd2e5d |
~action:(fun _ -> self#return; break());
|
|
Packit |
bd2e5d |
List.iter ~f:Unix.close [in2;out2;err2];
|
|
Packit |
bd2e5d |
if use_threads then begin
|
|
Packit |
bd2e5d |
let fileinput_thread fd =
|
|
Packit |
bd2e5d |
let buf = Bytes.create 1024 in
|
|
Packit |
bd2e5d |
let len = ref 0 in
|
|
Packit |
bd2e5d |
try while len := Unix.read fd ~buf ~pos:0 ~len:1024; !len > 0 do
|
|
Packit |
bd2e5d |
Mutex.lock imutex;
|
|
Packit |
bd2e5d |
Buffer.add_subbytes ibuffer buf 0 !len;
|
|
Packit |
bd2e5d |
Mutex.unlock imutex
|
|
Packit |
bd2e5d |
done with Unix.Unix_error _ -> ()
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
ithreads <- List.map [in1; err1] ~f:(Thread.create fileinput_thread);
|
|
Packit |
bd2e5d |
let rec read_buffer () =
|
|
Packit |
bd2e5d |
Mutex.lock imutex;
|
|
Packit |
bd2e5d |
if Buffer.length ibuffer > 0 then begin
|
|
Packit |
bd2e5d |
self#insert (Str.global_replace ~!"\r\n" "\n"
|
|
Packit |
bd2e5d |
(Buffer.contents ibuffer));
|
|
Packit |
bd2e5d |
Buffer.reset ibuffer;
|
|
Packit |
bd2e5d |
Text.mark_set textw ~mark:"input" ~index:(`Mark"insert",[`Char(-1)])
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
Mutex.unlock imutex;
|
|
Packit |
bd2e5d |
Timer.set ~ms:100 ~callback:read_buffer
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
read_buffer ()
|
|
Packit |
bd2e5d |
end else begin
|
|
Packit |
bd2e5d |
try
|
|
Packit |
bd2e5d |
List.iter [in1;err1] ~f:
|
|
Packit |
bd2e5d |
begin fun fd ->
|
|
Packit |
bd2e5d |
Fileevent.add_fileinput ~fd
|
|
Packit |
bd2e5d |
~callback:(fun () -> ignore (self#read ~fd ~len:1024))
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
with _ -> ()
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
end
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* Specific use of shell, for OCamlBrowser *)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let shells : (string * shell) list ref = ref []
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
(* Called before exiting *)
|
|
Packit |
bd2e5d |
let kill_all () =
|
|
Packit |
bd2e5d |
List.iter !shells ~f:(fun (_,sh) -> if sh#alive then sh#kill);
|
|
Packit |
bd2e5d |
shells := []
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let get_all () =
|
|
Packit |
bd2e5d |
let all = List.filter !shells ~f:(fun (_,sh) -> sh#alive) in
|
|
Packit |
bd2e5d |
shells := all;
|
|
Packit |
bd2e5d |
all
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let may_exec_unix prog =
|
|
Packit |
bd2e5d |
try Unix.access prog ~perm:[Unix.X_OK]; prog
|
|
Packit |
bd2e5d |
with Unix.Unix_error _ -> ""
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let may_exec_win prog =
|
|
Packit |
bd2e5d |
let has_ext =
|
|
Packit |
bd2e5d |
List.exists ~f:(Filename.check_suffix prog) ["exe"; "com"; "bat"] in
|
|
Packit |
bd2e5d |
if has_ext then may_exec_unix prog else
|
|
Packit |
bd2e5d |
List.fold_left [prog^".bat"; prog^".exe"; prog^".com"] ~init:""
|
|
Packit |
bd2e5d |
~f:(fun res prog -> if res = "" then may_exec_unix prog else res)
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let may_exec =
|
|
Packit |
bd2e5d |
if is_win32 then may_exec_win else may_exec_unix
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let path_sep = if is_win32 then ";" else ":"
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let warnings = ref Warnings.defaults_w
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let program_not_found prog =
|
|
Packit |
bd2e5d |
Jg_message.info ~title:"Error"
|
|
Packit |
bd2e5d |
("Program \"" ^ prog ^ "\"\nwas not found in path")
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let protect_arg s =
|
|
Packit |
bd2e5d |
if String.contains s ' ' then "\"" ^ s ^ "\"" else s
|
|
Packit |
bd2e5d |
|
|
Packit |
bd2e5d |
let f ~prog ~title =
|
|
Packit |
bd2e5d |
let progargs =
|
|
Packit |
bd2e5d |
List.filter ~f:((<>) "") (Str.split ~!" " prog) in
|
|
Packit |
bd2e5d |
if progargs = [] then () else
|
|
Packit |
bd2e5d |
let prog = List.hd progargs in
|
|
Packit |
bd2e5d |
let path =
|
|
Packit |
bd2e5d |
try Sys.getenv "PATH" with Not_found -> "/bin" ^ path_sep ^ "/usr/bin" in
|
|
Packit |
bd2e5d |
let exec_path = Str.split ~!path_sep path in
|
|
Packit |
bd2e5d |
let exec_path = if is_win32 then "."::exec_path else exec_path in
|
|
Packit |
bd2e5d |
let progpath =
|
|
Packit |
bd2e5d |
if not (Filename.is_implicit prog) then may_exec prog else
|
|
Packit |
bd2e5d |
List.fold_left exec_path ~init:"" ~f:
|
|
Packit |
bd2e5d |
(fun res dir ->
|
|
Packit |
bd2e5d |
if res = "" then may_exec (Filename.concat dir prog) else res) in
|
|
Packit |
bd2e5d |
if progpath = "" then program_not_found prog else
|
|
Packit |
bd2e5d |
let tl = Jg_toplevel.titled title in
|
|
Packit |
bd2e5d |
let menus = Menu.create tl ~name:"menubar" ~typ:`Menubar in
|
|
Packit |
bd2e5d |
Toplevel.configure tl ~menu:menus;
|
|
Packit |
bd2e5d |
let file_menu = new Jg_menu.c "File" ~parent:menus
|
|
Packit |
bd2e5d |
and history_menu = new Jg_menu.c "History" ~parent:menus
|
|
Packit |
bd2e5d |
and signal_menu = new Jg_menu.c "Signal" ~parent:menus in
|
|
Packit |
bd2e5d |
let frame, tw, sb = Jg_text.create_with_scrollbar tl in
|
|
Packit |
bd2e5d |
Text.configure tw ~background:`White;
|
|
Packit |
bd2e5d |
pack [sb] ~fill:`Y ~side:`Right;
|
|
Packit |
bd2e5d |
pack [tw] ~fill:`Both ~expand:true ~side:`Left;
|
|
Packit |
bd2e5d |
pack [frame] ~fill:`Both ~expand:true;
|
|
Packit |
bd2e5d |
let env = Array.map (Unix.environment ()) ~f:
|
|
Packit |
bd2e5d |
begin fun s ->
|
|
Packit |
bd2e5d |
if Str.string_match ~!"TERM=" s 0 then "TERM=dumb" else s
|
|
Packit |
bd2e5d |
end in
|
|
Packit |
bd2e5d |
let load_path =
|
|
Packit |
bd2e5d |
List2.flat_map !Config.load_path ~f:(fun dir -> ["-I"; dir]) in
|
|
Packit |
bd2e5d |
let load_path =
|
|
Packit |
bd2e5d |
if is_win32 then List.map ~f:protect_arg load_path else load_path in
|
|
Packit |
bd2e5d |
let labels = if !Clflags.classic then ["-nolabels"] else [] in
|
|
Packit |
bd2e5d |
let rectypes = if !Clflags.recursive_types then ["-rectypes"] else [] in
|
|
Packit |
bd2e5d |
let warnings =
|
|
Packit |
bd2e5d |
if List.mem "-w" progargs || !warnings = "Al" then []
|
|
Packit |
bd2e5d |
else ["-w"; !warnings]
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
let args =
|
|
Packit |
bd2e5d |
Array.of_list (progargs @ labels @ warnings @ rectypes @ load_path) in
|
|
Packit |
bd2e5d |
let history = new history () in
|
|
Packit |
bd2e5d |
let start_shell () =
|
|
Packit |
bd2e5d |
let sh = new shell ~textw:tw ~prog:progpath ~env ~args ~history in
|
|
Packit |
bd2e5d |
shells := (title, sh) :: !shells;
|
|
Packit |
bd2e5d |
sh
|
|
Packit |
bd2e5d |
in
|
|
Packit |
bd2e5d |
let sh = ref (start_shell ()) in
|
|
Packit |
bd2e5d |
let current_dir = ref (Unix.getcwd ()) in
|
|
Packit |
bd2e5d |
file_menu#add_command "Restart" ~command:
|
|
Packit |
bd2e5d |
begin fun () ->
|
|
Packit |
bd2e5d |
(!sh)#kill;
|
|
Packit |
bd2e5d |
Text.configure tw ~state:`Normal;
|
|
Packit |
bd2e5d |
Text.insert tw ~index:(`End,[]) ~text:"\n";
|
|
Packit |
bd2e5d |
Text.see tw ~index:(`End,[]);
|
|
Packit |
bd2e5d |
Text.mark_set tw ~mark:"insert" ~index:(`End,[]);
|
|
Packit |
bd2e5d |
sh := start_shell ();
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
file_menu#add_command "Use..." ~command:
|
|
Packit |
bd2e5d |
begin fun () ->
|
|
Packit |
bd2e5d |
Fileselect.f ~title:"Use File" ~filter:"*.ml"
|
|
Packit |
bd2e5d |
~sync:true ~dir:!current_dir ()
|
|
Packit |
bd2e5d |
~action:(fun l ->
|
|
Packit |
bd2e5d |
if l = [] then () else
|
|
Packit |
bd2e5d |
let name = Fileselect.caml_dir (List.hd l) in
|
|
Packit |
bd2e5d |
current_dir := Filename.dirname name;
|
|
Packit |
bd2e5d |
if Filename.check_suffix name ".ml"
|
|
Packit |
bd2e5d |
then
|
|
Packit |
bd2e5d |
let cmd = "#use \"" ^ String.escaped name ^ "\";;\n" in
|
|
Packit |
bd2e5d |
(!sh)#insert cmd; (!sh)#send cmd)
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
file_menu#add_command "Load..." ~command:
|
|
Packit |
bd2e5d |
begin fun () ->
|
|
Packit |
bd2e5d |
Fileselect.f ~title:"Load File" ~filter:"*.cm[oa]" ~sync:true ()
|
|
Packit |
bd2e5d |
~dir:!current_dir
|
|
Packit |
bd2e5d |
~action:(fun l ->
|
|
Packit |
bd2e5d |
if l = [] then () else
|
|
Packit |
bd2e5d |
let name = Fileselect.caml_dir (List.hd l) in
|
|
Packit |
bd2e5d |
current_dir := Filename.dirname name;
|
|
Packit |
bd2e5d |
if Filename.check_suffix name ".cmo" ||
|
|
Packit |
bd2e5d |
Filename.check_suffix name ".cma"
|
|
Packit |
bd2e5d |
then
|
|
Packit |
bd2e5d |
let cmd = "#load \"" ^ String.escaped name ^ "\";;\n" in
|
|
Packit |
bd2e5d |
(!sh)#insert cmd; (!sh)#send cmd)
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
file_menu#add_command "Import path" ~command:
|
|
Packit |
bd2e5d |
begin fun () ->
|
|
Packit |
bd2e5d |
List.iter (List.rev !Config.load_path) ~f:
|
|
Packit |
bd2e5d |
(fun dir ->
|
|
Packit |
bd2e5d |
(!sh)#send ("#directory \"" ^ String.escaped dir ^ "\";;\n"))
|
|
Packit |
bd2e5d |
end;
|
|
Packit |
bd2e5d |
file_menu#add_command "Close" ~command:(fun () -> destroy tl);
|
|
Packit |
bd2e5d |
history_menu#add_command "Previous " ~accelerator:"M-p"
|
|
Packit |
bd2e5d |
~command:(fun () -> (!sh)#history `Previous);
|
|
Packit |
bd2e5d |
history_menu#add_command "Next" ~accelerator:"M-n"
|
|
Packit |
bd2e5d |
~command:(fun () -> (!sh)#history `Next);
|
|
Packit |
bd2e5d |
signal_menu#add_command "Interrupt " ~accelerator:"C-c"
|
|
Packit |
bd2e5d |
~command:(fun () -> (!sh)#interrupt);
|
|
Packit |
bd2e5d |
signal_menu#add_command "Kill" ~command:(fun () -> (!sh)#kill)
|