Private
Public Access
1
0
Files
u-desk/web/src/views/db-cli/composables/useTabEditor.ts
绝尘 0229cab550 重构:CodeMirror 架构优化
核心优化:
- 新增统一导出避免多实例问题
- 语言加载器从动态改为静态导入
- 使用 Compartment 实现主题/语言动态切换

依赖清理:
- 移除废弃的 @codemirror/highlight
- 移除不再使用的 @codemirror/legacy-modes

组件优化:
- CodeEditor 添加内容更新防抖
- 改进亮色主题样式
- 移除不必要的编辑器重建逻辑

构建配置:
- 简化 Vite manualChunks 配置
- 优化依赖预加载列表

文档清理:
- 删除过期的代码审查文档
- 更新版本号 0.3.0 → 0.3.2
2026-03-31 11:49:25 +08:00

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
}
}