diff options
Diffstat (limited to 'otherlibs/labltk/browser/editor.ml')
-rw-r--r-- | otherlibs/labltk/browser/editor.ml | 543 |
1 files changed, 0 insertions, 543 deletions
diff --git a/otherlibs/labltk/browser/editor.ml b/otherlibs/labltk/browser/editor.ml deleted file mode 100644 index c5c662f01..000000000 --- a/otherlibs/labltk/browser/editor.ml +++ /dev/null @@ -1,543 +0,0 @@ -(* $Id$ *) - -open Tk -open Parsetree -open Location -open Jg_tk -open Mytypes - -let lex_on_load = ref true -and type_on_load = ref false - -let compiler_preferences () = - let tl = Jg_toplevel.titled "Compiler" in - Wm.transient_set tl master:Widget.default_toplevel; - let mk_chkbutton :text :ref = - let variable = Textvariable.create on:tl () in - if !ref then Textvariable.set variable to:"1"; - Checkbutton.create parent:tl :text :variable (), - (fun () -> ref := Textvariable.get variable = "1") - in - let chkbuttons, setflags = List.split - (List.map fun:(fun (text, ref) -> mk_chkbutton :text :ref) - ["No pervasives", Clflags.nopervasives; - "No warnings", Typecheck.nowarnings; - "Classic", Clflags.classic; - "Lex on load", lex_on_load; - "Type on load", type_on_load]) - in - let buttons = Frame.create parent:tl () in - let ok = Button.create parent:buttons text:"Ok" padx:(`Pix 20) () command: - begin fun () -> - List.iter fun:(fun f -> f ()) setflags; - destroy tl - end - and cancel = Jg_button.create_destroyer tl parent:buttons text:"Cancel" - in - pack chkbuttons side:`Top anchor:`W; - pack [ok;cancel] side:`Left fill:`X expand:true; - pack [buttons] side:`Bottom fill:`X - -let rec exclude elt:txt = function - [] -> [] - | x :: l -> if txt.number = x.number then l else x :: exclude elt:txt l - -let goto_line tw = - let tl = Jg_toplevel.titled "Go to" in - Wm.transient_set tl master:Widget.default_toplevel; - Jg_bind.escape_destroy tl; - let ef = Frame.create parent:tl () in - let fl = Frame.create parent:ef () - and fi = Frame.create parent:ef () in - let ll = Label.create parent:fl text:"Line number:" () - and il = Entry.create parent:fi width:10 () - and lc = Label.create parent:fl text:"Col number:" () - and ic = Entry.create parent:fi width:10 () - and get_int ew = - try int_of_string (Entry.get ew) - with Failure "int_of_string" -> 0 - in - let buttons = Frame.create parent:tl () in - let ok = Button.create parent:buttons text:"Ok" () command: - begin fun () -> - let l = get_int il - and c = get_int ic in - Text.mark_set tw mark:"insert" index:(`Linechar (l,0), [`Char c]); - Text.see tw index:(`Mark "insert", []); - destroy tl - end - and cancel = Jg_button.create_destroyer tl parent:buttons text:"Cancel" in - - Focus.set il; - List.iter [il; ic] fun: - begin fun w -> - Jg_bind.enter_focus w; - Jg_bind.return_invoke w button:ok - end; - pack [ll; lc] side:`Top anchor:`W; - pack [il; ic] side:`Top fill:`X expand:true; - pack [fl; fi] side:`Left fill:`X expand:true; - pack [ok; cancel] side:`Left fill:`X expand:true; - pack [ef; buttons] side:`Top fill:`X expand:true - -let select_shell txt = - let shells = Shell.get_all () in - let shells = Sort.list shells order:(fun (x,_) (y,_) -> x <= y) in - let tl = Jg_toplevel.titled "Select Shell" in - Jg_bind.escape_destroy tl; - Wm.transient_set tl master:(Winfo.toplevel txt.tw); - let label = Label.create parent:tl text:"Send to:" () - and box = Listbox.create parent:tl () - and frame = Frame.create parent:tl () in - Jg_bind.enter_focus box; - let cancel = Jg_button.create_destroyer tl parent:frame text:"Cancel" - and ok = Button.create parent:frame text:"Ok" () command: - begin fun () -> - try - let name = Listbox.get box index:`Active in - txt.shell <- Some (name, List.assoc key:name shells); - destroy tl - with Not_found -> txt.shell <- None; destroy tl - end - in - Listbox.insert box index:`End texts:(List.map fun:fst shells); - Listbox.configure box height:(List.length shells); - bind box events:[[],`KeyPressDetail"Return"] - action:(`Setbreakable([], fun _ -> Button.invoke ok; break ())); - bind box events:[[`Double],`ButtonPressDetail 1] - action:(`Setbreakable([`MouseX;`MouseY], fun ev -> - Listbox.activate box index:(`Atxy (ev.ev_MouseX, ev.ev_MouseY)); - Button.invoke ok; break ())); - pack [label] side:`Top anchor:`W; - pack [box] side:`Top fill:`Both; - pack [frame] side:`Bottom fill:`X expand:true; - pack [ok;cancel] side:`Left fill:`X expand:true - -let send_region txt = - if txt.shell = None then begin - match Shell.get_all () with [] -> () - | [sh] -> txt.shell <- Some sh - | l -> select_shell txt - end; - match txt.shell with None -> () - | Some (_,sh) -> - try - let i1,i2 = Text.tag_nextrange txt.tw tag:"sel" start:tstart in - sh#send (Text.get txt.tw start:(i1,[]) end:(i2,[])); - sh#send";;\n" - with _ -> () - -let search_pos_window txt :x :y = - if txt.structure = [] & txt.psignature = [] then () else - let `Linechar (l, c) = Text.index txt.tw index:(`Atxy(x,y), []) in - let text = Jg_text.get_all txt.tw in - let pos = Searchpos.lines_to_chars l in:text + c in - try if txt.structure <> [] then - try Searchpos.search_pos_structure txt.structure :pos - with Searchpos.Found_str (kind, env) -> - Searchpos.view_type kind :env - else - try Searchpos.search_pos_signature - txt.psignature :pos env:!Searchid.start_env; - () - with Searchpos.Found_sig (kind, lid, env) -> - Searchpos.view_decl lid :kind :env - with Not_found -> () - -let search_pos_menu txt :x :y = - if txt.structure = [] & txt.psignature = [] then () else - let `Linechar (l, c) = Text.index txt.tw index:(`Atxy(x,y), []) in - let text = Jg_text.get_all txt.tw in - let pos = Searchpos.lines_to_chars l in:text + c in - try if txt.structure <> [] then - try Searchpos.search_pos_structure txt.structure :pos - with Searchpos.Found_str (kind, env) -> - let menu = Searchpos.view_type_menu kind :env parent:txt.tw in - let x = x + Winfo.rootx txt.tw and y = y + Winfo.rooty txt.tw - 10 in - Menu.popup menu :x :y - else - try Searchpos.search_pos_signature - txt.psignature :pos env:!Searchid.start_env; - () - with Searchpos.Found_sig (kind, lid, env) -> - let menu = Searchpos.view_decl_menu lid :kind :env parent:txt.tw in - let x = x + Winfo.rootx txt.tw and y = y + Winfo.rooty txt.tw - 10 in - Menu.popup menu :x :y - with Not_found -> () - -let string_width s = - let width = ref 0 in - for i = 0 to String.length s - 1 do - if s.[i] = '\t' then width := (!width / 8 + 1) * 8 - else incr width - done; - !width - -let indent_line = - let ins = `Mark"insert" and reg = Str.regexp "[ \t]*" in - fun tw -> - let `Linechar(l,c) = Text.index tw index:(ins,[]) - and line = Text.get tw start:(ins,[`Linestart]) end:(ins,[]) in - Str.string_match reg line pos:0; - if Str.match_end () < c then - Text.insert tw index:(ins,[]) text:"\t" - else let indent = - if l <= 1 then 2 else - let previous = - Text.get tw start:(ins,[`Line(-1);`Linestart]) - end:(ins,[`Line(-1);`Lineend]) in - Str.string_match reg previous pos:0; - let previous = Str.matched_string previous in - let width = string_width line - and width_previous = string_width previous in - if width_previous <= width then 2 else width_previous - width - in - Text.insert tw index:(ins,[]) text:(String.make len:indent ' ') - -(* The editor class *) - -class editor :top :menus = object (self) - val file_menu = new Jg_menu.c "File" parent:menus - val edit_menu = new Jg_menu.c "Edit" parent:menus - val compiler_menu = new Jg_menu.c "Compiler" parent:menus - val module_menu = new Jg_menu.c "Modules" parent:menus - val window_menu = new Jg_menu.c "Windows" parent:menus - val label = - Checkbutton.create parent:menus state:`Disabled - onvalue:"modified" offvalue:"unchanged" () - val mutable current_dir = Unix.getcwd () - val mutable error_messages = [] - val mutable windows = [] - val mutable current_tw = Text.create parent:top () - val vwindow = Textvariable.create on:top () - val mutable window_counter = 0 - - method reset_window_menu = - Menu.delete window_menu#menu first:(`Num 0) last:`End; - List.iter - (Sort.list windows order: - (fun w1 w2 -> Filename.basename w1.name < Filename.basename w2.name)) - fun: - begin fun txt -> - Menu.add_radiobutton window_menu#menu - label:(Filename.basename txt.name) - variable:vwindow value:txt.number - command:(fun () -> self#set_edit txt) - end - - method set_edit txt = - if windows <> [] then - Pack.forget [(List.hd windows).frame]; - windows <- txt :: exclude elt:txt windows; - self#reset_window_menu; - current_tw <- txt.tw; - Checkbutton.configure label text:(Filename.basename txt.name) - variable:txt.modified; - Textvariable.set vwindow to:txt.number; - Text.yview txt.tw scroll:(`Page 0); - pack [txt.frame] fill:`Both expand:true side:`Bottom - - method new_window name = - let tl, tw, sb = Jg_text.create_with_scrollbar parent:top in - Text.configure tw background:`White; - Jg_bind.enter_focus tw; - window_counter <- window_counter + 1; - let txt = - { name = name; tw = tw; frame = tl; - number = string_of_int window_counter; - modified = Textvariable.create on:tw (); - shell = None; - structure = []; signature = []; psignature = [] } - in - let control c = Char.chr (Char.code c - 96) in - bind tw events:[[`Alt], `KeyPress] action:(`Set ([], fun _ -> ())); - bind tw events:[[], `KeyPress] - action:(`Set ([`Char], fun ev -> - if ev.ev_Char <> "" & - (ev.ev_Char.[0] >= ' ' or - List.mem elt:ev.ev_Char.[0] - (List.map fun:control ['d'; 'h'; 'i'; 'k'; 'o'; 't'; 'w'; 'y'])) - then Textvariable.set txt.modified to:"modified")); - bind tw events:[[],`KeyPressDetail"Tab"] - action:(`Setbreakable ([], fun _ -> - indent_line tw; - Textvariable.set txt.modified to:"modified"; - break ())); - bind tw events:[[`Control],`KeyPressDetail"k"] - action:(`Set ([], fun _ -> - let text = - Text.get tw start:(`Mark"insert",[]) end:(`Mark"insert",[`Lineend]) - in Str.string_match (Str.regexp "[ \t]*") text pos:0; - if Str.match_end () <> String.length text then begin - Clipboard.clear (); - Clipboard.append data:text () - end)); - bind tw events:[[], `KeyRelease] - action:(`Set ([`Char], fun ev -> - if ev.ev_Char <> "" then - Lexical.tag tw start:(`Mark"insert", [`Linestart]) - end:(`Mark"insert", [`Lineend]))); - bind tw events:[[], `Motion] action:(`Set ([], fun _ -> Focus.set tw)); - bind tw events:[[], `ButtonPressDetail 2] - action:(`Set ([], fun _ -> - Textvariable.set txt.modified to:"modified"; - Lexical.tag txt.tw start:(`Mark"insert", [`Linestart]) - end:(`Mark"insert", [`Lineend]))); - bind tw events:[[`Double], `ButtonPressDetail 1] - action:(`Set ([`MouseX;`MouseY], fun ev -> - search_pos_window txt x:ev.ev_MouseX y:ev.ev_MouseY)); - bind tw events:[[], `ButtonPressDetail 3] - action:(`Set ([`MouseX;`MouseY], fun ev -> - search_pos_menu txt x:ev.ev_MouseX y:ev.ev_MouseY)); - - pack [sb] fill:`Y side:`Right; - pack [tw] fill:`Both expand:true side:`Left; - self#set_edit txt; - Checkbutton.deselect label; - Lexical.init_tags txt.tw - - method clear_errors () = - Text.tag_remove current_tw tag:"error" start:tstart end:tend; - List.iter error_messages - fun:(fun tl -> try destroy tl with Protocol.TkError _ -> ()); - error_messages <- [] - - method typecheck () = - self#clear_errors (); - error_messages <- Typecheck.f (List.hd windows) - - method lex () = - Text.tag_remove current_tw tag:"error" start:tstart end:tend; - Lexical.tag current_tw - - method save_text ?name:l txt = - let l = match l with None -> [txt.name] | Some l -> l in - if l = [] then () else - let name = List.hd l in - if txt.name <> name then current_dir <- Filename.dirname name; - try - if Sys.file_exists name then - if txt.name = name then - Sys.rename old:name new:(name ^ "~") - else begin match - Jg_message.ask master:top title:"Save" - ("File `" ^ name ^ "' exists. Overwrite it?") - with `yes -> () | `no | `cancel -> raise Exit - end; - let file = open_out name in - let text = Text.get txt.tw start:tstart end:(tposend 1) in - output_string text to:file; - close_out file; - Checkbutton.configure label text:(Filename.basename name); - Checkbutton.deselect label; - txt.name <- name - with - Sys_error _ | Exit -> () - - method load_text l = - if l = [] then () else - let name = List.hd l in - try - let index = - try - self#set_edit (List.find windows pred:(fun x -> x.name = name)); - let txt = List.hd windows in - if Textvariable.get txt.modified = "modified" then - begin match Jg_message.ask master:top title:"Open" - ("`" ^ Filename.basename txt.name ^ "' modified. Save it?") - with `yes -> self#save_text txt - | `no -> () - | `cancel -> raise Exit - end; - Checkbutton.deselect label; - (Text.index current_tw index:(`Mark"insert", []), []) - with Not_found -> self#new_window name; tstart - in - current_dir <- Filename.dirname name; - let file = open_in name - and tw = current_tw - and len = ref 0 - and buffer = String.create len:4096 in - Text.delete tw start:tstart end:tend; - while - len := input file :buffer pos:0 len:4096; - !len > 0 - do - Jg_text.output tw :buffer pos:0 len:!len - done; - close_in file; - Text.mark_set tw mark:"insert" :index; - Text.see tw :index; - if Filename.check_suffix name suff:".ml" or - Filename.check_suffix name suff:".mli" - then begin - if !lex_on_load then self#lex (); - if !type_on_load then self#typecheck () - end - with - Sys_error _ | Exit -> () - - method close_window txt = - try - if Textvariable.get txt.modified = "modified" then - begin match Jg_message.ask master:top title:"Close" - ("`" ^ Filename.basename txt.name ^ "' modified. Save it?") - with `yes -> self#save_text txt - | `no -> () - | `cancel -> raise Exit - end; - windows <- exclude elt:txt windows; - if windows = [] then - self#new_window (current_dir ^ "/untitled") - else self#set_edit (List.hd windows); - destroy txt.frame - with Exit -> () - - method open_file () = - Fileselect.f title:"Open File" action:self#load_text - dir:current_dir filter:("*.{ml,mli}") sync:true () - - method save_file () = self#save_text (List.hd windows) - - method close_file () = self#close_window (List.hd windows) - - method quit () = - try List.iter windows - fun:(fun txt -> - if Textvariable.get txt.modified = "modified" then - match Jg_message.ask master:top title:"Quit" - ("`" ^ Filename.basename txt.name ^ "' modified. Save it?") - with `yes -> self#save_text txt - | `no -> () - | `cancel -> raise Exit); - bind top events:[[],`Destroy] action:`Remove; - destroy top; break () - with Exit -> break () - - method reopen :file :pos = - if not (Winfo.ismapped top) then Wm.deiconify top; - match file with None -> () - | Some file -> - self#load_text [file]; - Text.mark_set current_tw mark:"insert" index:(tpos pos); - Text.yview_index current_tw - index:(`Linechar(1,0),[`Char pos; `Line (-2)]) - - initializer - (* Create a first window *) - self#new_window (current_dir ^ "/untitled"); - - (* Bindings for the main window *) - List.iter - [ [`Control], "s", (fun () -> Jg_text.search_string current_tw); - [`Control], "g", (fun () -> goto_line current_tw); - [`Alt], "x", (fun () -> send_region (List.hd windows)); - [`Alt], "l", self#lex; - [`Alt], "t", self#typecheck ] - fun:begin fun (modi,key,act) -> - bind top events:[modi, `KeyPressDetail key] - action:(`Setbreakable ([], fun _ -> act (); break ())) - end; - - bind top events:[[],`Destroy] - action:(`Setbreakable - ([`Widget], fun ev -> - if Widget.name ev.ev_Widget = Widget.name top - then self#quit ())); - - (* File menu *) - file_menu#add_command "Open File..." command:self#open_file; - file_menu#add_command "Reopen" - command:(fun () -> self#load_text [(List.hd windows).name]); - file_menu#add_command "Save File" command:self#save_file; - file_menu#add_command "Save As..." underline:5 - command:begin fun () -> - let txt = List.hd windows in - Fileselect.f title:"Save as File" - action:(fun name -> self#save_text txt :name) - dir:(Filename.dirname txt.name) - filter:"*.{ml,mli}" - file:(Filename.basename txt.name) - sync:true usepath:false () - end; - file_menu#add_command "Close File" command:self#close_file; - file_menu#add_command "Close Window" command:self#quit underline:6; - - (* Edit menu *) - edit_menu#add_command "Paste selection" command: - begin fun () -> - Text.insert current_tw index:(`Mark"insert",[]) - text:(Selection.get displayof:top ()) - end; - edit_menu#add_command "Goto..." accelerator:"C-g" - command:(fun () -> goto_line current_tw); - edit_menu#add_command "Search..." accelerator:"C-s" - command:(fun () -> Jg_text.search_string current_tw); - edit_menu#add_command "To shell" accelerator:"M-x" - command:(fun () -> send_region (List.hd windows)); - edit_menu#add_command "Select shell..." - command:(fun () -> select_shell (List.hd windows)); - - (* Compiler menu *) - compiler_menu#add_command "Preferences..." - command:compiler_preferences; - compiler_menu#add_command "Lex" accelerator:"M-l" - command:self#lex; - compiler_menu#add_command "Typecheck" accelerator:"M-t" - command:self#typecheck; - compiler_menu#add_command "Clear errors" - command:self#clear_errors; - compiler_menu#add_command "Signature..." command: - begin fun () -> - let txt = List.hd windows in if txt.signature <> [] then - let basename = Filename.basename txt.name in - let modname = String.capitalize - (try Filename.chop_extension basename with _ -> basename) in - let env = - Env.add_module (Ident.create modname) - (Types.Tmty_signature txt.signature) - Env.initial - in Viewer.view_defined (Longident.Lident modname) :env - end; - - (* Modules *) - module_menu#add_command "Path editor..." - command:(fun () -> Setpath.f dir:current_dir; ()); - module_menu#add_command "Reset cache" - command:(fun () -> Setpath.exec_update_hooks (); Env.reset_cache ()); - module_menu#add_command "Search symbol..." - command:Viewer.search_symbol; - module_menu#add_command "Close all" - command:Viewer.close_all_views; - - (* pack everything *) - pack (List.map fun:(fun m -> coe m#button) - [file_menu; edit_menu; compiler_menu; module_menu; window_menu] - @ [coe label]) - side:`Left ipadx:(`Pix 5) anchor:`W; - pack [menus] before:(List.hd windows).frame side:`Top fill:`X -end - -(* The main function starts here ! *) - -let already_open : editor option ref = ref None - -let editor ?:file ?:pos{= 0} () = - - if match !already_open with None -> false - | Some ed -> - try ed#reopen :file :pos; true - with Protocol.TkError _ -> already_open := None; false - then () else - let top = Jg_toplevel.titled "Editor" in - let menus = Frame.create parent:top name:"menubar" () in - let ed = new editor :top :menus in - already_open := Some ed; - if file <> None then ed#reopen :file :pos - -let f ?:file ?:pos ?:opendialog{=false} () = - if opendialog then - Fileselect.f title:"Open File" - action:(function [file] -> editor :file () | _ -> ()) - filter:("*.{ml,mli}") sync:true () - else editor ?:file ?:pos () |