Skip to content

Commit 5ff315e

Browse files
authored
feat: Render whitespace and tab in formatter setting editor (#624)
* render whitespace and tab
1 parent cf1d120 commit 5ff315e

5 files changed

Lines changed: 134 additions & 2 deletions

File tree

1012 Bytes
Binary file not shown.

src/formatter-settings/assets/features/formatterSettings/FormatterSettingView.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import React from "react";
4+
import React, { useEffect } from "react";
55
import { Col, Container, Nav, Row } from "react-bootstrap";
66
import { useSelector, useDispatch } from "react-redux";
77
import { Dispatch } from "@reduxjs/toolkit";
88
import { changeActiveCategory } from "./formatterSettingViewSlice";
99
import { highlight } from "./components/Highlight";
1010
import { Category } from "../../../types";
1111
import Setting from "./components/Setting";
12+
import { renderWhitespace } from "../../whitespace";
1213

1314
const FormatterSettingsView = (): JSX.Element => {
1415
const activeCategory: Category = useSelector((state: any) => state.formatterSettings.activeCategory);
@@ -62,6 +63,10 @@ const FormatterSettingsView = (): JSX.Element => {
6263
</Nav>
6364
);
6465

66+
useEffect(() => {
67+
renderWhitespace();
68+
}, [contentText]);
69+
6570
return (
6671
<Container className="root d-flex flex-column">
6772
<Row>

src/formatter-settings/assets/style.scss

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,53 @@ code.hljs {
212212
}
213213
}
214214

215+
@font-face {
216+
font-family: SpaceDiff;
217+
src: url("./SpaceDiff.woff2");
218+
}
219+
220+
body.vscode-dark {
221+
.whitespace-style {
222+
color: rgba(212, 212, 212, 0.3);
223+
}
224+
.tab-style {
225+
color: rgba(212, 212, 212, 0.3);
226+
}
227+
}
228+
229+
body.vscode-light {
230+
.whitespace-style {
231+
color: rgba(0, 0, 0, 0.3);
232+
}
233+
.tab-style {
234+
color: rgba(0, 0, 0, 0.3);
235+
}
236+
}
237+
238+
body.vscode-high-contrast {
239+
.whitespace-style {
240+
color: rgba(255, 255, 255, 0.3);
241+
}
242+
.tab-style {
243+
color: rgba(255, 255, 255, 0.3);
244+
}
245+
}
246+
247+
.whitespace-style {
248+
font-style: normal;
249+
font-family: SpaceDiff;
250+
}
251+
252+
.tab-style {
253+
font-style: normal;
254+
}
255+
256+
.tab-style::before {
257+
content: "";
258+
position: relative;
259+
width: 0px;
260+
height: 0px;
261+
display: inline-block;
262+
}
263+
215264
@import "../../assets/vscode.scss";
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
}

webpack.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ module.exports = function (env, argv) {
5151
}, {
5252
loader: 'css-loader'
5353
}]
54-
}
54+
}, {
55+
test: /\.woff2$/,
56+
loader: 'url-loader',
57+
},
5558
]
5659
},
5760
output: {

0 commit comments

Comments
 (0)