feat: 字符分類規則集抽象(charClass,可配置;向下兼容)
把散落於各 pass 之硬編碼字符分類(中文 CJK/西文 ANS/全寬標點 BD_* 子類)統一抽象為
規則集 Ruleset:內置默認即現狀值,消費端經 options.charClass 擴充,以覆寫 spacing/
jiya/jinze 對特定字符之分類判定。
動機:英文全角句號 .(U+FF0E,異於中文句號 。U+3002)實證為**完全未分類**——不被
charify、不挤壓、與 CJK 間不補隙。與其每次硬編碼補字符(如先前 € 之西文集),不如給
消費端可配置之分類鉤子。
API(追加語義):
charClass?: {
cjk?: string; // 追加為中文
western?: string; // 追加為西文(半寬標點亦歸此)
biaodian?: { stop?, pause?, open?, close?, middle?, liga? }; // 追加為全寬標點,按子類
}
實作:
- core/unicode.ts:新增 Ruleset/CharClassOverride/BdSubclass 型別、DEFAULT_RULESET、
resolveRuleset(追加合併);classifyBiaodian(ch, biaodian) 改吃規則集(預設參數向下兼容)。
- types.ts:JuzhenOptions.charClass;ResolvedOptions.ruleset;index 再匯出公開型別。
- index.ts:resolveRuleset(opts.charClass) → ResolvedOptions.ruleset。
- typeset/spacing.ts:所有 CJK/ANS 依賴正則移入 makeRules(ruleset),per-instance 編譯
(非每 block);ALNUM/ALPHA/運算符等結構性 ASCII 仍為固定常數。
- typeset/jiya.ts:classifyBiaodian(ch, ruleset.biaodian)。
- typeset/jinze.ts:詞元邊界之 anyCjk 由 ruleset.cjk 建。
向下兼容:不傳 charClass 則 ruleset === DEFAULT_RULESET、行為與重構前完全一致。
v1 為追加語義,不支援重分類/移除內置字符(範圍式集合難挖洞、需求罕見,列後續)。
驗證:實 Chromium end-to-end——charClass.biaodian.stop="." → . charify 為 bd-stop
(开明式句末維持全角,比照 。);western="★"/cjk="〇" → 與對側正確補/不補中西間隙。
測試 66 通過(含 9 條 charClass 新測——默認未分類/各子類 charify/實例隔離不可變
(resolveRuleset 不污染 DEFAULT)/追加不破壞內置/多子類組合;57 既有不變)。
- README「自定義字符分類」節+ARCHITECTURE §4.2 規則集抽象。dist 重建。
This commit is contained in:
Vendored
+147
-101
@@ -522,18 +522,52 @@ var Juzhen = (() => {
|
||||
var BD_MIDDLE = "\xB7\u30FB\u2027";
|
||||
var BD_LIGA = "\u2026\u2014";
|
||||
var BD_ALL = BD_STOP + BD_PAUSE + BD_OPEN + BD_CLOSE + BD_MIDDLE + BD_LIGA;
|
||||
var BD_SETS = [
|
||||
["bd-stop", BD_STOP],
|
||||
["bd-pause", BD_PAUSE],
|
||||
["bd-open", BD_OPEN],
|
||||
["bd-close", BD_CLOSE],
|
||||
["bd-middle", BD_MIDDLE],
|
||||
["bd-liga", BD_LIGA]
|
||||
var BD_ORDER = [
|
||||
"stop",
|
||||
"pause",
|
||||
"open",
|
||||
"close",
|
||||
"middle",
|
||||
"liga"
|
||||
];
|
||||
function classifyBiaodian(ch) {
|
||||
for (const [name, set] of BD_SETS) {
|
||||
if (set.indexOf(ch) >= 0) {
|
||||
return name;
|
||||
var DEFAULT_RULESET = {
|
||||
cjk: CJK,
|
||||
ansAfter: ANS_AFTER_CJK,
|
||||
ansBefore: ANS_BEFORE_CJK,
|
||||
biaodian: {
|
||||
stop: BD_STOP,
|
||||
pause: BD_PAUSE,
|
||||
open: BD_OPEN,
|
||||
close: BD_CLOSE,
|
||||
middle: BD_MIDDLE,
|
||||
liga: BD_LIGA
|
||||
}
|
||||
};
|
||||
function resolveRuleset(override) {
|
||||
if (!override) {
|
||||
return DEFAULT_RULESET;
|
||||
}
|
||||
const west = override.western || "";
|
||||
const bd = { ...DEFAULT_RULESET.biaodian };
|
||||
if (override.biaodian) {
|
||||
for (const sub of BD_ORDER) {
|
||||
const extra = override.biaodian[sub];
|
||||
if (extra) {
|
||||
bd[sub] = bd[sub] + extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
cjk: CJK + (override.cjk || ""),
|
||||
ansAfter: ANS_AFTER_CJK + west,
|
||||
ansBefore: ANS_BEFORE_CJK + west,
|
||||
biaodian: bd
|
||||
};
|
||||
}
|
||||
function classifyBiaodian(ch, biaodian = DEFAULT_RULESET.biaodian) {
|
||||
for (const sub of BD_ORDER) {
|
||||
if (biaodian[sub].indexOf(ch) >= 0) {
|
||||
return "bd-" + sub;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -642,9 +676,10 @@ var Juzhen = (() => {
|
||||
let style = null;
|
||||
let langClass = null;
|
||||
const biasOverrides = ctx.options.jiyaConfig.bias;
|
||||
const biaodian = ctx.options.ruleset.biaodian;
|
||||
for (let i = 0; i < data.length; i += 1) {
|
||||
const ch = data.charAt(i);
|
||||
const bdClass = classifyBiaodian(ch);
|
||||
const bdClass = classifyBiaodian(ch, biaodian);
|
||||
if (!bdClass) {
|
||||
continue;
|
||||
}
|
||||
@@ -737,46 +772,46 @@ var Juzhen = (() => {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isTokenChar(ch) {
|
||||
return !ANY_CJK.test(ch) && !/\s/.test(ch);
|
||||
function isTokenChar(ch, anyCjk) {
|
||||
return !anyCjk.test(ch) && !/\s/.test(ch);
|
||||
}
|
||||
function leadingTokenEnd(d) {
|
||||
if (ANY_CJK.test(d.charAt(0))) {
|
||||
function leadingTokenEnd(d, anyCjk) {
|
||||
if (anyCjk.test(d.charAt(0))) {
|
||||
return 1;
|
||||
}
|
||||
let e = 1;
|
||||
while (e < d.length && isTokenChar(d.charAt(e))) {
|
||||
while (e < d.length && isTokenChar(d.charAt(e), anyCjk)) {
|
||||
e += 1;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
function trailingTokenStart(d) {
|
||||
function trailingTokenStart(d, anyCjk) {
|
||||
const last = d.length - 1;
|
||||
if (ANY_CJK.test(d.charAt(last))) {
|
||||
if (anyCjk.test(d.charAt(last))) {
|
||||
return last;
|
||||
}
|
||||
let s = last;
|
||||
while (s > 0 && isTokenChar(d.charAt(s - 1))) {
|
||||
while (s > 0 && isTokenChar(d.charAt(s - 1), anyCjk)) {
|
||||
s -= 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function splitBoundaries(t, needFirst, needLast) {
|
||||
function splitBoundaries(t, needFirst, needLast, anyCjk) {
|
||||
let node = t;
|
||||
if (needFirst) {
|
||||
const e = leadingTokenEnd(node.data);
|
||||
const e = leadingTokenEnd(node.data, anyCjk);
|
||||
if (e < node.data.length) {
|
||||
node = node.splitText(e);
|
||||
}
|
||||
}
|
||||
if (needLast) {
|
||||
const s = trailingTokenStart(node.data);
|
||||
const s = trailingTokenStart(node.data, anyCjk);
|
||||
if (s > 0) {
|
||||
node.splitText(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
function processParent(parent, finder) {
|
||||
function processParent(parent, finder, anyCjk) {
|
||||
for (let c = parent.firstChild; c; c = c.nextSibling) {
|
||||
if (c.nodeType === 1 && c.nodeName === "JZ-JINZE") {
|
||||
return;
|
||||
@@ -806,7 +841,7 @@ var Juzhen = (() => {
|
||||
const needFirst = i2 > 0 && forbidden[i2 - 1];
|
||||
const needLast = i2 < n - 1 && forbidden[i2];
|
||||
if (needFirst || needLast) {
|
||||
splitBoundaries(u, needFirst, needLast);
|
||||
splitBoundaries(u, needFirst, needLast, anyCjk);
|
||||
}
|
||||
}
|
||||
const kids = [];
|
||||
@@ -836,6 +871,7 @@ var Juzhen = (() => {
|
||||
order: 40,
|
||||
enabled: (o) => o.features.jinze,
|
||||
render(ctx) {
|
||||
const anyCjk = new RegExp("[" + ctx.options.ruleset.cjk + "]");
|
||||
const parents = /* @__PURE__ */ new Set();
|
||||
ctx.root.querySelectorAll("jz-char").forEach((c) => {
|
||||
if (c.parentNode) {
|
||||
@@ -843,7 +879,7 @@ var Juzhen = (() => {
|
||||
}
|
||||
});
|
||||
for (const parent of parents) {
|
||||
processParent(parent, ctx.finder);
|
||||
processParent(parent, ctx.finder, anyCjk);
|
||||
}
|
||||
},
|
||||
revert(ctx) {
|
||||
@@ -1089,79 +1125,88 @@ var Juzhen = (() => {
|
||||
var LBR_EXT = "\\(\\[\\{<>";
|
||||
var RBR_EXT = "\\)\\]\\}<>";
|
||||
var DEL = String.fromCharCode(57347);
|
||||
var STRIP_CJK_SPACE_ANS = new RegExp("([" + CJK + "]) ([" + ANS_AFTER_CJK + "])", "g");
|
||||
var STRIP_ANS_SPACE_CJK = new RegExp("([" + ANS_BEFORE_CJK + "]) ([" + CJK + "])", "g");
|
||||
var STRIP_CJK_SPACE_CJK = new RegExp("([" + CJK + "]) (?=[" + CJK + "])", "g");
|
||||
var DOTS_CJK = new RegExp("([\\.]{2,})([" + CJK + "])", "g");
|
||||
var CJK_PUNCTUATION = new RegExp("([" + CJK + "])([!;,\\?:]+)(?=[" + CJK + ALNUM + "])", "g");
|
||||
var AN_PUNCTUATION_CJK = new RegExp("([" + ALNUM + "])([!;,\\?]+)([" + CJK + "])", "g");
|
||||
var CJK_TILDE = new RegExp("([" + CJK + "])(~+)(?!=)(?=[" + CJK + ALNUM + "])", "g");
|
||||
var CJK_TILDE_EQUALS = new RegExp("([" + CJK + "])(~=)", "g");
|
||||
var CJK_PERIOD = new RegExp("([" + CJK + "])(\\.)(?![" + ALNUM + "\\./])(?=[" + CJK + ALNUM + "])", "g");
|
||||
var AN_PERIOD_CJK = new RegExp("([" + ALNUM + "])(\\.)([" + CJK + "])", "g");
|
||||
var AN_COLON_CJK = new RegExp("([" + ALNUM + "])(:)([" + CJK + "])", "g");
|
||||
var CJK_QUOTE = new RegExp("([" + CJK + "])([" + QUOTES_CLS + "])", "g");
|
||||
var QUOTE_CJK = new RegExp("([" + QUOTES_CLS + "])([" + CJK + "])", "g");
|
||||
var CJK_QUOTE_AN = new RegExp("([" + CJK + '])(")([' + ALNUM + "])", "g");
|
||||
var CJK_HASH = new RegExp("([" + CJK + "])(#([^ ]))", "g");
|
||||
var HASH_CJK = new RegExp("(([^ ])#)([" + CJK + "])", "g");
|
||||
var SINGLE_LETTER_GRADE = new RegExp("\\b([" + ALPHA + "])([" + OPS_GRADE + "])([" + CJK + "])", "g");
|
||||
var CJK_OPERATOR_ANS = new RegExp("([" + CJK + "])([" + OPS_HYPHEN + "])([" + ALNUM + "])", "g");
|
||||
var ANS_OPERATOR_CJK = new RegExp("([" + ALNUM + "])([" + OPS_HYPHEN + "])([" + CJK + "])", "g");
|
||||
var CJK_LESS_THAN = new RegExp("([" + CJK + "])(<)([" + ALNUM + "])", "g");
|
||||
var LESS_THAN_CJK = new RegExp("([" + ALNUM + "])(<)([" + CJK + "])", "g");
|
||||
var CJK_GREATER_THAN = new RegExp("([" + CJK + "])(>)([" + ALNUM + "])", "g");
|
||||
var GREATER_THAN_CJK = new RegExp("([" + ALNUM + "])(>)([" + CJK + "])", "g");
|
||||
var CJK_LEFT_BRACKET = new RegExp("([" + CJK + "])([" + LBR_EXT + "])", "g");
|
||||
var RIGHT_BRACKET_CJK = new RegExp("([" + RBR_EXT + "])([" + CJK + "])", "g");
|
||||
var AN_LEFT_BRACKET = new RegExp("([" + ALNUM + "])(?<!\\.[" + ALNUM + "]*)([" + LBR_BASIC + "])", "g");
|
||||
var RIGHT_BRACKET_AN = new RegExp("([" + RBR_BASIC + "])([" + ALNUM + "])", "g");
|
||||
var CJK_ANS = new RegExp("([" + CJK + "])([" + ANS_AFTER_CJK + "])", "g");
|
||||
var ANS_CJK = new RegExp("([" + ANS_BEFORE_CJK + "])([" + CJK + "])", "g");
|
||||
var S_A = new RegExp("(%)([" + ALPHA + "])", "g");
|
||||
function makeRules(rs) {
|
||||
const CJK2 = rs.cjk;
|
||||
const ANS_AFTER = rs.ansAfter;
|
||||
const ANS_BEFORE = rs.ansBefore;
|
||||
return {
|
||||
anyCjk: new RegExp("[" + CJK2 + "]"),
|
||||
pillNeighbor: new RegExp("[" + ALNUM + CJK2 + "]"),
|
||||
STRIP_CJK_SPACE_ANS: new RegExp("([" + CJK2 + "]) ([" + ANS_AFTER + "])", "g"),
|
||||
STRIP_ANS_SPACE_CJK: new RegExp("([" + ANS_BEFORE + "]) ([" + CJK2 + "])", "g"),
|
||||
STRIP_CJK_SPACE_CJK: new RegExp("([" + CJK2 + "]) (?=[" + CJK2 + "])", "g"),
|
||||
DOTS_CJK: new RegExp("([\\.]{2,})([" + CJK2 + "])", "g"),
|
||||
CJK_PUNCTUATION: new RegExp("([" + CJK2 + "])([!;,\\?:]+)(?=[" + CJK2 + ALNUM + "])", "g"),
|
||||
AN_PUNCTUATION_CJK: new RegExp("([" + ALNUM + "])([!;,\\?]+)([" + CJK2 + "])", "g"),
|
||||
CJK_TILDE: new RegExp("([" + CJK2 + "])(~+)(?!=)(?=[" + CJK2 + ALNUM + "])", "g"),
|
||||
CJK_TILDE_EQUALS: new RegExp("([" + CJK2 + "])(~=)", "g"),
|
||||
CJK_PERIOD: new RegExp("([" + CJK2 + "])(\\.)(?![" + ALNUM + "\\./])(?=[" + CJK2 + ALNUM + "])", "g"),
|
||||
AN_PERIOD_CJK: new RegExp("([" + ALNUM + "])(\\.)([" + CJK2 + "])", "g"),
|
||||
AN_COLON_CJK: new RegExp("([" + ALNUM + "])(:)([" + CJK2 + "])", "g"),
|
||||
CJK_QUOTE: new RegExp("([" + CJK2 + "])([" + QUOTES_CLS + "])", "g"),
|
||||
QUOTE_CJK: new RegExp("([" + QUOTES_CLS + "])([" + CJK2 + "])", "g"),
|
||||
CJK_QUOTE_AN: new RegExp("([" + CJK2 + '])(")([' + ALNUM + "])", "g"),
|
||||
CJK_HASH: new RegExp("([" + CJK2 + "])(#([^ ]))", "g"),
|
||||
HASH_CJK: new RegExp("(([^ ])#)([" + CJK2 + "])", "g"),
|
||||
SINGLE_LETTER_GRADE: new RegExp("\\b([" + ALPHA + "])([" + OPS_GRADE + "])([" + CJK2 + "])", "g"),
|
||||
CJK_OPERATOR_ANS: new RegExp("([" + CJK2 + "])([" + OPS_HYPHEN + "])([" + ALNUM + "])", "g"),
|
||||
ANS_OPERATOR_CJK: new RegExp("([" + ALNUM + "])([" + OPS_HYPHEN + "])([" + CJK2 + "])", "g"),
|
||||
CJK_LESS_THAN: new RegExp("([" + CJK2 + "])(<)([" + ALNUM + "])", "g"),
|
||||
LESS_THAN_CJK: new RegExp("([" + ALNUM + "])(<)([" + CJK2 + "])", "g"),
|
||||
CJK_GREATER_THAN: new RegExp("([" + CJK2 + "])(>)([" + ALNUM + "])", "g"),
|
||||
GREATER_THAN_CJK: new RegExp("([" + ALNUM + "])(>)([" + CJK2 + "])", "g"),
|
||||
CJK_LEFT_BRACKET: new RegExp("([" + CJK2 + "])([" + LBR_EXT + "])", "g"),
|
||||
RIGHT_BRACKET_CJK: new RegExp("([" + RBR_EXT + "])([" + CJK2 + "])", "g"),
|
||||
AN_LEFT_BRACKET: new RegExp("([" + ALNUM + "])(?<!\\.[" + ALNUM + "]*)([" + LBR_BASIC + "])", "g"),
|
||||
RIGHT_BRACKET_AN: new RegExp("([" + RBR_BASIC + "])([" + ALNUM + "])", "g"),
|
||||
CJK_ANS: new RegExp("([" + CJK2 + "])([" + ANS_AFTER + "])", "g"),
|
||||
ANS_CJK: new RegExp("([" + ANS_BEFORE + "])([" + CJK2 + "])", "g"),
|
||||
S_A: new RegExp("(%)([" + ALPHA + "])", "g")
|
||||
};
|
||||
}
|
||||
var COMPOUND_WORD = /\b(?:[A-Za-z0-9]*[a-z][A-Za-z0-9]*-[A-Za-z0-9]+|[A-Za-z0-9]+-[A-Za-z0-9]*[a-z][A-Za-z0-9]*|[A-Za-z]+-[0-9]+|[A-Za-z]+[0-9]+-[A-Za-z0-9]+)(?:-[A-Za-z0-9]+)*\b/g;
|
||||
var PH_PATTERN = new RegExp(PH_OPEN + "(\\d+)" + PH_CLOSE, "g");
|
||||
function panguSpace(text) {
|
||||
if (text.length <= 1 || !ANY_CJK.test(text)) {
|
||||
function panguSpace(text, R) {
|
||||
if (text.length <= 1 || !R.anyCjk.test(text)) {
|
||||
return text;
|
||||
}
|
||||
let s = text;
|
||||
s = s.replace(STRIP_CJK_SPACE_ANS, "$1$2");
|
||||
s = s.replace(STRIP_ANS_SPACE_CJK, "$1$2");
|
||||
s = s.replace(STRIP_CJK_SPACE_CJK, "$1" + DEL);
|
||||
s = s.replace(DOTS_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(CJK_PUNCTUATION, "$1$2" + MARK);
|
||||
s = s.replace(AN_PUNCTUATION_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(CJK_TILDE, "$1$2" + MARK);
|
||||
s = s.replace(CJK_TILDE_EQUALS, "$1" + MARK + "$2" + MARK);
|
||||
s = s.replace(CJK_PERIOD, "$1$2" + MARK);
|
||||
s = s.replace(AN_PERIOD_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(AN_COLON_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(CJK_QUOTE, "$1" + MARK + "$2");
|
||||
s = s.replace(QUOTE_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(CJK_QUOTE_AN, "$1$2" + MARK + "$3");
|
||||
s = s.replace(CJK_HASH, "$1" + MARK + "$2");
|
||||
s = s.replace(HASH_CJK, "$1" + MARK + "$3");
|
||||
s = s.replace(R.STRIP_CJK_SPACE_ANS, "$1$2");
|
||||
s = s.replace(R.STRIP_ANS_SPACE_CJK, "$1$2");
|
||||
s = s.replace(R.STRIP_CJK_SPACE_CJK, "$1" + DEL);
|
||||
s = s.replace(R.DOTS_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(R.CJK_PUNCTUATION, "$1$2" + MARK);
|
||||
s = s.replace(R.AN_PUNCTUATION_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_TILDE, "$1$2" + MARK);
|
||||
s = s.replace(R.CJK_TILDE_EQUALS, "$1" + MARK + "$2" + MARK);
|
||||
s = s.replace(R.CJK_PERIOD, "$1$2" + MARK);
|
||||
s = s.replace(R.AN_PERIOD_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(R.AN_COLON_CJK, "$1$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_QUOTE, "$1" + MARK + "$2");
|
||||
s = s.replace(R.QUOTE_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(R.CJK_QUOTE_AN, "$1$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_HASH, "$1" + MARK + "$2");
|
||||
s = s.replace(R.HASH_CJK, "$1" + MARK + "$3");
|
||||
const compounds = [];
|
||||
s = s.replace(COMPOUND_WORD, (m) => {
|
||||
compounds.push(m);
|
||||
return PH_OPEN + (compounds.length - 1) + PH_CLOSE;
|
||||
});
|
||||
s = s.replace(SINGLE_LETTER_GRADE, "$1$2" + MARK + "$3");
|
||||
s = s.replace(CJK_OPERATOR_ANS, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(ANS_OPERATOR_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(CJK_LESS_THAN, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(LESS_THAN_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(CJK_GREATER_THAN, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(GREATER_THAN_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.SINGLE_LETTER_GRADE, "$1$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_OPERATOR_ANS, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.ANS_OPERATOR_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_LESS_THAN, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.LESS_THAN_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.CJK_GREATER_THAN, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(R.GREATER_THAN_CJK, "$1" + MARK + "$2" + MARK + "$3");
|
||||
s = s.replace(PH_PATTERN, (_m, idx) => compounds[Number(idx)] || "");
|
||||
s = s.replace(CJK_LEFT_BRACKET, "$1" + MARK + "$2");
|
||||
s = s.replace(RIGHT_BRACKET_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(AN_LEFT_BRACKET, "$1" + MARK + "$2");
|
||||
s = s.replace(RIGHT_BRACKET_AN, "$1" + MARK + "$2");
|
||||
s = s.replace(CJK_ANS, "$1" + MARK + "$2");
|
||||
s = s.replace(ANS_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(S_A, "$1" + MARK + "$2");
|
||||
s = s.replace(R.CJK_LEFT_BRACKET, "$1" + MARK + "$2");
|
||||
s = s.replace(R.RIGHT_BRACKET_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(R.AN_LEFT_BRACKET, "$1" + MARK + "$2");
|
||||
s = s.replace(R.RIGHT_BRACKET_AN, "$1" + MARK + "$2");
|
||||
s = s.replace(R.CJK_ANS, "$1" + MARK + "$2");
|
||||
s = s.replace(R.ANS_CJK, "$1" + MARK + "$2");
|
||||
s = s.replace(R.S_A, "$1" + MARK + "$2");
|
||||
return s;
|
||||
}
|
||||
function wrapNodeInGap(node) {
|
||||
@@ -1186,11 +1231,11 @@ var Juzhen = (() => {
|
||||
}
|
||||
wrapNodeInGap(tn);
|
||||
}
|
||||
function processBlock(block, finder) {
|
||||
function processBlock(block, finder, R) {
|
||||
const runs = finder.collectRuns(block);
|
||||
for (const run of runs) {
|
||||
const { text, map } = run;
|
||||
const spaced = panguSpace(text);
|
||||
const spaced = panguSpace(text, R);
|
||||
if (spaced === text) {
|
||||
continue;
|
||||
}
|
||||
@@ -1231,8 +1276,7 @@ var Juzhen = (() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
var PILL_NEIGHBOR = new RegExp("[A-Za-z0-9" + CJK + "]");
|
||||
function processPills(root, finder, pillSelector) {
|
||||
function processPills(root, finder, pillSelector, pillNeighbor) {
|
||||
root.querySelectorAll(pillSelector).forEach((pill) => {
|
||||
if (finder.isAvoided(pill) || !finder.inScope(pill)) {
|
||||
return;
|
||||
@@ -1242,36 +1286,36 @@ var Juzhen = (() => {
|
||||
}
|
||||
const prev = finder.adjacentLogicalChar(pill, -1);
|
||||
if (prev) {
|
||||
handlePillSide(prev, pill, -1);
|
||||
handlePillSide(prev, pill, -1, pillNeighbor);
|
||||
}
|
||||
const next = finder.adjacentLogicalChar(pill, 1);
|
||||
if (next) {
|
||||
handlePillSide(next, pill, 1);
|
||||
handlePillSide(next, pill, 1, pillNeighbor);
|
||||
}
|
||||
});
|
||||
}
|
||||
function handlePillSide(adj, pill, dir) {
|
||||
function handlePillSide(adj, pill, dir, pillNeighbor) {
|
||||
if (dir === -1) {
|
||||
if (PILL_NEIGHBOR.test(adj.ch)) {
|
||||
if (pillNeighbor.test(adj.ch)) {
|
||||
wrapGapLeft({ textNode: adj.textNode, offset: adj.offset });
|
||||
return;
|
||||
}
|
||||
if (adj.ch === " " && adj.offset >= 1) {
|
||||
const tn = adj.textNode;
|
||||
const bc = tn.data.charAt(adj.offset - 1);
|
||||
if (bc !== " " && PILL_NEIGHBOR.test(bc)) {
|
||||
if (bc !== " " && pillNeighbor.test(bc)) {
|
||||
tn.data = tn.data.slice(0, adj.offset) + tn.data.slice(adj.offset + 1);
|
||||
wrapGapLeft({ textNode: tn, offset: adj.offset - 1 });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (PILL_NEIGHBOR.test(adj.ch)) {
|
||||
if (pillNeighbor.test(adj.ch)) {
|
||||
wrapNodeInGap(pill);
|
||||
return;
|
||||
}
|
||||
if (adj.ch === " " && adj.textNode.data.length >= 2) {
|
||||
const ac = adj.textNode.data.charAt(1);
|
||||
if (ac !== " " && PILL_NEIGHBOR.test(ac)) {
|
||||
if (ac !== " " && pillNeighbor.test(ac)) {
|
||||
adj.textNode.data = adj.textNode.data.slice(1);
|
||||
wrapNodeInGap(pill);
|
||||
}
|
||||
@@ -1285,13 +1329,14 @@ var Juzhen = (() => {
|
||||
enabled: (o) => o.features.spacing,
|
||||
render(ctx) {
|
||||
const { finder, options } = ctx;
|
||||
const R = makeRules(options.ruleset);
|
||||
ctx.finder.eachBlock(ctx.root, (block) => {
|
||||
if (!finder.featureEnabledFor(block, PASS4)) {
|
||||
return;
|
||||
}
|
||||
processBlock(block, finder);
|
||||
processBlock(block, finder, R);
|
||||
});
|
||||
processPills(ctx.root, finder, options.finder.pillSelector);
|
||||
processPills(ctx.root, finder, options.finder.pillSelector, R.pillNeighbor);
|
||||
},
|
||||
revert(ctx) {
|
||||
revertPass(PASS4, ctx.root);
|
||||
@@ -1482,6 +1527,7 @@ var Juzhen = (() => {
|
||||
...langOpt.style ? { style: langOpt.style } : {},
|
||||
policy: langOpt.policy || {}
|
||||
},
|
||||
ruleset: resolveRuleset(opts.charClass),
|
||||
autospaceClass: opts.autospaceClass || "",
|
||||
justifyAtoms: opts.justifyAtoms === false ? false : true,
|
||||
scope: {
|
||||
|
||||
Reference in New Issue
Block a user