Skip to content

Commit 94ade7e

Browse files
committed
Use data-x attribute as local-lt to make more links work
This ends up quite noisy in the output and can probably be minified somehow. It does link more things together however.
1 parent 330630b commit 94ade7e

File tree

1 file changed

+105
-35
lines changed

1 file changed

+105
-35
lines changed

wattsi2bikeshed.js

Lines changed: 105 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,40 @@ function hasMarkup(text) {
3939

4040
// Get the "topic" for cross-references like Wattsi:
4141
// https://github.com/whatwg/wattsi/blob/b9c28036a2a174f7f87315164f001120596a95f1/src/wattsi.pas#L882-L894
42-
function getTopic(elem) {
42+
//
43+
// Also return whether it came from a data-x attribute or text content, so that
44+
// data-x attribute values can be detected and processed further.
45+
function getTopicAndSource(elem) {
4346
let result;
47+
let source;
4448
while (true) {
4549
if (elem.hasAttribute(kCrossRefAttribute)) {
4650
result = elem.getAttribute(kCrossRefAttribute);
51+
source = kCrossRefAttribute;
4752
break;
4853
} else if (isElement(elem.firstChild) && elem.firstChild === elem.lastChild) {
4954
elem = elem.firstChild;
5055
continue;
5156
} else {
5257
result = elem.textContent;
58+
source = 'textContent';
5359
break;
5460
}
5561
}
5662
// This matches Wattsi's MungeStringToTopic in spirit,
5763
// but perhaps not in every detail:
58-
return result
59-
.replaceAll('#', '')
60-
.replaceAll(/\s+/g, ' ')
61-
.toLowerCase()
62-
.trim();
64+
return [
65+
result
66+
.replaceAll('#', '')
67+
.replaceAll(/\s+/g, ' ')
68+
.toLowerCase()
69+
.trim(),
70+
source
71+
];
72+
}
73+
74+
function getTopic(elem) {
75+
return getTopicAndSource(elem)[0];
6376
}
6477

6578
// Convert a topic to an ID like Wattsi:
@@ -73,60 +86,90 @@ function getId(topic) {
7386

7487
// Get the linking text like Bikeshed:
7588
// https://github.com/speced/bikeshed/blob/50d0ec772915adcd5cec0c2989a27fa761d70e71/bikeshed/h/dom.py#L174-L201
76-
function getBikeshedLinkTexts(elem) {
89+
function getBikeshedLinkTextSet(elem) {
90+
const texts = new Set();
91+
7792
const dataLt = elem.getAttribute('data-lt');
7893
if (dataLt === '') {
79-
return [];
94+
return texts;
8095
}
8196

82-
let texts = [];
97+
const add = (x) => texts.add(x.trim().replaceAll(/\s+/g, ' '));
98+
8399
if (dataLt) {
84100
// TODO: what's the `rawText in ["|", "||", "|||"]` condition for?
85-
texts = dataLt.split('|');
101+
dataLt.split('|').map(add);
86102
} else {
87103
switch (elem.localName) {
88104
case 'dfn':
89105
case 'a':
90-
texts = [elem.textContent];
106+
add(elem.textContent);
91107
break;
92108
case 'h2':
93109
case 'h3':
94110
case 'h4':
95111
case 'h5':
96112
case 'h6':
97-
texts = [(elem.querySelector('.content') ?? elem).textContent];
113+
add((elem.querySelector('.content') ?? elem).textContent);
98114
break;
99115
}
100116
}
101117

102-
return texts.map((x) => x.trim().replaceAll(/\s+/g, ' '));
118+
const dataLocalLt = elem.getAttribute('data-local-lt');
119+
if (dataLocalLt) {
120+
if (dataLocalLt.includes('|')) {
121+
console.warn('Ignoring data-local-lt value containing |:', dataLocalLt);
122+
} else {
123+
add(dataLocalLt);
124+
}
125+
}
126+
127+
return texts;
128+
}
129+
130+
// Get the *first* linking text like Bikeshed:
131+
// https://github.com/speced/bikeshed/blob/50d0ec772915adcd5cec0c2989a27fa761d70e71/bikeshed/h/dom.py#L215-L220
132+
function getBikeshedLinkText(elem) {
133+
for (const text of getBikeshedLinkTextSet(elem)) {
134+
return text;
135+
}
136+
return null;
103137
}
104138

105139
// Add for and lt to ensure that Bikeshed will link the <a> to the right <dfn>.
106-
function ensureLink(a, dfn) {
140+
function ensureLink(a, dfn, dfnLtCounts) {
107141
if (dfn.hasAttribute('for')) {
108142
a.setAttribute('for', dfn.getAttribute('for'));
109143
// TODO: don't add when it's already unambiguous.
110144
}
111145

112-
const dfnLts = getBikeshedLinkTexts(dfn);
113-
if (dfnLts.length === 0) {
146+
const dfnLts = getBikeshedLinkTextSet(dfn);
147+
if (dfnLts.size === 0) {
114148
console.warn('No linking text for', dfn.outerHTML);
115149
return;
116150
}
117-
const aLts = getBikeshedLinkTexts(a);
118-
if (aLts.length !== 1) {
119-
console.warn('Zero or too many linking texts for', a.outerHTML);
151+
const aLt = getBikeshedLinkText(a);
152+
if (!aLt) {
153+
console.warn('No linking text for', a.outerHTML);
154+
return;
155+
}
156+
157+
if (a.hasAttribute('for')) {
158+
// TODO: look in dfnLts when that tracks <dfn for>
159+
return;
120160
}
121-
if (!dfnLts.some((lt) => lt === aLts[0])) {
122-
// console.log('Fixing link from', a.outerHTML, 'to', dfn.outerHTML, 'with lt');
123-
// Note: data-lt is rewritten to lt later. It would also work to remove
124-
// any data-lt attribute here and just add lt.
125-
a.setAttribute('data-lt', dfnLts[0]);
161+
162+
for (const lt of dfnLts) {
163+
if (dfnLtCounts.get(lt) === 1) {
164+
// This is a unique linking text.
165+
// Note: data-lt is rewritten to lt later. It would also work to remove
166+
// any data-lt attribute here and just add lt.
167+
a.setAttribute('data-lt', lt);
168+
return;
169+
}
126170
}
127171

128-
// TODO: check if Bikeshed would now find the right <dfn> and if not
129-
// add additional attributes to make it so.
172+
// TODO: handle cases that end up here.
130173
}
131174

132175
function convert(infile, outfile) {
@@ -147,28 +190,51 @@ function convert(infile, outfile) {
147190
// https://github.com/whatwg/wattsi/blob/b9c28036a2a174f7f87315164f001120596a95f1/src/wattsi.pas#L735-L759
148191

149192
// Scan all definitions
150-
const crossRefs = new Map();
193+
const crossRefs = new Map(); // map from Wattsi topic to <dfn>
194+
const dfnLtCounts = new Map(); // map from Bikeshed link text to number of uses in <dfn>
151195
for (const dfn of document.querySelectorAll('dfn')) {
152196
if (dfn.getAttribute(kCrossRefAttribute) === '') {
153197
continue;
154198
}
155-
const topic = getTopic(dfn);
199+
const [topic, source] = getTopicAndSource(dfn);
156200
if (crossRefs.has(topic)) {
157201
console.warn('Duplicate <dfn> topic:', topic);
158202
}
159203
crossRefs.set(topic, dfn);
160204

161205
if (!dfn.hasAttribute('id')) {
206+
// TODO: avoid if Bikeshed would generate the same ID
162207
dfn.setAttribute('id', getId(topic));
163208
}
164209

210+
const lts = getBikeshedLinkTextSet(dfn);
211+
165212
// Remove "new" from the linking text of constructors.
166213
if (dfn.hasAttribute('constructor') && !dfn.hasAttribute('data-lt')) {
167-
const lt = getBikeshedLinkTexts(dfn)[0];
168-
if (lt?.startsWith('new ')) {
169-
dfn.setAttribute('data-lt', lt.substring(4));
214+
for (const lt of lts) {
215+
if (lt.startsWith('new ')) {
216+
dfn.setAttribute('data-lt', lt.substring(4));
217+
break;
218+
}
170219
}
171220
}
221+
222+
// Put data-x values into local-lt to enable disambiguating <dfn>s
223+
// with the same linking text, but only if it's not already in lts.
224+
if (source === kCrossRefAttribute && !lts.has(topic)) {
225+
dfn.setAttribute('data-local-lt', topic);
226+
lts.add(topic); // equivalent to calling getBikeshedLinkTextSet(dfn) again
227+
}
228+
229+
// Count uses of each Bikeshed linking text
230+
if (dfn.hasAttribute('for')) {
231+
// TODO: track <dfn for> as well
232+
continue;
233+
}
234+
for (const lt of lts) {
235+
const count = (dfnLtCounts.get(lt) ?? 0) + 1
236+
dfnLtCounts.set(lt, count);
237+
}
172238
}
173239

174240
// Track used <dfn>s in order to identify the unused ones.
@@ -264,7 +330,7 @@ function convert(infile, outfile) {
264330
}
265331
span.replaceWith(a);
266332

267-
ensureLink(a, dfn);
333+
ensureLink(a, dfn, dfnLtCounts);
268334
usedDfns.add(dfn);
269335
}
270336

@@ -287,7 +353,7 @@ function convert(infile, outfile) {
287353
i.parentNode.insertBefore(a, i);
288354
a.appendChild(i);
289355

290-
ensureLink(a, dfn);
356+
ensureLink(a, dfn, dfnLtCounts);
291357
usedDfns.add(dfn);
292358
}
293359

@@ -359,15 +425,19 @@ function convert(infile, outfile) {
359425
code.replaceWith(a);
360426
a.appendChild(code);
361427

362-
ensureLink(a, dfn);
428+
ensureLink(a, dfn, dfnLtCounts);
363429
usedDfns.add(dfn);
364430
}
365431

366-
// Rewrite data-lt to lt.
432+
// Rewrite data-lt to lt and data-local-lt to local-lt.
367433
for (const elem of document.querySelectorAll('[data-lt]')) {
368434
elem.setAttribute('lt', elem.getAttribute('data-lt'));
369435
elem.removeAttribute('data-lt');
370436
}
437+
for (const elem of document.querySelectorAll('[data-local-lt]')) {
438+
elem.setAttribute('local-lt', elem.getAttribute('data-local-lt'));
439+
elem.removeAttribute('data-local-lt');
440+
}
371441

372442
for (const elem of document.querySelectorAll('[data-x]')) {
373443
elem.removeAttribute(kCrossRefAttribute);

0 commit comments

Comments
 (0)