| <script lang="ts"> |
| import { basicSetup, EditorView } from 'codemirror'; |
| import { keymap, placeholder } from '@codemirror/view'; |
| import { Compartment, EditorState } from '@codemirror/state'; |
| |
| import { acceptCompletion } from '@codemirror/autocomplete'; |
| import { indentWithTab } from '@codemirror/commands'; |
| |
| import { indentUnit } from '@codemirror/language'; |
| import { languages } from '@codemirror/language-data'; |
| |
| { python } from '@codemirror/lang-python'; |
| { javascript } from '@codemirror/lang-javascript'; |
| |
| import { oneDark } from '@codemirror/theme-one-dark'; |
| |
| import { onMount, createEventDispatcher, getContext, tick } from 'svelte'; |
| |
| import { formatPythonCode } from '$lib/apis/utils'; |
| import { toast } from 'svelte-sonner'; |
| |
| const dispatch = createEventDispatcher(); |
| const i18n = getContext('i18n'); |
| |
| export let boilerplate = ''; |
| export let value = ''; |
| let _value = ''; |
| |
| $: if (value) { |
| updateValue(); |
| } |
| |
| const updateValue = () => { |
| if (_value !== value) { |
| _value = value; |
| if (codeEditor) { |
| codeEditor.dispatch({ |
| changes: [{ from: 0, to: codeEditor.state.doc.length, insert: _value }] |
| }); |
| } |
| } |
| }; |
| |
| export let id = ''; |
| export let lang = ''; |
| |
| let codeEditor; |
| |
| let isDarkMode = false; |
| let editorTheme = new Compartment(); |
| let editorLanguage = new Compartment(); |
| |
| const getLang = async () => { |
| const language = languages.find((l) => l.alias.includes(lang)); |
| return await language?.load(); |
| }; |
| |
| export const formatPythonCodeHandler = async () => { |
| 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 }] |
| }); |
| |
| _value = formattedCode; |
| dispatch('change', { value: _value }); |
| await tick(); |
| |
| toast.success($i18n.t('Code formatted successfully')); |
| return true; |
| } |
| return false; |
| } |
| return false; |
| }; |
| |
| let extensions = [ |
| basicSetup, |
| keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]), |
| indentUnit.of(' '), |
| placeholder('Enter your code here...'), |
| EditorView.updateListener.of((e) => { |
| if (e.docChanged) { |
| _value = e.state.doc.toString(); |
| dispatch('change', { value: _value }); |
| } |
| }), |
| editorTheme.of([]), |
| editorLanguage.of([]) |
| ]; |
| |
| $: if (lang) { |
| setLanguage(); |
| } |
| |
| const setLanguage = async () => { |
| const language = await getLang(); |
| if (language) { |
| codeEditor.dispatch({ |
| effects: editorLanguage.reconfigure(language) |
| }); |
| } |
| }; |
| |
| onMount(() => { |
| console.log(value); |
| if (value === '') { |
| value = boilerplate; |
| } |
| |
| _value = value; |
| |
| |
| isDarkMode = document.documentElement.classList.contains('dark'); |
| |
| |
| codeEditor = new EditorView({ |
| state: EditorState.create({ |
| doc: _value, |
| extensions: extensions |
| }), |
| parent: document.getElementById(`code-textarea-${id}`) |
| }); |
| |
| if (isDarkMode) { |
| codeEditor.dispatch({ |
| effects: editorTheme.reconfigure(oneDark) |
| }); |
| } |
| |
| |
| 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'] |
| }); |
| |
| const keydownHandler = async (e) => { |
| if ((e.ctrlKey || e.metaKey) && e.key === 's') { |
| e.preventDefault(); |
| dispatch('save'); |
| } |
| |
| |
| if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'f') { |
| e.preventDefault(); |
| await formatPythonCodeHandler(); |
| } |
| }; |
| |
| document.addEventListener('keydown', keydownHandler); |
| |
| return () => { |
| observer.disconnect(); |
| document.removeEventListener('keydown', keydownHandler); |
| }; |
| }); |
| </script> |
|
|
| <div id="code-textarea-{id}" class="h-full w-full" /> |
|
|