Spaces:
Running
Running
<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, LanguageDescription } from '@codemirror/language'; | |
import { languages } from '@codemirror/language-data'; | |
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(); | |
languages.push( | |
LanguageDescription.of({ | |
name: 'HCL', | |
extensions: ['hcl', 'tf'], | |
load() { | |
return import('codemirror-lang-hcl').then((m) => m.hcl()); | |
} | |
}) | |
); | |
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) { | |
codeEditor.dispatch({ | |
effects: editorLanguage.reconfigure(language) | |
}); | |
} | |
}; | |
onMount(() => { | |
console.log(value); | |
if (value === '') { | |
value = boilerplate; | |
} | |
_value = value; | |
// Check if html class has dark mode | |
isDarkMode = document.documentElement.classList.contains('dark'); | |
// python code editor, highlight python code | |
codeEditor = new EditorView({ | |
state: EditorState.create({ | |
doc: _value, | |
extensions: extensions | |
}), | |
parent: document.getElementById(`code-textarea-${id}`) | |
}); | |
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'] | |
}); | |
const keydownHandler = async (e) => { | |
if ((e.ctrlKey || e.metaKey) && e.key === 's') { | |
e.preventDefault(); | |
dispatch('save'); | |
} | |
// Format code when Ctrl + Shift + F is pressed | |
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" /> | |