From e89b513ce2673429a98dd0bf2eb65ed231705d17 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 10:53:39 -0700 Subject: [PATCH 1/8] Replace `ensurePathExists` with `fs.createDirectory` --- src/features/DebugSession.ts | 4 ++-- src/features/PesterTests.ts | 2 +- src/features/RunCode.ts | 10 +++++----- src/logging.ts | 2 -- src/utils.ts | 20 ++------------------ 5 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index af23e26f9e..65add51e65 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -317,9 +317,9 @@ export class DebugSessionFeature extends LanguageClientConsumer // TODO: This should be cleaned up to support multiple temporary consoles. this.tempDebugProcess = this.sessionManager.createDebugSessionProcess(sessionFilePath, settings); this.tempSessionDetails = await this.tempDebugProcess.start(`DebugSession-${this.sessionCount++}`); - utils.writeSessionFile(sessionFilePath, this.tempSessionDetails); + await utils.writeSessionFile(sessionFilePath, this.tempSessionDetails); } else { - utils.writeSessionFile(sessionFilePath, this.sessionManager.getSessionDetails()); + await utils.writeSessionFile(sessionFilePath, this.sessionManager.getSessionDetails()); } return config; diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 32cc795a0b..843f0f56ac 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -131,7 +131,7 @@ export class PesterTestsFeature implements vscode.Disposable { vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file - utils.writeSessionFile( + await utils.writeSessionFile( utils.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index 288a7b1295..95f818644a 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -35,21 +35,21 @@ export class RunCodeFeature implements vscode.Disposable { const launchType = runInDebugger ? LaunchType.Debug : LaunchType.Run; const launchConfig = createLaunchConfig(launchType, scriptToRun, args); - this.launch(launchConfig); + await this.launch(launchConfig); } - private launch(launchConfig) { + private async launch(launchConfig: string | vscode.DebugConfiguration) { // Create or show the interactive console // TODO #367: Check if "newSession" mode is configured - vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); + await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file - utils.writeSessionFile( + await utils.writeSessionFile( utils.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); // TODO: Update to handle multiple root workspaces. - vscode.debug.startDebugging(vscode.workspace.workspaceFolders?.[0], launchConfig); + await vscode.debug.startDebugging(vscode.workspace.workspaceFolders?.[0], launchConfig); } } diff --git a/src/logging.ts b/src/logging.ts index b0438a3b48..d98a7e7185 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -5,7 +5,6 @@ import fs = require("fs"); import os = require("os"); import path = require("path"); import vscode = require("vscode"); -import utils = require("./utils"); export enum LogLevel { Diagnostic, @@ -44,7 +43,6 @@ export class Logger implements ILogger { if (logBasePath === undefined) { // No workspace, we have to use another folder. this.logBasePath = vscode.Uri.file(path.resolve(__dirname, "../logs")); - utils.ensurePathExists(this.logBasePath.fsPath); } else { this.logBasePath = vscode.Uri.joinPath(logBasePath, "logs"); } diff --git a/src/utils.ts b/src/utils.ts index e2f4567217..8c6ee8f90d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,19 +10,6 @@ import vscode = require("vscode"); export const PowerShellLanguageId = "powershell"; -export function ensurePathExists(targetPath: string): void { - // Ensure that the path exists - try { - // TODO: Use vscode.workspace.fs - fs.mkdirSync(targetPath); - } catch (e) { - // If the exception isn't to indicate that the folder exists already, rethrow it. - if (e.code !== "EEXIST") { - throw e; - } - } -} - // Check that the file exists in an asynchronous manner that relies solely on the VS Code API, not Node's fs library. export async function fileExists(targetPath: string | vscode.Uri): Promise { try { @@ -67,9 +54,6 @@ export type IReadSessionFileCallback = (details: IEditorServicesSessionDetails) const sessionsFolder = path.resolve(__dirname, "../sessions"); const sessionFilePathPrefix = path.resolve(sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID); -// Create the sessions path if it doesn't exist already -ensurePathExists(sessionsFolder); - export function getSessionFilePath(uniqueId: number) { return `${sessionFilePathPrefix}-${uniqueId}`; } @@ -78,8 +62,8 @@ export function getDebugSessionFilePath() { return `${sessionFilePathPrefix}-Debug`; } -export function writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { - ensurePathExists(sessionsFolder); +export async function writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { + await vscode.workspace.fs.createDirectory(vscode.Uri.file(sessionsFolder)); const writeStream = fs.createWriteStream(sessionFilePath); writeStream.write(JSON.stringify(sessionDetails)); From 8c12f3a48a2556088098a42cd5dd88648719b4c9 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 11:05:53 -0700 Subject: [PATCH 2/8] Move `getSessionFilePath` to `SessionManager` class --- src/features/DebugSession.ts | 2 +- src/features/PesterTests.ts | 2 +- src/features/RunCode.ts | 2 +- src/session.ts | 12 +++++++++++- src/utils.ts | 9 --------- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index 65add51e65..49c81654c0 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -311,7 +311,7 @@ export class DebugSessionFeature extends LanguageClientConsumer // Create or show the interactive console vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); - const sessionFilePath = utils.getDebugSessionFilePath(); + const sessionFilePath = this.sessionManager.getDebugSessionFilePath(); if (config.createTemporaryIntegratedConsole) { // TODO: This should be cleaned up to support multiple temporary consoles. diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 843f0f56ac..a46d60f24f 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -132,7 +132,7 @@ export class PesterTestsFeature implements vscode.Disposable { // Write out temporary debug session file await utils.writeSessionFile( - utils.getDebugSessionFilePath(), + this.sessionManager.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); // TODO: Update to handle multiple root workspaces. diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index 95f818644a..b1da2308d9 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -45,7 +45,7 @@ export class RunCodeFeature implements vscode.Disposable { // Write out temporary debug session file await utils.writeSessionFile( - utils.getDebugSessionFilePath(), + this.sessionManager.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); // TODO: Update to handle multiple root workspaces. diff --git a/src/session.ts b/src/session.ts index 09d93e3cb6..fea12a1f3a 100644 --- a/src/session.ts +++ b/src/session.ts @@ -56,6 +56,8 @@ export class SessionManager implements Middleware { private languageServerClient: LanguageClient = undefined; private sessionSettings: Settings.ISettings = undefined; private sessionDetails: utils.IEditorServicesSessionDetails; + private sessionsFolder = path.resolve(__dirname, "../sessions"); + private sessionFilePathPrefix = path.resolve(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID); private bundledModulesPath: string; private started: boolean = false; @@ -259,6 +261,14 @@ export class SessionManager implements Middleware { return this.versionDetails; } + private getSessionFilePath(uniqueId: number): string { + return `${this.sessionFilePathPrefix}-${uniqueId}`; + } + + public getDebugSessionFilePath(): string { + return `${this.sessionFilePathPrefix}-Debug`; + } + public createDebugSessionProcess( sessionPath: string, sessionSettings: Settings.ISettings): PowerShellProcess { @@ -446,7 +456,7 @@ export class SessionManager implements Middleware { this.setSessionStatus("Starting...", SessionStatus.Initializing); const sessionFilePath = - utils.getSessionFilePath( + this.getSessionFilePath( Math.floor(100000 + Math.random() * 900000)); this.languageServerProcess = diff --git a/src/utils.ts b/src/utils.ts index 8c6ee8f90d..1b06c0800d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -52,15 +52,6 @@ export interface IEditorServicesSessionDetails { export type IReadSessionFileCallback = (details: IEditorServicesSessionDetails) => void; const sessionsFolder = path.resolve(__dirname, "../sessions"); -const sessionFilePathPrefix = path.resolve(sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID); - -export function getSessionFilePath(uniqueId: number) { - return `${sessionFilePathPrefix}-${uniqueId}`; -} - -export function getDebugSessionFilePath() { - return `${sessionFilePathPrefix}-Debug`; -} export async function writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { await vscode.workspace.fs.createDirectory(vscode.Uri.file(sessionsFolder)); From 684742fcb0c9c19a565288f989dc6dd2d0f1aa74 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 13:53:09 -0700 Subject: [PATCH 3/8] Move session file functions to `session` from `utils` --- src/features/DebugSession.ts | 11 ++++----- src/features/PesterTests.ts | 2 +- src/features/RunCode.ts | 2 +- src/process.ts | 17 +++++++------- src/session.ts | 43 +++++++++++++++++++++++++++++++++--- src/utils.ts | 38 ------------------------------- 6 files changed, 55 insertions(+), 58 deletions(-) diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index 49c81654c0..c724d78529 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -8,9 +8,8 @@ import { NotificationType, RequestType } from "vscode-languageclient"; import { LanguageClient } from "vscode-languageclient/node"; import { getPlatformDetails, OperatingSystem } from "../platform"; import { PowerShellProcess} from "../process"; -import { SessionManager, SessionStatus } from "../session"; +import { IEditorServicesSessionDetails, SessionManager, SessionStatus } from "../session"; import Settings = require("../settings"); -import utils = require("../utils"); import { Logger } from "../logging"; import { LanguageClientConsumer } from "../languageClientConsumer"; @@ -25,8 +24,7 @@ export class DebugSessionFeature extends LanguageClientConsumer private sessionCount: number = 1; private tempDebugProcess: PowerShellProcess; - private tempDebugEventHandler: vscode.Disposable; - private tempSessionDetails: utils.IEditorServicesSessionDetails; + private tempSessionDetails: IEditorServicesSessionDetails; constructor(context: ExtensionContext, private sessionManager: SessionManager, private logger: Logger) { super(); @@ -314,12 +312,11 @@ export class DebugSessionFeature extends LanguageClientConsumer const sessionFilePath = this.sessionManager.getDebugSessionFilePath(); if (config.createTemporaryIntegratedConsole) { - // TODO: This should be cleaned up to support multiple temporary consoles. this.tempDebugProcess = this.sessionManager.createDebugSessionProcess(sessionFilePath, settings); this.tempSessionDetails = await this.tempDebugProcess.start(`DebugSession-${this.sessionCount++}`); - await utils.writeSessionFile(sessionFilePath, this.tempSessionDetails); + await this.sessionManager.writeSessionFile(sessionFilePath, this.tempSessionDetails); } else { - await utils.writeSessionFile(sessionFilePath, this.sessionManager.getSessionDetails()); + await this.sessionManager.writeSessionFile(sessionFilePath, this.sessionManager.getSessionDetails()); } return config; diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index a46d60f24f..08f60cf2f5 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -131,7 +131,7 @@ export class PesterTestsFeature implements vscode.Disposable { vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file - await utils.writeSessionFile( + await this.sessionManager.writeSessionFile( this.sessionManager.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index b1da2308d9..8c921f7956 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -44,7 +44,7 @@ export class RunCodeFeature implements vscode.Disposable { await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file - await utils.writeSessionFile( + await this.sessionManager.writeSessionFile( this.sessionManager.getDebugSessionFilePath(), this.sessionManager.getSessionDetails()); diff --git a/src/process.ts b/src/process.ts index 6c3dba72f5..73806edfc6 100644 --- a/src/process.ts +++ b/src/process.ts @@ -8,10 +8,11 @@ import vscode = require("vscode"); import { Logger } from "./logging"; import Settings = require("./settings"); import utils = require("./utils"); +import { IEditorServicesSessionDetails, SessionManager } from "./session"; export class PowerShellProcess { - public static escapeSingleQuotes(pspath: string): string { - return pspath.replace(new RegExp("'", "g"), "''"); + public static escapeSingleQuotes(psPath: string): string { + return psPath.replace(new RegExp("'", "g"), "''"); } // This is used to warn the user that the extension is taking longer than expected to startup. @@ -36,7 +37,7 @@ export class PowerShellProcess { this.onExited = this.onExitedEmitter.event; } - public async start(logFileName: string): Promise { + public async start(logFileName: string): Promise { const editorServicesLogPath = this.log.getLogFilePath(logFileName); const psesModulePath = @@ -99,7 +100,7 @@ export class PowerShellProcess { " PowerShell Editor Services args: " + startEditorServices); // Make sure no old session file exists - utils.deleteSessionFile(this.sessionFilePath); + SessionManager.deleteSessionFile(this.sessionFilePath); // Launch PowerShell in the integrated terminal const terminalOptions: vscode.TerminalOptions = { @@ -149,7 +150,7 @@ export class PowerShellProcess { public dispose() { // Clean up the session file - utils.deleteSessionFile(this.sessionFilePath); + SessionManager.deleteSessionFile(this.sessionFilePath); if (this.consoleCloseSubscription) { this.consoleCloseSubscription.dispose(); @@ -189,7 +190,7 @@ export class PowerShellProcess { return true; } - private async waitForSessionFile(): Promise { + private async waitForSessionFile(): Promise { // Determine how many tries by dividing by 2000 thus checking every 2 seconds. const numOfTries = this.sessionSettings.developer.waitForSessionFileTimeoutSeconds / 2; const warnAt = numOfTries - PowerShellProcess.warnUserThreshold; @@ -198,8 +199,8 @@ export class PowerShellProcess { for (let i = numOfTries; i > 0; i--) { if (utils.checkIfFileExists(this.sessionFilePath)) { this.log.write("Session file found"); - const sessionDetails = utils.readSessionFile(this.sessionFilePath); - utils.deleteSessionFile(this.sessionFilePath); + const sessionDetails = SessionManager.readSessionFile(this.sessionFilePath); + SessionManager.deleteSessionFile(this.sessionFilePath); return sessionDetails; } diff --git a/src/session.ts b/src/session.ts index fea12a1f3a..58dc03145b 100644 --- a/src/session.ts +++ b/src/session.ts @@ -34,6 +34,20 @@ export enum SessionStatus { Failed, } +export interface IEditorServicesSessionDetails { + status: string; + reason: string; + detail: string; + powerShellVersion: string; + channel: string; + languageServicePort: number; + debugServicePort: number; + languageServicePipeName: string; + debugServicePipeName: string; +} + +export type IReadSessionFileCallback = (details: IEditorServicesSessionDetails) => void; + export class SessionManager implements Middleware { public HostName: string; public HostVersion: string; @@ -55,7 +69,7 @@ export class SessionManager implements Middleware { private registeredCommands: vscode.Disposable[] = []; private languageServerClient: LanguageClient = undefined; private sessionSettings: Settings.ISettings = undefined; - private sessionDetails: utils.IEditorServicesSessionDetails; + private sessionDetails: IEditorServicesSessionDetails; private sessionsFolder = path.resolve(__dirname, "../sessions"); private sessionFilePathPrefix = path.resolve(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID); private bundledModulesPath: string; @@ -249,7 +263,7 @@ export class SessionManager implements Middleware { await this.start(exeNameOverride); } - public getSessionDetails(): utils.IEditorServicesSessionDetails { + public getSessionDetails(): IEditorServicesSessionDetails { return this.sessionDetails; } @@ -269,6 +283,29 @@ export class SessionManager implements Middleware { return `${this.sessionFilePathPrefix}-Debug`; } + public async writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { + await vscode.workspace.fs.createDirectory(vscode.Uri.file(this.sessionsFolder)); + + const writeStream = fs.createWriteStream(sessionFilePath); + writeStream.write(JSON.stringify(sessionDetails)); + writeStream.close(); + } + + public static readSessionFile(sessionFilePath: string): IEditorServicesSessionDetails { + // TODO: Use vscode.workspace.fs.readFile instead of fs.readFileSync. + const fileContents = fs.readFileSync(sessionFilePath, "utf-8"); + return JSON.parse(fileContents); + } + + public static async deleteSessionFile(sessionFilePath: string) { + try { + await vscode.workspace.fs.delete(vscode.Uri.file(sessionFilePath)); + // fs.unlinkSync(sessionFilePath); + } catch (e) { + // TODO: Be more specific about what we're catching + } + } + public createDebugSessionProcess( sessionPath: string, sessionSettings: Settings.ISettings): PowerShellProcess { @@ -526,7 +563,7 @@ export class SessionManager implements Middleware { } } - private startLanguageClient(sessionDetails: utils.IEditorServicesSessionDetails) { + private startLanguageClient(sessionDetails: IEditorServicesSessionDetails) { // Log the session details object this.log.write(JSON.stringify(sessionDetails)); diff --git a/src/utils.ts b/src/utils.ts index 1b06c0800d..5e60ff2f12 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -37,44 +37,6 @@ export function getPipePath(pipeName: string) { } } -export interface IEditorServicesSessionDetails { - status: string; - reason: string; - detail: string; - powerShellVersion: string; - channel: string; - languageServicePort: number; - debugServicePort: number; - languageServicePipeName: string; - debugServicePipeName: string; -} - -export type IReadSessionFileCallback = (details: IEditorServicesSessionDetails) => void; - -const sessionsFolder = path.resolve(__dirname, "../sessions"); - -export async function writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { - await vscode.workspace.fs.createDirectory(vscode.Uri.file(sessionsFolder)); - - const writeStream = fs.createWriteStream(sessionFilePath); - writeStream.write(JSON.stringify(sessionDetails)); - writeStream.close(); -} - - -export function readSessionFile(sessionFilePath: string): IEditorServicesSessionDetails { - const fileContents = fs.readFileSync(sessionFilePath, "utf-8"); - return JSON.parse(fileContents); -} - -export function deleteSessionFile(sessionFilePath: string) { - try { - fs.unlinkSync(sessionFilePath); - } catch (e) { - // TODO: Be more specific about what we're catching - } -} - export function checkIfFileExists(filePath: string): boolean { try { fs.accessSync(filePath, fs.constants.R_OK); From 801ca3d7100492e2c8838c978eb4e7c17dfe960c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 13:54:18 -0700 Subject: [PATCH 4/8] Await `executeCommand` in `launch` --- src/features/PesterTests.ts | 2 +- src/features/RunCode.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 08f60cf2f5..13ab30086b 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -128,7 +128,7 @@ export class PesterTestsFeature implements vscode.Disposable { private async launch(launchConfig): Promise { // Create or show the interactive console // TODO: #367 Check if "newSession" mode is configured - vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); + await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file await this.sessionManager.writeSessionFile( diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index 8c921f7956..b463181434 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -40,7 +40,7 @@ export class RunCodeFeature implements vscode.Disposable { private async launch(launchConfig: string | vscode.DebugConfiguration) { // Create or show the interactive console - // TODO #367: Check if "newSession" mode is configured + // TODO: #367: Check if "newSession" mode is configured await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); // Write out temporary debug session file From e4a5878ab4d3db640689a63c8dff82c56cf8ffc9 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 14:44:41 -0700 Subject: [PATCH 5/8] Use `storageUri` for session file Note that we seemed to be unnecessarily writing a new debug session file for the `RunCode` and `PesterTests` features; they work fine without that logic as the debugger itself handles the session file. --- src/features/DebugSession.ts | 2 +- src/features/PesterTests.ts | 5 ----- src/features/RunCode.ts | 5 ----- src/process.ts | 6 +++--- src/session.ts | 39 ++++++++++++++++++------------------ test/core/paths.test.ts | 2 +- 6 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index c724d78529..bf213b9e91 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -309,7 +309,7 @@ export class DebugSessionFeature extends LanguageClientConsumer // Create or show the interactive console vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); - const sessionFilePath = this.sessionManager.getDebugSessionFilePath(); + const sessionFilePath = this.sessionManager.getNewSessionFilePath(); if (config.createTemporaryIntegratedConsole) { this.tempDebugProcess = this.sessionManager.createDebugSessionProcess(sessionFilePath, settings); diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 13ab30086b..397ab1aba1 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -130,11 +130,6 @@ export class PesterTestsFeature implements vscode.Disposable { // TODO: #367 Check if "newSession" mode is configured await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); - // Write out temporary debug session file - await this.sessionManager.writeSessionFile( - this.sessionManager.getDebugSessionFilePath(), - this.sessionManager.getSessionDetails()); - // TODO: Update to handle multiple root workspaces. // // Ensure the necessary script exists (for testing). The debugger will diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index b463181434..4f2855b517 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -43,11 +43,6 @@ export class RunCodeFeature implements vscode.Disposable { // TODO: #367: Check if "newSession" mode is configured await vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); - // Write out temporary debug session file - await this.sessionManager.writeSessionFile( - this.sessionManager.getDebugSessionFilePath(), - this.sessionManager.getSessionDetails()); - // TODO: Update to handle multiple root workspaces. await vscode.debug.startDebugging(vscode.workspace.workspaceFolders?.[0], launchConfig); } diff --git a/src/process.ts b/src/process.ts index 73806edfc6..706b2dcabb 100644 --- a/src/process.ts +++ b/src/process.ts @@ -31,7 +31,7 @@ export class PowerShellProcess { private title: string, private log: Logger, private startPsesArgs: string, - private sessionFilePath: string, + private sessionFilePath: vscode.Uri, private sessionSettings: Settings.ISettings) { this.onExited = this.onExitedEmitter.event; @@ -53,7 +53,7 @@ export class PowerShellProcess { this.startPsesArgs += `-LogPath '${PowerShellProcess.escapeSingleQuotes(editorServicesLogPath.fsPath)}' ` + - `-SessionDetailsPath '${PowerShellProcess.escapeSingleQuotes(this.sessionFilePath)}' ` + + `-SessionDetailsPath '${PowerShellProcess.escapeSingleQuotes(this.sessionFilePath.fsPath)}' ` + `-FeatureFlags @(${featureFlags}) `; if (this.sessionSettings.integratedConsole.useLegacyReadLine) { @@ -197,7 +197,7 @@ export class PowerShellProcess { // Check every 2 seconds for (let i = numOfTries; i > 0; i--) { - if (utils.checkIfFileExists(this.sessionFilePath)) { + if (utils.checkIfFileExists(this.sessionFilePath.fsPath)) { this.log.write("Session file found"); const sessionDetails = SessionManager.readSessionFile(this.sessionFilePath); SessionManager.deleteSessionFile(this.sessionFilePath); diff --git a/src/session.ts b/src/session.ts index 58dc03145b..83495b7d83 100644 --- a/src/session.ts +++ b/src/session.ts @@ -70,8 +70,7 @@ export class SessionManager implements Middleware { private languageServerClient: LanguageClient = undefined; private sessionSettings: Settings.ISettings = undefined; private sessionDetails: IEditorServicesSessionDetails; - private sessionsFolder = path.resolve(__dirname, "../sessions"); - private sessionFilePathPrefix = path.resolve(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID); + private sessionsFolder: vscode.Uri; private bundledModulesPath: string; private started: boolean = false; @@ -86,6 +85,12 @@ export class SessionManager implements Middleware { version: string, private telemetryReporter: TelemetryReporter) { + if (extensionContext.storageUri !== undefined) { + this.sessionsFolder = vscode.Uri.joinPath(extensionContext.storageUri, "sessions"); + } else { + this.sessionsFolder = vscode.Uri.file(path.resolve(__dirname, "../sessions")); + } + this.platformDetails = getPlatformDetails(); this.HostName = hostName; @@ -275,39 +280,35 @@ export class SessionManager implements Middleware { return this.versionDetails; } - private getSessionFilePath(uniqueId: number): string { - return `${this.sessionFilePathPrefix}-${uniqueId}`; - } - - public getDebugSessionFilePath(): string { - return `${this.sessionFilePathPrefix}-Debug`; + public getNewSessionFilePath(): vscode.Uri { + const uniqueId: number = Math.floor(100000 + Math.random() * 900000); + return vscode.Uri.joinPath(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID + "-" + uniqueId + ".json"); } - public async writeSessionFile(sessionFilePath: string, sessionDetails: IEditorServicesSessionDetails) { - await vscode.workspace.fs.createDirectory(vscode.Uri.file(this.sessionsFolder)); + public async writeSessionFile(sessionFilePath: vscode.Uri, sessionDetails: IEditorServicesSessionDetails) { + await vscode.workspace.fs.createDirectory(this.sessionsFolder); - const writeStream = fs.createWriteStream(sessionFilePath); + const writeStream = fs.createWriteStream(sessionFilePath.fsPath); writeStream.write(JSON.stringify(sessionDetails)); writeStream.close(); } - public static readSessionFile(sessionFilePath: string): IEditorServicesSessionDetails { + public static readSessionFile(sessionFilePath: vscode.Uri): IEditorServicesSessionDetails { // TODO: Use vscode.workspace.fs.readFile instead of fs.readFileSync. - const fileContents = fs.readFileSync(sessionFilePath, "utf-8"); + const fileContents = fs.readFileSync(sessionFilePath.fsPath, "utf-8"); return JSON.parse(fileContents); } - public static async deleteSessionFile(sessionFilePath: string) { + public static async deleteSessionFile(sessionFilePath: vscode.Uri) { try { - await vscode.workspace.fs.delete(vscode.Uri.file(sessionFilePath)); - // fs.unlinkSync(sessionFilePath); + await vscode.workspace.fs.delete(sessionFilePath); } catch (e) { // TODO: Be more specific about what we're catching } } public createDebugSessionProcess( - sessionPath: string, + sessionPath: vscode.Uri, sessionSettings: Settings.ISettings): PowerShellProcess { // NOTE: We only support one temporary integrated console at a time. To @@ -492,9 +493,7 @@ export class SessionManager implements Middleware { private startPowerShell() { this.setSessionStatus("Starting...", SessionStatus.Initializing); - const sessionFilePath = - this.getSessionFilePath( - Math.floor(100000 + Math.random() * 900000)); + const sessionFilePath = this.getNewSessionFilePath(); this.languageServerProcess = new PowerShellProcess( diff --git a/test/core/paths.test.ts b/test/core/paths.test.ts index 8559db5f79..0213b363d7 100644 --- a/test/core/paths.test.ts +++ b/test/core/paths.test.ts @@ -23,7 +23,7 @@ describe("Path assumptions", function () { }); it("Creates the session folder at the correct path", function () { - assert(fs.existsSync(path.resolve(utils.rootPath, "sessions"))); + assert(fs.existsSync(vscode.Uri.joinPath(storageUri, "sessions").fsPath)); }); it("Creates the log folder at the correct path", function () { From 8b27d866b59619916568ffcd93a2445993d45b37 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 15:15:57 -0700 Subject: [PATCH 6/8] Remove `writeSessionFile` as only the server writes it No idea why we were writing it as the client. Maybe I've totally misunderstood, but everything works as expected without this (and moreover, we delete any existing session file and then wait for it, after passing the path to the server). --- src/features/DebugSession.ts | 7 +------ src/session.ts | 20 +++++--------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index bf213b9e91..00635e5ced 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -309,14 +309,9 @@ export class DebugSessionFeature extends LanguageClientConsumer // Create or show the interactive console vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true); - const sessionFilePath = this.sessionManager.getNewSessionFilePath(); - if (config.createTemporaryIntegratedConsole) { - this.tempDebugProcess = this.sessionManager.createDebugSessionProcess(sessionFilePath, settings); + this.tempDebugProcess = this.sessionManager.createDebugSessionProcess(settings); this.tempSessionDetails = await this.tempDebugProcess.start(`DebugSession-${this.sessionCount++}`); - await this.sessionManager.writeSessionFile(sessionFilePath, this.tempSessionDetails); - } else { - await this.sessionManager.writeSessionFile(sessionFilePath, this.sessionManager.getSessionDetails()); } return config; diff --git a/src/session.ts b/src/session.ts index 83495b7d83..76de1f7653 100644 --- a/src/session.ts +++ b/src/session.ts @@ -85,11 +85,13 @@ export class SessionManager implements Middleware { version: string, private telemetryReporter: TelemetryReporter) { + // Create a folder for the session files. if (extensionContext.storageUri !== undefined) { this.sessionsFolder = vscode.Uri.joinPath(extensionContext.storageUri, "sessions"); } else { this.sessionsFolder = vscode.Uri.file(path.resolve(__dirname, "../sessions")); } + vscode.workspace.fs.createDirectory(this.sessionsFolder); this.platformDetails = getPlatformDetails(); @@ -285,14 +287,6 @@ export class SessionManager implements Middleware { return vscode.Uri.joinPath(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID + "-" + uniqueId + ".json"); } - public async writeSessionFile(sessionFilePath: vscode.Uri, sessionDetails: IEditorServicesSessionDetails) { - await vscode.workspace.fs.createDirectory(this.sessionsFolder); - - const writeStream = fs.createWriteStream(sessionFilePath.fsPath); - writeStream.write(JSON.stringify(sessionDetails)); - writeStream.close(); - } - public static readSessionFile(sessionFilePath: vscode.Uri): IEditorServicesSessionDetails { // TODO: Use vscode.workspace.fs.readFile instead of fs.readFileSync. const fileContents = fs.readFileSync(sessionFilePath.fsPath, "utf-8"); @@ -307,9 +301,7 @@ export class SessionManager implements Middleware { } } - public createDebugSessionProcess( - sessionPath: vscode.Uri, - sessionSettings: Settings.ISettings): PowerShellProcess { + public createDebugSessionProcess(sessionSettings: Settings.ISettings): PowerShellProcess { // NOTE: We only support one temporary integrated console at a time. To // support more, we need to track each separately, and tie the session @@ -327,7 +319,7 @@ export class SessionManager implements Middleware { "[TEMP] PowerShell Integrated Console", this.log, this.editorServicesArgs + "-DebugServiceOnly ", - sessionPath, + this.getNewSessionFilePath(), sessionSettings); // Similar to the regular integrated console, we need to send a key @@ -493,8 +485,6 @@ export class SessionManager implements Middleware { private startPowerShell() { this.setSessionStatus("Starting...", SessionStatus.Initializing); - const sessionFilePath = this.getNewSessionFilePath(); - this.languageServerProcess = new PowerShellProcess( this.PowerShellExeDetails.exePath, @@ -502,7 +492,7 @@ export class SessionManager implements Middleware { "PowerShell Integrated Console", this.log, this.editorServicesArgs, - sessionFilePath, + this.getNewSessionFilePath(), this.sessionSettings); this.languageServerProcess.onExited( From 14c7d4a6be92f79f3c1395ec95a64e96ddfc0086 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 20 Jul 2022 15:21:19 -0700 Subject: [PATCH 7/8] Move `readSessionFile` and `deleteSessionFile` to `process` As that's the only place they're used. --- src/process.ts | 23 +++++++++-- src/session.ts | 110 ++++++++++++++++++++++--------------------------- 2 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/process.ts b/src/process.ts index 706b2dcabb..f98513efb2 100644 --- a/src/process.ts +++ b/src/process.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import fs = require("fs"); import cp = require("child_process"); import * as semver from "semver"; import path = require("path"); @@ -100,7 +101,7 @@ export class PowerShellProcess { " PowerShell Editor Services args: " + startEditorServices); // Make sure no old session file exists - SessionManager.deleteSessionFile(this.sessionFilePath); + await PowerShellProcess.deleteSessionFile(this.sessionFilePath); // Launch PowerShell in the integrated terminal const terminalOptions: vscode.TerminalOptions = { @@ -150,7 +151,7 @@ export class PowerShellProcess { public dispose() { // Clean up the session file - SessionManager.deleteSessionFile(this.sessionFilePath); + PowerShellProcess.deleteSessionFile(this.sessionFilePath); if (this.consoleCloseSubscription) { this.consoleCloseSubscription.dispose(); @@ -190,6 +191,20 @@ export class PowerShellProcess { return true; } + private static readSessionFile(sessionFilePath: vscode.Uri): IEditorServicesSessionDetails { + // TODO: Use vscode.workspace.fs.readFile instead of fs.readFileSync. + const fileContents = fs.readFileSync(sessionFilePath.fsPath, "utf-8"); + return JSON.parse(fileContents); + } + + private static async deleteSessionFile(sessionFilePath: vscode.Uri) { + try { + await vscode.workspace.fs.delete(sessionFilePath); + } catch (e) { + // TODO: Be more specific about what we're catching + } + } + private async waitForSessionFile(): Promise { // Determine how many tries by dividing by 2000 thus checking every 2 seconds. const numOfTries = this.sessionSettings.developer.waitForSessionFileTimeoutSeconds / 2; @@ -199,8 +214,8 @@ export class PowerShellProcess { for (let i = numOfTries; i > 0; i--) { if (utils.checkIfFileExists(this.sessionFilePath.fsPath)) { this.log.write("Session file found"); - const sessionDetails = SessionManager.readSessionFile(this.sessionFilePath); - SessionManager.deleteSessionFile(this.sessionFilePath); + const sessionDetails = PowerShellProcess.readSessionFile(this.sessionFilePath); + PowerShellProcess.deleteSessionFile(this.sessionFilePath); return sessionDetails; } diff --git a/src/session.ts b/src/session.ts index 76de1f7653..6bda55604c 100644 --- a/src/session.ts +++ b/src/session.ts @@ -16,13 +16,15 @@ import utils = require("./utils"); import { CloseAction, DocumentSelector, ErrorAction, LanguageClientOptions, Middleware, NotificationType, RequestType0, - ResolveCodeLensSignature, RevealOutputChannelOn } from "vscode-languageclient"; + ResolveCodeLensSignature, RevealOutputChannelOn +} from "vscode-languageclient"; import { LanguageClient, StreamInfo } from "vscode-languageclient/node"; import { GitHubReleaseInformation, InvokePowerShellUpdateCheck } from "./features/UpdatePowerShell"; import { getPlatformDetails, IPlatformDetails, IPowerShellExeDetails, - OperatingSystem, PowerShellExeFinder } from "./platform"; + OperatingSystem, PowerShellExeFinder +} from "./platform"; import { LanguageClientConsumer } from "./languageClientConsumer"; export enum SessionStatus { @@ -287,20 +289,6 @@ export class SessionManager implements Middleware { return vscode.Uri.joinPath(this.sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID + "-" + uniqueId + ".json"); } - public static readSessionFile(sessionFilePath: vscode.Uri): IEditorServicesSessionDetails { - // TODO: Use vscode.workspace.fs.readFile instead of fs.readFileSync. - const fileContents = fs.readFileSync(sessionFilePath.fsPath, "utf-8"); - return JSON.parse(fileContents); - } - - public static async deleteSessionFile(sessionFilePath: vscode.Uri) { - try { - await vscode.workspace.fs.delete(sessionFilePath); - } catch (e) { - // TODO: Be more specific about what we're catching - } - } - public createDebugSessionProcess(sessionSettings: Settings.ISettings): PowerShellProcess { // NOTE: We only support one temporary integrated console at a time. To @@ -337,7 +325,7 @@ export class SessionManager implements Middleware { } public async waitUntilStarted(): Promise { - while(!this.started) { + while (!this.started) { await utils.sleep(300); } } @@ -348,43 +336,43 @@ export class SessionManager implements Middleware { codeLens: vscode.CodeLens, token: vscode.CancellationToken, next: ResolveCodeLensSignature): vscode.ProviderResult { - const resolvedCodeLens = next(codeLens, token); - const resolveFunc = - (codeLensToFix: vscode.CodeLens): vscode.CodeLens => { - if (codeLensToFix.command?.command === "editor.action.showReferences") { - const oldArgs = codeLensToFix.command.arguments; - - // Our JSON objects don't get handled correctly by - // VS Code's built in editor.action.showReferences - // command so we need to convert them into the - // appropriate types to send them as command - // arguments. - - codeLensToFix.command.arguments = [ - vscode.Uri.parse(oldArgs[0]), - new vscode.Position(oldArgs[1].line, oldArgs[1].character), - oldArgs[2].map((position) => { - return new vscode.Location( - vscode.Uri.parse(position.uri), - new vscode.Range( - position.range.start.line, - position.range.start.character, - position.range.end.line, - position.range.end.character)); - }), - ]; - } + const resolvedCodeLens = next(codeLens, token); + const resolveFunc = + (codeLensToFix: vscode.CodeLens): vscode.CodeLens => { + if (codeLensToFix.command?.command === "editor.action.showReferences") { + const oldArgs = codeLensToFix.command.arguments; + + // Our JSON objects don't get handled correctly by + // VS Code's built in editor.action.showReferences + // command so we need to convert them into the + // appropriate types to send them as command + // arguments. + + codeLensToFix.command.arguments = [ + vscode.Uri.parse(oldArgs[0]), + new vscode.Position(oldArgs[1].line, oldArgs[1].character), + oldArgs[2].map((position) => { + return new vscode.Location( + vscode.Uri.parse(position.uri), + new vscode.Range( + position.range.start.line, + position.range.start.character, + position.range.end.line, + position.range.end.character)); + }), + ]; + } - return codeLensToFix; - }; + return codeLensToFix; + }; - if ((resolvedCodeLens as Thenable).then) { - return (resolvedCodeLens as Thenable).then(resolveFunc); - } else if (resolvedCodeLens as vscode.CodeLens) { - return resolveFunc(resolvedCodeLens as vscode.CodeLens); - } + if ((resolvedCodeLens as Thenable).then) { + return (resolvedCodeLens as Thenable).then(resolveFunc); + } else if (resolvedCodeLens as vscode.CodeLens) { + return resolveFunc(resolvedCodeLens as vscode.CodeLens); + } - return resolvedCodeLens; + return resolvedCodeLens; } // Move old setting codeFormatting.whitespaceAroundPipe to new setting codeFormatting.addWhitespaceAroundPipe @@ -440,9 +428,9 @@ export class SessionManager implements Middleware { this.sessionSettings.cwd.toLowerCase() || settings.powerShellDefaultVersion.toLowerCase() !== this.sessionSettings.powerShellDefaultVersion.toLowerCase() || - settings.developer.editorServicesLogLevel.toLowerCase() !== + settings.developer.editorServicesLogLevel.toLowerCase() !== this.sessionSettings.developer.editorServicesLogLevel.toLowerCase() || - settings.developer.bundledModulesPath.toLowerCase() !== + settings.developer.bundledModulesPath.toLowerCase() !== this.sessionSettings.developer.bundledModulesPath.toLowerCase() || settings.integratedConsole.useLegacyReadLine !== this.sessionSettings.integratedConsole.useLegacyReadLine)) { @@ -451,9 +439,9 @@ export class SessionManager implements Middleware { "The PowerShell runtime configuration has changed, would you like to start a new session?", "Yes", "No"); - if (response === "Yes") { - await this.restartSession(); - } + if (response === "Yes") { + await this.restartSession(); + } } } @@ -567,7 +555,7 @@ export class SessionManager implements Middleware { "connect", () => { this.log.write("Language service connected."); - resolve({writer: socket, reader: socket}); + resolve({ writer: socket, reader: socket }); }); }); }; @@ -576,7 +564,7 @@ export class SessionManager implements Middleware { documentSelector: this.documentSelector, synchronize: { // backend uses "files" and "search" to ignore references. - configurationSection: [ utils.PowerShellLanguageId, "files", "search" ], + configurationSection: [utils.PowerShellLanguageId, "files", "search"], // fileEvents: vscode.workspace.createFileSystemWatcher('**/.eslintrc') }, // NOTE: Some settings are only applicable on startup, so we send them during initialization. @@ -819,8 +807,8 @@ export class SessionManager implements Middleware { case SessionStatus.NeverStarted: case SessionStatus.Stopping: const currentPowerShellExe = - availablePowerShellExes - .find((item) => item.displayName.toLowerCase() === this.PowerShellExeDetails.displayName.toLowerCase()); + availablePowerShellExes + .find((item) => item.displayName.toLowerCase() === this.PowerShellExeDetails.displayName.toLowerCase()); const powerShellSessionName = currentPowerShellExe ? @@ -887,7 +875,7 @@ class SessionMenuItem implements vscode.QuickPickItem { constructor( public readonly label: string, // tslint:disable-next-line:no-empty - public readonly callback: () => void = () => {}) { + public readonly callback: () => void = () => { }) { } } From 5f80a6e2e576cbbfca659e33ab8384b26d22b046 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 21 Jul 2022 08:36:56 -0700 Subject: [PATCH 8/8] Fix null exception in setting change detection Since `cwd` can be `undefined` we need to use optional chaining. --- src/session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/session.ts b/src/session.ts index 6bda55604c..40d576cf56 100644 --- a/src/session.ts +++ b/src/session.ts @@ -424,8 +424,8 @@ export class SessionManager implements Middleware { // Detect any setting changes that would affect the session if (!this.suppressRestartPrompt && - (settings.cwd.toLowerCase() !== - this.sessionSettings.cwd.toLowerCase() || + (settings.cwd?.toLowerCase() !== + this.sessionSettings.cwd?.toLowerCase() || settings.powerShellDefaultVersion.toLowerCase() !== this.sessionSettings.powerShellDefaultVersion.toLowerCase() || settings.developer.editorServicesLogLevel.toLowerCase() !==