Skip to content

Commit 6f58654

Browse files
committed
Adding search support in the reference driver
Signed-off-by: Paul-Elliot <peada@free.fr>
1 parent 8fa240e commit 6f58654

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

doc/driver.md

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,19 +217,26 @@ let link ?(ignore_output = false) file =
217217
if not ignore_output then
218218
add_prefixed_output cmd link_output (Fpath.to_string file) lines
219219
220-
let html_generate ?(ignore_output = false) file source =
220+
let html_generate ?(ignore_output = false) ?(search_files = []) file source =
221221
let open Cmd in
222222
let source = match source with None -> empty | Some source -> v "--source" % p source in
223+
let search =
224+
List.fold_left
225+
(fun acc filename -> acc % "--search-file" % filename)
226+
empty
227+
(List.map Fpath.filename search_files)
228+
in
223229
let cmd =
224-
odoc % "html-generate" %% source % p file % "-o" % "html" % "--theme-uri" % "odoc"
230+
odoc % "html-generate" %% source % p file %% search % "-o" % "html" % "--theme-uri" % "odoc"
225231
% "--support-uri" % "odoc"
226232
in
227233
let lines = run cmd in
228234
if not ignore_output then
229235
add_prefixed_output cmd generate_output (Fpath.to_string file) lines
230236
231-
let support_files () =
237+
let support_files ?(search_files = []) () =
232238
let open Cmd in
239+
let search = List.fold_left (fun acc f -> acc % "--search-file" % p f) empty search_files in
233240
let cmd = odoc % "support-files" % "-o" % "html/odoc" in
234241
run cmd
235242
```
@@ -582,20 +589,78 @@ let link_all odoc_files =
582589

583590
Now we simply run `odoc html-generate` over all of the resulting `odocl` files.
584591
This will generate sources, as well as documentation for non-hidden units.
592+
We notify the generator that the javascript file to use for search is `index.js`.
585593

586594
```ocaml env=e1
587595
let generate_all odocl_files =
596+
let search_files = [ Fpath.v "index.js" ] in
588597
let relativize_opt = function None -> None | Some file -> Some (relativize file) in
589-
List.iter (fun (f, source) -> ignore(html_generate f (relativize_opt source))) odocl_files;
590-
support_files ()
598+
List.iter
599+
(fun (f, source) -> ignore(html_generate ~search_files f (relativize_opt source)))
600+
odocl_files;
601+
support_files ~search_files ()
602+
```
603+
604+
Finally, we generate an index of all values, types, ... This index is meant to be consumed by search engines, to create their own index. It consists of a JSON array, containing entries with the name, full name, associated comment, link and anchor, and kind.
605+
Generating the index is done via `odoc compile-index`, which create a json file. This second format is meant to be consumed by search engines to populate their search index. Search engines written in OCaml can also call the `Odoc_model.Fold.unit` and `Odoc_model.Fold.page` function, in conjunction with `Odoc_search.Entry.entry_of_item` in order to get a (version stable) OCaml value of each element to be indexed.
606+
607+
```ocaml env=e1
608+
let index_generate ?(ignore_output = false) () =
609+
let open Cmd in
610+
let cmd = odoc % "compile-index" % "-o" % "html/index.json" % "-I" % "." in
611+
let lines = run cmd in
612+
if not ignore_output then
613+
add_prefixed_output cmd generate_output "index compilation" lines;
591614
```
592615

616+
We turn the JSON index into a javascript file. In order to never block the UI, this file will be used as a web worker by `odoc`, to perform searches:
617+
618+
- The search query will be sent as a plain string to the web worker, using the standard mechanism of message passing
619+
- The web worker has to sent back the result as a message to the main thread, containing the list of result. Each entry of this list must have the same form as it had in the original JSON file.
620+
- The file must be named `index.js` and be located in the `odoc-support` URI.
621+
622+
In this driver, we use the minisearch javascript library. For more involved application, we could use `index.js` to call a server-side search engine via an API call.
623+
624+
```ocaml env=e1
625+
let js_index () =
626+
let index = Bos.OS.File.read Fpath.(v "html" / "index.json") |> get_ok in
627+
let minisearch = Bos.OS.File.read Fpath.(v "minisearch.js") |> get_ok in
628+
Bos.OS.File.writef Fpath.(v "html" / "odoc" / "index.js") {|%s
629+
let documents =
630+
%s
631+
;
632+
633+
let miniSearch = new MiniSearch({
634+
fields: ['id', 'doc'], // fields to index for full-text search
635+
storeFields: ['display'], // fields to return with search results
636+
extractField: (document, fieldName) => {
637+
if (fieldName === 'id') {
638+
return document.id.map(e => e.kind + "-" + e.name).join('.')
639+
}
640+
return document[fieldName]
641+
}
642+
})
643+
644+
645+
// Index all documents (except Type extensions which don't have a unique ID)
646+
miniSearch.addAll(documents.filter(entry => entry.extra.kind != "TypeExtension"));
647+
648+
onmessage = (m) => {
649+
let query = m.data;
650+
let result = miniSearch.search(query);
651+
postMessage(result.slice(0,200).map(a => a.display));
652+
}
653+
|} minisearch index
654+
```
655+
656+
593657
The following code executes all of the above, and we're done!
594658

595659
```ocaml env=e1
596660
let compiled = compile_all () in
597661
let linked = link_all compiled in
598-
generate_all linked
662+
let _ = generate_all linked in
663+
let () = index_generate () in js_index ()
599664
```
600665

601666
Let's see if there was any output from the `odoc` invocations:

doc/minisearch.js

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)