-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmd-minutes-to-details.html
352 lines (294 loc) · 9.72 KB
/
md-minutes-to-details.html
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
<doctype html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
Markdown:
<br>
<textarea id='e_in' style='width: 100%; height: 50vh;'>
<!-- Output copied to clipboard! -->
# WGSL 2021-00-00 Minutes
### Example 1
* lorem
## H2
### [Example 2](example.com)
* ipsum
### Example [5](example.com/5) (stuff)
* dolor
## H2 Example [3](example.com/3)
* sit amet
</textarea>
<br>Override URL: <input type=url id=e_link_url style='width: 50%'></input>
<br>Override Title: <input type=text id=e_link_title style='width: 50%'></input>
<br><button id='e_extract'>Extract</button>
<hr>
<div id='e_out'></div>
<script>
'use strict';
function count_char_repeats(str, c) {
let i = 0;
while (str[i] == c) {
i += 1;
}
return i;
}
{
if (count_char_repeats('### hi', '#') != 3) throw 3;
}
function MdSection(title) {
this.title = title;
this.h_level = count_char_repeats(title || '', '#');
this.children = [];
this.add_child = child => {
child.parent = this;
this.children.push(child);
};
}
function md_to_tree(text) {
const lines = text.split('\n');
const root = new MdSection(null);
let cur_sec = root;
while (lines.length) {
const line = lines.shift();
const h_level = count_char_repeats(line, '#');
if (!h_level) {
cur_sec.children.push(line);
continue;
}
const new_sec = new MdSection(line);
const parent_h_level = new_sec.h_level - 1;
while (cur_sec.h_level > parent_h_level) {
cur_sec = cur_sec.parent;
}
//while (cur_sec.h_level < parent_h_level) {
// const dummy = new MdSection('#'.repeat(cur_sec.h_level + 1));
// dummy.title = undefined;
// cur_sec.add_child(dummy);
// cur_sec = dummy;
//}
cur_sec.add_child(new_sec);
cur_sec = new_sec;
}
return root;
}
function* range(n) {
for (let i = 0; i < n; i++) {
yield i;
}
}
function trimPrefix(str, prefix) {
if (str.startsWith(prefix)) {
str = str.slice(prefix.length);
}
return str;
}
function withoutPrefix(str, prefix) {
const ret = trimPrefix(str, prefix);
if (ret == str) throw {str, prefix};
return ret;
}
const RE_DATE = /\d\d\d\d-\d\d-\d\d/;
function find_date(str) {
const match = str.match(RE_DATE) || [];
return match;
}
if (find_date('# WGSL 2021-04-13 Minutes') != '2021-04-13') throw 'find_date';
const UNICODE_HYPHEN = '\u2010'; // Unicode Character 'HYPHEN' (U+2010)
const VERBOSE = true;
function canon_json_from(x) {
if (typeof(x) == 'object') {
const unsorted = x;
x = {};
for (const k of Object.keys(x).sort()) {
x[k] = unsorted[k];
}
}
return JSON.stringify(x);
}
function to_github_general_url_slug(text) {
let parts = text.split(/ +/);
parts = parts.map(s => {
s = [].filter.call(s, c => c.match(/[0-9A-Za-z-\u2010_()\/]/)); // Filter character set.
s = s.join('');
return s;
});
return parts.join('-');
}
function to_github_page_slug(text) {
let ret = text;
ret = ret.replaceAll('-', UNICODE_HYPHEN); // Yes, if you ask for a wiki page named 'A-Z', it gives you 'A\u2010Z'.
ret = to_github_general_url_slug(ret);
ret = ret.replaceAll(/[()\/]/g, '-');
console.log(`to_github_page_slug('${text}') -> '${ret}'`);
return ret;
}
function to_github_anchor_slug(text) {
let ret = text;
ret = ret.toLowerCase();
ret = to_github_general_url_slug(ret);
ret = ret.replaceAll(/[()\/]/g, '');
console.log(`to_github_anchor_slug('${text}') -> '${ret}'`);
return ret;
}
{
const title = '[wgsl] Proposal: Remove pointer out parameters from modf, frexp · Issue #1480 · gpuweb/gpuweb';
const expected = 'wgsl-proposal-remove-pointer-out-parameters-from-modf-frexp--issue-1480--gpuwebgpuweb';
const was = to_github_anchor_slug(title);
if (was != expected) throw was;
}
{
const title = 'Triage the issues without milestones (timebox 15m).';
const expected = 'triage-the-issues-without-milestones-timebox-15m';
const was = to_github_anchor_slug(title);
if (was != expected) throw was;
}
// https://github.com/gpuweb/gpuweb/wiki/WGSL-2024-03-12-Minutes
{
const title = 'WGSL 2024-03-12 Minutes';
const expected = 'WGSL-2024\u201003\u201012-Minutes';
const was = to_github_page_slug(title);
if (was != expected) throw was;
}
{
const title = 'Feature request: textureQueryLod equivalent · Issue #4180';
const expected = 'feature-request-texturequerylod-equivalent--issue-4180';
const was = to_github_anchor_slug(title);
if (was != expected) throw was;
}
const RE_URL = /\[(.*?)\]\(([^\)]*)\)/g;
function parse_title(title) {
if (!title.startsWith('#')) throw title;
const section_title = title.replace(/#+ /, '').trim();
const found = Array.from(section_title.matchAll(RE_URL));
console.log(...found);
const issue_url = (found[0] || [])[2];
const title_text = section_title.replaceAll(RE_URL, '$1');
console.log({section_title, issue_url, title_text});
const slug = to_github_anchor_slug(title_text);
return {issue_url, slug};
}
// https://github.com/gpuweb/gpuweb/wiki/GPU-Web-2024-03-06
{
const fn_test = () => parse_title('## Should we constrain the location of user input-output stage variables WGSL [#1962](https://github.com/gpuweb/gpuweb/issues/1962#issuecomment-1934840808) (PR [#4503](https://github.com/gpuweb/gpuweb/pull/4503))');
const expected = {
issue_url: 'https://github.com/gpuweb/gpuweb/issues/1962#issuecomment-1934840808',
slug: 'should-we-constrain-the-location-of-user-input-output-stage-variables-wgsl-1962-pr-4503',
};
const was = fn_test();
console.assert(canon_json_from(was) == canon_json_from(expected), {fn_test: fn_test.toString(), was, expected});
}
function section_desc(node) {
let title_info = {};
if (node.title) {
title_info = parse_title(node.title);
}
// -
// A section's `children` contain, zero or more string children, followed
// by zero or more subsection children.
let content = [];
let subsections = [];
for (const child of node.children) {
if (child.h_level) { // section
subsections.push(child);
} else {
if (subsections.length) throw node;
content.push(child);
}
}
content = content.join('\n');
content = content.replace(/^\n+/, '');
content = content.replace(/\n+$/, '');
// -
const ret = Object.assign({
content,
subsections,
}, title_info);
if (!ret.content) delete ret.content;
if (!ret.subsections.length) delete ret.subsections;
return ret;
}
// -
// Tests
{
const content = `\
* DM: Do we want to spend innovation budget on anonymous structs here?
* BC: Previously there were concerns about having to have a named struct to return here, but now with type inference, we could return anonymous structs here. Not necessarily proposing userland anonymous structs, and if we decided to name them in the future, we could name them.
* DM: Some complexity, new types for these builtins.`;
const h3_section = {
title: '### [[wgsl] Proposal: Remove pointer out parameters from modf, frexp · Issue #1480 · gpuweb/gpuweb](https://github.com/gpuweb/gpuweb/issues/1480)',
h_level: 3,
children: content.split('\n'),
};
const expected = {
issue_url: 'https://github.com/gpuweb/gpuweb/issues/1480',
slug: 'wgsl-proposal-remove-pointer-out-parameters-from-modf-frexp--issue-1480--gpuwebgpuweb',
content: content.trim(),
};
const was = section_desc(h3_section);
if (canon_json_from(expected) != canon_json_from(was)) throw {was, expected};
}
// -
function make_details(desc) {
return `\
<details><summary><a href="${e_link_url.value}#${desc.slug}">${e_link_title.value}</a></summary>
${desc.content}
</details>`;
}
function extract_minutes() {
const text = e_in.value;
const tree = md_to_tree(text);
console.log('tree', tree);
const first_titled = (() => {
for (const x of tree.children) {
if (x.title) return x;
}
throw tree.children;
})();
console.assert(first_titled.title.startsWith('# '));
const page_title = first_titled.title.slice(2);
const page_slug = to_github_page_slug(page_title);
const wiki_url = `https://github.com/gpuweb/gpuweb/wiki/${page_slug}`;
if (!e_link_url.value) {
e_link_url.value = wiki_url;
}
if (!e_link_title.value) {
e_link_title.value = page_title;
}
while (e_out.firstChild) {
e_out.removeChild(e_out.firstChild);
}
function per_section_node(cur) {
if (cur.h_level === undefined) return; // A string, not a section: Don't recurse.
const section = section_desc(cur);
console.log(section);
if (section.content) {
const details = make_details(section);
e_out.appendChild(document.createElement('br'));
const out = e_out.appendChild(document.createElement('div'));
let e_section_title;
if (section.issue_url) {
e_section_title = out.appendChild(document.createElement('a'));
e_section_title.href = section.issue_url;
} else {
e_section_title = out.appendChild(document.createElement('span'));
}
e_section_title.textContent = cur.title;
out.appendChild(document.createElement('br'));
const e_details = out.appendChild(document.createElement('pre'));
e_details.style.border = '1px solid black';
e_details.textContent = details;
}
// -
if (section.subsections) {
for (const sub of section.subsections) {
per_section_node(sub);
}
}
}
per_section_node(tree);
}
e_extract.addEventListener('click', extract_minutes, false);
</script>
</body>
</html>