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>