fix: 行內元素三分類(透明/隔離/整體)——jinze 下潛肢解 KaTeX 修復(下游問題六);v1 相容層移除
下游問題六:行內公式緊鄰禁則標點時,jinze 為綁鄰字透明下潛 .katex(全樹皆 SPAN,
僅憑標籤名判透明),把公式內側末字連同完整殼層逐層克隆切出(splitoff),上標漂移
錯位;pillSelector/skipAttribute/scope.avoid 三層防護全被刺穿。復現另揭兩向量:
相鄰 pill 時 descend 刺穿致 jz-hws 插入公式內部;pill 內部標點被 jiya charify。
確立三分類(§4.1,所有遍歷原語共用同一判定):
透明(styleInlines+jz-*)— 穿越/run 延續/可克隆切分;
隔離(isolateSelector,預設 "mark",新增選項)— 邊界斷 run/斷相鄰/不可切分、
兩端補隙(同 pill),內部照常處理(自成脈絡);
整體(pillSelector)— 內部對一切 pass 不可見(insidePill 過濾),禁則時作為
原子鄰字單位整體綁入 jz-jinze。
機制修復:
finder — transparentInline 改以元素判定(先排除 pill/isolate/avoid 再查標籤集);
avoidsSelf 自身判定(isAvoided 僅查祖先,自身命中 avoid 曾被穿越);descend 三態
(邊界字/無內容續找兄弟/blocked 終止——不可越界跳找更遠之字);eachTextNode
過濾 pill 內部;collectRuns 隔離元素內部自成 run。
core/split — canSplit 諮詢 finder(pill/isolate/avoid 不可切 → 退化整綁);
MARK 移出 SPLITTABLE(背景盒切分有可見接縫)。
jiyaAdjacency — pill/avoid(自身判定)斷相鄰不下探;isolate 斷而內部自成脈絡。
spacing Pass B — 邊界選擇器擴為 pill ∪ isolate;巢於 pill 內者跳過。
orphan — avoidsSelf 邊界;avoid 塊不綁(舊漏判:avoid 子樹內裸文本曾被計數包綁)。
v1 相容層移除(消費端已全數遷移 v2 API):src/compat/v1.ts、IIFE 全域別名
createCjkAutospace、死選項 autospaceClass(經查從未被任何 pass 讀取)一併刪;
I8 退役。dist/compat 殘留聲明清除、dist/core/split.d.ts 補入庫(前次遺漏)。
測試 112 全綠(問題六 ×7、isolate ×6、compat 移除 ×1);headless Chrome 實證
公式完整(splitoff=0、內部無 jz-hws/jz-char)、mark 與公式整體入 jz-jinze。
文檔:README 三分類節+選項示例+功能表(hanging 行補「已移除」);ARCHITECTURE
§4.1 三分類/§3.5 相容層移除/§6.1/§6.3/§6.4/I8 退役。
This commit is contained in:
@@ -122,10 +122,12 @@ function processParent(parent: Node, finder: Finder, anyCjk: RegExp): void
|
||||
{
|
||||
// 元素單元(樣式行內如 <strong>)最小綁定(下游 Bug:整元素入 nowrap inline-block
|
||||
// 致行末整體掉行):只隔離與 jz-char 相鄰側之邊界詞元,餘下留原位。雙側禁斷時先
|
||||
// tail 後 head——head 作用於切剩之左半(仍為原元素 u)。不可切之元素退化整綁。
|
||||
// tail 後 head——head 作用於切剩之左半(仍為原元素 u)。不可切之元素(含 pill/
|
||||
// isolate/avoid,由 canSplit 諮詢 finder 判定)退化整綁——pill 即「作為原子
|
||||
// 鄰字單位整體綁入 jz-jinze」之期望行為(下游問題六)。
|
||||
const el = u as Element;
|
||||
if (needLast) { isolateBoundaryToken(el, "tail", anyCjk, PASS); }
|
||||
if (needFirst) { isolateBoundaryToken(el, "head", anyCjk, PASS); }
|
||||
if (needLast) { isolateBoundaryToken(el, "tail", anyCjk, PASS, finder); }
|
||||
if (needFirst) { isolateBoundaryToken(el, "head", anyCjk, PASS, finder); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -158,8 +158,12 @@ export const jiyaAdjacencyPass: StandalonePass = {
|
||||
if (prev) { squeeze(prev, el); }
|
||||
prev = el; // jz-char 為原子,不下探(內含 jz-inner)
|
||||
}
|
||||
else if (finder.pillMatches(el)) { prev = null; } // pill(code/kbd):斷相鄰、不下探(其內容對排版不可見,不應被當透明行內穿越)
|
||||
else if (finder.isAvoided(el)) { prev = null; } // avoid/skip 子樹:斷、不下探
|
||||
else if (finder.pillMatches(el)) { prev = null; } // pill(整體):斷相鄰、不下探(內容對排版不可見)
|
||||
else if (finder.avoidsSelf(el)) { prev = null; } // avoid 邊界(自身判定):斷、不下探
|
||||
else if (finder.isolateMatches(el))
|
||||
{
|
||||
prev = null; visit(el); prev = null; // 隔離:前後皆斷相鄰,內部自成脈絡(同 block)
|
||||
}
|
||||
else if (blockSet.has(nm))
|
||||
{
|
||||
prev = null; visit(el); prev = null; // block 邊界:前後皆斷相鄰,內部另計
|
||||
|
||||
@@ -70,7 +70,9 @@ function collectSubst(
|
||||
}
|
||||
if (node.nodeType !== 1) { return; }
|
||||
const el = node as Element;
|
||||
if (finder.isAvoided(el)) { return; }
|
||||
// avoid 以自身判定(舊版僅查祖先,自身命中 avoid 之元素被下潛);pill 整體
|
||||
// 不計不下探;isolate 內部為正常文本,照常下探計數(綁定時不可切、整綁)。
|
||||
if (finder.avoidsSelf(el)) { return; }
|
||||
if (blockSet.has(el.nodeName) || finder.pillMatches(el)) { return; } // 邊界:不計、不下探
|
||||
for (let c = el.firstChild; c; c = c.nextSibling) { visit(c, directChild); }
|
||||
};
|
||||
@@ -111,7 +113,7 @@ function processBlock(
|
||||
else if (boundary.textNode && startChild.nodeType === 1)
|
||||
{
|
||||
const clone = splitElementAt(
|
||||
startChild as Element, boundary.textNode, boundary.offset, PASS,
|
||||
startChild as Element, boundary.textNode, boundary.offset, PASS, finder,
|
||||
);
|
||||
if (clone) { startChild = clone; }
|
||||
}
|
||||
@@ -144,9 +146,11 @@ export const orphanPass: StandalonePass = {
|
||||
|
||||
finder.eachBlock(ctx.root, (block) =>
|
||||
{
|
||||
// 段落級閘 + per-node 功能閘。
|
||||
// 段落級閘 + per-node 功能閘 + avoid(自身或祖先命中皆不綁——舊版
|
||||
// 漏判,avoid 子樹內塊之裸文本直接子曾被計數並包入 jz-orphan)。
|
||||
if (!finder.levelAllows(block, "paragraph")) { return; }
|
||||
if (!finder.featureEnabledFor(block, PASS)) { return; }
|
||||
if (finder.avoidsSelf(block) || finder.isAvoided(block)) { return; }
|
||||
// 僅處理葉段落(無塊級後代):含巢狀塊之容器,其實義字屬內層段落,另行處理。
|
||||
const sel = Array.from(blockSet).map((t) => t.toLowerCase()).join(",");
|
||||
if (sel && block.querySelector(sel)) { return; }
|
||||
|
||||
+16
-5
@@ -246,11 +246,16 @@ function processBlock(block: Element, finder: Finder, R: Rules): void
|
||||
}
|
||||
}
|
||||
|
||||
function processPills(root: Element, finder: Finder, pillSelector: string, pillNeighbor: RegExp): void
|
||||
// boundarySelector = pill(整體)∪ isolate(隔離):兩端補隙之口徑一致(§4.1
|
||||
// 三分類);差異僅在內部(pill 不可見、isolate 照常處理),不在此 pass。
|
||||
function processPills(root: Element, finder: Finder, boundarySelector: string, pillNeighbor: RegExp): void
|
||||
{
|
||||
root.querySelectorAll(pillSelector).forEach((pill) =>
|
||||
root.querySelectorAll(boundarySelector).forEach((pill) =>
|
||||
{
|
||||
if (finder.isAvoided(pill) || !finder.inScope(pill)) { return; }
|
||||
if (finder.isAvoided(pill) || finder.insidePill(pill) || !finder.inScope(pill))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// per-node 功能閘(T6):data-juzhen-off="spacing" 等區域內之 pill 不補間隙
|
||||
// (與 Pass A 逐 block 之 featureEnabledFor 一致;舊版漏判)。
|
||||
if (!finder.featureEnabledFor(pill, PASS)) { return; }
|
||||
@@ -331,8 +336,14 @@ export const spacingPass: StandalonePass = {
|
||||
{
|
||||
processBlock(ctx.root, finder, R);
|
||||
}
|
||||
// Pass B:pill 邊界。
|
||||
processPills(ctx.root, finder, options.finder.pillSelector, R.pillNeighbor);
|
||||
// Pass B:pill/isolate 邊界。
|
||||
const boundarySelector = [ options.finder.pillSelector, options.finder.isolateSelector ]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
if (boundarySelector)
|
||||
{
|
||||
processPills(ctx.root, finder, boundarySelector, R.pillNeighbor);
|
||||
}
|
||||
},
|
||||
revert(ctx: RenderContext): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user