Source file rfc_extension.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(** RFC extension for odoc.
Provides tags for linking to IETF RFCs:
- [@rfc 9110] - Link to RFC 9110
- [@rfc 9110 Section 5.5] - Link to RFC 9110 Section 5.5
- [@rfc 9110 section-5.5] - Link to RFC 9110 with anchor
The extension generates links to https://www.rfc-editor.org/rfc/rfcNNNN
*)
open Odoc_extension_api
let prefix = "rfc"
(** CSS styles for RFC references *)
let rfc_css = {|
/* RFC extension styles */
.rfc-reference {
font-family: monospace;
background: #f5f5f5;
padding: 0.1em 0.3em;
border-radius: 3px;
border: 1px solid #ddd;
}
.rfc-reference a {
text-decoration: none;
color: #0366d6;
}
.rfc-reference a:hover {
text-decoration: underline;
}
|}
(** Parse RFC reference from tag content.
Supports formats:
- "9110" -> RFC 9110
- "9110 Section 5.5" -> RFC 9110 Section 5.5
- "9110 section-5.5" -> RFC 9110 with anchor #section-5.5
- "RFC 9110" -> RFC 9110 (optional RFC prefix)
*)
let parse_rfc_reference content =
let text = String.trim (text_of_nestable_block_elements content) in
let text =
if String.length text > 4 &&
(String.sub text 0 4 = "RFC " || String.sub text 0 4 = "rfc ") then
String.sub text 4 (String.length text - 4)
else
text
in
match String.index_opt text ' ' with
| None ->
(text, None)
| Some i ->
let rfc_num = String.sub text 0 i in
let rest = String.trim (String.sub text (i + 1) (String.length text - i - 1)) in
if String.length rest > 8 &&
(String.sub rest 0 8 = "Section " || String.sub rest 0 8 = "section ") then
(rfc_num, Some (`Section (String.sub rest 8 (String.length rest - 8))))
else if String.length rest > 0 && rest.[0] = '#' then
(rfc_num, Some (`Anchor (String.sub rest 1 (String.length rest - 1))))
else if String.contains rest '-' then
(rfc_num, Some (`Anchor rest))
else
(rfc_num, Some (`Section rest))
(** Generate URL for RFC reference *)
let rfc_url rfc_num section =
let base = Printf.sprintf "https://www.rfc-editor.org/rfc/rfc%s" rfc_num in
match section with
| None -> base
| Some (`Anchor anchor) -> base ^ "#" ^ anchor
| Some (`Section sec) ->
let anchor = "section-" ^ (String.map (fun c -> if c = ' ' then '-' else c) sec) in
base ^ "#" ^ anchor
(** Generate display text for RFC reference *)
let rfc_display_text rfc_num section =
match section with
| None -> Printf.sprintf "RFC %s" rfc_num
| Some (`Anchor _) -> Printf.sprintf "RFC %s" rfc_num
| Some (`Section sec) -> Printf.sprintf "RFC %s Section %s" rfc_num sec
(** Document phase - generate RFC link *)
let to_document ~tag:_ content =
let rfc_num, section = parse_rfc_reference content in
let url = rfc_url rfc_num section in
let display = rfc_display_text rfc_num section in
let link_inline = Inline.[{
attr = [];
desc = Link {
target = External url;
content = [{ attr = []; desc = Text display }];
tooltip = Some (Printf.sprintf "IETF %s" display);
}
}] in
let content = Block.[{
attr = [ "rfc-reference" ];
desc = Inline link_inline
}] in
{
content;
overrides = [];
resources = [
Css_url "extensions/rfc.css";
];
assets = [];
}
let () =
Registry.register (module struct
let prefix = prefix
let to_document = to_document
end);
Registry.register_support_file ~prefix {
filename = "extensions/rfc.css";
content = rfc_css;
}