feat: 完成扩展资源验收与同步服务部署
This commit is contained in:
@@ -25,17 +25,15 @@ function reconcile(view: EditorView) {
|
||||
if (view.state.doc.rangeHasMark(start, end, mark)) return
|
||||
const tr = view.state.tr.delete(end - 1, end).delete(start, start + 1)
|
||||
tr.removeMark(start, end - 2).addMark(start, end - 2, mark.create())
|
||||
// Filling an existing pair must keep subsequent letters inside code. Typing
|
||||
// the closing delimiter explicitly should instead leave code as usual.
|
||||
// 补全已有成对标记时,后续字母必须留在行内代码中;显式输入结束标记则应像通常一样离开代码范围。
|
||||
if ($from.pos < end) tr.setStoredMarks([mark.create()])
|
||||
else tr.removeStoredMark(mark)
|
||||
view.dispatch(tr)
|
||||
}
|
||||
|
||||
// DOM input can bypass handleTextInput (IME, replacement text, missing event.data).
|
||||
// Observe it in capture phase, then wait for ProseMirror's DOM observer and its
|
||||
// composition cleanup before inspecting the document. Never reconcile on load,
|
||||
// paste, undo, or a selection change alone.
|
||||
// DOM 输入可能绕过 handleTextInput(输入法、替换文本或缺少 event.data)。
|
||||
// 在捕获阶段观察输入,等待 ProseMirror 的 DOM 观察器和组合输入清理完成后再检查文档。
|
||||
// 加载、粘贴、撤销或仅改变选区时不执行协调。
|
||||
export const inlineCodeInputPlugin = $prose(() => new Plugin({
|
||||
view(view) {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
@@ -67,7 +65,7 @@ export const inlineCodeInputPlugin = $prose(() => new Plugin({
|
||||
const start = () => { composing = true; cancel() }
|
||||
const end = () => { composing = false; pending = true; attempts = 0; schedule() }
|
||||
const keydown = (event: KeyboardEvent) => {
|
||||
// ProseMirror handles undo/paste itself, so those actions need not emit input.
|
||||
// ProseMirror 会自行处理撤销和粘贴,因此这些操作无需触发 input。
|
||||
if (event.ctrlKey || event.metaKey || ['Backspace', 'Delete', 'Escape'].includes(event.key)) cancel()
|
||||
else if (pending && !composing && !view.composing && event.key === 'Enter') {
|
||||
cancel()
|
||||
|
||||
@@ -82,7 +82,7 @@ async function loadCodeLanguage(requestedLanguage: string) {
|
||||
return { shiki, language }
|
||||
}
|
||||
|
||||
// Bounded LRU of dual-theme HTML. Large one-off blocks never remain in the cache.
|
||||
// 双主题 HTML 使用有界 LRU;大型一次性代码块不会留在缓存中。
|
||||
const highlightedBlocks = new Map<string, string>()
|
||||
let highlightedCharacters = 0
|
||||
const highlightBudget = 1_000_000
|
||||
@@ -101,7 +101,7 @@ export async function highlightCode(source: string, requestedLanguage = 'text'):
|
||||
})
|
||||
const cost = key.length + html.length
|
||||
if (cost <= highlightBudget / 4) {
|
||||
// A concurrent caller may already have filled the same entry.
|
||||
// 并发调用方可能已经填充同一条目。
|
||||
const previous = highlightedBlocks.get(key)
|
||||
if (previous !== undefined) { highlightedCharacters -= key.length + previous.length; highlightedBlocks.delete(key) }
|
||||
while (highlightedBlocks.size && (highlightedBlocks.size >= 64 || highlightedCharacters + cost > highlightBudget)) {
|
||||
@@ -114,7 +114,7 @@ export async function highlightCode(source: string, requestedLanguage = 'text'):
|
||||
return html
|
||||
}
|
||||
|
||||
/** Share the initialized grammar/theme registry with editable code blocks. */
|
||||
/** 与可编辑代码块共享已初始化的语法和主题注册表。 */
|
||||
export async function getCodeTokenizer(theme: 'github-light' | 'github-dark', requestedLanguage = 'text') {
|
||||
const { shiki } = await loadCodeLanguage(requestedLanguage)
|
||||
return (source: string, requestedLanguage: string) => {
|
||||
@@ -158,8 +158,7 @@ export async function renderMarkdown(source: string, options?: { themeId?: strin
|
||||
}
|
||||
const highlighted = await highlightCode(code.textContent ?? '', requestedLanguage)
|
||||
const fragment = document.createRange().createContextualFragment(highlighted)
|
||||
// Shiki separates line spans with newlines. Block layout must not render those
|
||||
// separators as additional blank rows; the untouched source remains available for copy.
|
||||
// Shiki 用换行符分隔行 span。块布局不能把分隔符渲染成额外空行;复制时仍使用未改动的源码。
|
||||
for (const node of [...(fragment.querySelector('code')?.childNodes ?? [])]) {
|
||||
if (node.nodeType === Node.TEXT_NODE && !node.textContent?.trim()) node.remove()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user