(* Lightweight thread library for Objective Caml * http://www.ocsigen.org/lwt * Module Toplevel * Copyright (C) 2009 Jérémie Dimino * Pierre Chambart * * 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. *) open Types open Lwt_read_line module TextSet = Set.Make(Text) type path = | Path of Path.t | Longident of Longident.t module PathMap = Map.Make(struct type t = path let compare = compare end) let keywords = Lwt_ocaml_completion.keywords let global_env = ref(lazy(raise Exit)) let local_envs = ref(PathMap.empty) (* Returns [acc] plus all modules of [dir] *) let add_modules_from_directory acc dir = let dir = if dir = "" then "./" else dir in let acc = ref acc in Array.iter (fun fname -> if Filename.check_suffix fname ".cmi" then acc := TextSet.add (Text.capitalize (Filename.chop_suffix fname ".cmi")) !acc) (Sys.readdir (if dir = "" then Filename.current_dir_name else dir)); !acc (* List all names of the module with path [path] *) let get_names_of_module path = try match match path with | Path path -> Env.find_module path !Toploop.toplevel_env | Longident ident -> snd (Env.lookup_module ident !Toploop.toplevel_env) with | Tmty_signature decls -> List.fold_left (fun acc decl -> match decl with | Tsig_value(id, _) | Tsig_type(id, _, _) | Tsig_exception(id, _) | Tsig_module(id, _, _) | Tsig_modtype(id, _) | Tsig_class(id, _, _) | Tsig_cltype(id, _, _) -> TextSet.add (Ident.name id) acc) TextSet.empty decls | _ -> TextSet.empty with Not_found -> TextSet.empty let names_of_module path = try PathMap.find path !local_envs with Not_found -> let names = get_names_of_module path in local_envs := PathMap.add path names !local_envs; names (* List all names accessible without a path *) let env_names () = let rec loop acc = function | Env.Env_empty -> acc | Env.Env_value(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_type(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_exception(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_module(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_modtype(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_class(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_cltype(summary, id, _) -> loop (TextSet.add (Ident.name id) acc) summary | Env.Env_open(summary, path) -> loop (TextSet.union acc (names_of_module (Path path))) summary in (* Add names of the environment: *) let acc = loop TextSet.empty (Env.summary !Toploop.toplevel_env) in (* Add accessible modules: *) List.fold_left add_modules_from_directory acc !Config.load_path let path_of_string text = match Text.split ~sep:"." text with | [] -> invalid_arg "Toplevel.make_path" | ident :: rest -> let rec loop path = function | [] -> Longident path | component :: rest -> loop (Longident.Ldot(path, component)) rest in loop (Longident.Lident ident) rest let complete_ident before ident after = match Text.rev_split ~sep:"." ~max:2 ident with | [ident]-> complete ~suffix:"" before ident after (TextSet.union keywords (Lazy.force !global_env)) | [path; ident] -> let before = before ^ path ^ "." in complete ~suffix:"" before ident after (names_of_module (path_of_string path)) | _ -> assert false let restart () = global_env := lazy(env_names ()); local_envs := PathMap.empty let () = Topfind.don't_load_deeply ["lwt"; "lwt.react"; "lwt.unix"; "lwt.text"; "lwt.top"]; Lwt_ocaml_completion.complete_ident := complete_ident; Lwt_ocaml_completion.restart := restart