Skip to content

Commit f987b50

Browse files
authored
refactor: switch to lezer highlight (#4347)
We already use lezer code highlighting in codemirror. Here I replaced prism.js with lezer for css preview as well. Also switched theme in code editor to the same solarized light extracted from thememirror.net. Html highlighting is improved a little. ![Screenshot 2024-10-27 at 13 23 42](https://github.com/user-attachments/assets/2fc42a38-456e-4c8c-8f05-ea767c9e1a13)
1 parent 65b32e8 commit f987b50

File tree

6 files changed

+95
-57
lines changed

6 files changed

+95
-57
lines changed

apps/builder/app/builder/features/navigator/css-preview.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import {
1717
import type { StyleMap, StyleProperty } from "@webstudio-is/css-engine";
1818
import { CollapsibleSection } from "~/builder/shared/collapsible-section";
1919
import { useMemo } from "react";
20-
import Prism from "prismjs";
2120
import { captureError } from "@webstudio-is/error-utils";
21+
import { highlightCss } from "~/builder/shared/code-highlight";
2222

2323
const preStyle = css(textVariants.mono, {
2424
margin: 0,
@@ -92,7 +92,7 @@ const useHighlightedCss = () => {
9292
return;
9393
}
9494
const cssText = getCssText(currentStyle);
95-
return Prism.highlight(cssText, Prism.languages.css, "css");
95+
return highlightCss(cssText);
9696
}, [currentStyle]);
9797
};
9898

@@ -108,7 +108,6 @@ export const CssPreview = () => {
108108
<ScrollArea css={{ padding: theme.panel.padding }}>
109109
<pre tabIndex={0} className={preStyle()}>
110110
<div
111-
className="language-css"
112111
style={{ whiteSpace: "break-spaces" }}
113112
dangerouslySetInnerHTML={{ __html: code }}
114113
></div>

apps/builder/app/builder/shared/code-editor-base.tsx

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ import {
2626
historyKeymap,
2727
indentWithTab,
2828
} from "@codemirror/commands";
29-
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
30-
import { tags } from "@lezer/highlight";
29+
import { syntaxHighlighting } from "@codemirror/language";
3130
import {
3231
theme,
3332
textVariants,
@@ -44,6 +43,7 @@ import {
4443
rawTheme,
4544
} from "@webstudio-is/design-system";
4645
import { CrossIcon, MaximizeIcon, MinimizeIcon } from "@webstudio-is/icons";
46+
import { solarizedLight } from "./code-highlight";
4747

4848
// This undocumented flag is required to keep contenteditable fields editable after the first activation of EditorView.
4949
// To reproduce the issue, open any Binding dialog and then try to edit a Navigation Item in the Navigation menu.
@@ -118,39 +118,6 @@ const editorContentStyle = css({
118118
},
119119
});
120120

121-
// https://thememirror.net/clouds
122-
const highlightStyle = HighlightStyle.define([
123-
// darker comment variant from https://github.com/vadimdemedes/thememirror/blob/main/source/themes/ayu-light.ts#L17-L20
124-
{
125-
tag: [tags.comment, tags.annotation],
126-
color: "#787b8099",
127-
},
128-
{
129-
tag: [tags.string, tags.special(tags.brace), tags.regexp, tags.url],
130-
color: "#5D90CD",
131-
},
132-
{
133-
tag: [tags.number, tags.bool, tags.null],
134-
color: "#46A609",
135-
},
136-
{
137-
tag: tags.keyword,
138-
color: "#AF956F",
139-
},
140-
{
141-
tag: [tags.definitionKeyword, tags.modifier],
142-
color: "#C52727",
143-
},
144-
{
145-
tag: [tags.angleBracket, tags.tagName, tags.attributeName, tags.separator],
146-
color: "#606060",
147-
},
148-
{
149-
tag: tags.self,
150-
color: "#000",
151-
},
152-
]);
153-
154121
const autocompletionTooltipTheme = EditorView.theme({
155122
".cm-tooltip.cm-tooltip-autocomplete": {
156123
...textVariants.mono,
@@ -262,7 +229,7 @@ export const EditorContent = ({
262229
history(),
263230
drawSelection(),
264231
dropCursor(),
265-
syntaxHighlighting(highlightStyle, { fallback: true }),
232+
syntaxHighlighting(solarizedLight, { fallback: true }),
266233
keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),
267234
EditorView.lineWrapping,
268235
EditorView.editable.of(readOnly === false),
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { HighlightStyle } from "@codemirror/language";
2+
import { highlightCode, tags } from "@lezer/highlight";
3+
import { parser } from "@lezer/css";
4+
5+
// inspired by https://thememirror.net/solarized-light
6+
export const solarizedLight = HighlightStyle.define([
7+
{
8+
tag: tags.comment,
9+
color: "#93A1A1",
10+
},
11+
{
12+
tag: tags.string,
13+
color: "#2AA198",
14+
},
15+
{
16+
tag: tags.regexp,
17+
color: "#D30102",
18+
},
19+
{
20+
tag: tags.number,
21+
color: "#D33682",
22+
},
23+
{
24+
tag: tags.variableName,
25+
color: "#268BD2",
26+
},
27+
{
28+
tag: [tags.keyword, tags.operator, tags.punctuation],
29+
color: "#859900",
30+
},
31+
{
32+
tag: [tags.definitionKeyword, tags.modifier],
33+
color: "#073642",
34+
},
35+
{
36+
tag: [tags.self, tags.definition(tags.propertyName)],
37+
color: "#268BD2",
38+
},
39+
{
40+
tag: tags.function(tags.variableName),
41+
color: "#268BD2",
42+
},
43+
{
44+
tag: [tags.bool, tags.null],
45+
color: "#B58900",
46+
},
47+
{
48+
tag: tags.tagName,
49+
color: "#268BD2",
50+
},
51+
{
52+
tag: tags.angleBracket,
53+
color: "#93A1A1",
54+
},
55+
{
56+
tag: tags.attributeName,
57+
color: "#93A1A1",
58+
},
59+
{
60+
tag: tags.typeName,
61+
color: "#859900",
62+
},
63+
]);
64+
65+
export const highlightCss = (code: string) => {
66+
const styles = solarizedLight.module?.getRules();
67+
// generated classes are scoped to parent
68+
let highlightedCode = `<style>@scope {${styles}}</style>`;
69+
highlightCode(
70+
code,
71+
parser.parse(code),
72+
solarizedLight,
73+
(text, classes) => {
74+
if (classes) {
75+
highlightedCode += `<span class="${classes}">${text}</span>`;
76+
} else {
77+
highlightedCode += text;
78+
}
79+
},
80+
() => {
81+
highlightedCode += "\n";
82+
}
83+
);
84+
return highlightedCode;
85+
};

apps/builder/app/routes/_ui.(builder).tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import { dashboardPath, isBuilder, isDashboard } from "~/shared/router-utils";
2626
import env from "~/env/env.server";
2727

2828
import builderStyles from "~/builder/builder.css?url";
29-
import prismStyles from "prismjs/themes/prism-solarizedlight.min.css?url";
3029
import { ClientOnly } from "~/shared/client-only";
3130
import { parseBuilderUrl } from "@webstudio-is/http-client";
3231
import { preventCrossOriginCookie } from "~/services/no-cross-origin-cookie";
@@ -40,10 +39,7 @@ import { loader as authWsLoader } from "./auth.ws";
4039
export { ErrorBoundary } from "~/shared/error/error-boundary";
4140

4241
export const links = () => {
43-
return [
44-
{ rel: "stylesheet", href: builderStyles },
45-
{ rel: "stylesheet", href: prismStyles },
46-
];
42+
return [{ rel: "stylesheet", href: builderStyles }];
4743
};
4844

4945
export const meta: MetaFunction<typeof loader> = ({ data }) => {

apps/builder/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@lexical/selection": "^0.16.0",
3838
"@lexical/utils": "^0.16.0",
3939
"@lezer/common": "^1.2.2",
40+
"@lezer/css": "^1.1.9",
4041
"@lezer/highlight": "^1.2.1",
4142
"@nanostores/react": "^0.7.1",
4243
"@radix-ui/react-select": "^2.1.2",
@@ -98,7 +99,6 @@
9899
"nanostores": "^0.9.3",
99100
"picocolors": "^1.1.0",
100101
"pretty-bytes": "^6.1.1",
101-
"prismjs": "^1.29.0",
102102
"react": "18.3.0-canary-14898b6a9-20240318",
103103
"react-colorful": "^5.6.1",
104104
"react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -129,7 +129,6 @@
129129
"@types/cookie": "^0.6.0",
130130
"@types/debug": "^4.1.12",
131131
"@types/dom-navigation": "^1.0.3",
132-
"@types/prismjs": "^1.26.0",
133132
"@types/react": "^18.2.70",
134133
"@types/react-dom": "^18.2.25",
135134
"@webstudio-is/jest-config": "workspace:*",

pnpm-lock.yaml

Lines changed: 3 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)