this repo has no description

Many changes

+483 -286
+1 -1
.ocamlformat
··· 1 - version=0.26.1 1 + version=0.27.0
+1 -1
example/dune
··· 28 28 (package js_top_worker-unix) 29 29 (modules unix_worker) 30 30 (link_flags (-linkall)) 31 - (libraries js_top_worker logs logs.fmt rpclib.core)) 31 + (libraries js_top_worker logs logs.fmt rpclib.core findlib.top)) 32 32 33 33 (executable 34 34 (name unix_client)
+1 -1
example/example.ml
··· 17 17 cmis = { dynamic_cmis = []; static_cmis = [] }; 18 18 stdlib_dcs = "/lib/ocaml/dynamic_cmis.json"; 19 19 findlib_index = "/lib/findlib_index"; 20 - findlib_requires = ["astring"]; 20 + findlib_requires = [ "astring" ]; 21 21 } 22 22 in 23 23 Lwt.return (Ok rpc)
+17 -4
example/example2.ml
··· 3 3 open Js_top_worker_rpc 4 4 module W = Js_top_worker_client.W 5 5 6 - let dcs = Js_top_worker_rpc.Toplevel_api_gen.{dcs_url="cmis/"; 7 - dcs_toplevel_modules = ["CamlinternalOO";"Stdlib";"CamlinternalFormat";"Std_exit";"CamlinternalMod";"CamlinternalFormatBasics";"CamlinternalLazy"]; 8 - dcs_file_prefixes = ["stdlib__"];} 6 + let dcs = 7 + Js_top_worker_rpc.Toplevel_api_gen. 8 + { 9 + dcs_url = "cmis/"; 10 + dcs_toplevel_modules = 11 + [ 12 + "CamlinternalOO"; 13 + "Stdlib"; 14 + "CamlinternalFormat"; 15 + "Std_exit"; 16 + "CamlinternalMod"; 17 + "CamlinternalFormatBasics"; 18 + "CamlinternalLazy"; 19 + ]; 20 + dcs_file_prefixes = [ "stdlib__" ]; 21 + } 9 22 10 23 let log s = Console.console##log (Js.string s) 11 24 ··· 18 31 { 19 32 path = "/static/cmis"; 20 33 cmas = []; 21 - cmis = { dynamic_cmis = [dcs]; static_cmis = [] }; 34 + cmis = { dynamic_cmis = [ dcs ]; static_cmis = [] }; 22 35 stdlib_dcs = "/lib/ocaml/dynamic_cmis.json"; 23 36 findlib_index = "/lib/findlib_index"; 24 37 findlib_requires = [];
+16 -16
example/unix_worker.ml
··· 93 93 done 94 94 95 95 let handle_findlib_error = function 96 - | Failure msg -> 97 - Printf.fprintf stderr "%s" msg 98 - | Fl_package_base.No_such_package(pkg, reason) -> 99 - Printf.fprintf stderr "No such package: %s%s\n" pkg (if reason <> "" then " - " ^ reason else "") 96 + | Failure msg -> Printf.fprintf stderr "%s" msg 97 + | Fl_package_base.No_such_package (pkg, reason) -> 98 + Printf.fprintf stderr "No such package: %s%s\n" pkg 99 + (if reason <> "" then " - " ^ reason else "") 100 100 | Fl_package_base.Package_loop pkg -> 101 101 Printf.fprintf stderr "Package requires itself: %s\n" pkg 102 - | exn -> 103 - raise exn 102 + | exn -> raise exn 104 103 105 104 module Server = Js_top_worker_rpc.Toplevel_api_gen.Make (Impl.IdlM.GenServer ()) 106 105 ··· 111 110 let sync_get _ = None 112 111 let create_file ~name:_ ~content:_ = failwith "Not implemented" 113 112 114 - let import_scripts urls = if List.length urls > 0 then failwith "Not implemented" else () 113 + let import_scripts urls = 114 + if List.length urls > 0 then failwith "Not implemented" else () 115 115 116 116 let init_function _ () = failwith "Not implemented" 117 - 118 117 let findlib_init _ = () 119 - 120 - let get_stdlib_dcs _uri = 121 - [] 118 + let get_stdlib_dcs _uri = [] 122 119 123 120 let require () packages = 124 121 try 125 - let eff_packages = Findlib.package_deep_ancestors !Topfind.predicates packages in 126 - Topfind.load eff_packages; [] 122 + let eff_packages = 123 + Findlib.package_deep_ancestors !Topfind.predicates packages 124 + in 125 + Topfind.load eff_packages; 126 + [] 127 127 with exn -> 128 - handle_findlib_error exn; [] 128 + handle_findlib_error exn; 129 + [] 129 130 end 130 131 131 132 module U = Impl.Make (S) ··· 150 151 rpc_fn (Jsonrpc.call_of_string (Bytes.unsafe_to_string x)) 151 152 >>= fun response -> Jsonrpc.string_of_response response |> return 152 153 in 153 - serve_requests process 154 - (Js_top_worker_rpc.Toplevel_api_gen.sockpath) 154 + serve_requests process Js_top_worker_rpc.Toplevel_api_gen.sockpath 155 155 156 156 let _ = start_server ()
+5 -1
idl/js_top_worker_client.ml
··· 89 89 string -> 90 90 (Toplevel_api_gen.exec_result, Toplevel_api_gen.err) result Lwt.t 91 91 92 - val compile_js : rpc -> string option -> string -> (string, Toplevel_api_gen.err) result Lwt.t 92 + val compile_js : 93 + rpc -> 94 + string option -> 95 + string -> 96 + (string, Toplevel_api_gen.err) result Lwt.t 93 97 end = struct 94 98 type init_libs = Toplevel_api_gen.init_libs 95 99 type err = Toplevel_api_gen.err
+14 -16
idl/js_top_worker_client.mli
··· 9 9 this exception. *) 10 10 11 11 type rpc = Rpc.call -> Rpc.response Lwt.t 12 - (** RPC function for communicating with the worker. This is used by each 13 - RPC function declared in {!W} *) 12 + (** RPC function for communicating with the worker. This is used by each RPC 13 + function declared in {!W} *) 14 14 15 15 val start : string -> int -> (unit -> unit) -> rpc 16 16 (** [start url timeout timeout_fn] initialises a web worker from [url] and 17 17 starts communications with it. [timeout] is the number of seconds to wait 18 - for a response from any RPC before raising an error, and [timeout_fn] is 19 - called when a timeout occurs. Returns the {!type-rpc} function used 20 - in the RPC calls. *) 18 + for a response from any RPC before raising an error, and [timeout_fn] is 19 + called when a timeout occurs. Returns the {!type-rpc} function used in the 20 + RPC calls. *) 21 21 22 22 module W : sig 23 23 (** {2 Type declarations} 24 - 25 - The following types are redeclared here for convenience. *) 24 + 25 + The following types are redeclared here for convenience. *) 26 26 27 27 type init_libs = Toplevel_api_gen.init_libs 28 28 type err = Toplevel_api_gen.err 29 29 type exec_result = Toplevel_api_gen.exec_result 30 30 31 31 (** {2 RPC calls} 32 - 33 - The first parameter of these calls is the rpc function returned by 34 - {!val-start}. If any of these calls fails to receive a response from 35 - the worker by the timeout set in the {!val-start} call, the {!Lwt} 36 - thread will be {{!Lwt.fail}failed}. 37 - *) 32 + 33 + The first parameter of these calls is the rpc function returned by 34 + {!val-start}. If any of these calls fails to receive a response from the 35 + worker by the timeout set in the {!val-start} call, the {!Lwt} thread will 36 + be {{!Lwt.fail}failed}. *) 38 37 39 38 val init : rpc -> init_libs -> (unit, err) result Lwt.t 40 39 (** Initialise the toplevel. This must be called before any other API. *) 41 40 42 41 val setup : rpc -> unit -> (exec_result, err) result Lwt.t 43 - (** Start the toplevel. Return value is the initial blurb 44 - printed when starting a toplevel. Note that the toplevel 45 - must be initialised first. *) 42 + (** Start the toplevel. Return value is the initial blurb printed when 43 + starting a toplevel. Note that the toplevel must be initialised first. *) 46 44 47 45 val typecheck : rpc -> string -> (exec_result, err) result Lwt.t 48 46 (** Typecheck a phrase using the toplevel. The toplevel must have been
+1 -1
idl/js_top_worker_client_fut.ml
··· 81 81 let compile_js rpc id s = Wraw.compile_js rpc id s |> Rpc_fut.T.get 82 82 let query_errors rpc doc = Wraw.query_errors rpc doc |> Rpc_fut.T.get 83 83 let exec_toplevel rpc doc = Wraw.exec_toplevel rpc doc |> Rpc_fut.T.get 84 - 84 + 85 85 let complete_prefix rpc doc pos = 86 86 Wraw.complete_prefix rpc doc pos |> Rpc_fut.T.get 87 87
+11 -2
lib/dune
··· 18 18 merlin-lib.query_commands 19 19 merlin-lib.ocaml_parsing 20 20 findlib 21 - findlib.top) 21 + findlib.top 22 + ) 22 23 (preprocess 23 24 (per_module 24 25 ((action ··· 34 35 (modules worker findlibish jslib) 35 36 (preprocess 36 37 (pps js_of_ocaml-ppx)) 37 - (libraries js_top_worker js_of_ocaml-toplevel logs.browser uri angstrom findlib fpath)) 38 + (libraries 39 + js_top_worker 40 + js_of_ocaml-ppx 41 + js_of_ocaml-toplevel 42 + logs.browser 43 + uri 44 + angstrom 45 + findlib 46 + fpath)) 38 47 39 48 (ocamllex uTop_lexer)
+145 -83
lib/findlibish.ml
··· 1 1 (* Kinda findlib, sorta *) 2 2 3 - 4 3 type library = { 5 4 name : string; 6 5 meta_uri : Uri.t; 7 6 archive_name : string option; 8 7 dir : string option; 9 8 deps : string list; 9 + children : library list; 10 10 mutable loaded : bool; 11 11 } 12 12 13 - let read_libraries_from_pkg_defs ~library_name meta_uri pkg_defs = 13 + let rec flatten_libs libs = 14 + let handle_lib l = 15 + let children = flatten_libs l.children in 16 + l :: children 17 + in 18 + List.map handle_lib libs |> List.flatten 19 + 20 + let preloaded = 21 + [ 22 + "logs"; 23 + "js_top_worker-rpc"; 24 + "js_of_ocaml-compiler"; 25 + "js_of_ocaml-ppx"; 26 + "astring"; 27 + "mime_printer"; 28 + "compiler-libs.common"; 29 + "compiler-libs.toplevel"; 30 + "merlin-lib.kernel"; 31 + "merlin-lib.utils"; 32 + "merlin-lib.query_protocol"; 33 + "merlin-lib.query_commands"; 34 + "merlin-lib.ocaml_parsing"; 35 + "findlib"; 36 + "findlib.top"; 37 + "js_top_worker"; 38 + "js_of_ocaml-ppx"; 39 + "js_of_ocaml-toplevel"; 40 + "logs.browser"; 41 + "uri"; 42 + "angstrom"; 43 + "findlib"; 44 + "fpath"; 45 + ] 46 + 47 + let rec read_libraries_from_pkg_defs ~library_name ~dir meta_uri pkg_expr = 14 48 try 49 + let pkg_defs = pkg_expr.Fl_metascanner.pkg_defs in 15 50 let archive_filename = 16 51 try Some (Fl_metascanner.lookup "archive" [ "byte" ] pkg_defs) 17 52 with _ -> ( ··· 21 56 22 57 let deps_str = Fl_metascanner.lookup "requires" [] pkg_defs in 23 58 let deps = Astring.String.fields ~empty:false deps_str in 24 - let dir = 59 + let subdir = 25 60 List.find_opt (fun d -> d.Fl_metascanner.def_var = "directory") pkg_defs 61 + |> Option.map (fun d -> d.Fl_metascanner.def_value) 26 62 in 27 - let dir = Option.map (fun d -> d.Fl_metascanner.def_value) dir in 63 + let dir = 64 + match (dir, subdir) with 65 + | None, None -> None 66 + | Some d, None -> Some d 67 + | None, Some d -> Some d 68 + | Some d1, Some d2 -> Some (Filename.concat d1 d2) 69 + in 28 70 let archive_name = 29 71 Option.bind archive_filename (fun a -> 30 72 let file_name_len = String.length a in 31 73 if file_name_len > 0 then Some (Filename.chop_extension a) else None) 32 74 in 33 - [ { name = library_name; archive_name; dir; deps; meta_uri; loaded=false } ] 34 - with Not_found -> [] 35 - 75 + let children = 76 + List.filter_map 77 + (fun (n, expr) -> 78 + let library_name = library_name ^ "." ^ n in 79 + match 80 + read_libraries_from_pkg_defs ~library_name ~dir meta_uri expr 81 + with 82 + | Ok l -> Some l 83 + | Error (`Msg m) -> 84 + Jslib.log "Error reading sub-library: %s" m; 85 + None) 86 + pkg_expr.pkg_children 87 + in 88 + Ok 89 + { 90 + name = library_name; 91 + archive_name; 92 + dir; 93 + deps; 94 + meta_uri; 95 + loaded = false; 96 + children; 97 + } 98 + with Not_found -> Error (`Msg "Failed to read libraries from pkg_defs") 36 99 37 100 type t = library list 38 101 ··· 41 104 let fetch_dynamic_cmis url = 42 105 match Jslib.sync_get url with 43 106 | None -> Error (`Msg "Failed to fetch dynamic cmis") 44 - | Some json -> 45 - let rpc = Jsonrpc.of_string json in 46 - Rpcmarshal.unmarshal Js_top_worker_rpc.Toplevel_api_gen.typ_of_dynamic_cmis rpc 107 + | Some json -> 108 + let rpc = Jsonrpc.of_string json in 109 + Rpcmarshal.unmarshal 110 + Js_top_worker_rpc.Toplevel_api_gen.typ_of_dynamic_cmis rpc 47 111 48 112 let init findlib_index : t = 49 - let findlib_metas = 113 + let findlib_metas = 50 114 match Jslib.sync_get findlib_index with 51 115 | None -> [] 52 - | Some txt -> 53 - Astring.String.fields ~empty:false txt 116 + | Some txt -> Astring.String.fields ~empty:false txt 54 117 in 55 - let metas = List.filter_map (fun x -> 56 - match Jslib.sync_get x with 57 - | Some meta -> Some (x, meta) 58 - | None -> None) findlib_metas in 59 - List.flatten @@ List.filter_map (fun (x, meta) -> 60 - match Angstrom.parse_string ~consume:All Uri.Parser.uri_reference x with 61 - | Ok uri -> ( 62 - Jslib.log "Parsed uri: %s" (Uri.path uri); 63 - let path = Uri.path uri in 64 - let file = Fpath.v path in 65 - let base_library_name = 66 - if Fpath.basename file = "META" then Fpath.parent file |> Fpath.basename 67 - else Fpath.get_ext file 68 - in 118 + let metas = 119 + List.filter_map 120 + (fun x -> 121 + match Jslib.sync_get x with Some meta -> Some (x, meta) | None -> None) 122 + findlib_metas 123 + in 124 + List.filter_map 125 + (fun (x, meta) -> 126 + match Angstrom.parse_string ~consume:All Uri.Parser.uri_reference x with 127 + | Ok uri -> ( 128 + Jslib.log "Parsed uri: %s" (Uri.path uri); 129 + let path = Uri.path uri in 130 + let file = Fpath.v path in 131 + let base_library_name = 132 + if Fpath.basename file = "META" then 133 + Fpath.parent file |> Fpath.basename 134 + else Fpath.get_ext file 135 + in 69 136 70 - let lexing = Lexing.from_string meta in 71 - try 72 - let meta = Fl_metascanner.parse_lexing lexing in 73 - let rec extract_name_and_archive ~prefix 74 - ((name, pkg_expr) : string * Fl_metascanner.pkg_expr) = 75 - let library_name = prefix ^ "." ^ name in 76 - let libraries = 77 - read_libraries_from_pkg_defs ~library_name uri pkg_expr.pkg_defs 78 - in 79 - let child_libraries = 80 - pkg_expr.pkg_children 81 - |> List.map (extract_name_and_archive ~prefix:library_name) 82 - |> List.flatten 83 - in 84 - libraries @ child_libraries 85 - in 86 - let libraries = 87 - read_libraries_from_pkg_defs ~library_name:base_library_name uri meta.pkg_defs 88 - in 89 - let libraries = 90 - libraries 91 - @ (meta.pkg_children 92 - |> List.map (extract_name_and_archive ~prefix:base_library_name) 93 - |> List.flatten) in 94 - Some libraries 95 - with _ -> 96 - Jslib.log "Failed to parse meta: %s" (Uri.path uri); 97 - None) 98 - | Error m -> 99 - Jslib.log "Failed to parse uri: %s" m; None) metas 100 - 137 + let lexing = Lexing.from_string meta in 138 + try 139 + let meta = Fl_metascanner.parse_lexing lexing in 140 + let libraries = 141 + read_libraries_from_pkg_defs ~library_name:base_library_name 142 + ~dir:None uri meta 143 + in 144 + Result.to_option libraries 145 + with _ -> 146 + Jslib.log "Failed to parse meta: %s" (Uri.path uri); 147 + None) 148 + | Error m -> 149 + Jslib.log "Failed to parse uri: %s" m; 150 + None) 151 + metas |> flatten_libs 101 152 102 153 let require v packages = 103 - let rec require dcss package : Js_top_worker_rpc.Toplevel_api_gen.dynamic_cmis list = 154 + let rec require dcss package : 155 + Js_top_worker_rpc.Toplevel_api_gen.dynamic_cmis list = 104 156 match List.find (fun lib -> lib.name = package) v with 105 157 | exception Not_found -> 106 - Jslib.log "Package %s not found" package; 107 - dcss 158 + Jslib.log "Package %s not found" package; 159 + dcss 108 160 | lib -> 109 - if lib.loaded 110 - then dcss 111 - else begin 112 - let dep_dcs = List.fold_left require dcss lib.deps in 113 - let path = Uri.path lib.meta_uri in 114 - let dir = Fpath.v path |> Fpath.parent in 115 - let dcs = Fpath.(dir / dcs_filename |> to_string) in 116 - let uri = Uri.with_path lib.meta_uri dcs in 117 - match fetch_dynamic_cmis (Uri.to_string uri) with 118 - | Ok dcs -> 119 - let () = match lib.archive_name with 120 - | None -> () 121 - | Some archive -> 122 - let dir = match lib.dir with None -> dir | Some d -> Fpath.append dir (Fpath.v d) in 123 - let archive_js = Fpath.(dir / (archive ^ ".cma.js") |> to_string) in 124 - Js_of_ocaml.Worker.import_scripts [(Uri.with_path uri archive_js |> Uri.to_string)]; 125 - lib.loaded <- true 161 + if lib.loaded then dcss 162 + else ( 163 + Jslib.log "Loading package %s" lib.name; 164 + Jslib.log "lib.dir: %s" (Option.value ~default:"None" lib.dir); 165 + let dep_dcs = List.fold_left require dcss lib.deps in 166 + let path = Fpath.(v (Uri.path lib.meta_uri) |> parent) in 167 + let dir = 168 + match lib.dir with None -> path | Some d -> Fpath.(path // v d) 126 169 in 127 - dcs :: dep_dcs 128 - | Error (`Msg m) -> 129 - Jslib.log "Failed to unmarshal dynamic_cms from url %s: %s" (Uri.to_string uri) m; 130 - dcss 131 - end 170 + let dcs = Fpath.(dir / dcs_filename |> to_string) in 171 + let uri = Uri.with_path lib.meta_uri dcs in 172 + Jslib.log "uri: %s" (Uri.to_string uri); 173 + match fetch_dynamic_cmis (Uri.to_string uri) with 174 + | Ok dcs -> 175 + let () = 176 + match lib.archive_name with 177 + | None -> () 178 + | Some archive -> 179 + let archive_js = 180 + Fpath.(dir / (archive ^ ".cma.js") |> to_string) 181 + in 182 + if List.mem lib.name preloaded then () 183 + else 184 + Js_of_ocaml.Worker.import_scripts 185 + [ Uri.with_path uri archive_js |> Uri.to_string ]; 186 + lib.loaded <- true 187 + in 188 + Jslib.log "Finished loading package %s" lib.name; 189 + dcs :: dep_dcs 190 + | Error (`Msg m) -> 191 + Jslib.log "Failed to unmarshal dynamic_cms from url %s: %s" 192 + (Uri.to_string uri) m; 193 + dcss) 132 194 in 133 195 List.fold_left require [] packages
+154 -122
lib/impl.ml
··· 5 5 6 6 type captured = { stdout : string; stderr : string } 7 7 8 - 9 8 module JsooTopPpx = struct 10 9 open Js_of_ocaml_compiler.Stdlib 11 10 12 - let ppx_rewriters = ref [fun _ -> Ppx_js.mapper] 11 + let ppx_rewriters = ref [ (fun _ -> Ppx_js.mapper) ] 13 12 14 - let () = Ast_mapper.register_function := fun _ f -> ppx_rewriters := f :: !ppx_rewriters 13 + let () = 14 + Ast_mapper.register_function := 15 + fun _ f -> ppx_rewriters := f :: !ppx_rewriters 15 16 16 17 let preprocess_structure str = 17 18 let open Ast_mapper in 18 - Printf.eprintf "Rewriting...\n%!"; 19 + Printf.eprintf "Rewriting...\n%!"; 19 20 List.fold_right !ppx_rewriters ~init:str ~f:(fun ppx_rewriter str -> 20 21 let mapper = ppx_rewriter [] in 21 22 mapper.structure mapper str) ··· 31 32 match phrase with 32 33 | Ptop_def str -> Ptop_def (preprocess_structure str) 33 34 | Ptop_dir _ as x -> x 34 - 35 35 end 36 + 36 37 module type S = sig 37 38 type findlib_t 39 + 38 40 val capture : (unit -> 'a) -> unit -> captured * 'a 39 41 val create_file : name:string -> content:string -> unit 40 42 val sync_get : string -> string option 41 - 42 43 val import_scripts : string list -> unit 43 - val init_function : string -> (unit -> unit ) 44 - 44 + val init_function : string -> unit -> unit 45 45 val get_stdlib_dcs : string -> Toplevel_api_gen.dynamic_cmis list 46 - 47 46 val findlib_init : string -> findlib_t 48 - 49 47 val require : findlib_t -> string list -> Toplevel_api_gen.dynamic_cmis list 50 48 end 51 49 ··· 148 146 let execute printval ?pp_code ?highlight_location pp_answer s = 149 147 let s = 150 148 let l = String.length s in 151 - if String.sub s (l-2) 2 = ";;" then s else s ^ ";;" in 149 + if String.sub s (l - 2) 2 = ";;" then s else s ^ ";;" 150 + in 152 151 let lb = Lexing.from_function (refill_lexbuf s (ref 0) pp_code) in 153 152 (try 154 153 while true do ··· 219 218 let dirs = get_paths () in 220 219 reset (); 221 220 List.iter (fun p -> prepend_dir (Dir.create p)) dirs 222 - 221 + 223 222 let add_dynamic_cmis dcs = 224 223 let fetch filename = 225 224 let url = Filename.concat dcs.Toplevel_api_gen.dcs_url filename in ··· 233 232 (fun name -> 234 233 let filename = filename_of_module name in 235 234 match fetch (filename_of_module name) with 236 - | Some content -> 235 + | Some content -> ( 237 236 let name = Filename.(concat path filename) in 238 - (try S.create_file ~name ~content with _ -> ()) 237 + try S.create_file ~name ~content with _ -> ()) 239 238 | None -> ()) 240 239 dcs.dcs_toplevel_modules; 241 240 ··· 247 246 (* Check if it's already been downloaded. This will be the 248 247 case for all toplevel cmis. Also check whether we're supposed 249 248 to handle this cmi *) 250 - (if Sys.file_exists fs_name then 251 - Logs.info (fun m -> m "Found: %s" fs_name)); 252 - (if 253 - (not (Sys.file_exists fs_name)) 254 - && List.exists 255 - (fun prefix -> String.starts_with ~prefix filename) 256 - dcs.dcs_file_prefixes 257 - then ( 258 - Logs.info (fun m -> m "Fetching %s\n%!" filename); 259 - match fetch filename with 260 - | Some x -> 261 - S.create_file ~name:fs_name ~content:x; 262 - (* At this point we need to tell merlin that the dir contents 249 + if Sys.file_exists fs_name then Logs.info (fun m -> m "Found: %s" fs_name); 250 + if 251 + (not (Sys.file_exists fs_name)) 252 + && List.exists 253 + (fun prefix -> String.starts_with ~prefix filename) 254 + dcs.dcs_file_prefixes 255 + then ( 256 + Logs.info (fun m -> m "Fetching %s\n%!" filename); 257 + match fetch filename with 258 + | Some x -> 259 + S.create_file ~name:fs_name ~content:x; 260 + (* At this point we need to tell merlin that the dir contents 263 261 have changed *) 264 - if s = "merl" then reset_dirs () else reset_dirs_comp () 265 - | None -> 266 - Printf.eprintf "Warning: Expected to find cmi at: %s\n%!" 267 - (Filename.concat dcs.Toplevel_api_gen.dcs_url filename))); 262 + if s = "merl" then reset_dirs () else reset_dirs_comp () 263 + | None -> 264 + Printf.eprintf "Warning: Expected to find cmi at: %s\n%!" 265 + (Filename.concat dcs.Toplevel_api_gen.dcs_url filename)); 268 266 old_loader ~unit_name 269 267 in 270 268 let furl = "file://" in 271 269 let l = String.length furl in 272 - if String.length dcs.dcs_url > l && String.sub dcs.dcs_url 0 l = furl then begin 270 + if String.length dcs.dcs_url > l && String.sub dcs.dcs_url 0 l = furl then 273 271 let path = String.sub dcs.dcs_url l (String.length dcs.dcs_url - l) in 274 272 Topdirs.dir_directory path 275 - end else begin 273 + else 276 274 let open Persistent_env.Persistent_signature in 277 275 let old_loader = !load in 278 - load := (new_load ~s:"comp" ~old_loader); 276 + load := new_load ~s:"comp" ~old_loader; 279 277 280 278 let open Ocaml_typing.Persistent_env.Persistent_signature in 281 279 let old_loader = !load in 282 - load := (new_load ~s:"merl" ~old_loader) 283 - end 280 + load := new_load ~s:"merl" ~old_loader 284 281 285 282 let init (init_libs : Toplevel_api_gen.init_libs) = 286 283 try ··· 290 287 findlib_v := Some (S.findlib_init init_libs.findlib_index); 291 288 292 289 (match S.get_stdlib_dcs init_libs.stdlib_dcs with 293 - |[dcs] -> add_dynamic_cmis dcs 290 + | [ dcs ] -> add_dynamic_cmis dcs 294 291 | _ -> ()); 295 292 Clflags.no_check_prims := true; 296 293 List.iter ··· 304 301 List.iter add_dynamic_cmis init_libs.cmis.dynamic_cmis; 305 302 306 303 S.import_scripts 307 - (List.map (fun cma -> cma.Toplevel_api_gen.url) init_libs.cmas); 304 + (List.map (fun cma -> cma.Toplevel_api_gen.url) init_libs.cmas); 308 305 309 306 requires := init_libs.findlib_requires; 310 307 functions := 311 308 Some 312 309 (List.map 313 - (fun func_name -> 314 - Logs.info (fun m -> m "Function: %s" func_name); 315 - S.init_function func_name) 316 - (List.map (fun cma -> cma.Toplevel_api_gen.fn) init_libs.cmas)); 317 - (* 318 - *) 310 + (fun func_name -> 311 + Logs.info (fun m -> m "Function: %s" func_name); 312 + S.init_function func_name) 313 + (List.map (fun cma -> cma.Toplevel_api_gen.fn) init_libs.cmas)); 314 + (* *) 319 315 functions := Some []; 320 316 Logs.info (fun m -> m "init() finished"); 321 317 ··· 329 325 Logs.info (fun m -> m "setup() ..."); 330 326 331 327 let o = 332 - 333 - (try 328 + try 334 329 match !functions with 335 330 | Some l -> setup l () 336 331 | None -> failwith "Error: toplevel has not been initialised" 337 - with 338 - | Persistent_env.Error e -> 339 - Persistent_env.report_error Format.err_formatter e; 340 - let err = Format.asprintf "%a" Persistent_env.report_error e in 341 - failwith ("Error: " ^ err) 342 - | Env.Error e -> 343 - Env.report_error Format.err_formatter e; 344 - let err = Format.asprintf "%a" Env.report_error e in 345 - failwith ("Error: " ^ err)) 346 - in 347 - 348 - let dcs = (match !findlib_v with 349 - | Some v -> 350 - S.require v !requires 351 - | None -> []) in 332 + with 333 + | Persistent_env.Error e -> 334 + Persistent_env.report_error Format.err_formatter e; 335 + let err = Format.asprintf "%a" Persistent_env.report_error e in 336 + failwith ("Error: " ^ err) 337 + | Env.Error e -> 338 + Env.report_error Format.err_formatter e; 339 + let err = Format.asprintf "%a" Env.report_error e in 340 + failwith ("Error: " ^ err) 341 + in 342 + 343 + let dcs = 344 + match !findlib_v with Some v -> S.require v !requires | None -> [] 345 + in 352 346 List.iter add_dynamic_cmis dcs; 353 347 354 348 Logs.info (fun m -> m "setup() finished"); ··· 439 433 440 434 let compile_js (id : string option) prog = 441 435 try 442 - 443 436 let l = Lexing.from_string prog in 444 437 let phr = Parse.toplevel_phrase l in 445 438 Typecore.reset_delayed_checks (); ··· 490 483 cu_debugsize = 0; 491 484 } 492 485 in 493 - 486 + 494 487 let fmt = Js_of_ocaml_compiler.Pretty_print.to_buffer b in 495 488 (* Symtable.patch_object code reloc; 496 489 Symtable.check_global_initialized reloc; ··· 505 498 let ic = open_in "/tmp/test.cmo" in 506 499 let p = Js_of_ocaml_compiler.Parse_bytecode.from_cmo cmo ic in 507 500 let wrap_with_fun = 508 - match id with 509 - | Some id -> `Named id 510 - | None -> `Iife 501 + match id with Some id -> `Named id | None -> `Iife 511 502 in 512 - Js_of_ocaml_compiler.Driver.f' ~standalone:false ~wrap_with_fun ~link:`No 513 - fmt p.debug p.code; 503 + Js_of_ocaml_compiler.Driver.f' ~standalone:false ~wrap_with_fun 504 + ~link:`No fmt p.debug p.code; 514 505 Format.(pp_print_flush std_formatter ()); 515 506 Format.(pp_print_flush err_formatter ()); 516 507 flush stdout; ··· 521 512 with e -> IdlM.ErrM.return ("Exception: %s" ^ Printexc.to_string e) 522 513 523 514 let handle_toplevel stripped = 524 - if String.length stripped < 2 || stripped.[0] <> '#' || stripped.[1] <> ' ' then begin 525 - Printf.eprintf "Warning, ignoring toplevel block without a leading '# '.\n"; 526 - IdlM.ErrM.return { Toplevel_api_gen.script=stripped; mime_vals=[] } 527 - end else begin 515 + if String.length stripped < 2 || stripped.[0] <> '#' || stripped.[1] <> ' ' 516 + then ( 517 + Printf.eprintf 518 + "Warning, ignoring toplevel block without a leading '# '.\n"; 519 + IdlM.ErrM.return { Toplevel_api_gen.script = stripped; mime_vals = [] }) 520 + else 528 521 let s = String.sub stripped 2 (String.length stripped - 2) in 529 522 let list = Ocamltop.parse_toplevel s in 530 523 let buf = Buffer.create 1024 in 531 - let mime_vals = List.fold_left (fun acc (phr, _output) -> 532 - let new_output = execute phr |> IdlM.T.get |> M.run |> Result.get_ok in 533 - Printf.bprintf buf "# %s\n" phr; 534 - let r = (Option.to_list new_output.stdout) @ (Option.to_list new_output.stderr) @ (Option.to_list new_output.caml_ppf) in 535 - let r = List.concat_map (fun l -> Astring.String.cuts ~sep:"\n" l) r in 536 - List.iter (fun x -> Printf.bprintf buf " %s\n" x) r; 537 - let mime_vals = new_output.mime_vals in 538 - acc @ mime_vals 539 - ) [] list in 524 + let mime_vals = 525 + List.fold_left 526 + (fun acc (phr, _output) -> 527 + let new_output = 528 + execute phr |> IdlM.T.get |> M.run |> Result.get_ok 529 + in 530 + Printf.bprintf buf "# %s\n" phr; 531 + let r = 532 + Option.to_list new_output.stdout 533 + @ Option.to_list new_output.stderr 534 + @ Option.to_list new_output.caml_ppf 535 + in 536 + let r = 537 + List.concat_map (fun l -> Astring.String.cuts ~sep:"\n" l) r 538 + in 539 + List.iter (fun x -> Printf.bprintf buf " %s\n" x) r; 540 + let mime_vals = new_output.mime_vals in 541 + acc @ mime_vals) 542 + [] list 543 + in 540 544 let content_txt = Buffer.contents buf in 541 - let content_txt = String.sub content_txt 0 (String.length content_txt - 1) in 542 - let result = { Toplevel_api_gen.script=content_txt; mime_vals } in 545 + let content_txt = 546 + String.sub content_txt 0 (String.length content_txt - 1) 547 + in 548 + let result = { Toplevel_api_gen.script = content_txt; mime_vals } in 543 549 IdlM.ErrM.return result 544 - end 545 - 546 - let exec_toplevel (phrase : string) = 547 - handle_toplevel phrase 550 + 551 + let exec_toplevel (phrase : string) = handle_toplevel phrase 548 552 549 553 let config () = 550 554 let path = ··· 658 662 659 663 let complete_prefix source position = 660 664 let source = Merlin_kernel.Msource.make source in 661 - let map_kind : [`Value|`Constructor|`Variant|`Label| 662 - `Module|`Modtype|`Type|`MethodCall|`Keyword] -> Toplevel_api_gen.kind_ty = function 665 + let map_kind : 666 + [ `Value 667 + | `Constructor 668 + | `Variant 669 + | `Label 670 + | `Module 671 + | `Modtype 672 + | `Type 673 + | `MethodCall 674 + | `Keyword ] -> 675 + Toplevel_api_gen.kind_ty = function 663 676 | `Value -> Value 664 677 | `Constructor -> Constructor 665 678 | `Variant -> Variant ··· 668 681 | `Modtype -> Modtype 669 682 | `Type -> Type 670 683 | `MethodCall -> MethodCall 671 - | `Keyword -> Keyword in 684 + | `Keyword -> Keyword 685 + in 672 686 let position = 673 687 match position with 674 688 | Toplevel_api_gen.Start -> `Start 675 689 | Offset x -> `Offset x 676 690 | Logical (x, y) -> `Logical (x, y) 677 - | End -> `End in 691 + | End -> `End 692 + in 678 693 match Completion.at_pos source position with 679 694 | Some (from, to_, compl) -> 680 695 let entries = 681 - List.map (fun (entry : Query_protocol.Compl.entry) -> 682 - { 683 - Toplevel_api_gen.name = entry.name; 684 - kind = map_kind entry.kind; 685 - desc = entry.desc; 686 - info = entry.info; 687 - deprecated = entry.deprecated; 688 - } ) compl.entries in 696 + List.map 697 + (fun (entry : Query_protocol.Compl.entry) -> 698 + { 699 + Toplevel_api_gen.name = entry.name; 700 + kind = map_kind entry.kind; 701 + desc = entry.desc; 702 + info = entry.info; 703 + deprecated = entry.deprecated; 704 + }) 705 + compl.entries 706 + in 689 707 IdlM.ErrM.return { Toplevel_api_gen.from; to_; entries } 690 708 | None -> 691 709 IdlM.ErrM.return { Toplevel_api_gen.from = 0; to_ = 0; entries = [] } ··· 699 717 let errors = 700 718 wdispatch source query 701 719 |> StdLabels.List.map 702 - ~f:(fun 703 - (Ocaml_parsing.Location.{ kind; main = _; sub; source } as error) 704 - -> 705 - let of_sub sub = 706 - Ocaml_parsing.Location.print_sub_msg Format.str_formatter sub; 707 - String.trim (Format.flush_str_formatter ()) 708 - in 709 - let loc = Ocaml_parsing.Location.loc_of_report error in 710 - let main = 711 - Format.asprintf "@[%a@]" Ocaml_parsing.Location.print_main error 712 - |> String.trim 713 - in 714 - { 715 - Toplevel_api_gen.kind; 716 - loc; 717 - main; 718 - sub = StdLabels.List.map ~f:of_sub sub; 719 - source; 720 - }) 720 + ~f:(fun 721 + (Ocaml_parsing.Location.{ kind; main = _; sub; source } as 722 + error) 723 + -> 724 + let of_sub sub = 725 + Ocaml_parsing.Location.print_sub_msg Format.str_formatter sub; 726 + String.trim (Format.flush_str_formatter ()) 727 + in 728 + let loc = Ocaml_parsing.Location.loc_of_report error in 729 + let main = 730 + Format.asprintf "@[%a@]" Ocaml_parsing.Location.print_main 731 + error 732 + |> String.trim 733 + in 734 + { 735 + Toplevel_api_gen.kind; 736 + loc; 737 + main; 738 + sub = StdLabels.List.map ~f:of_sub sub; 739 + source; 740 + }) 721 741 in 722 742 IdlM.ErrM.return errors 723 743 with e -> ··· 730 750 | Toplevel_api_gen.Start -> `Start 731 751 | Offset x -> `Offset x 732 752 | Logical (x, y) -> `Logical (x, y) 733 - | End -> `End in 753 + | End -> `End 754 + in 734 755 let source = Merlin_kernel.Msource.make source in 735 756 let query = Query_protocol.Type_enclosing (None, position, None) in 736 757 let enclosing = wdispatch source query in 737 - let map_index_or_string = function | `Index i -> Toplevel_api_gen.Index i | `String s -> String s in 738 - let map_tail_position = function | `No -> Toplevel_api_gen.No | `Tail_position -> Tail_position | `Tail_call -> Tail_call in 739 - let enclosing = List.map (fun (x,y,z) -> (x,map_index_or_string y,map_tail_position z)) enclosing in 758 + let map_index_or_string = function 759 + | `Index i -> Toplevel_api_gen.Index i 760 + | `String s -> String s 761 + in 762 + let map_tail_position = function 763 + | `No -> Toplevel_api_gen.No 764 + | `Tail_position -> Tail_position 765 + | `Tail_call -> Tail_call 766 + in 767 + let enclosing = 768 + List.map 769 + (fun (x, y, z) -> (x, map_index_or_string y, map_tail_position z)) 770 + enclosing 771 + in 740 772 IdlM.ErrM.return enclosing 741 773 end
-1
lib/jslib.ml
··· 18 18 None) 19 19 (fun b -> Some (Typed_array.String.of_arrayBuffer b)) 20 20 | _ -> None 21 -
+7 -6
lib/ocamltop.ml
··· 2 2 if !p = String.length s then 0 3 3 else 4 4 let len' = 5 - try (String.index_from s !p '\n' - !p + 1) 6 - with _ -> (String.length s - !p) 5 + try String.index_from s !p '\n' - !p + 1 with _ -> String.length s - !p 7 6 in 8 7 let len'' = min len len' in 9 8 String.blit s !p buffer 0 len''; ··· 11 10 len'' 12 11 13 12 let parse_toplevel s = 14 - let s = s in 13 + Logs.warn (fun m -> m "Parsing toplevel phrases"); 15 14 let lexbuf = Lexing.from_string s in 16 15 let rec loop pos = 17 16 let _phr = !Toploop.parse_toplevel_phrase lexbuf in 18 17 let new_pos = Lexing.lexeme_end lexbuf in 19 18 let phr = String.sub s pos (new_pos - pos) in 20 - let (cont, output) = Toplexer.entry lexbuf in 19 + let cont, is_legacy, output = Toplexer.entry lexbuf in 20 + if is_legacy then 21 + Logs.warn (fun m -> m "Warning: Legacy toplevel output detected"); 21 22 let new_pos = Lexing.lexeme_end lexbuf in 22 - if cont then (phr, output) :: loop new_pos else [(phr, output)] 23 + if cont then (phr, output) :: loop new_pos else [ (phr, output) ] 23 24 in 24 - loop 0 25 + loop 0
+32 -9
lib/toplexer.mll
··· 2 2 3 3 rule entry = parse 4 4 | (_ # '\n')* "\n" { 5 - output_line [] lexbuf 5 + line_prefix [] lexbuf 6 6 } 7 - | _ | eof { false, [] } 7 + | _ | eof { false, false, [] } 8 8 9 - and output_line acc = parse 10 - | " " ((_ # '\n')* as line) "\n" { 11 - output_line (line :: acc) lexbuf 9 + and line_prefix acc = parse 10 + | " " { 11 + line acc lexbuf 12 12 } 13 13 | "# " { 14 - true, List.rev acc 14 + true, false, List.rev acc 15 + } 16 + | _ as c { 17 + output_line_legacy c acc lexbuf 15 18 } 16 19 | eof { 17 - false, List.rev acc 20 + false, false, List.rev acc 21 + } 22 + 23 + and line acc = parse 24 + | ((_ # '\n')* as line) "\n" { 25 + line_prefix (line :: acc) lexbuf 18 26 } 19 - | _ { 20 - false, List.rev acc 27 + | ((_ # '\n')* as line) eof { 28 + false, false, List.rev (line :: acc) 29 + } 30 + 31 + and output_line_legacy c acc = parse 32 + | ((_ # '\n')* as line) "\n# " { 33 + true, true, List.rev ((String.make 1 c ^ line) :: acc) 21 34 } 35 + | ((_ # '\n')* as line) "\n" (_ as c') { 36 + output_line_legacy c' ((String.make 1 c ^ line) :: acc) lexbuf 37 + } 38 + | (_ # '\n')* as line eof { 39 + false, true, List.rev ((String.make 1 c ^ line) :: acc) 40 + } 41 + | eof { 42 + false, true, List.rev ((String.make 1 c) :: acc) 43 + } 44 +
+12 -14
lib/uTop.mli
··· 21 21 (** {6 Parsing} *) 22 22 23 23 type location = int * int 24 - (** Type of a string-location. It is composed of a start and stop 25 - offsets (in bytes). *) 24 + (** Type of a string-location. It is composed of a start and stop offsets (in 25 + bytes). *) 26 26 27 27 type lines = { start : int; stop : int } 28 28 (** Type for a range of lines in a buffer from start to stop. *) ··· 57 57 58 58 val parse_toplevel_phrase_default : 59 59 string -> bool -> Parsetree.toplevel_phrase result 60 - (** The default parser for toplevel phrases. It uses the standard ocaml parser. *) 60 + (** The default parser for toplevel phrases. It uses the standard ocaml parser. 61 + *) 61 62 62 63 val parse_default : (Lexing.lexbuf -> 'a) -> string -> bool -> 'a result 63 64 (** The default parser. It uses the standard ocaml parser. *) ··· 67 68 toplevel. *) 68 69 69 70 val lexbuf_of_string : bool ref -> string -> Lexing.lexbuf 70 - (** [lexbuf_of_string eof str] is the same as [Lexing.from_string 71 - str] 72 - except that if the lexer reach the end of [str] then [eof] is set to [true]. *) 71 + (** [lexbuf_of_string eof str] is the same as [Lexing.from_string str] except 72 + that if the lexer reach the end of [str] then [eof] is set to [true]. *) 73 73 74 74 (** {6 Helpers} *) 75 75 ··· 78 78 prints as a string. *) 79 79 80 80 val get_ocaml_error_message : exn -> location * string * lines option 81 - (** [get_ocaml_error_message exn] returns the location and error 82 - message for the exception [exn] which must be an exception from 83 - the compiler. *) 81 + (** [get_ocaml_error_message exn] returns the location and error message for the 82 + exception [exn] which must be an exception from the compiler. *) 84 83 85 84 val check_phrase : 86 85 Parsetree.toplevel_phrase -> 87 86 (location list * string * lines option list) option 88 - (** [check_phrase phrase] checks that [phrase] can be executed 89 - without typing or compilation errors. It returns [None] if 90 - [phrase] is OK and an error message otherwise. 91 - If the result is [None] it is guaranteed that 92 - [Toploop.execute_phrase] won't raise any exception. *) 87 + (** [check_phrase phrase] checks that [phrase] can be executed without typing or 88 + compilation errors. It returns [None] if [phrase] is OK and an error message 89 + otherwise. If the result is [None] it is guaranteed that 90 + [Toploop.execute_phrase] won't raise any exception. *) 93 91 94 92 val collect_formatters : Buffer.t -> Format.formatter list -> (unit -> 'a) -> 'a 95 93 (** [collect_formatters buf pps f] executes [f] and redirect everything it
+2 -8
lib/worker.ml
··· 1 1 open Js_top_worker_rpc 2 2 open Js_top_worker 3 - 4 3 module Server = Toplevel_api_gen.Make (Impl.IdlM.GenServer ()) 5 4 6 5 (* OCamlorg toplevel in a web worker ··· 54 53 55 54 let sync_get = Jslib.sync_get 56 55 let create_file = Js_of_ocaml.Sys_js.create_file 57 - 58 - let get_stdlib_dcs uri = 59 - Findlibish.fetch_dynamic_cmis uri |> Result.to_list 60 - 56 + let get_stdlib_dcs uri = Findlibish.fetch_dynamic_cmis uri |> Result.to_list 61 57 let import_scripts = Js_of_ocaml.Worker.import_scripts 62 - 63 58 let findlib_init = Findlibish.init 64 59 65 60 let require v = function ··· 69 64 let init_function func_name = 70 65 let open Js_of_ocaml in 71 66 let func = Js.Unsafe.js_expr func_name in 72 - fun () -> 73 - Js.Unsafe.fun_call func [| Js.Unsafe.inject Dom_html.window |] 67 + fun () -> Js.Unsafe.fun_call func [| Js.Unsafe.inject Dom_html.window |] 74 68 end 75 69 76 70 module M = Impl.Make (S)
+3
test/cram/dune
··· 1 + (cram 2 + (deps %{bin:unix_worker} %{bin:unix_client}) 3 + )
+25
test/cram/simple.t/run.t
··· 1 + $ ./script.sh 2 + unix_worker: [INFO] init() 3 + unix_worker: [INFO] init() finished 4 + N 5 + unix_worker: [INFO] setup() ... 6 + unix_worker: [INFO] Setup complete 7 + unix_worker: [INFO] setup() finished 8 + {mime_vals:[];stderr:S(error while evaluating #enable "pretty";; 9 + error while evaluating #disable "shortvar";;);stdout:S(OCaml version 5.1.0 10 + Unknown directive `enable'. 11 + Unknown directive `disable'.)} 12 + unix_worker: [WARNING] Parsing toplevel phrases 13 + {mime_vals:[];script:S(# Printf.printf "Hello, world\n";; 14 + Hello, world 15 + - : unit = ())} 16 + unix_worker: [WARNING] Parsing toplevel phrases 17 + unix_worker: [WARNING] Warning: Legacy toplevel output detected 18 + unix_worker: [WARNING] Warning: Legacy toplevel output detected 19 + {mime_vals:[];script:S(# let x = 1 + 2;; 20 + val x : int = 3 21 + # let x = 2+3;; 22 + val x : int = 5)} 23 + unix_worker: [WARNING] Parsing toplevel phrases 24 + {mime_vals:[];script:S(# let x = 1 + 2;; 25 + val x : int = 3)}
+4
test/cram/simple.t/s1
··· 1 + # let x = 1 + 2;; 2 + foobarbaz 3 + # let x = 2+3;; 4 + foobarbz
+4
test/cram/simple.t/s2
··· 1 + # let x = 1 + 2;; 2 + foobarbaz 3 + # let x = 2+3;; 4 + foobarbz
+18
test/cram/simple.t/script.sh
··· 1 + #!/bin/bash 2 + 3 + 4 + export OCAMLRUNPARAM=b 5 + 6 + unix_worker & 7 + pid=$! 8 + 9 + sleep 1 10 + 11 + unix_client init '{ init_libs:[], stdlib_dcs:"", findlib_requires:[], findlib_index:"", cmas:[], cmis:{dynamic_cmis:[], static_cmis:[]}, path:"" }' 12 + unix_client setup 13 + unix_client exec_toplevel '# Printf.printf "Hello, world\n";;' 14 + unix_client exec_toplevel "$(cat s1)" 15 + unix_client exec_toplevel "$(cat s2)" 16 + 17 + kill $pid 18 +
+4
test/libtest/dune
··· 1 + (executable 2 + (name parse_test) 3 + (modes byte) 4 + (libraries js_top_worker fmt))
+6
test/libtest/parse_test.ml
··· 1 + 2 + let fmt = Fmt.Dump.(list (pair string (list string))) 3 + 4 + let _ = 5 + let phr = Js_top_worker.Ocamltop.parse_toplevel "# foo;;\n bar\n# baz;;\n moo\n" in 6 + Format.printf "%a" fmt phr;