diff --git a/src/session.ts b/src/session.ts index a6bfde9b94..4d54f76807 100644 --- a/src/session.ts +++ b/src/session.ts @@ -126,6 +126,11 @@ export class SessionManager implements Middleware { private versionDetails: IPowerShellVersionDetails | undefined; private traceLogLevelHandler?: vscode.Disposable; + // Promise-based gate resolved when the session reaches Running status. + // Used by waitUntilStarted() and the didOpen()/didChange() notifications. + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + private started = Promise.withResolvers(); + constructor( private extensionContext: vscode.ExtensionContext, private sessionSettings: Settings, @@ -295,6 +300,7 @@ export class SessionManager implements Middleware { `Started PowerShell v${this.versionDetails.version}.`, ); this.setSessionRunningStatus(); // Yay, we made it! + this.started.resolve(); // Release didOpen()/didChange() notifications and waitUntilStarted() gate await this.writePidIfInDevMode(this.languageServerProcess); @@ -328,6 +334,8 @@ export class SessionManager implements Middleware { } this.languageClient = undefined; + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + this.started = Promise.withResolvers(); // Stop and dispose the PowerShell process(es). this.debugSessionProcess?.dispose(); @@ -497,9 +505,27 @@ export class SessionManager implements Middleware { } public async waitUntilStarted(): Promise { - while (this.sessionStatus !== SessionStatus.Running) { - await utils.sleep(200); - } + await this.started.promise; + } + + // Middleware hooks to delay document sync notifications until the server + // is fully initialized. This prevents stale parser diagnostics (e.g. + // unresolved custom attribute types) that would otherwise appear because + // textDocument/didOpen is sent before the server's type resolution is ready. + public async didOpen( + document: vscode.TextDocument, + next: (document: vscode.TextDocument) => Promise, + ): Promise { + await this.started.promise; + return next(document); + } + + public async didChange( + event: vscode.TextDocumentChangeEvent, + next: (event: vscode.TextDocumentChangeEvent) => Promise, + ): Promise { + await this.started.promise; + return next(event); } // TODO: Is this used by the magic of "Middleware" in the client library?