Skip to content

Commit f1f0e9f

Browse files
committed
Fix bugs for ctrl+U
1 parent 7cc84b0 commit f1f0e9f

2 files changed

Lines changed: 98 additions & 10 deletions

File tree

src/subcommands/chat/react/inputReducer.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,47 @@ describe("chatInputStateReducers", () => {
19661966
expect(result.cursorOnSegmentIndex).toBe(0);
19671967
expect(result.cursorInSegmentOffset).toBe(0);
19681968
});
1969+
1970+
it("preserves text after cursor when line starts with largePaste", () => {
1971+
const initialState: ChatUserInputState = {
1972+
segments: [
1973+
{ type: "largePaste", content: "x".repeat(100) },
1974+
{ type: "text", content: "should deleteshould keep" },
1975+
],
1976+
cursorOnSegmentIndex: 1,
1977+
cursorInSegmentOffset: 13,
1978+
};
1979+
1980+
const result = deleteToLineStart(initialState);
1981+
1982+
expect(result.segments.length).toBe(1);
1983+
expect(result.segments[0].type).toBe("text");
1984+
expect(result.segments[0].content).toBe("should keep");
1985+
expect(result.cursorOnSegmentIndex).toBe(0);
1986+
expect(result.cursorInSegmentOffset).toBe(0);
1987+
});
1988+
1989+
it("deletes largePaste when cursor is at paste start with text on both sides", () => {
1990+
const initialState: ChatUserInputState = {
1991+
segments: [
1992+
{ type: "text", content: "before" },
1993+
{ type: "largePaste", content: "x".repeat(100) },
1994+
{ type: "text", content: "after" },
1995+
],
1996+
cursorOnSegmentIndex: 1,
1997+
cursorInSegmentOffset: 0,
1998+
};
1999+
2000+
const result = deleteToLineStart(initialState);
2001+
2002+
expect(result.segments.length).toBe(2);
2003+
expect(result.segments[0].type).toBe("largePaste");
2004+
expect(result.segments[0].content).toBe("x".repeat(100));
2005+
expect(result.segments[1].type).toBe("text");
2006+
expect(result.segments[1].content).toBe("after");
2007+
expect(result.cursorOnSegmentIndex).toBe(0);
2008+
expect(result.cursorInSegmentOffset).toBe(0);
2009+
});
19692010
});
19702011

19712012
describe("deleteToLineEnd", () => {
@@ -2068,7 +2109,25 @@ describe("chatInputStateReducers", () => {
20682109
expect(result.cursorOnSegmentIndex).toBe(0);
20692110
expect(result.cursorInSegmentOffset).toBe(9);
20702111
});
2112+
it("deletes trailing largePaste when cursor is in text before it", () => {
2113+
const initialState: ChatUserInputState = {
2114+
segments: [
2115+
{ type: "text", content: "keep this" },
2116+
{ type: "largePaste", content: "x".repeat(100) },
2117+
{ type: "text", content: "" },
2118+
],
2119+
cursorOnSegmentIndex: 0,
2120+
cursorInSegmentOffset: 9,
2121+
};
2122+
2123+
const result = deleteToLineEnd(initialState);
20712124

2125+
expect(result.segments.length).toBe(1);
2126+
expect(result.segments[0].type).toBe("text");
2127+
expect(result.segments[0].content).toBe("keep this");
2128+
expect(result.cursorOnSegmentIndex).toBe(0);
2129+
expect(result.cursorInSegmentOffset).toBe(9);
2130+
});
20722131
it("deletes to newline and preserves text after newline", () => {
20732132
const initialState: ChatUserInputState = {
20742133
segments: [{ type: "text", content: "line one\nline two" }],

src/subcommands/chat/react/inputReducer.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,18 +1041,44 @@ export function deleteToLineStart(state: ChatUserInputState): ChatUserInputState
10411041
const lineStartSegment = draft.segments[lineStartPosition.segmentIndex];
10421042
const currentSegment = draft.segments[currentSegmentIndex];
10431043

1044-
if (lineStartSegment !== undefined && lineStartSegment.type === "text") {
1045-
// Keep content before line start
1046-
lineStartSegment.content = lineStartSegment.content.slice(0, lineStartPosition.offset);
1047-
}
1044+
// If cursor is at start of current segment (offset 0), preserve current segment
1045+
if (currentOffset === 0) {
1046+
// Delete segments between line start and current (exclusive of current)
1047+
const segmentsToRemove = currentSegmentIndex - lineStartPosition.segmentIndex - 1;
1048+
if (segmentsToRemove > 0) {
1049+
draft.segments.splice(lineStartPosition.segmentIndex + 1, segmentsToRemove);
1050+
draft.cursorOnSegmentIndex = lineStartPosition.segmentIndex + 1;
1051+
}
10481052

1049-
if (currentSegment !== undefined && currentSegment.type === "text") {
1050-
// Keep content after cursor
1051-
const contentAfterCursor = currentSegment.content.slice(currentOffset);
1052-
// Merge into line start segment if it's text
1053+
// Truncate line start segment at line start position
10531054
if (lineStartSegment !== undefined && lineStartSegment.type === "text") {
1054-
lineStartSegment.content += contentAfterCursor;
1055+
lineStartSegment.content = lineStartSegment.content.slice(0, lineStartPosition.offset);
1056+
if (lineStartSegment.content.length === 0) {
1057+
// Remove empty line start segment
1058+
draft.segments.splice(lineStartPosition.segmentIndex, 1);
1059+
draft.cursorOnSegmentIndex = lineStartPosition.segmentIndex;
1060+
}
10551061
}
1062+
1063+
draft.cursorInSegmentOffset = 0;
1064+
return;
1065+
}
1066+
1067+
let contentAfterCursor = "";
1068+
if (currentSegment !== undefined && currentSegment.type === "text") {
1069+
contentAfterCursor = currentSegment.content.slice(currentOffset);
1070+
}
1071+
1072+
if (lineStartSegment !== undefined && lineStartSegment.type === "text") {
1073+
// Keep content before line start and merge content after cursor
1074+
lineStartSegment.content =
1075+
lineStartSegment.content.slice(0, lineStartPosition.offset) + contentAfterCursor;
1076+
} else if (contentAfterCursor.length > 0) {
1077+
// Line starts with non-text segment, create new text segment for content after cursor
1078+
draft.segments[lineStartPosition.segmentIndex] = {
1079+
type: "text",
1080+
content: contentAfterCursor,
1081+
};
10561082
}
10571083

10581084
// Remove segments between line start and cursor (inclusive of current if different)
@@ -1063,7 +1089,10 @@ export function deleteToLineStart(state: ChatUserInputState): ChatUserInputState
10631089

10641090
// Position cursor at line start
10651091
draft.cursorOnSegmentIndex = lineStartPosition.segmentIndex;
1066-
draft.cursorInSegmentOffset = lineStartPosition.offset;
1092+
draft.cursorInSegmentOffset =
1093+
lineStartSegment !== undefined && lineStartSegment.type === "text"
1094+
? lineStartPosition.offset
1095+
: 0;
10671096
});
10681097
}
10691098

0 commit comments

Comments
 (0)