fill.ts가 A의 실제 moveFormula·toA1을 쓰도록 정리(식 채우기 시험 통과). 실무 구조도 xlsx 세 벌 실측 형식 코드 66개 대조 — `_x` trailing 공백을 trim이 삼키던 버그, 글 값에 순수 수 형식이 적용되던 버그, `General"단위"` 접미 버릇, `[$통화-로캘]` 태그 미지원을 고침. 분수 형식(`0/0`)은 1단계 형식 부분집합 밖이라 남겨둠. 회귀 시험 추가. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RrQ65VtGZbae2VKgYVMhmc
91 lines
4.0 KiB
TypeScript
91 lines
4.0 KiB
TypeScript
/* =============================================================================
|
|
* spreadsheet_fill.ts (주인 B)
|
|
* 채우기 핸들 — 원본 범위를 대상 범위로 늘림: 수 하나 = 복사 · 수 둘 이상 = 등차 연속 ·
|
|
* 글 끝 번호(`구-01` → `구-02`) · 식 = 상대 참조 옮김(A `moveFormula`) · 서식 같이.
|
|
* 결과는 `{ 종류: "칸" }` 명령에 넣을 칸 뭉치.
|
|
* ========================================================================== */
|
|
|
|
import type { Cell, CellRange, FillDirection, Workbook } from "./spreadsheet_types";
|
|
import { moveFormula } from "./spreadsheet_refshift";
|
|
import { toA1 } from "./spreadsheet_address";
|
|
|
|
function detectDirection(source: CellRange, target: CellRange): FillDirection {
|
|
if (target.r1 > source.r1 && target.c0 === source.c0 && target.c1 === source.c1) return "down";
|
|
if (target.r0 < source.r0 && target.c0 === source.c0 && target.c1 === source.c1) return "up";
|
|
if (target.c1 > source.c1 && target.r0 === source.r0 && target.r1 === source.r1) return "right";
|
|
return "left";
|
|
}
|
|
|
|
/** 글 끝 숫자를 옮김(`구-01`,step 1 → `구-02`) · 끝 숫자가 없으면 그대로 */
|
|
function incrementTrailingNumber(text: string, step: number): string {
|
|
const m = /^(.*?)(\d+)$/.exec(text);
|
|
if (!m) return text;
|
|
const [, prefix, digits] = m;
|
|
const next = BigInt(digits) + BigInt(step);
|
|
const clamped = next < 0n ? 0n : next;
|
|
return prefix + clamped.toString().padStart(digits.length, "0");
|
|
}
|
|
|
|
/** `target` 은 `source` 를 한 방향으로 늘린 범위(방향은 둘을 견줘 앎) · A1 → 칸(null = 비움) */
|
|
export function fillCells(
|
|
book: Workbook,
|
|
sheet: string,
|
|
source: CellRange,
|
|
target: CellRange,
|
|
): Record<string, Cell | null> {
|
|
const sheetObj = book.시트.find((s) => s.id === sheet);
|
|
const out: Record<string, Cell | null> = {};
|
|
if (!sheetObj) return out;
|
|
|
|
const direction = detectDirection(source, target);
|
|
const vertical = direction === "down" || direction === "up";
|
|
const lineCount = vertical ? source.c1 - source.c0 + 1 : source.r1 - source.r0 + 1;
|
|
|
|
for (let li = 0; li < lineCount; li++) {
|
|
const fixed = vertical ? source.c0 + li : source.r0 + li;
|
|
const sourceStart = vertical ? source.r0 : source.c0;
|
|
const sourceEnd = vertical ? source.r1 : source.c1;
|
|
const targetStart = vertical ? target.r0 : target.c0;
|
|
const targetEnd = vertical ? target.r1 : target.c1;
|
|
const lineLen = sourceEnd - sourceStart + 1;
|
|
|
|
const key = (pos: number): string => (vertical ? toA1(pos, fixed) : toA1(fixed, pos));
|
|
const sourceCells: (Cell | undefined)[] = [];
|
|
for (let p = sourceStart; p <= sourceEnd; p++) sourceCells.push(sheetObj.칸[key(p)]);
|
|
|
|
const allNumeric = sourceCells.every(
|
|
(c) => c !== undefined && c.식 === undefined && typeof c.값 === "number",
|
|
);
|
|
const nums = allNumeric ? sourceCells.map((c) => c!.값 as number) : [];
|
|
const step = nums.length > 1 ? (nums[nums.length - 1] - nums[0]) / (nums.length - 1) : 0;
|
|
|
|
for (let p = targetStart; p <= targetEnd; p++) {
|
|
if (p >= sourceStart && p <= sourceEnd) continue;
|
|
const rel = p - sourceStart;
|
|
const slot = ((rel % lineLen) + lineLen) % lineLen;
|
|
const period = (rel - slot) / lineLen;
|
|
const template = sourceCells[slot];
|
|
const outKey = key(p);
|
|
if (template === undefined) {
|
|
out[outKey] = null;
|
|
continue;
|
|
}
|
|
const cell: Cell = {};
|
|
if (template.서식 !== undefined) cell.서식 = template.서식;
|
|
if (allNumeric) {
|
|
cell.값 = nums[0] + step * rel;
|
|
} else if (template.식 !== undefined) {
|
|
const dr = vertical ? period * lineLen : 0;
|
|
const dc = vertical ? 0 : period * lineLen;
|
|
cell.식 = moveFormula(template.식, dr, dc);
|
|
} else if (typeof template.값 === "string" && lineLen === 1) {
|
|
cell.값 = incrementTrailingNumber(template.값, rel);
|
|
} else if (template.값 !== undefined) {
|
|
cell.값 = template.값;
|
|
}
|
|
out[outKey] = cell;
|
|
}
|
|
}
|
|
return out;
|
|
}
|