forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Disable env activation in PowerShell using command prompt (#2892)
* Disable powershell activation using command prompt * Change to cmd prompt and fix code review comments * Change news entry * Fix tests
- Loading branch information
1 parent
a339d2f
commit 542ae64
Showing
38 changed files
with
692 additions
and
189 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Disable activation of conda environments in PowerShell. |
79 changes: 79 additions & 0 deletions
79
src/client/application/diagnostics/checks/powerShellActivation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
|
||
import { inject, injectable } from 'inversify'; | ||
import { DiagnosticSeverity } from 'vscode'; | ||
import '../../../common/extensions'; | ||
import { error } from '../../../common/logger'; | ||
import { useCommandPromptAsDefaultShell } from '../../../common/terminal/commandPrompt'; | ||
import { IConfigurationService, ICurrentProcess } from '../../../common/types'; | ||
import { IServiceContainer } from '../../../ioc/types'; | ||
import { BaseDiagnostic, BaseDiagnosticsService } from '../base'; | ||
import { IDiagnosticsCommandFactory } from '../commands/types'; | ||
import { DiagnosticCodes } from '../constants'; | ||
import { DiagnosticCommandPromptHandlerServiceId, MessageCommandPrompt } from '../promptHandler'; | ||
import { DiagnosticScope, IDiagnostic, IDiagnosticHandlerService } from '../types'; | ||
|
||
const PowershellActivationNotSupportedWithBatchFilesMessage = 'Activation of the selected Python environment is not supported in PowerShell. Consider changing your shell to Command Prompt.'; | ||
|
||
export class PowershellActivationNotAvailableDiagnostic extends BaseDiagnostic { | ||
constructor() { | ||
super(DiagnosticCodes.EnvironmentActivationInPowerShellWithBatchFilesNotSupportedDiagnostic, | ||
PowershellActivationNotSupportedWithBatchFilesMessage, | ||
DiagnosticSeverity.Warning, DiagnosticScope.Global); | ||
} | ||
} | ||
|
||
export const PowerShellActivationHackDiagnosticsServiceId = 'EnvironmentActivationInPowerShellWithBatchFilesNotSupportedDiagnostic'; | ||
|
||
@injectable() | ||
export class PowerShellActivationHackDiagnosticsService extends BaseDiagnosticsService { | ||
protected readonly messageService: IDiagnosticHandlerService<MessageCommandPrompt>; | ||
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { | ||
super([DiagnosticCodes.EnvironmentActivationInPowerShellWithBatchFilesNotSupportedDiagnostic], serviceContainer); | ||
this.messageService = serviceContainer.get<IDiagnosticHandlerService<MessageCommandPrompt>>(IDiagnosticHandlerService, DiagnosticCommandPromptHandlerServiceId); | ||
} | ||
public async diagnose(): Promise<IDiagnostic[]> { | ||
return []; | ||
} | ||
public async handle(diagnostics: IDiagnostic[]): Promise<void> { | ||
// This class can only handle one type of diagnostic, hence just use first item in list. | ||
if (diagnostics.length === 0 || !this.canHandle(diagnostics[0])) { | ||
return; | ||
} | ||
const diagnostic = diagnostics[0]; | ||
if (await this.filterService.shouldIgnoreDiagnostic(diagnostic.code)) { | ||
return; | ||
} | ||
const commandFactory = this.serviceContainer.get<IDiagnosticsCommandFactory>(IDiagnosticsCommandFactory); | ||
const currentProcess = this.serviceContainer.get<ICurrentProcess>(ICurrentProcess); | ||
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService); | ||
const options = [ | ||
{ | ||
prompt: 'Use Command Prompt', | ||
// tslint:disable-next-line:no-object-literal-type-assertion | ||
command: { | ||
diagnostic, invoke: async (): Promise<void> => { | ||
useCommandPromptAsDefaultShell(currentProcess, configurationService) | ||
.catch(ex => error('Use Command Prompt as default shell', ex)); | ||
} | ||
} | ||
}, | ||
{ | ||
prompt: 'Ignore' | ||
}, | ||
{ | ||
prompt: 'Always Ignore', | ||
command: commandFactory.createCommand(diagnostic, { type: 'ignore', options: DiagnosticScope.Global }) | ||
}, | ||
{ | ||
prompt: 'More Info', | ||
command: commandFactory.createCommand(diagnostic, { type: 'launch', options: 'https://aka.ms/Niq35h' }) | ||
} | ||
]; | ||
|
||
await this.messageService.handle(diagnostic, { commandPrompts: options }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
|
||
import { Terminal, Uri } from 'vscode'; | ||
import { sleep } from '../../utils/async'; | ||
import { ITerminalActivator, ITerminalHelper, TerminalShellType } from '../types'; | ||
|
||
export class BaseTerminalActivator implements ITerminalActivator { | ||
private readonly activatedTerminals: Set<Terminal> = new Set<Terminal>(); | ||
constructor(private readonly helper: ITerminalHelper) { } | ||
public async activateEnvironmentInTerminal(terminal: Terminal, resource: Uri | undefined, preserveFocus: boolean = true) { | ||
if (this.activatedTerminals.has(terminal)) { | ||
return false; | ||
} | ||
this.activatedTerminals.add(terminal); | ||
const shellPath = this.helper.getTerminalShellPath(); | ||
const terminalShellType = !shellPath || shellPath.length === 0 ? TerminalShellType.other : this.helper.identifyTerminalShell(shellPath); | ||
|
||
const activationCommamnds = await this.helper.getEnvironmentActivationCommands(terminalShellType, resource); | ||
if (activationCommamnds) { | ||
for (const command of activationCommamnds!) { | ||
terminal.show(preserveFocus); | ||
terminal.sendText(command); | ||
await this.waitForCommandToProcess(terminalShellType); | ||
} | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
protected async waitForCommandToProcess(shell: TerminalShellType) { | ||
// Give the command some time to complete. | ||
// Its been observed that sending commands too early will strip some text off in VS Code Terminal. | ||
await sleep(500); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
|
||
import { inject, injectable, multiInject } from 'inversify'; | ||
import { Terminal, Uri } from 'vscode'; | ||
import { ITerminalActivationHandler, ITerminalActivator, ITerminalHelper } from '../types'; | ||
import { BaseTerminalActivator } from './base'; | ||
|
||
@injectable() | ||
export class TerminalActivator implements ITerminalActivator { | ||
protected baseActivator!: ITerminalActivator; | ||
constructor(@inject(ITerminalHelper) readonly helper: ITerminalHelper, | ||
@multiInject(ITerminalActivationHandler) private readonly handlers: ITerminalActivationHandler[]) { | ||
this.initialize(); | ||
} | ||
public async activateEnvironmentInTerminal(terminal: Terminal, resource: Uri | undefined, preserveFocus: boolean = true) { | ||
const activated = await this.baseActivator.activateEnvironmentInTerminal(terminal, resource, preserveFocus); | ||
this.handlers.forEach(handler => handler.handleActivation(terminal, resource, preserveFocus, activated).ignoreErrors()); | ||
return activated; | ||
} | ||
protected initialize() { | ||
this.baseActivator = new BaseTerminalActivator(this.helper); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/client/common/terminal/activator/powershellFailedHandler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
|
||
import { inject, injectable, named } from 'inversify'; | ||
import { Terminal, Uri } from 'vscode'; | ||
import { PowerShellActivationHackDiagnosticsServiceId, PowershellActivationNotAvailableDiagnostic } from '../../../application/diagnostics/checks/powerShellActivation'; | ||
import { IDiagnosticsService } from '../../../application/diagnostics/types'; | ||
import { IPlatformService } from '../../platform/types'; | ||
import { ITerminalActivationHandler, ITerminalHelper, TerminalShellType } from '../types'; | ||
|
||
@injectable() | ||
export class PowershellTerminalActivationFailedHandler implements ITerminalActivationHandler { | ||
constructor(@inject(ITerminalHelper) private readonly helper: ITerminalHelper, | ||
@inject(IPlatformService) private readonly platformService: IPlatformService, | ||
@inject(IDiagnosticsService) @named(PowerShellActivationHackDiagnosticsServiceId) private readonly diagnosticService: IDiagnosticsService) { | ||
} | ||
public async handleActivation(_terminal: Terminal, resource: Uri | undefined, _preserveFocus: boolean, activated: boolean) { | ||
if (activated || !this.platformService.isWindows) { | ||
return; | ||
} | ||
const shell = this.helper.identifyTerminalShell(this.helper.getTerminalShellPath()); | ||
if (shell !== TerminalShellType.powershell && shell !== TerminalShellType.powershellCore) { | ||
return; | ||
} | ||
// Check if we can activate in Command Prompt. | ||
const activationCommands = await this.helper.getEnvironmentActivationCommands(TerminalShellType.commandPrompt, resource); | ||
if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { | ||
return; | ||
} | ||
this.diagnosticService.handle([new PowershellActivationNotAvailableDiagnostic()]).ignoreErrors(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
'use strict'; | ||
|
||
import * as path from 'path'; | ||
import { ConfigurationTarget } from 'vscode'; | ||
import { IConfigurationService, ICurrentProcess } from '../types'; | ||
|
||
export function getCommandPromptLocation(currentProcess: ICurrentProcess) { | ||
// https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts#L218 | ||
// Determine the correct System32 path. We want to point to Sysnative | ||
// when the 32-bit version of VS Code is running on a 64-bit machine. | ||
// The reason for this is because PowerShell's important PSReadline | ||
// module doesn't work if this is not the case. See #27915. | ||
const is32ProcessOn64Windows = currentProcess.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); | ||
const system32Path = path.join(currentProcess.env.windir!, is32ProcessOn64Windows ? 'Sysnative' : 'System32'); | ||
return path.join(system32Path, 'cmd.exe'); | ||
} | ||
export async function useCommandPromptAsDefaultShell(currentProcess: ICurrentProcess, configService: IConfigurationService) { | ||
const cmdPromptLocation = getCommandPromptLocation(currentProcess); | ||
await configService.updateSectionSetting('terminal', 'integrated.shell.windows', cmdPromptLocation, undefined, ConfigurationTarget.Global); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.