Skip to content

Commit e8108c2

Browse files
Prevent extra lines from sticking to terminal after cycling through multi line commands in history
1 parent 201c790 commit e8108c2

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

src/commandwindow/CommandWindow.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,9 @@ export default class CommandWindow implements vscode.Pseudoterminal {
566566
}
567567

568568
private _eraseExistingPromptLine (): void {
569-
const numberOfLinesBehind = Math.floor(this._getAbsoluteIndexOnLine(this._cursorIndex) / this._terminalDimensions.columns);
569+
const textUpToCursor = this._currentPromptLine.substring(0, this._getAbsoluteIndexOnLine(this._cursorIndex));
570+
const numberOfExplicitNewlines = (textUpToCursor.match(/\r?\n/g) ?? []).length;
571+
const numberOfLinesBehind = Math.floor(this._getAbsoluteIndexOnLine(this._cursorIndex) / this._terminalDimensions.columns) + numberOfExplicitNewlines;
570572
if (numberOfLinesBehind !== 0) {
571573
this._writeEmitter.fire(ACTION_KEYS.UP.repeat(numberOfLinesBehind))
572574
}

src/test/tools/tester/TerminalTester.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ export class TerminalTester {
4848
return await this.vs.poll(this.doesTerminalContain.bind(this, expected), true, `Assertion on terminal content: ${message}`)
4949
}
5050

51+
/**
52+
* Assert the MATLAB terminal does not contain some content
53+
*/
54+
public async assertNotContains (expected: string, message: string): Promise<void> {
55+
return await this.vs.poll(this.doesTerminalNotContain.bind(this, expected), true, `Assertion on terminal content: ${message}`)
56+
}
57+
5158
/**
5259
* Checks if the MATLAB terminal contains some content (no polling)
5360
*/
@@ -56,6 +63,14 @@ export class TerminalTester {
5663
return content.includes(expected)
5764
}
5865

66+
/**
67+
* Checks if the MATLAB terminal does not contain some content (no polling)
68+
*/
69+
private async doesTerminalNotContain (expected: string): Promise<boolean> {
70+
const content = await this.getTerminalContent()
71+
return !content.includes(expected)
72+
}
73+
5974
public async type (text: string): Promise<void> {
6075
const container = await this.terminal.findElement(vet.By.className('xterm-helper-textarea'));
6176
return await container.sendKeys(text)

src/test/ui/terminal.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,26 @@ suite('Terminal UI Tests', () => {
9090
await vs.terminal.assertContains('a = 123;', 'Up arrow after typing "a" should recall matching command')
9191
await vs.terminal.type(Key.ESCAPE)
9292
});
93+
94+
test('Test multi-line command history cycling', async () => {
95+
// Execute a multi-line command by pasting (simulates copy-paste of multi-line text)
96+
await vs.terminal.type('x = [1 2\n 3 4]')
97+
await vs.terminal.type(Key.RETURN)
98+
99+
// Execute another command to move forward in history
100+
await vs.terminal.executeCommand('y = 5;')
101+
await vs.terminal.executeCommand('clc')
102+
103+
// Recall the multi-line command with up arrow
104+
await vs.terminal.type(Key.ARROW_UP)
105+
await vs.terminal.type(Key.ARROW_UP)
106+
await vs.terminal.assertContains('x = [1 2', 'Up arrow should recall first line of multi-line command')
107+
await vs.terminal.assertContains('3 4]', 'Up arrow should recall second line of multi-line command')
108+
109+
// Cycle away from the multi-line command
110+
await vs.terminal.type(Key.ARROW_DOWN)
111+
await vs.terminal.assertNotContains('x = [1 2', 'First line should not stick after cycling away with down arrow')
112+
await vs.terminal.assertNotContains('3 4]', 'Second line should not stick after cycling away with down arrow')
113+
await vs.terminal.type(Key.ESCAPE)
114+
});
93115
});

0 commit comments

Comments
 (0)