核心优化: - 新增统一导出避免多实例问题 - 语言加载器从动态改为静态导入 - 使用 Compartment 实现主题/语言动态切换 依赖清理: - 移除废弃的 @codemirror/highlight - 移除不再使用的 @codemirror/legacy-modes 组件优化: - CodeEditor 添加内容更新防抖 - 改进亮色主题样式 - 移除不必要的编辑器重建逻辑 构建配置: - 简化 Vite manualChunks 配置 - 优化依赖预加载列表 文档清理: - 删除过期的代码审查文档 - 更新版本号 0.3.0 → 0.3.2
139 lines
3.9 KiB
TypeScript
139 lines
3.9 KiB
TypeScript
import { ref, nextTick } from 'vue'
|
|
import { EditorView, EditorState } from '@/utils/codemirrorExports'
|
|
|
|
export interface TabEditorTab {
|
|
id?: number
|
|
key: string
|
|
title: string
|
|
content: string
|
|
connectionId?: number
|
|
}
|
|
|
|
export interface TabEditorOptions {
|
|
findContainer: (tabKey: string, retryCount?: number) => Promise<{ container: HTMLElement; pane?: HTMLElement } | null>
|
|
checkContainerSize: (container: HTMLElement) => Promise<void>
|
|
createExtensions: (tab: TabEditorTab) => any[]
|
|
getInitialContent: (tab: TabEditorTab) => string
|
|
onContentChange?: (tabKey: string, content: string) => void
|
|
onEditorReady?: (tabKey: string, editor: EditorView) => void
|
|
}
|
|
|
|
const INIT_DELAY = 200
|
|
|
|
export function useTabEditor(options: TabEditorOptions) {
|
|
const { findContainer, checkContainerSize, createExtensions, getInitialContent, onContentChange, onEditorReady } = options
|
|
|
|
const editorViews = ref<Map<string, EditorView>>(new Map())
|
|
|
|
const getEditor = (tabKey: string): EditorView | null => {
|
|
return editorViews.value.get(tabKey) as EditorView || null
|
|
}
|
|
|
|
const destroyEditor = (tabKey: string): void => {
|
|
const editor = editorViews.value.get(tabKey)
|
|
if (!editor) return
|
|
|
|
if (onContentChange) {
|
|
onContentChange(tabKey, editor.state.doc.toString())
|
|
}
|
|
editor.destroy()
|
|
editorViews.value.delete(tabKey)
|
|
}
|
|
|
|
const focusEditor = (editor: EditorView, delay = 0): void => {
|
|
if (!editor) return
|
|
|
|
const focus = () => {
|
|
editor.requestMeasure?.()
|
|
editor.dispatch({ effects: [] })
|
|
requestAnimationFrame(() => editor.focus())
|
|
}
|
|
|
|
delay > 0 ? setTimeout(focus, delay) : requestAnimationFrame(focus)
|
|
}
|
|
|
|
const initEditor = async (tabKey: string, tab: any, isActive: boolean, forceInit = false): Promise<boolean> => {
|
|
if (!isActive && !forceInit) return false
|
|
|
|
const existingEditor = editorViews.value.get(tabKey)
|
|
if (existingEditor instanceof EditorView) {
|
|
if (isActive) focusEditor(existingEditor, 100)
|
|
return true
|
|
}
|
|
|
|
destroyEditor(tabKey)
|
|
await nextTick()
|
|
|
|
const containerResult = await findContainer(tabKey)
|
|
if (!containerResult) return false
|
|
|
|
const { container } = containerResult
|
|
await checkContainerSize(container)
|
|
|
|
const rect = container.getBoundingClientRect()
|
|
if (rect.width === 0 || rect.height === 0) {
|
|
if (isActive) {
|
|
setTimeout(() => initEditor(tabKey, tab, isActive, forceInit), 100)
|
|
}
|
|
return false
|
|
}
|
|
|
|
const state = EditorState.create({
|
|
doc: getInitialContent(tab),
|
|
extensions: createExtensions(tab)
|
|
})
|
|
|
|
container.innerHTML = ''
|
|
const editorView = new EditorView({ state, parent: container })
|
|
editorViews.value.set(tabKey, editorView)
|
|
|
|
if (onEditorReady) onEditorReady(tabKey, editorView)
|
|
if (isActive) focusEditor(editorView, INIT_DELAY)
|
|
|
|
return true
|
|
}
|
|
|
|
const destroyAll = (): void => {
|
|
editorViews.value.forEach((_, tabKey) => destroyEditor(tabKey))
|
|
editorViews.value.clear()
|
|
}
|
|
|
|
const updateEditorContent = (tabKey: string, content: string): boolean => {
|
|
const editor = editorViews.value.get(tabKey)
|
|
if (!editor) return false
|
|
|
|
const update = () => {
|
|
const state = editor.state
|
|
if (!state?.doc) return false
|
|
if (state.doc.toString() === content) return true
|
|
|
|
try {
|
|
editor.dispatch(state.update({ changes: { from: 0, to: state.doc.length, insert: content } }))
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
try {
|
|
return update() || update()
|
|
} catch (error: unknown) {
|
|
const errorMessage = error instanceof Error ? error.message : ''
|
|
if (errorMessage?.includes('doesn\'t start from the previous state')) {
|
|
try { return update() } catch {}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
return {
|
|
editorViews,
|
|
getEditor,
|
|
destroyEditor,
|
|
initEditor,
|
|
focusEditor,
|
|
destroyAll,
|
|
updateEditorContent
|
|
}
|
|
}
|