|
| 1 | +// fsdocs puts a member's XML doc into its tooltip as raw text, tags included. |
| 2 | +// This renders the tags: the summary in its own colour, one line per parameter, the return value, and |
| 3 | +// <see cref="..."/> / <paramref name="..."/> as the name they refer to. |
| 4 | + |
| 5 | +const docTag = /<\/?(summary|param|returns|remarks|exception|typeparam|see|paramref|typeparamref|para|c|code)\b/; |
| 6 | + |
| 7 | +function refName(el) { |
| 8 | + const name = el.getAttribute("cref") || el.getAttribute("name") || el.getAttribute("langword") || el.textContent; |
| 9 | + return name.replace(/^[A-Z]:/, ""); |
| 10 | +} |
| 11 | + |
| 12 | +function inline(node, target) { |
| 13 | + for (const child of node.childNodes) { |
| 14 | + if (child.nodeType === Node.TEXT_NODE) { |
| 15 | + target.append(child.textContent.replace(/\s+/g, " ")); |
| 16 | + } else if (child.nodeType === Node.ELEMENT_NODE) { |
| 17 | + const tag = child.localName; |
| 18 | + if (tag === "see" || tag === "seealso" || tag === "paramref" || tag === "typeparamref" || tag === "c" || tag === "code") { |
| 19 | + const code = document.createElement("code"); |
| 20 | + code.textContent = child.textContent.trim() || refName(child); |
| 21 | + target.append(code); |
| 22 | + } else { |
| 23 | + inline(child, target); |
| 24 | + } |
| 25 | + } |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +function line(cls, label, el) { |
| 30 | + const body = document.createDocumentFragment(); |
| 31 | + inline(el, body); |
| 32 | + body.normalize(); |
| 33 | + if (body.firstChild?.nodeType === Node.TEXT_NODE) body.firstChild.textContent = body.firstChild.textContent.trimStart(); |
| 34 | + if (body.lastChild?.nodeType === Node.TEXT_NODE) body.lastChild.textContent = body.lastChild.textContent.trimEnd(); |
| 35 | + const div = document.createElement("div"); |
| 36 | + div.className = cls; |
| 37 | + if (label) { |
| 38 | + const name = document.createElement("span"); |
| 39 | + name.className = "fsdocs-tip-doc-name"; |
| 40 | + name.textContent = label; |
| 41 | + div.append(name, " "); |
| 42 | + } |
| 43 | + div.append(body); |
| 44 | + return div; |
| 45 | +} |
| 46 | + |
| 47 | +function render(text) { |
| 48 | + const xml = new DOMParser().parseFromString(`<doc>${text}</doc>`, "application/xml"); |
| 49 | + if (xml.getElementsByTagName("parsererror").length > 0) return null; |
| 50 | + const doc = document.createElement("div"); |
| 51 | + doc.className = "fsdocs-tip-doc"; |
| 52 | + for (const el of xml.documentElement.children) { |
| 53 | + switch (el.localName) { |
| 54 | + case "summary": doc.append(line("fsdocs-tip-doc-summary", null, el)); break; |
| 55 | + case "remarks": doc.append(line("fsdocs-tip-doc-remarks", null, el)); break; |
| 56 | + case "param": |
| 57 | + case "typeparam": doc.append(line("fsdocs-tip-doc-param", el.getAttribute("name") + ":", el)); break; |
| 58 | + case "returns": doc.append(line("fsdocs-tip-doc-param", "Returns:", el)); break; |
| 59 | + case "exception": doc.append(line("fsdocs-tip-doc-param", "Throws " + refName(el) + ":", el)); break; |
| 60 | + } |
| 61 | + } |
| 62 | + return doc.childNodes.length > 0 ? doc : null; |
| 63 | +} |
| 64 | + |
| 65 | +for (const em of document.querySelectorAll("div.fsdocs-tip > em")) { |
| 66 | + if (!docTag.test(em.textContent)) continue; |
| 67 | + const doc = render(em.textContent); |
| 68 | + if (doc) em.replaceWith(doc); |
| 69 | +} |
0 commit comments