diff --git a/src/core.js b/src/core.js
index ee8f3c8..626e42b 100644
--- a/src/core.js
+++ b/src/core.js
@@ -115,6 +115,7 @@ class MarkdownEditor {
this._zenMode = false; // v0.1.12: Zen 模式
this._wordWrap = true; // v0.1.12: 自动换行
this._syncing = false; // 滚动同步锁
+ this._hlRaf = null; // v0.1.14: 高亮节流
// 渲染函数:自定义覆盖内置解析器
this._renderFn = (typeof this.config.render === 'function') ? this.config.render : parseMarkdown;
@@ -1318,32 +1319,44 @@ class MarkdownEditor {
// ============ 语法高亮编辑区(v0.1.14) ============
_highlightEditor() {
- if (!this.highlightLayer) return;
+ if (!this.highlightLayer || !this.config.syntaxHighlight) return;
+ // rAF 节流,避免高频更新
+ if (this._hlRaf) return;
+ this._hlRaf = requestAnimationFrame(() => {
+ this._hlRaf = null;
+ this._doHighlight();
+ });
+ }
+
+ _doHighlight() {
const text = this._value || '';
if (!text) { this.highlightLayer.innerHTML = ''; return; }
+ // 超过 50KB 跳过,避免大文档卡顿
+ if (text.length > 50000) { this.highlightLayer.innerHTML = escapeHTML(text) + '\n'; return; }
- // 轻量级 tokenizer:正则匹配所有语法模式,包裹为 span
let html = escapeHTML(text);
- // 标题
- html = html.replace(/^(#{1,6})\s+(.+)$/gm, '$&');
- // 粗体
- html = html.replace(/(\*\*|__)(.+?)\1/g, '$&');
- // 斜体
- html = html.replace(/(?$&');
- // 删除线
- html = html.replace(/~~(.+?)~~/g, '$&');
- // 行内代码
- html = html.replace(/`([^`]+)`/g, '$&');
- // 链接
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$&');
- // 图片
+ // 正则按优先级:先匹配更具体的模式,避免被泛模式吞掉
+ // 1. 图片(先于链接)
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '$&');
- // 列表标记
+ // 2. 链接
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$&');
+ // 3. 行内代码(先于粗体/斜体,避免 `**` 被误匹配)
+ html = html.replace(/`([^`]+)`/g, '$&');
+ // 4. 粗体 **text** / __text__
+ html = html.replace(/(\*\*|__)(.+?)\1/g, '$&');
+ // 5. 删除线
+ html = html.replace(/~~(.+?)~~/g, '$&');
+ // 6. 斜体 *text* / _text_(在粗体之后,避免 ** 被拆成两个斜体)
+ html = html.replace(/(?$&');
+ html = html.replace(/(?$&');
+ // 7. 标题(行级)
+ html = html.replace(/^(#{1,6})\s+(.+)$/gm, '$&');
+ // 8. 列表标记
html = html.replace(/^(\s*[-*+]|\d+\.)\s/gm, '$&');
- // 引用
+ // 9. 引用
html = html.replace(/^(>)\s?/gm, '$&');
- // 水平线
+ // 10. 水平线
html = html.replace(/^([-*_]{3,})\s*$/gm, '$&');
this.highlightLayer.innerHTML = html + '\n';
@@ -1351,6 +1364,19 @@ class MarkdownEditor {
if (this.textarea) this.highlightLayer.scrollTop = this.textarea.scrollTop;
}
+ /** 切换语法高亮(v0.1.14) */
+ toggleSyntaxHighlight() {
+ this.config.syntaxHighlight = !this.config.syntaxHighlight;
+ if (this.textarea) {
+ this.textarea.classList.toggle('me-textarea-highlight', this.config.syntaxHighlight);
+ }
+ if (!this.config.syntaxHighlight && this.highlightLayer) {
+ this.highlightLayer.innerHTML = '';
+ }
+ if (this.config.syntaxHighlight) this._doHighlight();
+ return this;
+ }
+
_updateWordCount() {
if (!this.config.wordCount || !this.statusEl) return;
const text = this._value || '';
@@ -1737,6 +1763,7 @@ class MarkdownEditor {
if (this._renderRaf) cancelAnimationFrame(this._renderRaf);
if (this._historyTimer) clearTimeout(this._historyTimer);
if (this._outlineTimer) clearTimeout(this._outlineTimer);
+ if (this._hlRaf) cancelAnimationFrame(this._hlRaf);
this._cleanups.forEach((fn) => { try { fn(); } catch (_) {} });
this._cleanups = [];