Merge PR #7906: fix(hashline): recover pipe-numbered read rows (@roboomp)
This commit is contained in:
@@ -6,6 +6,10 @@
|
||||
|
||||
- Pasting an empty named register (`PUT … @name` with no matching capture) no longer errors — it pastes nothing (a span target is still removed) and surfaces a warning naming the available registers
|
||||
|
||||
### Fixed
|
||||
|
||||
- Recovered pipe-numbered `read`/`search` rows copied into top-level and bare-body patch payloads ([#7905](https://github.com/can1357/oh-my-pi/issues/7905))
|
||||
|
||||
## [17.2.10] - 2026-08-06
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -112,7 +112,7 @@ function bodylessTargetMessage(target: BlockTarget, hadColon: boolean): string |
|
||||
*/
|
||||
const BARE_LITERAL_VALUE_RE = /^\s*(?:"[^"]*"|'[^']*'|[-+]?\d+(?:\.\d+)?)\s*,?\s*$/;
|
||||
|
||||
const TOP_LEVEL_SNAPSHOT_ROW_RE = /^\s*([1-9]\d*):(.*)$/;
|
||||
const TOP_LEVEL_SNAPSHOT_ROW_RE = /^\s*([1-9]\d*)[:|](.*)$/;
|
||||
|
||||
function parseTopLevelSnapshotRow(text: string): { line: number; text: string } | null {
|
||||
const match = TOP_LEVEL_SNAPSHOT_ROW_RE.exec(text);
|
||||
@@ -595,11 +595,11 @@ export class Executor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a single read-output line-number prefix (`N:`) from every bare body
|
||||
* row, but only when *all* bare rows carry one. A uniform set of prefixes is
|
||||
* the signature of content pasted straight from `read`/`search` output; a
|
||||
* mixed set means the `N:` is genuine payload content and must stay. Rows
|
||||
* authored with an explicit `+` are not bare and are never touched.
|
||||
* Strip a single read-output line-number prefix (`N:` or `N|`) from every
|
||||
* bare body row, but only when *all* bare rows carry one. A uniform set of
|
||||
* prefixes is the signature of content pasted straight from `read`/`search`
|
||||
* output; a mixed set means the prefix is genuine payload content and must
|
||||
* stay. Rows authored with an explicit `+` are not bare and are never touched.
|
||||
*/
|
||||
#stripBarePrefixesIfUniform(payloads: PayloadRow[]): void {
|
||||
let sawBare = false;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* When a hashline payload is authored against `read`/`search` output, each
|
||||
* line is prefixed with either a hashline-mode line number (`123:`) or, for
|
||||
* diff-style echoes, a leading `+`. These helpers detect that and recover
|
||||
* the raw text. Two strip modes are exposed:
|
||||
* When a payload is authored against `read`/`search` output, each line is
|
||||
* prefixed with either a line number (`123:` in hashline mode or `123|`
|
||||
* otherwise) or, for diff-style echoes, a leading `+`. These helpers detect
|
||||
* that and recover the raw text. Two strip modes are exposed:
|
||||
*
|
||||
* - {@link stripNewLinePrefixes} — opportunistic: strips when the input
|
||||
* clearly carries hashline or diff prefixes, leaves it alone otherwise.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { HL_FILE_HASH_LENGTH } from "./format";
|
||||
|
||||
const HL_PREFIX_RE = /^\s*(?:>>>|>>)?\s*(?:[+*-]\s*)?\d+:/;
|
||||
const HL_PREFIX_RE = /^\s*(?:>>>|>>)?\s*(?:[+*-]\s*)?\d+[:|]/;
|
||||
const HL_PREFIX_PLUS_RE = /^\s*(?:>>>|>>)?\s*\+\s*\d+:/;
|
||||
const HL_HEADER_RE = new RegExp(`^\\s*\\[[^#\\r\\n]+#[0-9a-fA-F]{${HL_FILE_HASH_LENGTH}}\\]\\s*$`);
|
||||
const DIFF_PLUS_RE = /^[+](?![+])/;
|
||||
@@ -41,10 +41,10 @@ function stripLeadingHashlinePrefixes(line: string): string {
|
||||
}
|
||||
/**
|
||||
* Single-pass variant of {@link stripLeadingHashlinePrefixes} that strips at
|
||||
* most one leading hashline prefix (`N:`, `>>>N:`, `+N:` etc.) and does NOT
|
||||
* loop. Use this when the input carries at most one snapshot prefix (e.g. a
|
||||
* bare body row paste from `read` output) — recursive stripping would corrupt
|
||||
* content whose own text starts with `digits:`.
|
||||
* most one leading line-number prefix (`N:`, `N|`, `>>>N:`, `+N:` etc.) and
|
||||
* does NOT loop. Use this when the input carries at most one snapshot prefix
|
||||
* (e.g. a bare body row paste from `read` output) — recursive stripping would
|
||||
* corrupt content whose own text starts with a line-number prefix.
|
||||
*/
|
||||
export function stripOneLeadingHashlinePrefix(line: string): string {
|
||||
return line.replace(HL_PREFIX_RE, "");
|
||||
@@ -90,7 +90,7 @@ function collectLinePrefixStats(lines: string[]): LinePrefixStats {
|
||||
|
||||
/**
|
||||
* Strip whichever prefix scheme the lines appear to be carrying:
|
||||
* - hashline line-number prefixes (`123:`) when every content line has one
|
||||
* - line-number prefixes (`123:` or `123|`) when every content line has one
|
||||
* - leading `+` (diff style) when at least half the lines have one
|
||||
* - mixed `+<n>:` form when present
|
||||
*
|
||||
|
||||
@@ -83,9 +83,11 @@ describe("hashline core — verb header forms", () => {
|
||||
});
|
||||
|
||||
it("recovers top-level numbered snapshot rows as single-line replacements", () => {
|
||||
const result = parsePatch("2:B\n4:D");
|
||||
expect(applyEdits(FILE, result.edits).text).toBe("a\nB\nc\nD\ne");
|
||||
expect(result.warnings.some(w => /snapshot row.*single-line `PUT N\.=N:`/i.test(w))).toBe(true);
|
||||
for (const separator of [":", "|"]) {
|
||||
const result = parsePatch(`2${separator}B\n4${separator}D`);
|
||||
expect(applyEdits(FILE, result.edits).text).toBe("a\nB\nc\nD\ne");
|
||||
expect(result.warnings.some(w => /snapshot row.*single-line `PUT N\.=N:`/i.test(w))).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("recovers a bare range header as an implicit PUT", () => {
|
||||
@@ -133,10 +135,12 @@ describe("hashline body contracts", () => {
|
||||
expect(result.warnings.some(w => /Auto-prefixed bare body row/.test(w))).toBe(true);
|
||||
});
|
||||
|
||||
it("strips read-output line number prefix from auto-piped bare body rows", () => {
|
||||
const result = parsePatch("PUT 2-2:\n2:hello");
|
||||
expect(applyEdits(FILE, result.edits).text).toBe("a\nhello\nc\nd\ne");
|
||||
expect(result.warnings.some(w => /Auto-prefixed bare body row/.test(w))).toBe(true);
|
||||
it("strips read-output line number prefixes from auto-piped bare body rows", () => {
|
||||
for (const separator of [":", "|"]) {
|
||||
const result = parsePatch(`PUT 2-2:\n2${separator}hello`);
|
||||
expect(applyEdits(FILE, result.edits).text).toBe("a\nhello\nc\nd\ne");
|
||||
expect(result.warnings.some(w => /Auto-prefixed bare body row/.test(w))).toBe(true);
|
||||
}
|
||||
});
|
||||
it("preserves `+N:` literal payloads without stripping", () => {
|
||||
const result = parsePatch("PUT 2-2:\n+3:keep");
|
||||
|
||||
Reference in New Issue
Block a user