|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT license. |
| 3 | + |
| 4 | +const SPACE_STYLE = "whitespace-style"; |
| 5 | +const TAB_STYLE = "tab-style"; |
| 6 | +const STYLE_ID = "whitespaceStyle"; |
| 7 | + |
| 8 | +export function renderWhitespace(): void { |
| 9 | + const style: HTMLElement | null = document.getElementById(STYLE_ID); |
| 10 | + if (!style) { |
| 11 | + const styleElement: HTMLStyleElement = document.createElement("style"); |
| 12 | + styleElement.id = STYLE_ID; |
| 13 | + styleElement.textContent = ``; |
| 14 | + document.head.appendChild(styleElement); |
| 15 | + } |
| 16 | + const elements = document.querySelectorAll("code"); |
| 17 | + for (let i = 0; i < elements.length; i++) { |
| 18 | + const treeWalker: TreeWalker = document.createTreeWalker(elements[i], NodeFilter.SHOW_TEXT); |
| 19 | + const nodes: Node[] = []; |
| 20 | + while (treeWalker.nextNode()) { |
| 21 | + nodes.push(treeWalker.currentNode); |
| 22 | + } |
| 23 | + for (const node of nodes) { |
| 24 | + replace(node); |
| 25 | + } |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +function replace(node: Node): void { |
| 30 | + const textValue: string | null = node.nodeValue; |
| 31 | + if (!textValue) { |
| 32 | + return; |
| 33 | + } |
| 34 | + const parent: (Node & ParentNode) | null = node.parentNode; |
| 35 | + if (!parent) { |
| 36 | + return; |
| 37 | + } |
| 38 | + const tabs: string[] = textValue.split("\t"); |
| 39 | + const tabSpaces: string[][] = tabs.map(s => s.split(" ")); |
| 40 | + if (tabSpaces.length === 1 && tabSpaces[0].length === 1) { |
| 41 | + return; |
| 42 | + } |
| 43 | + for (let i = 0; i < tabSpaces.length; i++) { |
| 44 | + if (i > 0) { |
| 45 | + parent.insertBefore<HTMLSpanElement>(createTabElement(), node); |
| 46 | + } |
| 47 | + let spaceCount = 0; |
| 48 | + for (let j = 0; j < tabSpaces[i].length; j++) { |
| 49 | + if (tabSpaces[i][j] === "" && j !== tabSpaces[i].length - 1) { |
| 50 | + spaceCount = spaceCount + 1; |
| 51 | + continue; |
| 52 | + } |
| 53 | + if (spaceCount > 0) { |
| 54 | + parent.insertBefore<HTMLSpanElement>(createSpaceElement(spaceCount), node); |
| 55 | + } |
| 56 | + parent.insertBefore<Text>(document.createTextNode(tabSpaces[i][j]), node); |
| 57 | + spaceCount = 1; |
| 58 | + } |
| 59 | + } |
| 60 | + parent.removeChild(node); |
| 61 | +} |
| 62 | + |
| 63 | +function createSpaceElement(count: number): HTMLSpanElement { |
| 64 | + const node: HTMLSpanElement = document.createElement("span"); |
| 65 | + node.classList.add(SPACE_STYLE); |
| 66 | + node.textContent = " ".repeat(count); |
| 67 | + return node; |
| 68 | +} |
| 69 | + |
| 70 | +function createTabElement(): HTMLSpanElement { |
| 71 | + const node: HTMLSpanElement = document.createElement("span"); |
| 72 | + node.classList.add(TAB_STYLE); |
| 73 | + node.textContent = "\t"; |
| 74 | + return node; |
| 75 | +} |
0 commit comments