Skip to content

Commit

Permalink
Use terminal data write event to figure out whether shell integration…
Browse files Browse the repository at this point in the history
… is working (#22872)

Closes #22439

Blocked on microsoft/vscode#204616
  • Loading branch information
Kartik Raj authored Feb 7, 2024
1 parent d674a17 commit 4030717
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
// Licensed under the MIT License.

import { injectable, inject } from 'inversify';
import { IApplicationShell, ITerminalManager, IWorkspaceService } from '../../common/application/types';
import { EventEmitter } from 'vscode';
import {
IApplicationEnvironment,
IApplicationShell,
ITerminalManager,
IWorkspaceService,
} from '../../common/application/types';
import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector';
import { TerminalShellType } from '../../common/terminal/types';
import { IPersistentStateFactory } from '../../common/types';
import { IDisposableRegistry, IPersistentStateFactory } from '../../common/types';
import { createDeferred, sleep } from '../../common/utils/async';
import { cache } from '../../common/utils/decorators';
import { traceError, traceInfo, traceVerbose } from '../../logging';
Expand Down Expand Up @@ -34,12 +40,55 @@ export class ShellIntegrationService implements IShellIntegrationService {
*/
private readonly USE_COMMAND_APPROACH = false;

private isWorkingForShell = new Set<TerminalShellType>();

private readonly didChange = new EventEmitter<void>();

private isDataWriteEventWorking = true;

constructor(
@inject(ITerminalManager) private readonly terminalManager: ITerminalManager,
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
@inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory,
) {}
@inject(IApplicationEnvironment) private readonly appEnvironment: IApplicationEnvironment,
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
) {
try {
this.appShell.onDidWriteTerminalData(
(e) => {
if (e.data.includes('\x1b]633;A\x07')) {
let { shell } = this.appEnvironment;
if ('shellPath' in e.terminal.creationOptions && e.terminal.creationOptions.shellPath) {
shell = e.terminal.creationOptions.shellPath;
}
const shellType = identifyShellFromShellPath(shell);
const wasWorking = this.isWorkingForShell.has(shellType);
this.isWorkingForShell.add(shellType);
if (!wasWorking) {
// If it wasn't working previously, status has changed.
this.didChange.fire();
}
}
},
this,
this.disposables,
);
this.appEnvironment.onDidChangeShell(
async (shell: string) => {
this.createDummyHiddenTerminal(shell);
},
this,
this.disposables,
);
this.createDummyHiddenTerminal(this.appEnvironment.shell);
} catch (ex) {
this.isDataWriteEventWorking = false;
traceError('Unable to check if shell integration is active', ex);
}
}

public readonly onDidChangeStatus = this.didChange.event;

public async isWorking(shell: string): Promise<boolean> {
return this._isWorking(shell).catch((ex) => {
Expand All @@ -62,8 +111,20 @@ export class ShellIntegrationService implements IShellIntegrationService {
return false;
}
if (!this.USE_COMMAND_APPROACH) {
// For now, based on problems with using the command approach, assume it always works.
return true;
// For now, based on problems with using the command approach, use terminal data write event.
if (!this.isDataWriteEventWorking) {
// Assume shell integration is working, if data write event isn't working.
return true;
}
if (shellType === TerminalShellType.powershell || shellType === TerminalShellType.powershellCore) {
// Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now.
return true;
}
if (!this.isWorkingForShell.has(shellType)) {
// Maybe data write event has not been processed yet, wait a bit.
await sleep(1000);
}
return this.isWorkingForShell.has(shellType);
}
const key = `${isShellIntegrationWorkingKey}_${shellType}`;
const persistedResult = this.persistentStateFactory.createGlobalPersistentState<boolean>(key);
Expand All @@ -76,6 +137,16 @@ export class ShellIntegrationService implements IShellIntegrationService {
return result;
}

/**
* Creates a dummy terminal so that we are guaranteed a data write event for this shell type.
*/
private createDummyHiddenTerminal(shell: string) {
this.terminalManager.createTerminal({
shellPath: shell,
hideFromUser: true,
});
}

private async checkIfWorkingByRunningCommand(shell: string): Promise<boolean> {
const shellType = identifyShellFromShellPath(shell);
const deferred = createDeferred<void>();
Expand Down
1 change: 1 addition & 0 deletions src/client/terminals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ITerminalEnvVarCollectionService {

export const IShellIntegrationService = Symbol('IShellIntegrationService');
export interface IShellIntegrationService {
onDidChangeStatus: Event<void>;
isWorking(shell: string): Promise<boolean>;
}

Expand Down

0 comments on commit 4030717

Please sign in to comment.