Skip to content

Commit 54beb3b

Browse files
Improved copilot cli terminal experience (#1506)
* Improved copilot cli terminal experience * Updates * Fixes * Update src/extension/chatSessions/vscode-node/copilotCLIShim.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove unused import --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 31c0403 commit 54beb3b

File tree

3 files changed

+39
-11
lines changed

3 files changed

+39
-11
lines changed

src/extension/chatSessions/vscode-node/copilotCLIShim.ps1

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,11 @@ function Test-AndLaunchCopilot {
211211
}
212212

213213
# Start the check and launch process
214-
Test-AndLaunchCopilot $args
214+
$finalArgs = $args
215+
# Handle --clear argument
216+
if ($args.Length -gt 0 -and $args[0] -eq '--clear') {
217+
Clear-Host
218+
$finalArgs = $args[1..($args.Length - 1)]
219+
}
220+
221+
Test-AndLaunchCopilot $finalArgs

src/extension/chatSessions/vscode-node/copilotCLIShim.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ async function validateVersion(version: string) {
133133
warn(`Try manually reinstalling with: npm install -g ${PACKAGE_NAME} (https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli)`);
134134
process.exit(1);
135135
}
136-
const { status } = spawnSync('copilot', process.argv.slice(2), { stdio: 'inherit', env });
136+
const args = process.argv.slice(2);
137+
138+
// In vscode we use `--clear` to indicate that the terminal should be cleared before running the command
139+
// Used when launching terminal in editor view (for best possible UX, so it doesn't look like a terminal)
140+
if (args[0] === '--clear') {
141+
console.clear();
142+
args.shift();
143+
}
144+
145+
const { status } = spawnSync('copilot', args, { stdio: 'inherit', env });
137146
process.exit(status);
138147
})();

src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
107107
}
108108

109109
public async openTerminal(name: string, cliArgs: string[] = []) {
110+
// Generate another set of shell args, but with --clear to clear the terminal before running the command.
111+
// We'd like to hide all of the custom shell commands we send to the terminal from the user.
112+
cliArgs.unshift('--clear');
113+
110114
const [shellPathAndArgs] = await Promise.all([
111115
this.getShellInfo(cliArgs),
112116
this.updateGHTokenInTerminalEnvVars(),
@@ -123,15 +127,16 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
123127
if (terminal) {
124128
this._register(terminal);
125129
const command = this.buildCommandForPythonTerminal(shellPathAndArgs?.copilotCommand, cliArgs, shellPathAndArgs);
126-
await this.sendCommandToTerminal(terminal, command);
130+
await this.sendCommandToTerminal(terminal, command, true);
127131
return;
128132
}
129133
}
130134

131135
if (!shellPathAndArgs) {
132136
const terminal = this._register(this.terminalService.createTerminal(options));
137+
cliArgs.shift(); // Remove --clear as we can't run it without a shell integration
133138
const command = this.buildCommandForTerminal(terminal, COPILOT_CLI_COMMAND, cliArgs);
134-
await this.sendCommandToTerminal(terminal, command);
139+
await this.sendCommandToTerminal(terminal, command, false);
135140
return;
136141
}
137142

@@ -153,7 +158,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
153158
return `${quoteArgsForShell(copilotCommand, [])} ${cliArgs.join(' ')}`;
154159
}
155160

156-
private async sendCommandToTerminal(terminal: Terminal, command: string) {
161+
private async sendCommandToTerminal(terminal: Terminal, command: string, waitForPythonActivation: boolean) {
157162
// Wait for shell integration to be available
158163
const shellIntegrationTimeout = 3000;
159164
let shellIntegrationAvailable = false;
@@ -173,14 +178,20 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
173178
});
174179

175180
await integrationPromise;
176-
terminal.show();
177181

178-
if (shellIntegrationAvailable && terminal.shellIntegration) {
182+
if (waitForPythonActivation) {
183+
// Wait for python extension to send its initialization commands.
184+
// Else if we send too early, the copilot command might not get executed properly.
179185
await new Promise<void>(resolve => this._register(disposableTimeout(resolve, 500))); // Wait a bit to ensure the terminal is ready
186+
}
187+
188+
if (shellIntegrationAvailable && terminal.shellIntegration) {
180189
terminal.shellIntegration.executeCommand(command);
181190
} else {
182191
terminal.sendText(command);
183192
}
193+
194+
terminal.show();
184195
}
185196

186197
private async getShellInfo(cliArgs: string[]): Promise<IShellInfo | undefined> {
@@ -212,7 +223,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
212223
shellArgs: [`-ci${shellArgs.includes('-l') ? 'l' : ''}`, quoteArgsForShell(this.shellScriptPath, cliArgs)],
213224
iconPath,
214225
copilotCommand: this.shellScriptPath,
215-
exitCommand: `&& exit`
226+
exitCommand: `; exit`
216227
};
217228
} else if (defaultProfile === 'bash' && this.shellScriptPath) {
218229
return {
@@ -221,7 +232,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
221232
shellArgs: [`-${shellArgs.includes('-l') ? 'l' : ''}ic`, quoteArgsForShell(this.shellScriptPath, cliArgs)],
222233
iconPath,
223234
copilotCommand: this.shellScriptPath,
224-
exitCommand: `&& exit`
235+
exitCommand: `; exit`
225236
};
226237
} else if (defaultProfile === 'pwsh' && this.powershellScriptPath && configPlatform !== 'windows') {
227238
return {
@@ -248,7 +259,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
248259
shellArgs: ['/c', this.shellScriptPath, ...cliArgs],
249260
iconPath,
250261
copilotCommand: this.shellScriptPath,
251-
exitCommand: undefined
262+
exitCommand: '; exit'
252263
};
253264
}
254265
}
@@ -289,7 +300,8 @@ function getCommonTerminalOptions(name: string): TerminalOptions {
289300
return {
290301
name,
291302
iconPath: new ThemeIcon('terminal'),
292-
location: { viewColumn: ViewColumn.Active }
303+
location: { viewColumn: ViewColumn.Active },
304+
hideFromUser: true
293305
};
294306
}
295307

0 commit comments

Comments
 (0)