148 lines
5.1 KiB
TypeScript
148 lines
5.1 KiB
TypeScript
import { editor, Selection } from 'monaco-editor'
|
|
import MonacoEditor, { Monaco } from '@monaco-editor/react'
|
|
import { shikiToMonaco } from '@shikijs/monaco'
|
|
import { createHighlighter } from 'shiki'
|
|
import useStyles from '@/components/Playground/CodeEditor/Editor/index.style'
|
|
import '@/components/Playground/CodeEditor/Editor/loader'
|
|
import { IEditorOptions, IFiles, ITsconfig } from '@/components/Playground/shared'
|
|
import { fileNameToLanguage, tsconfigJsonDiagnosticsOptions } from '@/components/Playground/files'
|
|
import { useEditor, useTypesProgress } from '@/components/Playground/CodeEditor/Editor/hooks'
|
|
import { MonacoEditorConfig } from '@/components/Playground/CodeEditor/Editor/monacoConfig'
|
|
|
|
export interface ExtraLib {
|
|
path: string
|
|
content: string
|
|
}
|
|
|
|
interface EditorProps {
|
|
isDarkMode?: boolean
|
|
tsconfig?: ITsconfig
|
|
files?: IFiles
|
|
selectedFileName?: string
|
|
readonly?: boolean
|
|
onChange?: (code: string | undefined) => void
|
|
options?: IEditorOptions
|
|
onJumpFile?: (fileName: string) => void
|
|
extraLibs?: ExtraLib[]
|
|
onEditorDidMount?: (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => void
|
|
}
|
|
|
|
const Editor = ({
|
|
isDarkMode,
|
|
tsconfig,
|
|
files = {},
|
|
selectedFileName = '',
|
|
readonly,
|
|
onChange,
|
|
options,
|
|
onJumpFile,
|
|
extraLibs = [],
|
|
onEditorDidMount
|
|
}: EditorProps) => {
|
|
const { styles } = useStyles()
|
|
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
|
const monacoRef = useRef<Monaco>()
|
|
const { doOpenEditor, autoLoadExtraLib } = useEditor()
|
|
const { total, finished, onWatch } = useTypesProgress()
|
|
const file = files[selectedFileName] || { name: 'Untitled' }
|
|
|
|
const handleOnEditorWillMount = (monaco: Monaco) => {
|
|
createHighlighter({
|
|
themes: ['vitesse-light', 'vitesse-dark'],
|
|
langs: ['javascript', 'jsx', 'typescript', 'tsx', 'css', 'json', 'xml']
|
|
}).then((highlighter) => {
|
|
shikiToMonaco(highlighter, monaco)
|
|
monaco.editor.setTheme(isDarkMode ? 'vitesse-dark' : 'vitesse-light')
|
|
})
|
|
|
|
monaco.languages.json.jsonDefaults.setDiagnosticsOptions(tsconfigJsonDiagnosticsOptions)
|
|
tsconfig &&
|
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
|
|
tsconfig.compilerOptions
|
|
)
|
|
|
|
if (files) {
|
|
monaco.editor.getModels().forEach((model) => model.dispose())
|
|
Object.entries(files).forEach(([key]) => {
|
|
if (!monaco.editor.getModel(monaco.Uri.parse(`file:///${key}`))) {
|
|
monaco.editor.createModel(
|
|
files[key].value,
|
|
fileNameToLanguage(key),
|
|
monaco.Uri.parse(`file:///${key}`)
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleOnEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
|
editorRef.current = editor
|
|
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
|
|
void editor.getAction('editor.action.formatDocument')?.run()
|
|
})
|
|
|
|
monacoRef.current = monaco
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-expect-error
|
|
editor['_codeEditorService'].doOpenEditor = function (
|
|
editor: editor.IStandaloneCodeEditor,
|
|
input: { options: { selection: Selection }; resource: { path: string } }
|
|
) {
|
|
const path = input.resource.path
|
|
if (!['/lib.dom.d.ts', '/node_modules/'].some((item) => path.startsWith(item))) {
|
|
onJumpFile?.(path.replace('/', ''))
|
|
doOpenEditor(editor, input)
|
|
}
|
|
}
|
|
|
|
extraLibs.forEach((item) =>
|
|
monaco.languages.typescript.typescriptDefaults.addExtraLib(item.content, item.path)
|
|
)
|
|
|
|
onEditorDidMount?.(editor, monaco)
|
|
|
|
void autoLoadExtraLib(editor, monaco, file.value, onWatch)
|
|
}
|
|
|
|
useEffect(() => {
|
|
monacoRef.current?.editor.setTheme(isDarkMode ? 'vitesse-dark' : 'vitesse-light')
|
|
}, [isDarkMode])
|
|
|
|
useEffect(() => {
|
|
editorRef.current?.focus()
|
|
}, [file.name])
|
|
|
|
useEffect(() => {
|
|
tsconfig &&
|
|
monacoRef.current?.languages.typescript.typescriptDefaults.setCompilerOptions(
|
|
tsconfig.compilerOptions
|
|
)
|
|
}, [tsconfig])
|
|
|
|
return (
|
|
<>
|
|
<div className={styles.root}>
|
|
<MonacoEditor
|
|
theme={isDarkMode ? 'vitesse-dark' : 'vitesse-light'}
|
|
path={file.name}
|
|
language={file.language}
|
|
value={file.value}
|
|
onChange={onChange}
|
|
beforeMount={handleOnEditorWillMount}
|
|
onMount={handleOnEditorDidMount}
|
|
options={{
|
|
...MonacoEditorConfig,
|
|
...options,
|
|
theme: undefined,
|
|
readOnly: readonly
|
|
}}
|
|
/>
|
|
{total > 0 && !finished && <div className={styles.loading} />}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Editor
|