A fork of mtelver's day10 project

feat(x-ocaml): implement universe discovery via meta tags

Add Add_cmis to X_protocol.request so pages can register external
package CMIs with the merlin worker. Page.create reads
<meta name="x-ocaml-packages" content="pkg1,pkg2,..."> and optional
<meta name="x-ocaml-cmis-url" content="./cmis/"> from the document
head. For each package, dynamic CMIs are registered so merlin can
provide completions, type-on-hover, and error checking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+46 -10
+1
x-ocaml/protocol/x_protocol.ml
··· 8 8 | Format of id * string 9 9 | Format_config of string 10 10 | Setup 11 + | Add_cmis of Merlin_protocol.cmis 11 12 12 13 type output = 13 14 | Stdout of string
+2
x-ocaml/src/jtw_client.ml
··· 184 184 185 185 | X_protocol.Setup -> () 186 186 187 + | X_protocol.Add_cmis _ -> () (* js_top_worker manages its own CMIs *) 188 + 187 189 let eval ~id ~line_number t code = 188 190 post t (X_protocol.Eval (id, line_number, code)) 189 191
+25 -4
x-ocaml/src/page.ml
··· 74 74 (match format_config with 75 75 | Some conf -> Backend.post backend (Format_config conf) 76 76 | None -> ()); 77 - (* Universe discovery: read <meta name="x-ocaml-packages"> *) 77 + (* Universe discovery: register external package CMIs with merlin. 78 + <meta name="x-ocaml-packages" content="pkg1,pkg2,..."> 79 + <meta name="x-ocaml-cmis-url" content="./cmis/"> (optional, default ./cmis/) 80 + For each package, we send Add_cmis with dynamic_cmis pointing to 81 + {base_url}/{pkg}/ with toplevel module {Pkg} and prefix {pkg}__. *) 78 82 (match read_meta_content "x-ocaml-packages" with 79 83 | None -> () 80 - | Some _packages -> 81 - (* TODO: send package configuration to backend once protocol supports it *) 82 - ()); 84 + | Some packages_str -> 85 + let base_url = 86 + match read_meta_content "x-ocaml-cmis-url" with 87 + | Some url -> url 88 + | None -> "./cmis/" 89 + in 90 + let packages = 91 + List.filter (fun s -> s <> "") 92 + (List.map String.trim (String.split_on_char ',' packages_str)) 93 + in 94 + List.iter (fun pkg -> 95 + let capitalised = String.capitalize_ascii pkg in 96 + let dcs = Protocol.{ 97 + dcs_url = base_url ^ pkg ^ "/"; 98 + dcs_toplevel_modules = [ capitalised ]; 99 + dcs_file_prefixes = [ pkg ^ "__" ]; 100 + } in 101 + Backend.post backend 102 + (Add_cmis { Protocol.static_cmis = []; dynamic_cmis = Some dcs })) 103 + packages); 83 104 t 84 105 85 106 let run_on_of_string = function "click" -> `Click | "load" | _ -> `Load
+16 -6
x-ocaml/src/page.mli
··· 40 40 {2 Universe discovery} 41 41 42 42 During creation, the page reads 43 - [<meta name="x-ocaml-packages" content="...">] from the document head. 44 - If present, the comma-separated package names are sent to the backend so 45 - that their modules become available during evaluation. *) 43 + [<meta name="x-ocaml-packages" content="pkg1,pkg2,...">] from the document 44 + head. If present, each package name is used to register dynamic CMIs with 45 + the merlin worker so that type information (completions, type-on-hover, 46 + error checking) is available for those packages. 47 + 48 + For each package [pkg], CMIs are expected at [{base_url}/{pkg}/] where 49 + [base_url] defaults to ["./cmis/"] and can be overridden with 50 + [<meta name="x-ocaml-cmis-url" content="...">]. The toplevel module is 51 + assumed to be [{Pkg}] (capitalised) with sub-modules prefixed [{pkg}__]. 52 + 53 + {b Note:} For the toplevel {e evaluator} to have access to the packages, 54 + their JavaScript bundles must be loaded separately via the [src-load] 55 + attribute on the [<script>] tag. Universe discovery only configures merlin. *) 46 56 47 57 type t 48 58 (** Mutable page-level state: cell registry, backend handle, and page-wide ··· 59 69 (** Create a new page and initialise the backend. 60 70 61 71 Sends [Setup] to the backend, registers a message handler that routes 62 - responses to the appropriate cell, reads 63 - [<meta name="x-ocaml-packages">] from the document and sends package 64 - configuration if present. 72 + responses to the appropriate cell, and reads 73 + [<meta name="x-ocaml-packages">] and [<meta name="x-ocaml-cmis-url">] 74 + from the document to configure merlin's dynamic CMI loading. 65 75 66 76 @param backend The evaluation/formatting backend. 67 77 @param extra_style Default stylesheet URL applied to every cell.
+2
x-ocaml/worker/x_worker.ml
··· 25 25 let result = Eval.execute ~output ~id ~line_number code in 26 26 respond (Top_response (id, result)) 27 27 | Setup -> Eval.setup_toplevel () 28 + | Add_cmis cmis -> 29 + ignore (Merlin_worker.on_message (Protocol.Add_cmis cmis) : Protocol.answer)