60cf2de16f
- Add detectOverlappingRanges() to reject edits with overlapping pos..end ranges instead of crashing with undefined.match() - Add bounds guard (?? "") in edit-operation-primitives for out-of-range line access - Add null guard in leadingWhitespace() for undefined/empty input - Fix restoreOldWrappedLines false unwrap: skip candidate spans containing blank/whitespace-only lines, preventing incorrect collapse of structural blank lines and indentation (the "애국가 bug") - Improve tool description for range replace clarity - Add tests: overlapping range detection, false unwrap prevention
57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { parseLineRef } from "./validation"
|
|
import type { HashlineEdit } from "./types"
|
|
|
|
export function getEditLineNumber(edit: HashlineEdit): number {
|
|
switch (edit.op) {
|
|
case "replace":
|
|
return parseLineRef(edit.end ?? edit.pos).line
|
|
case "append":
|
|
return edit.pos ? parseLineRef(edit.pos).line : Number.NEGATIVE_INFINITY
|
|
case "prepend":
|
|
return edit.pos ? parseLineRef(edit.pos).line : Number.NEGATIVE_INFINITY
|
|
default:
|
|
return Number.POSITIVE_INFINITY
|
|
}
|
|
}
|
|
|
|
export function collectLineRefs(edits: HashlineEdit[]): string[] {
|
|
return edits.flatMap((edit) => {
|
|
switch (edit.op) {
|
|
case "replace":
|
|
return edit.end ? [edit.pos, edit.end] : [edit.pos]
|
|
case "append":
|
|
case "prepend":
|
|
return edit.pos ? [edit.pos] : []
|
|
default:
|
|
return []
|
|
}
|
|
})
|
|
}
|
|
|
|
export function detectOverlappingRanges(edits: HashlineEdit[]): string | null {
|
|
const ranges: { start: number; end: number; idx: number }[] = []
|
|
for (let i = 0; i < edits.length; i++) {
|
|
const edit = edits[i]
|
|
if (edit.op !== "replace" || !edit.end) continue
|
|
const start = parseLineRef(edit.pos).line
|
|
const end = parseLineRef(edit.end).line
|
|
ranges.push({ start, end, idx: i })
|
|
}
|
|
if (ranges.length < 2) return null
|
|
|
|
ranges.sort((a, b) => a.start - b.start || a.end - b.end)
|
|
for (let i = 1; i < ranges.length; i++) {
|
|
const prev = ranges[i - 1]
|
|
const curr = ranges[i]
|
|
if (curr.start <= prev.end) {
|
|
return (
|
|
`Overlapping range edits detected: ` +
|
|
`edit ${prev.idx + 1} (lines ${prev.start}-${prev.end}) overlaps with ` +
|
|
`edit ${curr.idx + 1} (lines ${curr.start}-${curr.end}). ` +
|
|
`Use pos-only replace for single-line edits.`
|
|
)
|
|
}
|
|
}
|
|
return null
|
|
}
|