this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

odoc: link mode and jkind names when --mode-links is set

When mode_links is configured, mode names (local, portable, etc.)
and jkind names (value_or_null, float64, etc.) are rendered as
hyperlinks to URI#name. The @ and @@ keywords remain unlinked.

Implementation approach:
- Add O.mode tag in document layer (codefmt.ml) to mark mode/jkind
names with a "mode" source tag
- Update all mode rendering sites in generator.ml: arrow arg/ret
modes, Poly jkind quantifiers, type param jkinds, and value
modalities
- In HTML renderer, detect "mode" tags and emit <a> with
class="mode-link" when mode_links config is set, otherwise
render as <span class="mode">
- Refactor format_params to return Codefmt.t instead of string
to support tagged jkind names in type parameters

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

+71 -33
+2
odoc/src/document/codefmt.ml
··· 249 249 250 250 let keyword keyword ppf = pf ppf "@{<keyword>%s@}" keyword 251 251 252 + let mode mode ppf = pf ppf "@{<mode>%s@}" mode 253 + 252 254 module Infix = struct 253 255 let ( ++ ) = ( ++ ) 254 256 end
+2
odoc/src/document/codefmt.mli
··· 34 34 35 35 val keyword : string -> t 36 36 37 + val mode : string -> t 38 + 37 39 module Infix : sig 38 40 val ( ++ ) : t -> t -> t 39 41 end
+27 -26
odoc/src/document/generator.ml
··· 33 33 34 34 let enclose ~l ~r x = O.span (O.txt l ++ x ++ O.txt r) 35 35 36 + let mode_names ms = 37 + O.list ms ~sep:(O.txt " ") ~f:(fun m -> O.mode m) 38 + 36 39 let resolved p content = 37 40 let link = { Link.target = Internal (Resolved p); content; tooltip = None } in 38 41 O.elt [ inline @@ Link link ] ··· 445 448 | Var (s, Some jkind) -> 446 449 enclose ~l:"(" ~r:")" 447 450 (type_var (Syntax.Type.var_prefix ^ s) 448 - ++ O.txt " " ++ O.keyword ":" ++ O.txt " " ++ O.txt jkind) 451 + ++ O.txt " " ++ O.keyword ":" ++ O.txt " " ++ O.mode jkind) 449 452 | Any -> type_var Syntax.Type.any 450 453 | Alias (te, alias) -> 451 454 enclose_parens_if_needed ··· 456 459 | [] -> O.noop 457 460 | ms -> 458 461 O.txt " " ++ O.keyword "@" ++ O.txt " " 459 - ++ O.txt (String.concat ~sep:" " ms) 462 + ++ mode_names ms 460 463 in 461 464 let dst_needs_parens = ret_modes <> [] && (match dst with Arrow _ -> true | _ -> false) in 462 465 let dst_rendered = type_expr ~needs_parentheses:dst_needs_parens dst in ··· 464 467 | [] -> O.noop 465 468 | ms -> 466 469 O.txt " " ++ O.keyword "@" ++ O.txt " " 467 - ++ O.txt (String.concat ~sep:" " ms) 470 + ++ mode_names ms 468 471 in 469 472 let res = 470 473 O.span ··· 488 491 | [] -> O.noop 489 492 | ms -> 490 493 O.txt " " ++ O.keyword "@" ++ O.txt " " 491 - ++ O.txt (String.concat ~sep:" " ms) 494 + ++ mode_names ms 492 495 in 493 496 let dst_needs_parens = ret_modes <> [] && (match dst with Arrow _ -> true | _ -> false) in 494 497 let dst_rendered = type_expr ~needs_parentheses:dst_needs_parens dst in ··· 496 499 | [] -> O.noop 497 500 | ms -> 498 501 O.txt " " ++ O.keyword "@" ++ O.txt " " 499 - ++ O.txt (String.concat ~sep:" " ms) 502 + ++ mode_names ms 500 503 in 501 504 let res = 502 505 O.span ··· 520 523 (Link.from_path (path :> Paths.Path.t)) 521 524 | Poly (polyvars, t) -> 522 525 let render_var (name, jkind) = match jkind with 523 - | None -> "'" ^ name 524 - | Some jk -> "('" ^ name ^ " : " ^ jk ^ ")" 526 + | None -> O.txt ("'" ^ name) 527 + | Some jk -> O.txt ("('" ^ name ^ " : ") ++ O.mode jk ++ O.txt ")" 525 528 in 526 - O.txt (String.concat ~sep:" " (List.map render_var polyvars) ^ ". ") 529 + O.list polyvars ~sep:(O.txt " ") ~f:render_var ++ O.txt ". " 527 530 ++ type_expr t 528 531 | Quote t -> 529 532 O.span (O.txt "<[ " ++ O.box_hv (type_expr t) ++ O.txt " ]>") ··· 872 875 = 873 876 let desc = 874 877 match desc with 875 - | Odoc_model.Lang.TypeDecl.Any -> [ "_" ] 876 - | Var (s, None) -> [ "'"; s ] 877 - | Var (s, Some jkind) -> [ "("; "'"; s; " : "; jkind; ")" ] 878 + | Odoc_model.Lang.TypeDecl.Any -> O.txt "_" 879 + | Var (s, None) -> O.txt ("'" ^ s) 880 + | Var (s, Some jkind) -> 881 + O.txt ("('" ^ s ^ " : ") ++ O.mode jkind ++ O.txt ")" 878 882 in 879 883 let var_desc = 880 884 match variance with 881 885 | None -> desc 882 - | Some Odoc_model.Lang.TypeDecl.Pos -> "+" :: desc 883 - | Some Odoc_model.Lang.TypeDecl.Neg -> "-" :: desc 884 - | Some Odoc_model.Lang.TypeDecl.Bivariant -> "+" :: "-" :: desc 886 + | Some Odoc_model.Lang.TypeDecl.Pos -> O.txt "+" ++ desc 887 + | Some Odoc_model.Lang.TypeDecl.Neg -> O.txt "-" ++ desc 888 + | Some Odoc_model.Lang.TypeDecl.Bivariant -> O.txt "+" ++ O.txt "-" ++ desc 885 889 in 886 - let final = if injectivity then "!" :: var_desc else var_desc in 887 - String.concat ~sep:"" final 890 + if injectivity then O.txt "!" ++ var_desc else var_desc 888 891 in 889 - O.txt 890 - (match params with 891 - | [] -> "" 892 - | [ x ] -> format_param x |> Syntax.Type.handle_format_params 893 - | lst -> ( 894 - let params = String.concat ~sep:", " (List.map format_param lst) in 895 - (match delim with `parens -> "(" | `brackets -> "[") 896 - ^ params 897 - ^ match delim with `parens -> ")" | `brackets -> "]")) 892 + match params with 893 + | [] -> O.noop 894 + | [ x ] -> Syntax.Type.handle_format_params (format_param x) 895 + | lst -> 896 + O.txt (match delim with `parens -> "(" | `brackets -> "[") 897 + ++ O.list lst ~sep:(O.txt ", ") ~f:format_param 898 + ++ O.txt (match delim with `parens -> ")" | `brackets -> "]") 898 899 899 900 let format_constraints constraints = 900 901 O.list constraints ~f:(fun (t1, t2) -> ··· 1029 1030 ++ (match t.modalities with 1030 1031 | [] -> O.noop 1031 1032 | ms -> O.txt " " ++ O.keyword "@@" ++ O.txt " " 1032 - ++ O.txt (String.concat ~sep:" " ms)) 1033 + ++ mode_names ms) 1033 1034 ++ if semicolon then O.txt ";" else O.noop) 1034 1035 in 1035 1036 let attr = [ "value" ] @ extra_attr in
+1 -1
odoc/src/document/generator_signatures.ml
··· 26 26 27 27 val handle_substitution_params : text -> text -> text 28 28 29 - val handle_format_params : string -> string 29 + val handle_format_params : text -> text 30 30 31 31 val type_def_semicolon : bool 32 32
+1 -1
odoc/src/document/reason.ml
··· 22 22 23 23 let handle_substitution_params name args = name ++ args 24 24 25 - let handle_format_params p = "(" ^ p ^ ")" 25 + let handle_format_params p = O.txt "(" ++ p ++ O.txt ")" 26 26 27 27 let type_def_semicolon = true 28 28
+38 -5
odoc/src/html/generator.ml
··· 64 64 let inline_math (s : Math.t) = 65 65 Html.code ~a:[ Html.a_class [ "odoc-katex-math" ] ] [ Html.txt s ] 66 66 67 + let source_text_content (t : Source.t) = 68 + let buf = Buffer.create 16 in 69 + let rec token (x : Source.token) = 70 + match x with 71 + | Elt inl -> 72 + List.iter 73 + (fun (one : Inline.one) -> 74 + match one.desc with Inline.Text s -> Buffer.add_string buf s | _ -> ()) 75 + inl 76 + | Tag (_, l) -> List.iter token l 77 + in 78 + List.iter token t; 79 + Buffer.contents buf 80 + 67 81 let block_math (s : Math.t) = 68 82 Html.pre ~a:[ Html.a_class [ "odoc-katex-math"; "display" ] ] [ Html.txt s ] 69 83 ··· 78 92 [ Html.Unsafe.data content ] 79 93 | _ -> [] 80 94 81 - and source k ?a (t : Source.t) = 95 + and source k ?a ?mode_links (t : Source.t) = 82 96 let rec token (x : Source.token) = 83 97 match x with 84 98 | Elt i -> k i 85 99 | Tag (None, l) -> 86 100 let content = tokens l in 87 101 if content = [] then [] else [ Html.span content ] 102 + | Tag (Some "mode", l) -> ( 103 + match mode_links with 104 + | Some base_uri -> 105 + let name = source_text_content l in 106 + let href = base_uri ^ "#" ^ name in 107 + let content = tokens l in 108 + let link = 109 + Html.a 110 + ~a:[ Html.a_href href; Html.a_class [ "mode-link" ] ] 111 + (Html.totl (Html.toeltl content)) 112 + in 113 + Html.totl (Html.toeltl [ link ]) 114 + | None -> [ Html.span ~a:[ Html.a_class [ "mode" ] ] (tokens l) ]) 88 115 | Tag (Some s, l) -> [ Html.span ~a:[ Html.a_class [ s ] ] (tokens l) ] 89 116 and tokens t = List.concat_map token t in 90 117 match tokens t with [] -> [] | l -> [ Html.code ?a l ] ··· 151 178 [ Html.a ~a:(Html.a_href href :: a) content ] 152 179 | Link { target = Internal t; content; tooltip } -> 153 180 internallink ~config ~emph_level ~resolve ~a t content tooltip 154 - | Source c -> source (inline ~config ~emph_level ~resolve) ~a c 181 + | Source c -> 182 + source (inline ~config ~emph_level ~resolve) ~a 183 + ?mode_links:(Config.mode_links config) c 155 184 | Math s -> [ inline_math s ] 156 185 | Raw_markup r -> raw_markup r 157 186 in ··· 260 289 let extra_class = [ "language-" ^ lang_tag ] in 261 290 mk_block Html.div 262 291 ((mk_block ~extra_class Html.pre 263 - (source (inline ~config ~resolve) c)) 292 + (source (inline ~config ~resolve) 293 + ?mode_links:(Config.mode_links config) c)) 264 294 @ block ~config ~resolve output) 265 295 | Math s -> mk_block Html.div [ block_math s ] 266 296 | Audio (target, alt) -> ··· 361 391 | [] -> [] 362 392 | (Code _ | Alternative _) :: _ -> 363 393 let code, _, rest = take_code t in 364 - source (inline ~config ~resolve) code @ to_html rest 394 + source (inline ~config ~resolve) 395 + ?mode_links:(Config.mode_links config) code 396 + @ to_html rest 365 397 | Subpage subp :: _ -> subpage ~config ~resolve subp 366 398 | (Documented _ | Nested _) :: _ -> 367 399 let l, _, rest = take_descr t in ··· 441 473 in 442 474 let a = spec_class (attr @ extra_class) @ extra_attr in 443 475 Html.summary ~a @@ anchor_link @ link_to_source 444 - @ source (inline ~config ~resolve) summary 476 + @ source (inline ~config ~resolve) 477 + ?mode_links:(Config.mode_links config) summary 445 478 in 446 479 let inner = 447 480 [