79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import type { Breakpoint } from './types';
|
||
import type { Token } from '../core/types';
|
||
|
||
type TcCandidateConfig = {
|
||
tcPunctuations: string[];
|
||
balanceRange: number;
|
||
};
|
||
|
||
/**
|
||
* 计算 BALANCE 的 idealPos 列表(确定性简化版)。
|
||
*
|
||
* - N=tokens.length
|
||
* - targetLines=min(maxLines,N)
|
||
* - lineIndex in 1..targetLines-1:
|
||
* idealPos=round(N*lineIndex/targetLines)
|
||
*/
|
||
export function computeTcIdealPositions(tokens: Token[], maxLines: number): number[] {
|
||
const n = tokens.length;
|
||
const targetLines = Math.max(1, Math.min(maxLines | 0, n));
|
||
const ideals: number[] = [];
|
||
|
||
for (let lineIndex = 1; lineIndex <= targetLines - 1; lineIndex++) {
|
||
const idealPos = Math.round((n * lineIndex) / targetLines);
|
||
ideals.push(idealPos);
|
||
}
|
||
|
||
// 去重并排序(稳定)
|
||
ideals.sort((a, b) => a - b);
|
||
return ideals.filter((v, idx) => idx === 0 || v !== ideals[idx - 1]);
|
||
}
|
||
|
||
/**
|
||
* TC 候选断点生成:
|
||
* - 标点后:kind=PUNCT,priority=30
|
||
* - 空格后:kind=SPACE,priority=20
|
||
* - BALANCE:kind=BALANCE,priority=5(围绕 idealPos ± balanceRange)
|
||
*
|
||
* 注意:BALANCE 断点允许生成在短语 span 内,是否可用交给评分阶段强惩罚淘汰。
|
||
*/
|
||
export function generateTcCandidates(tokens: Token[], maxLines: number, config: TcCandidateConfig): { candidates: Breakpoint[]; idealPositions: number[] } {
|
||
const n = tokens.length;
|
||
if (n <= 1) return { candidates: [], idealPositions: [] };
|
||
|
||
const punctSet = new Set(config.tcPunctuations);
|
||
const candidates: Breakpoint[] = [];
|
||
|
||
// PUNCT / SPACE(扫描 token)
|
||
for (let i = 0; i < n; i++) {
|
||
const t = tokens[i]?.text ?? '';
|
||
const pos = i + 1;
|
||
|
||
// 只允许行内断点
|
||
if (pos < 1 || pos > n - 1) continue;
|
||
|
||
if (punctSet.has(t)) {
|
||
candidates.push({ pos, kind: 'PUNCT', priority: 30 });
|
||
}
|
||
|
||
if (t === ' ') {
|
||
candidates.push({ pos, kind: 'SPACE', priority: 20 });
|
||
}
|
||
}
|
||
|
||
// BALANCE
|
||
const idealPositions = computeTcIdealPositions(tokens, maxLines);
|
||
const range = Math.max(0, config.balanceRange | 0);
|
||
if (range > 0 && idealPositions.length > 0) {
|
||
for (const ideal of idealPositions) {
|
||
for (let pos = ideal - range; pos <= ideal + range; pos++) {
|
||
if (pos < 1 || pos > n - 1) continue;
|
||
candidates.push({ pos, kind: 'BALANCE', priority: 5 });
|
||
}
|
||
}
|
||
}
|
||
|
||
return { candidates, idealPositions };
|
||
}
|
||
|