Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions extensions/cli/src/ui/TextBuffer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,11 +664,11 @@ describe("TextBuffer", () => {
expect(buffer.cursor).toBe(2);
});

it("should handle delete key", () => {
it("should handle delete key as forward deletion", () => {
const result = buffer.handleInput("", { delete: true } as any);
expect(result).toBe(true);
expect(buffer.text).toBe("hllo");
expect(buffer.cursor).toBe(1);
expect(buffer.text).toBe("helo");
expect(buffer.cursor).toBe(2);
});

it("should handle backspace key", () => {
Expand All @@ -678,6 +678,60 @@ describe("TextBuffer", () => {
expect(buffer.cursor).toBe(1);
});

it("should handle home and end keys on single-line text", () => {
let result = buffer.handleInput("", { home: true } as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(0);

result = buffer.handleInput("", { end: true } as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(5);
});

it("should handle home and end keys on multi-line text", () => {
const text = "first line\nsecond line\nthird line";
buffer.setText(text);
const secondLineStart = text.indexOf("second line");
buffer.setCursor(secondLineStart + 4);

let result = buffer.handleInput("", { home: true } as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(secondLineStart);

result = buffer.handleInput("", { end: true } as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(secondLineStart + "second line".length);
});

it("should handle escape sequences for home, end, and delete", () => {
// Home sequence \u001b[1~
let result = buffer.handleInput("\u001b[1~", {} as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(0);

// End sequence \u001b[4~
result = buffer.handleInput("\u001b[4~", {} as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(5);

// Alternative Home sequence \u001b[H
result = buffer.handleInput("\u001b[H", {} as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(0);

// Alternative End sequence \u001b[F
result = buffer.handleInput("\u001b[F", {} as any);
expect(result).toBe(true);
expect(buffer.cursor).toBe(5);

// Forward delete sequence \u001b[3~
buffer.setCursor(2);
result = buffer.handleInput("\u001b[3~", {} as any);
expect(result).toBe(true);
expect(buffer.text).toBe("helo");
expect(buffer.cursor).toBe(2);
});

it("should handle meta key combinations", () => {
const result = buffer.handleInput("", {
meta: true,
Expand Down
51 changes: 49 additions & 2 deletions extensions/cli/src/ui/TextBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ export class TextBuffer {
this._cursor = this._text.length;
}

moveToLineStart(): void {
const lastNewline = this._text.lastIndexOf("\n", this._cursor - 1);
this._cursor = lastNewline === -1 ? 0 : lastNewline + 1;
}

moveToLineEnd(): void {
const nextNewline = this._text.indexOf("\n", this._cursor);
this._cursor = nextNewline === -1 ? this._text.length : nextNewline;
}

moveLeft(): void {
this._cursor = Math.max(0, this._cursor - 1);
}
Expand Down Expand Up @@ -420,6 +430,19 @@ export class TextBuffer {
this.deleteWordBackward();
return true;
}
// Standard terminal escape sequences for navigation / editing
if (sequence === "[1~" || sequence === "[H" || sequence === "OH") {
this.moveToLineStart();
return true;
}
if (sequence === "[4~" || sequence === "[F" || sequence === "OF") {
this.moveToLineEnd();
return true;
}
if (sequence === "[3~") {
this.deleteForward();
return true;
}
return true; // Consume other option sequences
}

Expand Down Expand Up @@ -477,6 +500,19 @@ export class TextBuffer {
return false;
}

private handleNavigationKeys(key: Key): boolean {
const navKey = key as Key & { home?: boolean; end?: boolean };
if (navKey.home && !key.meta) {
this.moveToLineStart();
return true;
}
if (navKey.end && !key.meta) {
this.moveToLineEnd();
return true;
}
return false;
}

private handleArrowKeys(key: Key): boolean {
if (key.leftArrow && !key.meta) {
this.moveLeft();
Expand All @@ -490,8 +526,14 @@ export class TextBuffer {
}

private handleDeleteKeys(key: Key): boolean {
// On Mac, backspace key registers as key.delete, so treat it as backward deletion
if ((key.delete || key.backspace) && !key.meta) {
if (key.meta) {
return false;
}
if (key.delete) {
this.deleteForward();
return true;
}
if (key.backspace) {
this.deleteBackward();
return true;
}
Expand All @@ -517,6 +559,11 @@ export class TextBuffer {
return true;
}

// Handle home / end navigation keys
if (this.handleNavigationKeys(key)) {
return true;
}

// Handle arrow keys
if (this.handleArrowKeys(key)) {
return true;
Expand Down
Loading