"vscode:/vscode.git/clone" did not exist on "0366fb8ef83afb50ed236582110ed3817d92b019"
CodeEditor.svelte 3.25 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
2
3
<script lang="ts">
	import { basicSetup, EditorView } from 'codemirror';
	import { keymap, placeholder } from '@codemirror/view';
Timothy J. Baek's avatar
Timothy J. Baek committed
4
	import { Compartment, EditorState } from '@codemirror/state';
Timothy J. Baek's avatar
Timothy J. Baek committed
5
6
7
8

	import { acceptCompletion } from '@codemirror/autocomplete';
	import { indentWithTab } from '@codemirror/commands';

Timothy J. Baek's avatar
Timothy J. Baek committed
9
	import { indentUnit } from '@codemirror/language';
Timothy J. Baek's avatar
Timothy J. Baek committed
10
11
12
	import { python } from '@codemirror/lang-python';
	import { oneDark } from '@codemirror/theme-one-dark';

Timothy J. Baek's avatar
Timothy J. Baek committed
13
14
15
16
17
	import { onMount, createEventDispatcher } from 'svelte';
	import { formatPythonCode } from '$lib/apis/utils';
	import { toast } from 'svelte-sonner';

	const dispatch = createEventDispatcher();
Timothy J. Baek's avatar
Timothy J. Baek committed
18

Timothy J. Baek's avatar
Timothy J. Baek committed
19
	export let boilerplate = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
20
21
	export let value = '';

Timothy J. Baek's avatar
Timothy J. Baek committed
22
23
24
25
26
	let codeEditor;

	let isDarkMode = false;
	let editorTheme = new Compartment();

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
27
	export const formatPythonCodeHandler = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
28
29
30
31
32
33
34
35
36
37
38
		if (codeEditor) {
			const res = await formatPythonCode(value).catch((error) => {
				toast.error(error);
				return null;
			});

			if (res && res.code) {
				const formattedCode = res.code;
				codeEditor.dispatch({
					changes: [{ from: 0, to: codeEditor.state.doc.length, insert: formattedCode }]
				});
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
39
40

				toast.success('Code formatted successfully');
Timothy J. Baek's avatar
Timothy J. Baek committed
41
42
43
44
				return true;
			}
			return false;
		}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
45
		return false;
Timothy J. Baek's avatar
Timothy J. Baek committed
46
47
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
48
49
50
51
	let extensions = [
		basicSetup,
		keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]),
		python(),
Timothy J. Baek's avatar
Timothy J. Baek committed
52
		indentUnit.of('    '),
Timothy J. Baek's avatar
Timothy J. Baek committed
53
54
55
56
57
58
59
60
61
		placeholder('Enter your code here...'),
		EditorView.updateListener.of((e) => {
			if (e.docChanged) {
				value = e.state.doc.toString();
			}
		}),
		editorTheme.of([])
	];

Timothy J. Baek's avatar
Timothy J. Baek committed
62
	onMount(() => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
63
64
		value = boilerplate;

Timothy J. Baek's avatar
Timothy J. Baek committed
65
66
67
		// Check if html class has dark mode
		isDarkMode = document.documentElement.classList.contains('dark');

Timothy J. Baek's avatar
Timothy J. Baek committed
68
		// python code editor, highlight python code
Timothy J. Baek's avatar
Timothy J. Baek committed
69
		codeEditor = new EditorView({
Timothy J. Baek's avatar
Timothy J. Baek committed
70
			state: EditorState.create({
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
71
				doc: value,
Timothy J. Baek's avatar
Timothy J. Baek committed
72
				extensions: extensions
Timothy J. Baek's avatar
Timothy J. Baek committed
73
74
75
			}),
			parent: document.getElementById('code-textarea')
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

		if (isDarkMode) {
			codeEditor.dispatch({
				effects: editorTheme.reconfigure(oneDark)
			});
		}

		// listen to html class changes this should fire only when dark mode is toggled
		const observer = new MutationObserver((mutations) => {
			mutations.forEach((mutation) => {
				if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
					const _isDarkMode = document.documentElement.classList.contains('dark');

					if (_isDarkMode !== isDarkMode) {
						isDarkMode = _isDarkMode;
						if (_isDarkMode) {
							codeEditor.dispatch({
								effects: editorTheme.reconfigure(oneDark)
							});
						} else {
							codeEditor.dispatch({
								effects: editorTheme.reconfigure()
							});
						}
					}
				}
			});
		});

		observer.observe(document.documentElement, {
			attributes: true,
			attributeFilter: ['class']
		});

Timothy J. Baek's avatar
Timothy J. Baek committed
110
111
112
		// Add a keyboard shortcut to format the code when Ctrl/Cmd + S is pressed
		// Override the default browser save functionality

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
113
		const handleSave = async (e) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
114
115
			if ((e.ctrlKey || e.metaKey) && e.key === 's') {
				e.preventDefault();
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
116
				dispatch('save');
Timothy J. Baek's avatar
Timothy J. Baek committed
117
118
119
120
121
			}
		};

		document.addEventListener('keydown', handleSave);

Timothy J. Baek's avatar
Timothy J. Baek committed
122
123
		return () => {
			observer.disconnect();
Timothy J. Baek's avatar
Timothy J. Baek committed
124
			document.removeEventListener('keydown', handleSave);
Timothy J. Baek's avatar
Timothy J. Baek committed
125
		};
Timothy J. Baek's avatar
Timothy J. Baek committed
126
127
128
	});
</script>

Timothy J. Baek's avatar
Timothy J. Baek committed
129
<div id="code-textarea" class="h-full w-full" />