Skip to content

Commit 8079e59

Browse files
committed
fix: skip color preview for css variables
1 parent 2f68de5 commit 8079e59

File tree

2 files changed

+96
-5
lines changed

2 files changed

+96
-5
lines changed

src/codemirror/colorView.js

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,87 @@ const RGBG = new RegExp(colorRegex.anyGlobal);
1717

1818
const enumColorType = { hex: "hex", rgb: "rgb", hsl: "hsl", named: "named" };
1919

20+
const disallowedBoundaryBefore = new Set(["-", ".", "/", "#"]);
21+
const disallowedBoundaryAfter = new Set(["-", ".", "/"]);
22+
const ignoredLeadingWords = new Set(["url"]);
23+
24+
function isWhitespace(char) {
25+
return (
26+
char === " " ||
27+
char === "\t" ||
28+
char === "\n" ||
29+
char === "\r" ||
30+
char === "\f"
31+
);
32+
}
33+
34+
function isAlpha(char) {
35+
if (!char) return false;
36+
const code = char.charCodeAt(0);
37+
return (
38+
(code >= 65 && code <= 90) || // A-Z
39+
(code >= 97 && code <= 122)
40+
);
41+
}
42+
43+
function charAt(doc, index) {
44+
if (index < 0 || index >= doc.length) return "";
45+
return doc.sliceString(index, index + 1);
46+
}
47+
48+
function findPrevNonWhitespace(doc, index) {
49+
for (let i = index - 1; i >= 0; i--) {
50+
if (!isWhitespace(charAt(doc, i))) return i;
51+
}
52+
return -1;
53+
}
54+
55+
function findNextNonWhitespace(doc, index) {
56+
for (let i = index; i < doc.length; i++) {
57+
if (!isWhitespace(charAt(doc, i))) return i;
58+
}
59+
return doc.length;
60+
}
61+
62+
function readWordBefore(doc, index) {
63+
let pos = index;
64+
while (pos >= 0 && isWhitespace(charAt(doc, pos))) pos--;
65+
if (pos < 0) return "";
66+
if (charAt(doc, pos) === "(") {
67+
pos--;
68+
}
69+
while (pos >= 0 && isWhitespace(charAt(doc, pos))) pos--;
70+
let end = pos;
71+
while (pos >= 0 && isAlpha(charAt(doc, pos))) pos--;
72+
const start = pos + 1;
73+
if (end < start) return "";
74+
return doc.sliceString(start, end + 1).toLowerCase();
75+
}
76+
77+
function shouldRenderColor(doc, start, end) {
78+
const immediatePrev = charAt(doc, start - 1);
79+
if (disallowedBoundaryBefore.has(immediatePrev)) return false;
80+
81+
const immediateNext = charAt(doc, end);
82+
if (disallowedBoundaryAfter.has(immediateNext)) return false;
83+
84+
const prevNonWhitespaceIndex = findPrevNonWhitespace(doc, start);
85+
if (prevNonWhitespaceIndex !== -1) {
86+
const prevNonWhitespaceChar = charAt(doc, prevNonWhitespaceIndex);
87+
if (disallowedBoundaryBefore.has(prevNonWhitespaceChar)) return false;
88+
const prevWord = readWordBefore(doc, prevNonWhitespaceIndex);
89+
if (ignoredLeadingWords.has(prevWord)) return false;
90+
}
91+
92+
const nextNonWhitespaceIndex = findNextNonWhitespace(doc, end);
93+
if (nextNonWhitespaceIndex < doc.length) {
94+
const nextNonWhitespaceChar = charAt(doc, nextNonWhitespaceIndex);
95+
if (disallowedBoundaryAfter.has(nextNonWhitespaceChar)) return false;
96+
}
97+
98+
return true;
99+
}
100+
20101
class ColorWidget extends WidgetType {
21102
constructor({ color, colorRaw, ...state }) {
22103
super();
@@ -59,14 +140,16 @@ class ColorWidget extends WidgetType {
59140
function colorDecorations(view) {
60141
const deco = [];
61142
const ranges = view.visibleRanges;
143+
const doc = view.state.doc;
62144
for (const { from, to } of ranges) {
63-
const text = view.state.doc.sliceString(from, to);
145+
const text = doc.sliceString(from, to);
64146
// Any color using global matcher from utils (captures named/rgb/rgba/hsl/hsla/hex)
65147
RGBG.lastIndex = 0;
66148
for (let m; (m = RGBG.exec(text)); ) {
67149
const raw = m[2];
68150
const start = from + m.index + m[1].length;
69151
const end = start + raw.length;
152+
if (!shouldRenderColor(doc, start, end)) continue;
70153
const c = color(raw);
71154
const colorHex = c.hex.toString(false);
72155
deco.push(

src/lib/editorManager.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,6 @@ async function EditorManager($header, $body) {
10961096
// TODO: Implement change annotation event for CodeMirror
10971097
// editor.on("changeAnnotation", toggleProblemButton);
10981098

1099-
11001099
// TODO: Implement resize event for CodeMirror
11011100
// editor.renderer.on("resize", () => {
11021101
// $vScrollbar.resize($vScrollbar.visible);
@@ -1209,7 +1208,10 @@ async function EditorManager($header, $body) {
12091208
const scroller = editor?.scrollDOM;
12101209
if (!scroller) return;
12111210
const normalized = clamp01(value);
1212-
const maxScroll = Math.max(scroller.scrollHeight - scroller.clientHeight, 0);
1211+
const maxScroll = Math.max(
1212+
scroller.scrollHeight - scroller.clientHeight,
1213+
0,
1214+
);
12131215
preventScrollbarV = true;
12141216
scroller.scrollTop = normalized * maxScroll;
12151217
lastScrollTop = scroller.scrollTop;
@@ -1295,7 +1297,10 @@ async function EditorManager($header, $body) {
12951297
if (preventScrollbarV) return;
12961298
const scroller = editor?.scrollDOM;
12971299
if (!scroller) return;
1298-
const maxScroll = Math.max(scroller.scrollHeight - scroller.clientHeight, 0);
1300+
const maxScroll = Math.max(
1301+
scroller.scrollHeight - scroller.clientHeight,
1302+
0,
1303+
);
12991304
if (maxScroll <= 0) {
13001305
lastScrollTop = 0;
13011306
$vScrollbar.value = 0;
@@ -1315,7 +1320,10 @@ async function EditorManager($header, $body) {
13151320
function onscrolltop() {
13161321
const scroller = editor?.scrollDOM;
13171322
if (!scroller) return;
1318-
const maxScroll = Math.max(scroller.scrollHeight - scroller.clientHeight, 0);
1323+
const maxScroll = Math.max(
1324+
scroller.scrollHeight - scroller.clientHeight,
1325+
0,
1326+
);
13191327
if (maxScroll <= 0) {
13201328
$vScrollbar.hide();
13211329
lastScrollTop = 0;

0 commit comments

Comments
 (0)