Skip to content

Commit

Permalink
feat: #900 “引用“语法支持嵌套,并支持嵌套其他语法(如表格、代码块、列表、信息面板等)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunsonliu committed Sep 20, 2024
1 parent 3b4bf0d commit d7a887d
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 64 deletions.
9 changes: 4 additions & 5 deletions src/Cherry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
}
Expand Down
7 changes: 7 additions & 0 deletions src/Engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
38 changes: 28 additions & 10 deletions src/core/ParagraphBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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];
}
}
}

Expand All @@ -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;
}

Expand All @@ -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}$`;
}

Expand Down
55 changes: 8 additions & 47 deletions src/core/hooks/Blockquote.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<blockquote data-sign="${sign}_${lineCount}" data-lines="${lineCount}">`;
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 += '<br>';
}
// 补充缩进
if (lastLevel < level) {
handledHtml += '<blockquote>'.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 = `<blockquote data-sign="${sign}_${lineCount}" data-lines="${lineCount}">`;
const $content = content.replace(/^([ \t]*>)/gm, '');
handledHtml += this.$engine.makeHtmlForBlockquote($content);
// 标签闭合
handledHtml += '</blockquote>'.repeat(lastLevel);
handledHtml += '</blockquote>';
return this.getCacheWithSpace(this.pushCache(handledHtml, sign, lineCount), match);
});
}

makeHtml(str, sentenceMakeFunc) {
if (!this.test(str)) {
return str;
}
return this.handleMatch(str, sentenceMakeFunc);
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/hooks/Detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
`<div class="cherry-detail cherry-detail__${type}" data-sign="${sign}" data-lines="${lineCount}" >${html}</div>`,
Expand Down
4 changes: 4 additions & 0 deletions src/core/hooks/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
`<div class="${className}" data-sign="${sign}" data-lines="${lineCount}" ${appendStyle}>${title}${body}</div>`,
Expand Down
15 changes: 14 additions & 1 deletion src/toolbars/PreviewerBubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}

Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/utils/listContentHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down

0 comments on commit d7a887d

Please sign in to comment.