(* Lightweight thread library for Objective Caml * http://www.ocsigen.org/lwt * Module Lwt_top * Copyright (C) 2009 Jérémie Dimino * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, with linking exceptions; * either version 2.1 of the License, or (at your option) any later * version. See COPYING file for details. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *) (* Integration with the toplevel: readline + let threads runs while reading user input. *) open Lwt_unix open Lwt open Lwt_text open Lwt_term (* +-----------------------------------------------------------------+ | Completion | +-----------------------------------------------------------------+ *) module TextSet = Set.Make(Text) let complete (before, after) = Lwt_ocaml_completion.complete_input before after (Lexing.from_string before) (* +-----------------------------------------------------------------+ | Read-line wrapper | +-----------------------------------------------------------------+ *) let mode = ref `real_time let completion_mode () = !mode let set_completion_mode m = mode := m let history = ref [] let _ = let hist_name = Filename.concat (try Unix.getenv "HOME" with _ -> "") ".lwt-top-history" in Lwt_main.at_exit (fun () -> Lwt_read_line.save_history hist_name !history); history := Lwt_main.run (Lwt_read_line.load_history hist_name) let input = ref "" let pos = ref 0 let rec read_input prompt buffer len = try if !pos = String.length !input then begin let prompt' = if prompt = " " then [fg blue; text "> "] else [fg yellow; text prompt] in !Lwt_ocaml_completion.restart (); let txt = Lwt_main.run begin lwt l = Lwt_read_line.Control.result (Lwt_read_line.Control.make ~complete ~mode:!mode ~history:(!history) ~prompt:(fun _ -> React.S.const prompt') ~filter:(fun state command -> match command with | Lwt_read_line.Command.Accept_line -> (* Do not accept the line if it does not terminates with ";;" *) let text = Lwt_read_line.Engine.all_input (Lwt_read_line.Control.engine_state state) in if Text.ends_with (Text.rstrip text) ";;" then return Lwt_read_line.Command.Accept_line else return (Lwt_read_line.Command.Char "\n") | command -> return command) ~map_result:return ()) in lwt () = Lwt_text.flush Lwt_text.stdout in return l end in history := Lwt_read_line.add_entry txt !history; input := txt ^ "\n"; pos := 0; read_input prompt buffer len end else begin let i = ref 0 in while !i < len && !pos < String.length !input do buffer.[!i] <- (!input).[!pos]; incr i; incr pos done; (!i, false) end with | Lwt_read_line.Interrupt -> (0, true) let read_input_non_interactive prompt buffer len = let rec loop i = if i = len then return (i, false) else Lwt_io.read_char_opt Lwt_io.stdin >>= function | Some c -> buffer.[i] <- c; if c = '\n' then return (i + 1, false) else loop (i + 1) | None -> return (i, true) in Lwt_main.run (Lwt_io.write Lwt_io.stdout prompt >> loop 0) let _ = (* If input is a tty, use interactive read-line and display and welcome message: *) if Unix.isatty Unix.stdin then begin Toploop.read_interactive_input := read_input; let txt = "Welcome to the Lwt powered OCaml toplevel!" in let col_border = cyan and col_txt = yellow in let len = Text.length txt in let col = React.S.value Lwt_term.columns in let space = (col - 4 - len) / 2 in let rep n txt = text (Text.repeat n txt) in Lwt_main.run (lwt () = printlc [fg col_border; rep space "─"; text "┬─"; rep len "─"; text "─┬"; rep (col - 4 - len - space) "─"] in lwt () = printlc [rep space " "; fg col_border; text "│ "; fg col_txt; text txt; fg col_border; text " │"] in lwt () = printlc [rep space " "; fg col_border; text "└─"; rep len "─"; text "─┘"] in Lwt_io.flush Lwt_io.stdout) end else (* Otherwise fallback to classic non-interactive mode: *) Toploop.read_interactive_input := read_input_non_interactive;