|
<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" /> |
|
|