diff --git a/src/Cherry.js b/src/Cherry.js index 93ee1341..871c4f54 100644 --- a/src/Cherry.js +++ b/src/Cherry.js @@ -232,8 +232,8 @@ export default class Cherry extends CherryStatic { if (this.options.autoScrollByHashAfterInit) { setTimeout(this.scrollByHash.bind(this)); } - // 强制进行一次渲染 - this.editText(null, this.editor.editor); + // 强制进行一次渲染 // 不记得为啥要强制渲染了,先屏蔽了 + // this.editText(null, this.editor.editor); if (this.options.toolbars.toc !== false) { this.createToc(); } @@ -886,11 +886,10 @@ export default class Cherry extends CherryStatic { initText(codemirror) { try { const markdownText = codemirror.getValue(); + this.lastMarkdownText = markdownText; const html = this.engine.makeHtml(markdownText); this.previewer.update(html); - setTimeout(() => { - this.$event.emit('afterInit', { markdownText, html }); - }, 10); + this.$event.emit('afterInit', { markdownText, html }); } catch (e) { throw new NestedError(e); } diff --git a/src/Engine.js b/src/Engine.js index 695fddc3..27bc9849 100644 --- a/src/Engine.js +++ b/src/Engine.js @@ -262,6 +262,13 @@ export default class Engine { return $md; } + makeHtmlForBlockquote(md) { + let $md = md; + $md = this.$dealParagraph($md); + $md = this.$afterMakeHtml($md); + return $md; + } + mounted() { this.$fireHookAction('', 'sentence', 'mounted'); this.$fireHookAction('', 'paragraph', 'mounted'); diff --git a/src/core/ParagraphBase.js b/src/core/ParagraphBase.js index daa32d2d..bcd65524 100644 --- a/src/core/ParagraphBase.js +++ b/src/core/ParagraphBase.js @@ -249,11 +249,12 @@ export default class ParagraphBase extends SyntaxBase { return; } const $sign = sign || this.$engine.md5(str); + const key = `${this.cacheKey}I${$sign}_L${lineCount}$`; this.cache[$sign] = { content: str, - using: true, + key, }; - return `${this.cacheKey}I${$sign}_L${lineCount}$`; + return key; } popCache(sign) { @@ -263,15 +264,28 @@ export default class ParagraphBase extends SyntaxBase { return this.cache[sign].content || ''; } + testHasCache(sign) { + if (!this.needCache || !this.cache[sign]) { + return false; + } + return this.cache[sign].key; + } + + // 当缓存全部被消费后,调用此方法清理多大的缓存 resetCache() { if (!this.needCache) { return; } - for (const key of Object.keys(this.cache)) { - if (!this.cache[key].using) delete this.cache[key]; - } - for (const key of Object.keys(this.cache)) { - this.cache[key].using = false; + // 当缓存队列比较大时,随机抛弃500个缓存 + if (Object.keys(this.cache).length > 3000) { + let limit = 0; + for (const key of Object.keys(this.cache)) { + limit += 1; + if (limit > 500) { + return; + } + delete this.cache[key]; + } } } @@ -285,7 +299,13 @@ export default class ParagraphBase extends SyntaxBase { 'g', ); const $html = html.replace(regex, (match, cacheSign) => this.popCache(cacheSign.replace(/_L\d+$/, ''))); - this.resetCache(); + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + this.timer = setTimeout(() => { + this.resetCache(); + }, 1000); return $html; } @@ -299,8 +319,6 @@ export default class ParagraphBase extends SyntaxBase { if (!this.cache[this.sign]) { return this.toHtml(wholeMatch, sentenceMakeFunc); } - // hit & mark cache - this.cache[this.sign].using = true; return `${this.cacheKey}I${this.sign}_L${lineCount}$`; } diff --git a/src/core/hooks/Blockquote.js b/src/core/hooks/Blockquote.js index 3c35a4db..89a4a79b 100644 --- a/src/core/hooks/Blockquote.js +++ b/src/core/hooks/Blockquote.js @@ -37,61 +37,22 @@ export default class Blockquote extends ParagraphBase { handleMatch(str, sentenceMakeFunc) { return str.replace(this.RULE.reg, (match, lines, content) => { - const { sign: contentSign, html: parsedHtml } = sentenceMakeFunc(content); - const sign = this.signWithCache(parsedHtml) || contentSign; const lineCount = this.getLineCount(match, lines); // 段落所占行数 - const listRegex = - /^(([ \t]{0,3}([*+-]|\d+[.]|[a-z]\.|[I一二三四五六七八九十]+\.)[ \t]+)([^\r]+?)($|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.]|[a-z]\.|[I一二三四五六七八九十]+\.)[ \t]+)))/; - let lastIndent = computeLeadingSpaces(lines); - // 逐行处理 - const contentLines = parsedHtml.split('\n'); - const replaceReg = /^[>\s]+/; - const countReg = />/g; - let lastLevel = 1; - let level = 0; - let handledHtml = `
`; - for (let i = 0; contentLines[i]; i++) { - if (i !== 0) { - const leadIndent = computeLeadingSpaces(contentLines[i]); - if (leadIndent <= lastIndent && listRegex.test(contentLines[i])) { - break; - } - lastIndent = leadIndent; - } - /* eslint-disable no-loop-func */ - const $line = contentLines[i].replace(replaceReg, (leadSymbol) => { - const leadSymbols = leadSymbol.match(countReg); - // 本行引用嵌套层级比上层要多 - if (leadSymbols && leadSymbols.length > lastLevel) { - level = leadSymbols.length; - } else { - // 否则保持当前缩进层级 - level = lastLevel; - } - return ''; - }); - // 同层级,且不为首行时补充一个换行 - if (lastLevel === level && i !== 0) { - handledHtml += '
'; - } - // 补充缩进 - if (lastLevel < level) { - handledHtml += ''.repeat(level - lastLevel); - lastLevel = level; - } - // 插入当前行内容 - handledHtml += $line; + const sign = this.$engine.md5(match); + const testHasCache = this.testHasCache(sign); + if (testHasCache !== false) { + return this.getCacheWithSpace(testHasCache, match); } + let handledHtml = `'; return this.getCacheWithSpace(this.pushCache(handledHtml, sign, lineCount), match); }); } makeHtml(str, sentenceMakeFunc) { - if (!this.test(str)) { - return str; - } return this.handleMatch(str, sentenceMakeFunc); } diff --git a/src/core/hooks/Detail.js b/src/core/hooks/Detail.js index 1dfba0b1..d21c7b6f 100644 --- a/src/core/hooks/Detail.js +++ b/src/core/hooks/Detail.js @@ -39,6 +39,10 @@ export default class Detail extends ParagraphBase { return str.replace(this.RULE.reg, (match, preLines, isOpen, title, content) => { const lineCount = this.getLineCount(match, preLines); const sign = this.$engine.md5(match); + const testHasCache = this.testHasCache(sign); + if (testHasCache !== false) { + return prependLineFeedForParagraph(match, testHasCache); + } const { type, html } = this.$getDetailInfo(isOpen, title, content, sentenceMakeFunc); const ret = this.pushCache( ``; + const $content = content.replace(/^([ \t]*>)/gm, ''); + handledHtml += this.$engine.makeHtmlForBlockquote($content); // 标签闭合 - handledHtml += ''.repeat(lastLevel); + handledHtml += '${html}`, diff --git a/src/core/hooks/Panel.js b/src/core/hooks/Panel.js index 6d377ff5..559bd023 100644 --- a/src/core/hooks/Panel.js +++ b/src/core/hooks/Panel.js @@ -42,6 +42,10 @@ export default class Panel extends ParagraphBase { return str.replace(this.RULE.reg, (match, preLines, name, content) => { const lineCount = this.getLineCount(match, preLines); const sign = this.$engine.md5(match); + const testHasCache = this.testHasCache(sign); + if (testHasCache !== false) { + return prependLineFeedForParagraph(match, testHasCache); + } const { title, body, appendStyle, className } = this.$getPanelInfo(name, content, sentenceMakeFunc); const ret = this.pushCache( `${title}${body}`, diff --git a/src/toolbars/PreviewerBubble.js b/src/toolbars/PreviewerBubble.js index 7c7dc10d..ce33e4f4 100644 --- a/src/toolbars/PreviewerBubble.js +++ b/src/toolbars/PreviewerBubble.js @@ -101,6 +101,10 @@ export default class PreviewerBubble { * @returns {boolean|HTMLElement} */ isCherryCodeBlock(element) { + // 引用里的代码块先不支持所见即所得编辑 + if (this.$getClosestNode(element, 'BLOCKQUOTE') !== false) { + return false; + } if (element.nodeName === 'DIV' && element.dataset.type === 'codeBlock') { return element; } @@ -127,6 +131,10 @@ export default class PreviewerBubble { if (/simple-table/.test(container.className) || !/cherry-table-container/.test(container.className)) { return false; } + // 引用里的表格先不支持所见即所得编辑 + if (this.$getClosestNode(element, 'BLOCKQUOTE') !== false) { + return false; + } return container; } @@ -314,7 +322,12 @@ export default class PreviewerBubble { e.stopPropagation(); // 阻止冒泡,避免触发预览区域的点击事件 break; case 'P': - if (target instanceof HTMLParagraphElement && target.parentElement instanceof HTMLLIElement) { + if ( + target instanceof HTMLParagraphElement && + target.parentElement instanceof HTMLLIElement && + // 引用里的列表先不支持所见即所得编辑 + this.$getClosestNode(target, 'BLOCKQUOTE') === false + ) { if (target.children.length !== 0) { // 富文本 e.preventDefault(); diff --git a/src/utils/listContentHandler.js b/src/utils/listContentHandler.js index ed3830fd..badf079e 100644 --- a/src/utils/listContentHandler.js +++ b/src/utils/listContentHandler.js @@ -128,7 +128,7 @@ export default class ListHandler { const from = { line: targetLine, ch: targetCh }; const to = { line: targetLine + targetContent.length - 1, - ch: targetCh + targetContent[targetContent.length - 1].length, + ch: targetCh + targetContent[targetContent.length - 1]?.length, }; this.editor.editor.setSelection(from, to); this.range = [from, to];