forked from mxx1111/mdlook
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheditor.ts
More file actions
158 lines (133 loc) · 3.83 KB
/
Copy patheditor.ts
File metadata and controls
158 lines (133 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import type { EditorView } from '@codemirror/view'
import { formatDoc } from '@/utils'
export interface EditorContentAdapter {
getContent: () => string
replaceContent: (content: string) => void
focus?: () => void
}
/**
* 编辑器 Store
* 负责管理当前激活编辑器的内容接口和 CodeMirror 专属操作。
* CodeMirror 与 Vditor 都通过 EditorContentAdapter 接入,调用方无需判断编辑器类型。
*/
export const useEditorStore = defineStore(`editor`, () => {
// 内容编辑器实例
const editor = ref<EditorView | null>(null)
const contentAdapter = shallowRef<EditorContentAdapter | null>(null)
const hasContentAdapter = computed(() => contentAdapter.value !== null)
const registerContentAdapter = (adapter: EditorContentAdapter) => {
contentAdapter.value = adapter
return () => {
if (contentAdapter.value === adapter)
contentAdapter.value = null
}
}
const replaceAllContent = (content: string, focus: boolean) => {
if (contentAdapter.value) {
contentAdapter.value.replaceContent(content)
if (focus)
contentAdapter.value.focus?.()
return
}
if (!editor.value)
return
editor.value.dispatch({
changes: { from: 0, to: editor.value.state.doc.length, insert: content },
})
if (focus)
editor.value.focus()
}
// 获取当前内容
const getContent = () => {
return contentAdapter.value?.getContent()
?? editor.value?.state.doc.toString()
?? ``
}
// 格式化文档
const formatContent = async () => {
if (!contentAdapter.value && !editor.value)
return
const doc = await formatDoc(getContent())
replaceAllContent(doc, false)
return doc
}
// 导入默认文档
const importContent = (content: string) => {
replaceAllContent(content, false)
}
const setContent = (content: string) => {
replaceAllContent(content, true)
}
// 清空内容
const clearContent = () => {
if (!contentAdapter.value && !editor.value)
return
replaceAllContent(``, false)
toast.success(`内容已清空`)
}
// 获取选中的文本
const getSelection = () => {
if (!editor.value)
return ``
const selection = editor.value.state.selection.main
return editor.value.state.doc.sliceString(selection.from, selection.to)
}
// 替换选中的文本
const replaceSelection = (text: string) => {
if (!editor.value)
return
editor.value.dispatch(editor.value.state.replaceSelection(text))
}
const replaceText = (oldText: string, newText: string) => {
if (!editor.value || !oldText)
return false
const content = editor.value.state.doc.toString()
const cursor = editor.value.state.selection.main.head
let bestFrom = -1
let bestDist = Infinity
let pos = 0
while (true) {
const idx = content.indexOf(oldText, pos)
if (idx === -1)
break
const dist = Math.abs(idx - cursor)
if (dist < bestDist) {
bestDist = dist
bestFrom = idx
}
pos = idx + 1
}
if (bestFrom === -1)
return false
editor.value.dispatch({
changes: { from: bestFrom, to: bestFrom + oldText.length, insert: newText },
})
editor.value.focus()
return true
}
// 在光标位置插入文本
const insertAtCursor = (text: string) => {
if (!editor.value)
return
const selection = editor.value.state.selection.main
editor.value.dispatch({
changes: { from: selection.from, to: selection.to, insert: text },
selection: { anchor: selection.from + text.length },
})
editor.value.focus()
}
return {
editor,
hasContentAdapter,
registerContentAdapter,
formatContent,
importContent,
setContent,
clearContent,
getContent,
getSelection,
replaceSelection,
replaceText,
insertAtCursor,
}
})