diff --git a/src/core/link-to-dfn.js b/src/core/link-to-dfn.js index 651653cd08..4298e37cc3 100644 --- a/src/core/link-to-dfn.js +++ b/src/core/link-to-dfn.js @@ -126,6 +126,28 @@ function mapTitleToDfns() { elements: duplicates, }); } + // When a dfn uses `data-lt="For/term"` style (e.g. "script/src"), also + // register the short title ("src") under the for-context ("script") so that + // scoped links like [^ script/src ^] can resolve to it. + if (key.includes("/")) { + const lastSlash = key.lastIndexOf("/"); + const forContext = key.slice(0, lastSlash); + const shortTitle = key.slice(lastSlash + 1); + if (shortTitle) { + for (const [dfnFor, typeMap] of result) { + // Only promote dfns that are registered with an empty for-context — + // dfns with an explicit for-context are already scoped correctly. + if (dfnFor !== "") continue; + if (!titleToDfns.has(shortTitle)) { + titleToDfns.set(shortTitle, new Map()); + } + const shortTitleMap = titleToDfns.get(shortTitle); + if (!shortTitleMap.has(forContext)) { + shortTitleMap.set(forContext, new Map(typeMap)); + } + } + } + } } return titleToDfns; } diff --git a/tests/spec/core/link-to-dfn-spec.js b/tests/spec/core/link-to-dfn-spec.js index 36e02b9106..f0f3c74fe1 100644 --- a/tests/spec/core/link-to-dfn-spec.js +++ b/tests/spec/core/link-to-dfn-spec.js @@ -387,4 +387,30 @@ describe("Core — Link to definitions", () => { expect(codeElem.textContent).toEqual("MediaDevices"); expect(codeElem.querySelector("code")).toBeNull(); }); + + it("resolves scoped link using data-lt with slash notation", async () => { + // Regression test for https://github.com/speced/respec/issues/4133. + // Authors sometimes write as shorthand for + // defining "attr" in the context of "element". The link [^ element/attr ^] + // generates a scoped link (data-link-for="element", text="attr") and must + // resolve to the dfn even though data-dfn-for is absent on the dfn. + const body = ` +
+

+ + The src IDL attribute + +

+ +
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + + const a = doc.querySelector("#link-test a"); + expect(a).toBeTruthy(); + expect(a.hash).toBe("#dfn-src"); + expect(a.classList).toContain("internalDFN"); + expect(a.classList).not.toContain("respec-offending-element"); + }); });