fix(hashline): prevented terminal newline from exposing editable blank line

- Prevented `formatNumberedLines` from appending a synthetic empty line when encountering a terminal newline.
- Added tests to ensure trailing newlines do not generate addressable blank lines.
This commit is contained in:
can1357
2026-08-12 03:17:59 +02:00
parent 72624f6669
commit 154c6222f6
3 changed files with 23 additions and 2 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Stopped `formatNumberedLines` from exposing a terminal newline as an editable blank line; deleting that synthetic row previously produced a misleading no-op.
### Added
- Post-apply parse advisory: when the applied result stops parsing while the pre-edit content parsed, the response now carries a warning naming the first changed line. This catches balance-neutral misplacements — a statement swapped onto the wrong line number leaves no delimiter anomaly for the repair heuristics — which previously applied with zero feedback and surfaced only at the next compile.
+6 -1
View File
@@ -139,8 +139,13 @@ export function formatNumberedLine(lineNumber: number, line: string): string {
return `${lineNumber}${HL_LINE_BODY_SEP}${line}`;
}
/** Format file text with hashline-mode line-number prefixes for display. */
/**
* Format file text with hashline-mode line-number prefixes for display.
* A terminal newline terminates the preceding line; it is not an addressable
* blank line.
*/
export function formatNumberedLines(text: string, startLine = 1): string {
const lines = text.split("\n");
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
return lines.map((line, i) => formatNumberedLine(startLine + i, line)).join("\n");
}
+13 -1
View File
@@ -1,5 +1,12 @@
import { describe, expect, it } from "bun:test";
import { applyEdits, parseLid, parsePatch, parsePatchStreaming, Tokenizer } from "@oh-my-pi/hashline";
import {
applyEdits,
formatNumberedLines,
parseLid,
parsePatch,
parsePatchStreaming,
Tokenizer,
} from "@oh-my-pi/hashline";
function applyPatch(text: string, diff: string): string {
return applyEdits(text, parsePatch(diff).edits).text;
@@ -98,6 +105,11 @@ describe("hashline format v4", () => {
expect(applyEdits("a\nb\n", edits).text).toBe("a\nb\n");
});
it("does not expose the terminal newline as an editable blank line", () => {
expect(formatNumberedLines("a\nb\n")).toBe("1:a\n2:b");
expect(formatNumberedLines("a\nb\n\n")).toBe("1:a\n2:b\n3:");
});
it("treats a cut range ending at the trailing sentinel as ending at the last real line", () => {
const edits = parsePatch("CUT 2-3").edits;
expect(applyEdits("a\nb\n", edits).text).toBe("a\n");