Skip to content

Commit 2c61dae

Browse files
authored
Merge pull request #60 from TheDeveloperDen/feature/syntax-hightlighting-and-open-lang-supp
feat: add syntax highlighting to fe and arbitrary lang support to be
2 parents 32d5b89 + ac59f53 commit 2c61dae

File tree

11 files changed

+904
-392
lines changed

11 files changed

+904
-392
lines changed

frontend/package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,37 @@
3030
},
3131
"dependencies": {
3232
"@babel/runtime": "^7.28.4",
33+
"@cmshiki/shiki": "^0.2.0",
34+
"@codemirror/lang-angular": "^0.1.4",
3335
"@codemirror/lang-cpp": "^6.0.3",
3436
"@codemirror/lang-css": "^6.3.1",
37+
"@codemirror/lang-go": "^6.0.1",
3538
"@codemirror/lang-html": "^6.4.11",
3639
"@codemirror/lang-java": "^6.0.2",
3740
"@codemirror/lang-javascript": "^6.2.4",
3841
"@codemirror/lang-json": "^6.0.2",
42+
"@codemirror/lang-less": "^6.0.2",
3943
"@codemirror/lang-markdown": "^6.5.0",
44+
"@codemirror/lang-php": "^6.0.2",
4045
"@codemirror/lang-python": "^6.2.1",
46+
"@codemirror/lang-rust": "^6.0.2",
47+
"@codemirror/lang-sass": "^6.0.2",
4148
"@codemirror/lang-sql": "^6.10.0",
49+
"@codemirror/lang-xml": "^6.1.0",
4250
"@codemirror/lang-yaml": "^6.1.2",
4351
"@codemirror/language": "^6.12.1",
4452
"@codemirror/state": "^6.5.3",
4553
"@codemirror/view": "^6.39.8",
4654
"@ethercorps/sveltekit-og": "^4.2.1",
55+
"@fsegurai/codemirror-theme-abyss": "^6.2.3",
56+
"@fsegurai/codemirror-theme-monokai": "^6.2.3",
57+
"@fsegurai/codemirror-theme-tokyo-night-storm": "^6.2.3",
4758
"@hey-api/openapi-ts": "^0.89.1",
4859
"@lezer/highlight": "^1.2.3",
49-
"@uiw/codemirror-theme-basic": "^4.25.4",
60+
"@replit/codemirror-lang-csharp": "^6.2.0",
5061
"axios": "^1.13.2",
5162
"codemirror": "^6.0.2",
63+
"codemirror-shiki": "^0.3.0",
5264
"date-fns": "^4.1.0",
5365
"dotenv": "^17.2.3",
5466
"sharp": "^0.34.5",

frontend/pnpm-lock.yaml

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

frontend/src/lib/components/code-editor.svelte

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,65 @@
22
import { onMount } from "svelte";
33
import { EditorView } from "@codemirror/view";
44
import { basicSetup } from "codemirror";
5-
import { EditorState, StateEffect } from "@codemirror/state";
6-
import { syntaxHighlighting } from "@codemirror/language";
7-
import { customTheme, customHighlight } from "$lib/editor-theme";
5+
import { EditorState } from "@codemirror/state";
6+
import { customTheme } from "$lib/editor-theme";
87
import { getLanguageExtension, type LanguageType } from "$lib/editor-lang";
8+
import { shikiToCodeMirror, updateEffect } from "@cmshiki/shiki";
9+
import { getSingletonHighlighter } from "shiki";
910
1011
let {
1112
value = $bindable(""),
1213
language = "yaml" as LanguageType,
1314
editable = false,
15+
theme = "ayu-dark",
1416
} = $props();
1517
1618
let editorRef: HTMLDivElement;
1719
let view: EditorView | null = null;
1820
19-
let extensionsConfig = $derived([
20-
basicSetup,
21-
...getLanguageExtension(language),
22-
customTheme,
23-
syntaxHighlighting(customHighlight),
24-
EditorState.readOnly.of(!editable),
25-
EditorView.editable.of(editable),
26-
]);
21+
async function getThemeBg(themeName: string): Promise<string> {
22+
const highlighter = await getSingletonHighlighter({
23+
themes: [themeName],
24+
langs: [],
25+
});
26+
return highlighter.getTheme(themeName).bg ?? "#1e1e1e";
27+
}
28+
29+
function toShikiLang(lang: LanguageType): string | null {
30+
return lang === "plain_text" ? null : lang;
31+
}
32+
33+
async function buildEditor(doc: string) {
34+
const shikiLang = toShikiLang(language);
35+
const [bg, shikiResult] = await Promise.all([
36+
getThemeBg(theme),
37+
shikiLang
38+
? shikiToCodeMirror({
39+
lang: shikiLang,
40+
theme,
41+
engine: "javascript",
42+
})
43+
: Promise.resolve(null),
44+
]);
45+
46+
const themeExtension = EditorView.theme({
47+
"&": { backgroundColor: bg },
48+
".cm-gutters": { backgroundColor: bg, borderRight: "none" },
49+
".cm-activeLineGutter": { backgroundColor: `${bg}cc` },
50+
});
2751
28-
onMount(() => {
29-
view = new EditorView({
52+
return new EditorView({
3053
state: EditorState.create({
31-
doc: value,
32-
extensions: extensionsConfig,
54+
doc,
55+
extensions: [
56+
basicSetup,
57+
...getLanguageExtension(language),
58+
customTheme(bg),
59+
themeExtension,
60+
...(shikiResult ? [shikiResult.shiki] : []),
61+
EditorState.readOnly.of(!editable),
62+
EditorView.editable.of(editable),
63+
],
3364
}),
3465
parent: editorRef,
3566
dispatchTransactions(trs, view) {
@@ -40,25 +71,39 @@
4071
}
4172
},
4273
});
74+
}
75+
76+
onMount(async () => {
77+
view = await buildEditor(value);
4378
});
4479
4580
$effect(() => {
46-
if (view) {
47-
view.dispatch({
48-
effects: StateEffect.reconfigure.of(extensionsConfig),
49-
});
50-
}
81+
// reactive deps
82+
const _lang = language;
83+
const _theme = theme;
84+
const _editable = editable;
85+
86+
if (!view) return;
87+
88+
(async () => {
89+
const currentDoc = view!.state.doc.toString();
90+
view!.destroy();
91+
view = await buildEditor(currentDoc);
92+
})();
5193
});
5294
</script>
5395

54-
<div bind:this={editorRef} class="w-full h-full"></div>
96+
<div
97+
bind:this={editorRef}
98+
class="w-full h-full overflow-scroll"
99+
spellcheck="false"
100+
></div>
55101

56102
<style>
57103
:global(.cm-editor) {
58104
height: 100%;
59105
outline: none !important;
60106
}
61-
62107
:global(.cm-scroller) {
63108
font-family: "Cascadia Code", "Fira Code", monospace !important;
64109
}

0 commit comments

Comments
 (0)