fix(hashline): prevented exposing terminal newline as an addressable row
- Added `splitAddressableFileLines` to strip terminal newlines from line addressability without removing genuine blank lines. - Updated coding-agent read tool context parsing to use addressable file lines.
This commit is contained in:
@@ -2,9 +2,6 @@
|
||||
|
||||
## [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
|
||||
|
||||
@@ -13,6 +10,7 @@
|
||||
### Fixed
|
||||
|
||||
- Fixed Rust lifetimes blinding the delimiter-balance scanner. `'` entered string state to end-of-line, so `&'static str {` hid its opening brace; a replacement range swallowing such a signature line looked balance-neutral and the mid-block advisory never fired, silently deleting the signature. Single-quote lexing is now language-aware: on `.rs` targets, `'` opens a literal only when it lexes as a real char literal (`'a'`, `'\n'`, `'\u{7FFF}'`), and lifetimes stay ordinary characters — apostrophes are never paired across lifetimes, which would swallow the delimiters between them (`<'a>(x: &'a str)`).
|
||||
- Fixed hashline reads exposing a terminal newline as an editable blank row. `splitAddressableFileLines` now removes that sentinel before consumers build line anchors while retaining genuine blank lines.
|
||||
|
||||
## [17.2.12] - 2026-08-08
|
||||
|
||||
|
||||
@@ -140,12 +140,19 @@ export function formatNumberedLine(lineNumber: number, line: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Split LF-delimited file text into lines hashline anchors can address.
|
||||
* A terminal newline terminates the preceding line; it is not content.
|
||||
*/
|
||||
export function formatNumberedLines(text: string, startLine = 1): string {
|
||||
export function splitAddressableFileLines(text: string): 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");
|
||||
return lines;
|
||||
}
|
||||
|
||||
/** Format file text with hashline-mode line-number prefixes for display. */
|
||||
export function formatNumberedLines(text: string, startLine = 1): string {
|
||||
return text
|
||||
.split("\n")
|
||||
.map((line, i) => formatNumberedLine(startLine + i, line))
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
parseLid,
|
||||
parsePatch,
|
||||
parsePatchStreaming,
|
||||
splitAddressableFileLines,
|
||||
Tokenizer,
|
||||
} from "@oh-my-pi/hashline";
|
||||
|
||||
@@ -105,9 +106,14 @@ 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("separates terminal newline sentinels from addressable file lines", () => {
|
||||
expect(splitAddressableFileLines("a\nb\n")).toEqual(["a", "b"]);
|
||||
expect(splitAddressableFileLines("a\nb\n\n")).toEqual(["a", "b", ""]);
|
||||
});
|
||||
|
||||
it("keeps a selected terminal blank line when formatting", () => {
|
||||
const selected = splitAddressableFileLines("a\n\nb\n").slice(0, 2).join("\n");
|
||||
expect(formatNumberedLines(selected)).toBe("1:a\n2:");
|
||||
});
|
||||
|
||||
it("treats a cut range ending at the trailing sentinel as ending at the last real line", () => {
|
||||
|
||||
Reference in New Issue
Block a user