diff --git a/app/src/history/history.ts b/app/src/history/history.ts index 99e75303b..2bd651f95 100644 --- a/app/src/history/history.ts +++ b/app/src/history/history.ts @@ -702,7 +702,7 @@ const bindEvent = (app: App, element: Element, dialog?: Dialog) => { mdElement.value = response.data.content; mdElement.classList.remove("fn__none"); docElement.classList.add("fn__none"); - searchTextMarkRender(mdElement, []); + searchTextMarkRender(historyEditor.protyle, ["TODO"], mdElement); } else { mdElement.classList.add("fn__none"); docElement.classList.remove("fn__none"); diff --git a/app/src/protyle/render/searchMarkRender.ts b/app/src/protyle/render/searchMarkRender.ts index ba5ef9cbe..a172cd026 100644 --- a/app/src/protyle/render/searchMarkRender.ts +++ b/app/src/protyle/render/searchMarkRender.ts @@ -1,12 +1,12 @@ import {Constants} from "../../constants"; -export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL:boolean) => { +export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolean) => { if (!isSupportCSSHL()) { return; } setTimeout(() => { protyle.highlight.markHL.clear(); - protyle.highlight.markHL.clear(); + protyle.highlight.mark.clear(); protyle.highlight.ranges = []; @@ -70,17 +70,76 @@ export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL:boolean protyle.highlight.ranges.push(item.range); }); CSS.highlights.set("search-mark-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.mark); - if (!protyle.options.backlinkData) { + if (isHL) { CSS.highlights.set("search-mark-hl-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.markHL); } }, protyle.wysiwyg.element.querySelector(".hljs") ? Constants.TIMEOUT_TRANSITION : 0); }; -export const searchTextMarkRender = (element: HTMLElement, k: string[]) => { +export const searchTextMarkRender = (protyle: IProtyle, keys: string[], element: HTMLElement,) => { if (!isSupportCSSHL()) { return; } + protyle.highlight.markHL.clear(); + protyle.highlight.mark.clear(); + + + // 准备一个数组来保存所有文本节点 + const textNodes: Node[] = []; + const textNodesSize: number[] = []; + let currentSize = 0; + + const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT); + let currentNode = treeWalker.nextNode(); + while (currentNode) { + textNodes.push(currentNode); + currentSize += currentNode.textContent.length + textNodesSize.push(currentSize); + currentNode = treeWalker.nextNode(); + } + + const text = element.textContent; + const rangeIndexes: { range: Range, startIndex: number }[] = []; + + keys.forEach(key => { + let startIndex = 0; + let endIndex = 0; + let currentNodeIndex = 0; + while ((startIndex = text.indexOf(key, startIndex)) !== -1) { + const range = new Range(); + endIndex = startIndex + key.length; + try { + while (currentNodeIndex < textNodes.length && textNodesSize[currentNodeIndex] <= startIndex) { + currentNodeIndex++; + } + let currentTextNode = textNodes[currentNodeIndex]; + range.setStart(currentTextNode, startIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0)); + + while (currentNodeIndex < textNodes.length && textNodesSize[currentNodeIndex] < endIndex) { + currentNodeIndex++ + } + currentTextNode = textNodes[currentNodeIndex] + range.setEnd(currentTextNode, endIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0)); + + rangeIndexes.push({range, startIndex}); + } catch (e) { + console.error("searchMarkRender error:", e); + } + startIndex = endIndex; + } + }) + + rangeIndexes.sort((b, a) => { + if (a.startIndex > b.startIndex) { + return -1 + } else { + return 0 + } + }).forEach((item, index) => { + protyle.highlight.mark.add(item.range); + }); + CSS.highlights.set("search-mark-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.mark); } export const isSupportCSSHL = () => {