diff --git a/.travis.yml b/.travis.yml index a8284f840f..80ae6b4308 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,10 @@ language: cpp git: depth: 1000 +env: + # Avoid expensive initialization of dotnet cli, see: https://donovanbrown.com/post/Stop-wasting-time-during-NET-Core-builds + DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + os: - linux - osx diff --git a/CHANGELOG.md b/CHANGELOG.md index 84580c0b33..7874c08a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,57 @@ # vscode-powershell Release History +## v1.11.0 +### Wednesday, January 23, 2019 +#### [vscode-powershell](https://github.com/powershell/vscode-powershell) + +- [vscode-PowerShell #1714](https://github.com/PowerShell/vscode-PowerShell/pull/1714) - + Do not run pester tests when user cancels questions using the x button (Thanks @bergmeister!) +- [vscode-PowerShell #1701](https://github.com/PowerShell/vscode-PowerShell/pull/1701) - + Interpret null Describe TestName to mean value can't be eval'd (Thanks @rkeithhill!) +- [vscode-PowerShell #1698](https://github.com/PowerShell/vscode-PowerShell/pull/1698) - + Add 'Run/Debug Pester tests' command and file tab menu (Thanks @bergmeister!) +- [vscode-PowerShell #1697](https://github.com/PowerShell/vscode-PowerShell/pull/1697) - + Remove Region Block snippet (Thanks @fullenw1!) +- [vscode-PowerShell #1690](https://github.com/PowerShell/vscode-PowerShell/pull/1690) - + Refresh CommandExplorer on visible +- [vscode-PowerShell #1688](https://github.com/PowerShell/vscode-PowerShell/pull/1688) - + Fix spacing of package.json after running Invoke-Build to not end up with local changes (Thanks @bergmeister!) +- [vscode-PowerShell #1638](https://github.com/PowerShell/vscode-PowerShell/pull/1638) - + Add icons and enable setting for Command Explorer (Thanks @corbob!) +- [vscode-PowerShell #1679](https://github.com/PowerShell/vscode-PowerShell/pull/1679) - + Update broken links in README (Thanks @josh-!) +- [vscode-PowerShell #1670](https://github.com/PowerShell/vscode-PowerShell/pull/1670) - + Update recommended NodeJS version to 8.x since 6.x will go out of support in April 2019 (Thanks @bergmeister!) + +#### [PowerShellEditorServices](https://github.com/powershell/PowerShellEditorServices) + +- [PowerShellEditorServices #851](https://github.com/PowerShell/PowerShellEditorServices/pull/851) - + Fix #827 Pester TestName w/expandable str returns nothing (Thanks @rkeithhill!) +- [PowerShellEditorServices #842](https://github.com/PowerShell/PowerShellEditorServices/pull/842) - + Fix typos (Thanks @alexandair!) +- [PowerShellEditorServices #838](https://github.com/PowerShell/PowerShellEditorServices/pull/838) - + Fix NullRefEx bug when accessing scriptFile.ReferencedFiles (Thanks @rkeithhill!) +- [PowerShellEditorServices #839](https://github.com/PowerShell/PowerShellEditorServices/pull/839) - + Fix FileNotFoundEx crash when Fold happens on untitled: scheme doc (Thanks @rkeithhill!) +- [PowerShellEditorServices #843](https://github.com/PowerShell/PowerShellEditorServices/pull/843) - + Simplify the parameter descriptions and fix typos (Thanks @alexandair!) +- [PowerShellEditorServices #844](https://github.com/PowerShell/PowerShellEditorServices/pull/844) - + Fix an empty verbose message when importing an editor command (Thanks @alexandair!) +- [PowerShellEditorServices #828](https://github.com/PowerShell/PowerShellEditorServices/pull/828) - + Compile against net452 because net451 is not supported any more (Thanks @bergmeister!) +- [PowerShellEditorServices #848](https://github.com/PowerShell/PowerShellEditorServices/pull/848) - + switch an instance of GetFile to TryGetFile to fix #1689 +- [PowerShellEditorServices #846](https://github.com/PowerShell/PowerShellEditorServices/pull/846) - + Workaround "attach to process" hang +- [PowerShellEditorServices #829](https://github.com/PowerShell/PowerShellEditorServices/pull/829) - + Update various NuGet packages (Thanks @bergmeister!) +- [PowerShellEditorServices #825](https://github.com/PowerShell/PowerShellEditorServices/pull/825) - + (GH-824)(GH-812) Improve code folding speed (Thanks @glennsarti!) +- [PowerShellEditorServices #850](https://github.com/PowerShell/PowerShellEditorServices/pull/850) - + Fix VSCODE 1683 - HelpCommentReqHdlr crash on GetFile (Thanks @rkeithhill!) +- [PowerShellEditorServices #837](https://github.com/PowerShell/PowerShellEditorServices/pull/837) - + (maint) Add traits for folding tests (Thanks @glennsarti!) + ## v1.10.2 ### Tuesday, December 18, 2018 diff --git a/InvokePesterStub.ps1 b/InvokePesterStub.ps1 new file mode 100755 index 0000000000..8b6f204cce --- /dev/null +++ b/InvokePesterStub.ps1 @@ -0,0 +1,82 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Stub around Invoke-Pester command used by VSCode PowerShell extension. +.DESCRIPTION + The stub checks the version of Pester and if >= 4.6.0, invokes Pester + using the LineNumber parameter (if specified). Otherwise, it invokes + using the TestName parameter (if specified). If the All parameter + is specified, then all the tests are invoked in the specifed file. + Finally, if none of these three parameters are specified, all tests + are invoked and a warning is issued indicating what the user can do + to allow invocation of individual Describe blocks. +.EXAMPLE + PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -LineNumber 14 + Invokes a specific test by line number in the specified file. +.EXAMPLE + PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -TestName 'Foo Tests' + Invokes a specific test by test name in the specified file. +.EXAMPLE + PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -All + Invokes all tests in the specified file. +.INPUTS + None +.OUTPUTS + None +#> +param( + # Specifies the path to the test script. + [Parameter(Position=0, Mandatory)] + [ValidateNotNullOrEmpty()] + [string] + $ScriptPath, + + # Specifies the name of the test taken from the Describe block's name. + [Parameter()] + [string] + $TestName, + + # Specifies the starting line number of the DescribeBlock. This feature requires + # Pester 4.6.0 or higher. + [Parameter()] + [ValidatePattern('\d*')] + [string] + $LineNumber, + + # If specified, executes all the tests in the specified test script. + [Parameter()] + [switch] + $All +) + +$pesterModule = Microsoft.PowerShell.Core\Get-Module Pester +if (!$pesterModule) { + Write-Output "Importing Pester module..." + $pesterModule = Microsoft.PowerShell.Core\Import-Module Pester -ErrorAction Ignore -PassThru + if (!$pesterModule) { + # If we still don't have an imported Pester module, that is (most likely) because Pester is not installed. + Write-Warning "Failed to import the Pester module. You must install Pester to run or debug Pester tests." + Write-Warning "You can install Pester by executing: Install-Module Pester -Scope CurrentUser -Force" + return + } +} + +if ($All) { + Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true} +} +elseif ($TestName) { + Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true} -TestName $TestName +} +elseif (($LineNumber -match '\d+') -and ($pesterModule.Version -ge '4.6.0')) { + Pester\Invoke-Pester -Script $ScriptPath -PesterOption (New-PesterOption -ScriptBlockFilter @{ + IncludeVSCodeMarker=$true; Line=$LineNumber; Path=$ScriptPath}) +} +else { + # We get here when the TestName expression is of type ExpandableStringExpressionAst. + # PSES will not attempt to "evaluate" the expression so it returns null for the TestName. + Write-Warning "The Describe block's TestName cannot be evaluated. EXECUTING ALL TESTS instead." + Write-Warning "To avoid this, install Pester >= 4.6.0 or remove any expressions in the TestName." + + Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true} +} diff --git a/appveyor.yml b/appveyor.yml index 8b030e8e86..c9ecc1588d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.10.3-insiders-{build}' +version: '1.11.0-insiders-{build}' image: Visual Studio 2017 clone_depth: 10 skip_tags: true diff --git a/package.json b/package.json index 94099f6893..bc7682a090 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "PowerShell", + "name": "powershell", "displayName": "PowerShell", - "version": "1.10.3", + "version": "1.11.0", "publisher": "ms-vscode", "description": "Develop PowerShell scripts in Visual Studio Code!", "engines": { @@ -31,6 +31,7 @@ "onCommand:PowerShell.NewProjectFromTemplate", "onCommand:PowerShell.OpenExamplesFolder", "onCommand:PowerShell.PickPSHostProcess", + "onCommand:PowerShell.PickRunspace", "onCommand:PowerShell.SpecifyScriptArgs", "onCommand:PowerShell.ShowSessionConsole", "onCommand:PowerShell.ShowSessionMenu", @@ -303,6 +304,7 @@ "debuggers": [ { "type": "PowerShell", + "label": "PowerShell", "enableBreakpointsFor": { "languageIds": [ "powershell" @@ -312,6 +314,7 @@ "runtime": "node", "variables": { "PickPSHostProcess": "PowerShell.PickPSHostProcess", + "PickRunspace": "PowerShell.PickRunspace", "SpecifyScriptArgs": "PowerShell.SpecifyScriptArgs" }, "languages": [ @@ -322,9 +325,9 @@ "label": "PowerShell: Launch Current File", "description": "Launch current file (in active editor window) under debugger", "body": { + "name": "PowerShell Launch Current File", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File", "script": "^\"\\${file}\"", "args": [], "cwd": "^\"\\${file}\"" @@ -334,9 +337,9 @@ "label": "PowerShell: Launch Current File in Temporary Console", "description": "Launch current file (in active editor window) under debugger in a temporary Integrated Console.", "body": { + "name": "PowerShell Launch Current File in Temporary Console", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File in Temporary Console", "script": "^\"\\${file}\"", "args": [], "cwd": "^\"\\${file}\"", @@ -347,9 +350,9 @@ "label": "PowerShell: Launch Current File w/Args Prompt", "description": "Launch current file (in active editor window) under debugger, prompting first for script arguments", "body": { + "name": "PowerShell Launch Current File w/Args Prompt", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File w/Args Prompt", "script": "^\"\\${file}\"", "args": [ "^\"\\${command:SpecifyScriptArgs}\"" @@ -361,9 +364,9 @@ "label": "PowerShell: Launch Script", "description": "Launch specified script or path to script under debugger", "body": { + "name": "PowerShell Launch ${Script}", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch ${Script}", "script": "^\"\\${workspaceFolder}/${Script}\"", "args": [], "cwd": "^\"\\${workspaceFolder}\"" @@ -373,9 +376,9 @@ "label": "PowerShell: Pester Tests", "description": "Invokes Pester tests under debugger", "body": { + "name": "PowerShell Pester Tests", "type": "PowerShell", "request": "launch", - "name": "PowerShell Pester Tests", "script": "Invoke-Pester", "args": [], "cwd": "^\"\\${workspaceFolder}\"" @@ -385,10 +388,9 @@ "label": "PowerShell: Attach to PowerShell Host Process", "description": "Open host process picker to select process to attach debugger to", "body": { + "name": "PowerShell Attach to Host Process", "type": "PowerShell", "request": "attach", - "name": "PowerShell Attach to Host Process", - "processId": "^\"\\${command:PickPSHostProcess}\"", "runspaceId": 1 } }, @@ -396,11 +398,21 @@ "label": "PowerShell: Interactive Session", "description": "Start interactive session (Debug Console) under debugger", "body": { + "name": "PowerShell Interactive Session", "type": "PowerShell", "request": "launch", - "name": "PowerShell Interactive Session", "cwd": "" } + }, + { + "label": "PowerShell: Attach Interactive Session Runspace", + "description": "Open runspace picker to select runspace to attach debugger to", + "body": { + "name": "PowerShell Attach Interactive Session Runspace", + "type": "PowerShell", + "request": "attach", + "processId": "current" + } } ], "configurationAttributes": { @@ -439,38 +451,43 @@ "processId": { "type": "string", "description": "The process id of the PowerShell host process to attach to. Works only on PowerShell 5 and above.", - "default": "${command:PickPSHostProcess}" + "default": null }, "runspaceId": { - "type": "number", + "type":["string","number"], "description": "Optional: The ID of the runspace to debug in the attached process. Defaults to 1. Works only on PowerShell 5 and above.", - "default": 1 + "default": null + }, + "customPipeName": { + "type": "string", + "description": "The custom pipe name of the PowerShell host process to attach to. Works only on PowerShell 6.2 and above.", + "default": null } } } }, "initialConfigurations": [ { + "name": "PowerShell Launch Current File", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File", "script": "${file}", "args": [], "cwd": "${file}" }, { + "name": "PowerShell Launch Current File in Temporary Console", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File in Temporary Console", "script": "${file}", "args": [], "cwd": "${file}", "createTemporaryIntegratedConsole": true }, { + "name": "PowerShell Launch Current File w/Args Prompt", "type": "PowerShell", "request": "launch", - "name": "PowerShell Launch Current File w/Args Prompt", "script": "${file}", "args": [ "${command:SpecifyScriptArgs}" @@ -478,17 +495,21 @@ "cwd": "${file}" }, { - "type": "PowerShell", - "request": "attach", "name": "PowerShell Attach to Host Process", - "processId": "${command:PickPSHostProcess}", - "runspaceId": 1 + "type": "PowerShell", + "request": "attach" }, { + "name": "PowerShell Interactive Session", "type": "PowerShell", "request": "launch", - "name": "PowerShell Interactive Session", "cwd": "" + }, + { + "name": "PowerShell Attach Interactive Session Runspace", + "type": "PowerShell", + "request": "attach", + "processId": "current" } ] } @@ -502,6 +523,11 @@ "default": true, "description": "Specifies the visibility of the Command Explorer in the PowerShell Side Bar." }, + "powershell.sideBar.CommandExplorerExcludeFilter": { + "type": "array", + "default": [], + "description": "Specify array of Modules to exclude from Command Explorer listing." + }, "powershell.powerShellExePath": { "type": "string", "default": "", @@ -533,7 +559,7 @@ }, "powershell.powerShellDefaultVersion": { "type": "string", - "description": "Specifies the name of the PowerShell version used in the startup session when the extension loads e.g \"Windows PowerShell (x86)\" or \"PowerShell Core 6.0.2 (x64)\"." + "description": "Specifies the PowerShell version name, as displayed by the 'PowerShell: Show Session Menu' command, used when the extension loads e.g \"Windows PowerShell (x86)\" or \"PowerShell Core 6 (x64)\"." }, "powershell.startAutomatically": { "type": "boolean", diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json index 787d47801d..d7a9f9545f 100644 --- a/snippets/PowerShell.json +++ b/snippets/PowerShell.json @@ -952,5 +952,72 @@ "#endregion" ], "description": "Region Block for organizing and folding of your code" + }, + "IfShouldProcess": { + "prefix": "IfShouldProcess", + "body": [ + "if (\\$PSCmdlet.ShouldProcess(\"${1:Target}\", \"${2:Operation}\")) {", + "\t$0", + "}" + ], + "description": "Creates ShouldProcess block" + }, + "CalculatedProperty": { + "prefix": "Calculated-Property", + "body": [ + "@{name='${1:PropertyName}';expression={${2:\\$_.PropertyValue}}}$0" + ], + "description": "Creates a Calculated Property typically used with Select-Object." + }, + "PesterDescribeContextIt": { + "prefix": "Describe-Context-It-Pester", + "body": [ + "Describe \"${1:DescribeName}\" {", + "\tContext \"${2:ContextName}\" {", + "\t\tIt \"${3:ItName}\" {", + "\t\t\t${4:Assertion}", + "\t\t}$0", + "\t}", + "}" + ], + "description": "Pester Describe block with nested Context & It blocks" + }, + "PesterDescribeBlock": { + "prefix": "Describe-Pester", + "body": [ + "Describe \"${1:DescribeName}\" {", + "\t$0", + "}" + ], + "description": "Pester Describe block" + }, + "PesterContextIt": { + "prefix": "Context-It-Pester", + "body": [ + "Context \"${1:ContextName}\" {", + "\tIt \"${2:ItName}\" {", + "\t\t${3:Assertion}", + "\t}$0", + "}" + ], + "description": "Pester - Context block with nested It block" + }, + "PesterContext": { + "prefix": "Context-Pester", + "body": [ + "Context \"${1:ContextName}\" {", + "\t$0", + "}" + ], + "description": "Pester - Context block" + }, + "PesterIt": { + "prefix": "It-Pester", + "body": [ + "It \"${1:ItName}\" {", + "\t${2:Assertion}", + "}$0" + ], + "description": "Pester - It block" } } diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index 6917fffa05..1c071af8ee 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -43,10 +43,10 @@ export class DebugSessionFeature implements IFeature, DebugConfigurationProvider } // DebugConfigurationProvider method - public resolveDebugConfiguration( + public async resolveDebugConfiguration( folder: WorkspaceFolder | undefined, config: DebugConfiguration, - token?: CancellationToken): ProviderResult { + token?: CancellationToken): Promise { // Make sure there is a session running before attempting to debug/run a program if (this.sessionManager.getSessionStatus() !== SessionStatus.Running) { @@ -68,12 +68,33 @@ export class DebugSessionFeature implements IFeature, DebugConfigurationProvider const platformDetails = getPlatformDetails(); const versionDetails = this.sessionManager.getPowerShellVersionDetails(); - if (platformDetails.operatingSystem !== OperatingSystem.Windows) { - const msg = "Attaching to a PowerShell Host Process is supported only on Windows."; + // Cross-platform attach to process was added in 6.2.0-preview.4 + if (versionDetails.version < "6.2.0" && platformDetails.operatingSystem !== OperatingSystem.Windows) { + const msg = `Attaching to a PowerShell Host Process on ${ + OperatingSystem[platformDetails.operatingSystem] } requires PowerShell 6.2 or higher.`; return vscode.window.showErrorMessage(msg).then((_) => { return undefined; }); } + + // if nothing is set, prompt for the processId + if (!config.customPipeName && !config.processId) { + config.processId = await vscode.commands.executeCommand("PowerShell.PickPSHostProcess"); + + // No process selected. Cancel attach. + if (!config.processId) { + return null; + } + } + + if (!config.runspaceId) { + config.runspaceId = await vscode.commands.executeCommand("PowerShell.PickRunspace", config.processId); + + // No runspace selected. Cancel attach. + if (!config.runspaceId) { + return null; + } + } } if (generateLaunchConfig) { @@ -379,3 +400,122 @@ export class PickPSHostProcessFeature implements IFeature { } } } + +interface IRunspaceItem extends vscode.QuickPickItem { + id: string; // payload for the QuickPick UI +} + +interface IRunspace { + id: number; + name: string; + availability: string; +} + +export const GetRunspaceRequestType = + new RequestType("powerShell/getRunspace"); + +export class PickRunspaceFeature implements IFeature { + + private command: vscode.Disposable; + private languageClient: LanguageClient; + private waitingForClientToken: vscode.CancellationTokenSource; + private getLanguageClientResolve: (value?: LanguageClient | Thenable) => void; + + constructor() { + this.command = + vscode.commands.registerCommand("PowerShell.PickRunspace", (processId) => { + return this.getLanguageClient() + .then((_) => this.pickRunspace(processId), (_) => undefined); + }, this); + } + + public setLanguageClient(languageClient: LanguageClient) { + this.languageClient = languageClient; + + if (this.waitingForClientToken) { + this.getLanguageClientResolve(this.languageClient); + this.clearWaitingToken(); + } + } + + public dispose() { + this.command.dispose(); + } + + private getLanguageClient(): Thenable { + if (this.languageClient) { + return Promise.resolve(this.languageClient); + } else { + // If PowerShell isn't finished loading yet, show a loading message + // until the LanguageClient is passed on to us + this.waitingForClientToken = new vscode.CancellationTokenSource(); + + return new Promise( + (resolve, reject) => { + this.getLanguageClientResolve = resolve; + + vscode.window + .showQuickPick( + ["Cancel"], + { placeHolder: "Attach to PowerShell host process: Please wait, starting PowerShell..." }, + this.waitingForClientToken.token) + .then((response) => { + if (response === "Cancel") { + this.clearWaitingToken(); + reject(); + } + }); + + // Cancel the loading prompt after 60 seconds + setTimeout(() => { + if (this.waitingForClientToken) { + this.clearWaitingToken(); + reject(); + + vscode.window.showErrorMessage( + "Attach to PowerShell host process: PowerShell session took too long to start."); + } + }, 60000); + }, + ); + } + } + + private pickRunspace(processId): Thenable { + return this.languageClient.sendRequest(GetRunspaceRequestType, processId).then((response) => { + const items: IRunspaceItem[] = []; + + for (const runspace of response) { + // Skip default runspace + if (runspace.id === 1 || runspace.name === "PSAttachRunspace") { + continue; + } + + items.push({ + label: runspace.name, + description: `ID: ${runspace.id} - ${runspace.availability}`, + id: runspace.id.toString(), + }); + } + + const options: vscode.QuickPickOptions = { + placeHolder: "Select PowerShell runspace to debug", + matchOnDescription: true, + matchOnDetail: true, + }; + + return vscode.window.showQuickPick(items, options).then((item) => { + // Return undefined when user presses Esc. + // This prevents VSCode from opening launch.json in this case which happens if we return "". + return item ? `${item.id}` : undefined; + }); + }); + } + + private clearWaitingToken() { + if (this.waitingForClientToken) { + this.waitingForClientToken.dispose(); + this.waitingForClientToken = undefined; + } + } +} diff --git a/src/features/GetCommands.ts b/src/features/GetCommands.ts index 0a269f3d3a..b109c74e73 100644 --- a/src/features/GetCommands.ts +++ b/src/features/GetCommands.ts @@ -64,6 +64,9 @@ export class GetCommandsFeature implements IFeature { return; } this.languageClient.sendRequest(GetCommandRequestType, "").then((result) => { + const SidebarConfig = vscode.workspace.getConfiguration("powershell.sideBar"); + const excludeFilter = (SidebarConfig.CommandExplorerExcludeFilter).map((filter) => filter.toLowerCase()); + result = result.filter((command) => (excludeFilter.indexOf(command.moduleName.toLowerCase()) === -1)); this.commandsExplorerProvider.powerShellCommands = result.map(toCommand); this.commandsExplorerProvider.refresh(); }); diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 75bb17698b..ea2c5fea9f 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -18,8 +18,11 @@ export class PesterTestsFeature implements IFeature { private command: vscode.Disposable; private languageClient: LanguageClient; + private invokePesterStubScriptPath: string; constructor(private sessionManager: SessionManager) { + this.invokePesterStubScriptPath = path.resolve(__dirname, "../../../InvokePesterStub.ps1"); + // File context-menu command - Run Pester Tests this.command = vscode.commands.registerCommand( "PowerShell.RunPesterTestsFromFile", @@ -35,8 +38,8 @@ export class PesterTestsFeature implements IFeature { // This command is provided for usage by PowerShellEditorServices (PSES) only this.command = vscode.commands.registerCommand( "PowerShell.RunPesterTests", - (uriString, runInDebugger, describeBlockName?) => { - this.launchTests(uriString, runInDebugger, describeBlockName); + (uriString, runInDebugger, describeBlockName?, describeBlockLineNumber?) => { + this.launchTests(uriString, runInDebugger, describeBlockName, describeBlockLineNumber); }); } @@ -51,34 +54,22 @@ export class PesterTestsFeature implements IFeature { private launchAllTestsInActiveEditor(launchType: LaunchType) { const uriString = vscode.window.activeTextEditor.document.uri.toString(); const launchConfig = this.createLaunchConfig(uriString, launchType); + launchConfig.args.push("-All"); this.launch(launchConfig); } - private async launchTests(uriString: string, runInDebugger: boolean, describeBlockName?: string) { - // PSES passes null for the describeBlockName to signal that it can't evaluate the TestName. - if (!describeBlockName) { - const answer = await vscode.window.showErrorMessage( - "This Describe block's TestName parameter cannot be evaluated. " + - `Would you like to ${runInDebugger ? "debug" : "run"} all the tests in this file?`, - "Yes", "No"); - - if (answer === "No") { - return; - } - } + private async launchTests( + uriString: string, + runInDebugger: boolean, + describeBlockName?: string, + describeBlockLineNumber?: number) { const launchType = runInDebugger ? LaunchType.Debug : LaunchType.Run; - const launchConfig = this.createLaunchConfig(uriString, launchType); - - if (describeBlockName) { - launchConfig.args.push("-TestName"); - launchConfig.args.push(`'${describeBlockName}'`); - } - + const launchConfig = this.createLaunchConfig(uriString, launchType, describeBlockName, describeBlockLineNumber); this.launch(launchConfig); } - private createLaunchConfig(uriString: string, launchType: LaunchType) { + private createLaunchConfig(uriString: string, launchType: LaunchType, testName?: string, lineNum?: number) { const uri = vscode.Uri.parse(uriString); const currentDocument = vscode.window.activeTextEditor.document; const settings = Settings.load(); @@ -91,12 +82,10 @@ export class PesterTestsFeature implements IFeature { request: "launch", type: "PowerShell", name: "PowerShell Launch Pester Tests", - script: "Invoke-Pester", + script: this.invokePesterStubScriptPath, args: [ - "-Script", + "-ScriptPath", `'${scriptPath}'`, - "-PesterOption", - "@{IncludeVSCodeMarker=$true}", ], internalConsoleOptions: "neverOpen", noDebug: (launchType === LaunchType.Run), @@ -107,6 +96,19 @@ export class PesterTestsFeature implements IFeature { : path.dirname(currentDocument.fileName), }; + if (lineNum) { + launchConfig.args.push("-LineNumber", `${lineNum}`); + } + + if (testName) { + // Escape single quotes inside double quotes by doubling them up + if (testName.includes("'")) { + testName = testName.replace(/'/g, "''"); + } + + launchConfig.args.push("-TestName", `'${testName}'`); + } + return launchConfig; } diff --git a/src/features/ShowHelp.ts b/src/features/ShowHelp.ts index e61f74d0a5..1c7b340d5c 100644 --- a/src/features/ShowHelp.ts +++ b/src/features/ShowHelp.ts @@ -22,7 +22,7 @@ export class ShowHelpFeature implements IFeature { "Unable to instantiate; language client undefined."); return; } - if (item === undefined) { + if (!item || !item.Name) { const editor = vscode.window.activeTextEditor; diff --git a/src/main.ts b/src/main.ts index 91273319f6..d79b4aab33 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,7 @@ import { ConsoleFeature } from "./features/Console"; import { CustomViewsFeature } from "./features/CustomViews"; import { DebugSessionFeature } from "./features/DebugSession"; import { PickPSHostProcessFeature } from "./features/DebugSession"; +import { PickRunspaceFeature } from "./features/DebugSession"; import { SpecifyScriptArgsFeature } from "./features/DebugSession"; import { DocumentFormatterFeature } from "./features/DocumentFormatter"; import { ExamplesFeature } from "./features/Examples"; @@ -36,7 +37,7 @@ import utils = require("./utils"); // NOTE: We will need to find a better way to deal with the required // PS Editor Services version... -const requiredEditorServicesVersion = "1.10.3"; +const requiredEditorServicesVersion = "1.11.0"; let logger: Logger; let sessionManager: SessionManager; @@ -138,6 +139,7 @@ export function activate(context: vscode.ExtensionContext): void { new SpecifyScriptArgsFeature(context), new HelpCompletionFeature(logger), new CustomViewsFeature(), + new PickRunspaceFeature(), ]; sessionManager.setExtensionFeatures(extensionFeatures); diff --git a/src/process.ts b/src/process.ts index ba0e64c6c9..95530dbadb 100644 --- a/src/process.ts +++ b/src/process.ts @@ -69,9 +69,14 @@ export class PowerShellProcess { powerShellArgs.push("-ExecutionPolicy", "Bypass"); } + const stringToEncode = "& '" + + PowerShellProcess.escapeSingleQuotes(startScriptPath) + + "' " + this.startArgs; + + // Use -EncodedCommand because the command is complex and has quotes in it that need to work xplat. powerShellArgs.push( - "-Command", - "& '" + PowerShellProcess.escapeSingleQuotes(startScriptPath) + "' " + this.startArgs); + "-EncodedCommand", + Buffer.from(stringToEncode, "utf16le").toString("base64")); let powerShellExePath = this.exePath; diff --git a/src/session.ts b/src/session.ts index daf47bdb20..ffea8ad012 100644 --- a/src/session.ts +++ b/src/session.ts @@ -418,8 +418,9 @@ export class SessionManager implements Middleware { if (!this.suppressRestartPrompt && (settings.useX86Host !== this.sessionSettings.useX86Host || settings.powerShellExePath.toLowerCase() !== this.sessionSettings.powerShellExePath.toLowerCase() || - settings.developer.powerShellExePath.toLowerCase() !== - this.sessionSettings.developer.powerShellExePath.toLowerCase() || + (settings.developer.powerShellExePath ? settings.developer.powerShellExePath.toLowerCase() : null) !== + (this.sessionSettings.developer.powerShellExePath + ? this.sessionSettings.developer.powerShellExePath.toLowerCase() : null) || settings.developer.editorServicesLogLevel.toLowerCase() !== this.sessionSettings.developer.editorServicesLogLevel.toLowerCase() || settings.developer.bundledModulesPath.toLowerCase() !== @@ -768,7 +769,7 @@ export class SessionManager implements Middleware { .filter((item) => item.exePath.toLowerCase() !== currentExePath) .map((item) => { return new SessionMenuItem( - `Switch to ${item.versionName}`, + `Switch to: ${item.versionName}`, () => { this.changePowerShellExePath(item.exePath); }); }); diff --git a/tools/releaseBuild/Image/DockerFile b/tools/releaseBuild/Image/DockerFile index 596b61bcd0..f573cfe3ea 100644 --- a/tools/releaseBuild/Image/DockerFile +++ b/tools/releaseBuild/Image/DockerFile @@ -23,7 +23,7 @@ COPY build.ps1 containerFiles/build.ps1 # Add an environment variable for build versioning ENV VSTS_BUILD=1 -ENV VSTS_BUILD_VERSION=1.10.3 +ENV VSTS_BUILD_VERSION=1.11.0 # Uncomment to debug locally # RUN Import-Module ./containerFiles/dockerInstall.psm1; ` diff --git a/tools/releaseBuild/setVstsVariables.ps1 b/tools/releaseBuild/setVstsVariables.ps1 index afaa5e570a..4fc5ffabc7 100644 --- a/tools/releaseBuild/setVstsVariables.ps1 +++ b/tools/releaseBuild/setVstsVariables.ps1 @@ -1,5 +1,5 @@ $vstsVariables = @{ - PSES_BRANCH = 'master' + PSES_BRANCH = 'legacy/1.x' } # Use VSTS's API to set an env vars diff --git a/vscode-powershell.build.ps1 b/vscode-powershell.build.ps1 index 39f20450bd..9d138b7f5b 100644 --- a/vscode-powershell.build.ps1 +++ b/vscode-powershell.build.ps1 @@ -57,9 +57,9 @@ task ResolveEditorServicesPath -Before CleanEditorServices, BuildEditorServices } } -task Restore RestoreNodeModules -Before Build +task Restore RestoreNodeModules -Before Build -If { -not (Test-Path "$PSScriptRoot/node_modules") } -task RestoreNodeModules -If { -not (Test-Path "$PSScriptRoot/node_modules") } { +task RestoreNodeModules { Write-Host "`n### Restoring vscode-powershell dependencies`n" -ForegroundColor Green @@ -73,6 +73,7 @@ task Clean { Write-Host "`n### Cleaning vscode-powershell`n" -ForegroundColor Green Remove-Item .\modules\* -Exclude "README.md" -Recurse -Force -ErrorAction Ignore Remove-Item .\out -Recurse -Force -ErrorAction Ignore + exec { & npm prune } } task CleanEditorServices {