@setoelkahfi / svara / commits / b0ad877

feat: added decent syntax higlighting

rewote theme handling so it no longer manually uses css classes

mellbacon committed Aug 18, 2024 at 16:20 UTC b0ad87724b7c9b542de6a375ba1cf895b09b0774
5 files changed +214 -20
src/config/extensions/default_themes/script.ts new
+101
@@ -0,0 +1,101 @@
1 +import { tags as t } from "@lezer/highlight"
2 +export const tokenStyles = {
3 + "dark": [
4 + {
5 + tag: [
6 + t.keyword,
7 + t.operatorKeyword,
8 + t.modifier,
9 + t.color,
10 + t.constant(t.name),
11 + t.standard(t.name),
12 + t.standard(t.tagName),
13 + t.special(t.brace),
14 + t.atom,
15 + t.bool,
16 + t.special(t.variableName),
17 + ],
18 + color: '#569cd6',
19 + },
20 + { tag: [t.controlKeyword, t.moduleKeyword], color: '#c586c0' },
21 + {
22 + tag: [
23 + t.name,
24 + t.deleted,
25 + t.character,
26 + t.macroName,
27 + t.propertyName,
28 + t.variableName,
29 + t.labelName,
30 + t.definition(t.name),
31 + ],
32 + color: '#9cdcfe',
33 + },
34 + { tag: t.heading, fontWeight: 'bold', color: '#9cdcfe' },
35 + {
36 + tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
37 + color: '#4ec9b0',
38 + },
39 + { tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#dcdcaa' },
40 + { tag: [t.number], color: '#b5cea8' },
41 + { tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#d4d4d4' },
42 + { tag: [t.regexp], color: '#d16969' },
43 + { tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#ce9178' },
44 + { tag: [t.angleBracket], color: '#808080' },
45 + { tag: t.strong, fontWeight: 'bold' },
46 + { tag: t.emphasis, fontStyle: 'italic' },
47 + { tag: t.strikethrough, textDecoration: 'line-through' },
48 + { tag: [t.meta, t.comment], color: '#6a9955' },
49 + { tag: t.link, color: '#6a9955', textDecoration: 'underline' },
50 + { tag: t.invalid, color: '#ff0000' },
51 + ],
52 + "light": [
53 + {
54 + tag: [
55 + t.keyword,
56 + t.operatorKeyword,
57 + t.modifier,
58 + t.color,
59 + t.constant(t.name),
60 + t.standard(t.name),
61 + t.standard(t.tagName),
62 + t.special(t.brace),
63 + t.atom,
64 + t.bool,
65 + t.special(t.variableName),
66 + ],
67 + color: '#0000ff',
68 + },
69 + { tag: [t.moduleKeyword, t.controlKeyword], color: '#af00db' },
70 + {
71 + tag: [
72 + t.name,
73 + t.deleted,
74 + t.character,
75 + t.macroName,
76 + t.propertyName,
77 + t.variableName,
78 + t.labelName,
79 + t.definition(t.name),
80 + ],
81 + color: '#0070c1',
82 + },
83 + { tag: t.heading, fontWeight: 'bold', color: '#0070c1' },
84 + {
85 + tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
86 + color: '#267f99',
87 + },
88 + { tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#795e26' },
89 + { tag: [t.number], color: '#098658' },
90 + { tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#383a42' },
91 + { tag: [t.regexp], color: '#af00db' },
92 + { tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#a31515' },
93 + { tag: [t.angleBracket], color: '#383a42' },
94 + { tag: t.strong, fontWeight: 'bold' },
95 + { tag: t.emphasis, fontStyle: 'italic' },
96 + { tag: t.strikethrough, textDecoration: 'line-through' },
97 + { tag: [t.meta, t.comment], color: '#008000' },
98 + { tag: t.link, color: '#4078f2', textDecoration: 'underline' },
99 + { tag: t.invalid, color: '#e45649' },
100 + ]
101 +}
\ No newline at end of file
src/config/extensions/default_themes/themes/default_dark.json
+3 -1
@@ -129,6 +129,8 @@
129 "terminal.brightBlue": "#7aa2f7",
130 "terminal.brightMagenta": "#bb9af7",
131 "terminal.brightCyan": "#7dcfff",
132 - "terminal.brightWhite": "#acb0d0"
132 + "terminal.brightWhite": "#acb0d0",
133 +
134 + "test.color": "#f52891cc"
135 }
136 }
src/config/extensions/default_themes/themes/default_light.json
+3 -1
@@ -128,6 +128,8 @@
128 "terminal.brightBlue": "#34548a",
129 "terminal.brightMagenta": "#5a4a78",
130 "terminal.brightCyan": "#0f4b6e",
131 - "terminal.brightWhite": "#828594"
131 + "terminal.brightWhite": "#828594",
132 +
133 + "test.color": "#f52891cc"
134 }
135 }
src/config/themehandler.ts
+54 -4
@@ -1,17 +1,19 @@
1 -import { homeDir, join } from "@tauri-apps/api/path";
1 import { themes } from "./extensionhandler";
2 import { info } from "tauri-plugin-log-api";
4 -import { exists, readDir } from "@tauri-apps/api/fs";
3 import { get, writable } from "svelte/store";
4 import { termTheme, updateTermTheme } from "../lib/Terminal.svelte";
7 -import { setColorScheme } from "../lib/Editor.svelte";
5 +import { setColorScheme, setEditorTheme } from "../lib/Editor.svelte";
6 +import { EditorView } from "@codemirror/view";
7 +import { HighlightStyle } from "@codemirror/language"
8
9 export function getThemes() {
10 return get(themes);
11 }
12
13 const stylesheet = document.styleSheets[0].cssRules[0] as CSSStyleRule;
14 -export const is_dark_theme = writable(true)
14 +export const is_dark_theme = writable(true);
15 +export const editorTheme = writable(EditorView.theme({}));
16 +export const editorHighlightStyle = writable(HighlightStyle.define([]))
17 export async function loadTheme(name: string) {
18 let theme = get(themes).find(n => n.name === name);
19 info(`Loading theme: ${name}...`, {file: "themehandler.ts", line: 16});
@@ -20,6 +22,8 @@ export async function loadTheme(name: string) {
22
23 // load base theme
24 let json = await import(`./extensions/default_themes/themes/default_${theme.scheme}.json`);
25 + let { tokenStyles } = await import(`./extensions/default_themes/script`);
26 + const tokens = tokenStyles[theme.scheme];
27 processStyles(json);
28
29 // load custom theme if it exists
@@ -51,6 +55,52 @@ export async function loadTheme(name: string) {
55 "foreground": getThemeProperty("terminal-foreground")
56 })
57
58 + editorTheme.set(EditorView.theme(
59 + {
60 + "&": {
61 + color: getThemeProperty("editor-foreground"),
62 + backgroundColor: getThemeProperty("editor-background"),
63 + },
64 + ".cm-content": {
65 + caretColor: getThemeProperty("editor-foreground")
66 + },
67 +
68 + "&.cm-focused .cm-cursor": {
69 + borderLeftColor: getThemeProperty("editor-foreground")
70 + },
71 +
72 + "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection": {
73 + backgroundColor: "#4d5054"
74 + },
75 + ".cm-activeLine": {
76 + backgroundColor: getThemeProperty("editor-activeLineBackground"),
77 + },
78 + ".cm-gutters": {
79 + backgroundColor: getThemeProperty("editor-background"),
80 + color: getThemeProperty("editor-gutterForeground"),
81 + border: "none"
82 + },
83 + ".cm-activeLineGutter": {
84 + backgroundColor: getThemeProperty("editor-gutterActiveBackground"),
85 + color: getThemeProperty("editor-gutterActiveForeground")
86 + },
87 + ".cm-tooltip": {
88 + border: "0.5px solid #292929",
89 + backgroundColor: getThemeProperty("editor-background"),
90 + color: getThemeProperty("window-foreground")
91 + },
92 + ".cm-tooltip-autocomplete": {
93 + "& > ul > li[aria-selected]": {
94 + backgroundColor: getThemeProperty("editor-activeLineBackground"),
95 + color: getThemeProperty("test-color")
96 + }
97 + }
98 + },
99 + { dark: true }
100 + ))
101 + editorHighlightStyle.set(HighlightStyle.define(tokens));
102 +
103 + setEditorTheme();
104 updateTermTheme();
105
106 info("Theme loaded sucessfully.", {file: "themehandler.ts", line: 35});
src/lib/Editor.svelte
+53 -14
@@ -10,10 +10,12 @@
10 import { appSettings } from "../config/config";
11 import { color, oneDark } from "../config/syntaxhighlighting/dark";
12 import { warn } from "tauri-plugin-log-api";
13 - import { foldGutter, bracketMatching, indentUnit } from "@codemirror/language";
13 + import { foldGutter, bracketMatching, indentUnit, syntaxHighlighting } from "@codemirror/language";
14 import { closeBrackets } from "@codemirror/autocomplete";
15 import { indentationMarkers } from '@replit/codemirror-indentation-markers';
16 - import { getThemeProperty, is_dark_theme } from "../config/themehandler";
16 + import { editorHighlightStyle, editorTheme, getThemeProperty, is_dark_theme } from "../config/themehandler";
17 + import { autocompletion } from "@codemirror/autocomplete";
18 +
19
20
21 let ref;
@@ -34,6 +36,9 @@
36 const tabSize = new Compartment();
37 const indentSize = new Compartment();
38 const colorScheme = new Compartment();
39 + const theme = new Compartment();
40 + const fontFamily = new Compartment();
41 + const fontSize = new Compartment();
42
43 export function updateFileInfo(file) {
44 file_info.set(file);
@@ -96,6 +101,27 @@
101 effects: colorScheme.reconfigure(EditorView.darkTheme.of(scheme))
102 })
103 }
104 + export function setTheme() {
105 + editorView.dispatch({
106 + effects: theme.reconfigure([get(editorTheme), syntaxHighlighting(get(editorHighlightStyle))])
107 + })
108 + }
109 + export function setFontFamily(family: string) {
110 + editorView.dispatch({
111 + effects: fontFamily.reconfigure(EditorView.theme(
112 + {
113 + "*": { fontFamily: family }
114 + }))
115 + })
116 + }
117 + export function setFontSize(size: number) {
118 + editorView.dispatch({
119 + effects: fontSize.reconfigure(EditorView.theme(
120 + {
121 + "*": { fontSize: `${size}px` }
122 + }))
123 + })
124 + }
125
126 onMount(async () => {
127 editorView = new EditorView({
@@ -114,6 +140,10 @@
140 drawSelection(),
141 crosshairCursor(),
142 rectangularSelection(),
143 + autocompletion({
144 + aboveCursor: true,
145 + closeOnBlur: false
146 + }),
147 indentSize.of(indentUnit.of(" ")),
148 indentationMarkers({
149 thickness: 1,
@@ -127,8 +157,9 @@
157 keymap.of([indentWithTab]),
158 lang.of([]),
159 EditorState.allowMultipleSelections.of(true),
130 - //syntaxHighlighting(defaultHighlightStyle, {fallback: true}),
131 - oneDark,
160 + theme.of([]),
161 + fontFamily.of([]),
162 + fontSize.of([]),
163 colorScheme.of(EditorView.darkTheme.of($is_dark_theme)),
164 keymap.of([
165 ...defaultKeymap
@@ -147,10 +178,10 @@
178 })
179 })
180 editorView.contentDOM.classList.add("mousetrap");
150 - setEditorFontSize(await appSettings.get("editor.fontSize"));
151 - setEditorFontFamily(await appSettings.get("editor.fontFamily"));
181 + setTheme();
182 + setFontSize(await appSettings.get("editor.fontSize"));
183 + setFontFamily(await appSettings.get("editor.fontFamily"));
184 setEditorLineHeight(await appSettings.get("editor.lineHeight"));
153 -
185 });
186
187 let _ = null;
@@ -214,16 +245,17 @@
245 return cmlang;
246 }
247 export function setEditorFontSize(value: number) {
217 - // there could be a better way to do all this but. eh
218 - const editors = document.querySelectorAll(".cm-scroller");
219 - for (const editorContainer of editors) {
220 - (editorContainer as HTMLElement).style.setProperty("font-size", `${value}px`, "important")
248 + for (const tab of get(tabs)) {
249 + if (tab.isfile) {
250 + tab.content.setFontSize(value);
251 + }
252 }
253 }
254 export function setEditorFontFamily(family: string) {
224 - const editors = document.querySelectorAll(".cm-scroller");
225 - for (const editorContainer of editors) {
226 - (editorContainer as HTMLElement).style.setProperty("font-family", family, "important")
255 + for (const tab of get(tabs)) {
256 + if (tab.isfile) {
257 + tab.content.setFontFamily(family);
258 + }
259 }
260 }
261 export function setEditorLineHeight(height: string) {
@@ -246,6 +278,13 @@
278 }
279 }
280 }
281 + export function setEditorTheme() {
282 + for (const tab of get(tabs)) {
283 + if (tab.isfile) {
284 + tab.content.setTheme();
285 + }
286 + }
287 + }
288 </script>
289
290 <div bind:this={ref} class="editor" class:hidden on:mousedown={updateLineInfo} on:keydown={handleKeyDown}></div>