Skip to content

Commit

Permalink
Multi-Root Workspace Support (#173)
Browse files Browse the repository at this point in the history
This enables multiple root workspaces to exist. There is more to do but this is a good first start. A separate test controller and pwsh instance are started for each workspace so they operate independently and don't clobber each other. PowerShell extension debugging currently all runs through the main terminal window, the goal however is to attach to a process in the future.
  • Loading branch information
JustinGrote authored Jul 31, 2023
1 parent 7b21a03 commit 095bbb1
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 251 deletions.
27 changes: 27 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@
"smartStep": true,
"preLaunchTask": "build-watch"
},
{
"name": "Run Extension Multi-Root Workspace",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--profile=Debug-PesterExtension",
"--trace-warnings",
"${workspaceFolder}/sample/Tests.code-workspace"
],
"env": {
"VSCODE_DEBUG_MODE": "true"
},
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"sourceMaps": true,
"autoAttachChildProcesses": true,
"skipFiles": [
"<node_internals>/**",
"**/extensionHostProcess.js",
"**/.vscode/extensions/**"
],
"showAsyncStacks": true,
"smartStep": true,
"preLaunchTask": "build-watch"
},
{
"name": "Test Extension",
"type": "node",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
"readline-transform": "^1.0.0",
"tslog": "^4.8.2"
},
"extensionDependencies": [
"ms-vscode.powershell"
],
"icon": "images/pesterlogo.png",
"badges": [
{
Expand Down
Empty file added sample/Tests.2/Empty.Tests.ps1
Empty file.
5 changes: 5 additions & 0 deletions sample/Tests.2/True.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Describe 'SimpleTest' {
It 'True should be true' {
$true | Should -Be $true
}
}
5 changes: 5 additions & 0 deletions sample/Tests.3/Test3Unique.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Context 'Test3 Unique Context' {
It 'Test3UniqueIt' {
$true
}
}
5 changes: 5 additions & 0 deletions sample/Tests.3/True.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Describe 'SimpleTest' {
It 'True should be true' {
$true | Should -Be $true
}
}
17 changes: 17 additions & 0 deletions sample/Tests.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"folders": [
{
"path": "Tests"
},
{
"path": "Tests.2"
},
{
"path": "Tests.3"
}
],
"settings": {
"git.openRepositoryInParentFolders": "never",
"extensions.ignoreRecommendations": true,
}
}
97 changes: 70 additions & 27 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,81 @@
import { type ExtensionContext, window, workspace, commands } from 'vscode'

import { PesterTestController } from './pesterTestController'
import { type ExtensionContext, window, workspace, Disposable, WorkspaceConfiguration, Extension } from 'vscode'
import {
getPowerShellExtension,
PowerShellExtensionClient
waitForPowerShellExtension,
PowerShellExtensionClient,
IPowerShellExtensionClient
} from './powershellExtensionClient'
import { watchWorkspace } from './workspaceWatcher'
import log, { VSCodeLogOutputChannelTransport } from './log'

export async function activate(context: ExtensionContext) {
// PowerShell extension is a prerequisite, but we allow either preview or normal, which is why we do this instead of
// leverage package.json dependencies
const powershellExtension = getPowerShellExtension(context)

// Short circuit this activate call if we didn't find a PowerShell Extension. Another activate will be triggered once
// the powershell extension is available
if (powershellExtension === undefined) {
return

log.attachTransport(new VSCodeLogOutputChannelTransport('Pester').transport)

subscriptions = context.subscriptions

// PowerShell extension is a prerequisite
const powershellExtension = await waitForPowerShellExtension()

pesterExtensionContext = {
extensionContext: context,
powerShellExtension: powershellExtension,
powershellExtensionPesterConfig: PowerShellExtensionClient.GetPesterSettings()
}

promptForPSLegacyCodeLensDisable()

await watchWorkspace()

// TODO: Rig this up for multiple workspaces
// const stopPowerShellCommand = commands.registerCommand('pester.stopPowershell', () => {
// if (controller.stopPowerShell()) {
// void window.showInformationMessage('PowerShell background process stopped.')
// } else {
// void window.showWarningMessage('No PowerShell background process was running !')
// }
// })

// context.subscriptions.push(
// controller,
// stopPowerShellCommand,
// )

}

/** Register a Disposable with the extension so that it can be cleaned up if the extension is disabled */
export function registerDisposable(disposable: Disposable) {
if (subscriptions == undefined) {
throw new Error('registerDisposable called before activate. This should never happen and is a bug.')
}
subscriptions.push(disposable)
}

export function registerDisposables(disposables: Disposable[]) {
subscriptions.push(Disposable.from(...disposables))
}

let subscriptions: Disposable[]

type PesterExtensionContext = {
extensionContext: ExtensionContext
powerShellExtension: Extension<IPowerShellExtensionClient>
powershellExtensionPesterConfig: WorkspaceConfiguration
}

/** Get the activated extension context */
export function getPesterExtensionContext() {
if (pesterExtensionContext == undefined) {
throw new Error('Pester Extension Context attempted to be fetched before activation. This should never happen and is a bug')
}

return pesterExtensionContext
}
let pesterExtensionContext: PesterExtensionContext

function promptForPSLegacyCodeLensDisable() {
// Disable PowerShell codelens setting if present
const config = PowerShellExtensionClient.GetPesterSettings()

const psExtensionCodeLensSetting: boolean = config.codeLens

const suppressCodeLensNotice = workspace.getConfiguration('pester').get<boolean>('suppressCodeLensNotice') ?? false
Expand Down Expand Up @@ -50,18 +107,4 @@ export async function activate(context: ExtensionContext) {
}
})
}

const controller = new PesterTestController(powershellExtension, context)
const stopPowerShellCommand = commands.registerCommand('pester.stopPowershell', () => {
if (controller.stopPowerShell()) {
void window.showInformationMessage('PowerShell background process stopped.')
} else {
void window.showWarningMessage('No PowerShell background process was running !')
}
})

context.subscriptions.push(
controller,
stopPowerShellCommand,
)
}
4 changes: 3 additions & 1 deletion src/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ type DefaultTSLogLevel =
| "FATAL"

export class VSCodeLogOutputChannelTransport {
// TODO: Make this lazy initialize so the window only starts once a log has been requested
/** Used to ensure multiple registered transports that request the same name use the same output window. NOTE: You can still get duplicate windows if you register channels outside this transport */
private static readonly channels = new Map<string, LogOutputChannel>()
private readonly name: string
Expand Down Expand Up @@ -80,6 +79,9 @@ log.attachTransport(new VSCodeOutputChannelTransport('Pester'))
const log = new Logger<DefaultLog>({
name: 'default',
type: 'pretty',
prettyErrorLoggerNameDelimiter: "-",
prettyErrorParentNamesSeparator: "-",
stylePrettyLogs: true,
argumentsArrayName: "args",
overwrite: {
transportFormatted: () => { return } // We want pretty formatting but no default output
Expand Down
Loading

0 comments on commit 095bbb1

Please sign in to comment.