Skip to content

Commit d6c0f03

Browse files
committed
Fix incorrect parser errors from unloaded assemblies
An attempt to fix #5381 where we believe the cause is that the didOpen()/didChange() notifications are being sent before PowerShell has totally finished loading. Since these run the PowerShell parser on the OmniSharp thread pool, they essentially race the analysis of the files by PSScriptAnalyzer on the PowerShell runspace pool. So when they beat it, the return errors. But when they're beaten by it, the PSSA analysis has caused the assemblies (and so custom attributes) to be loaded, no longer erroring. I posited we could gate the notifications instead of duplicating them like in #5402, and if the `Middleware` works as suspected by me (and Claude) this should fix it. Morever, we now also use a proper `Promise` instead of a while loop around a sleep to wait for the LSP server to be running. While all of this could just be an AI hallucination...it seems right.
1 parent d4d90b8 commit d6c0f03

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

src/session.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ export class SessionManager implements Middleware {
126126
private versionDetails: IPowerShellVersionDetails | undefined;
127127
private traceLogLevelHandler?: vscode.Disposable;
128128

129+
// Promise-based gate resolved when the session reaches Running status.
130+
// Used by waitUntilStarted() and the didOpen()/didChange() notifications.
131+
private started = Promise.withResolvers<undefined>();
132+
129133
constructor(
130134
private extensionContext: vscode.ExtensionContext,
131135
private sessionSettings: Settings,
@@ -295,6 +299,7 @@ export class SessionManager implements Middleware {
295299
`Started PowerShell v${this.versionDetails.version}.`,
296300
);
297301
this.setSessionRunningStatus(); // Yay, we made it!
302+
this.started.resolve(undefined); // Release didOpen()/didChange() notifications and waitUntilStarted() gate
298303

299304
await this.writePidIfInDevMode(this.languageServerProcess);
300305

@@ -328,6 +333,8 @@ export class SessionManager implements Middleware {
328333
}
329334

330335
this.languageClient = undefined;
336+
this.started.resolve(undefined);
337+
this.started = Promise.withResolvers<undefined>();
331338

332339
// Stop and dispose the PowerShell process(es).
333340
this.debugSessionProcess?.dispose();
@@ -497,9 +504,27 @@ export class SessionManager implements Middleware {
497504
}
498505

499506
public async waitUntilStarted(): Promise<void> {
500-
while (this.sessionStatus !== SessionStatus.Running) {
501-
await utils.sleep(200);
502-
}
507+
await this.started.promise;
508+
}
509+
510+
// Middleware hooks to delay document sync notifications until the server
511+
// is fully initialized. This prevents stale parser diagnostics (e.g.
512+
// unresolved custom attribute types) that would otherwise appear because
513+
// textDocument/didOpen is sent before the server's type resolution is ready.
514+
public async didOpen(
515+
document: vscode.TextDocument,
516+
next: (document: vscode.TextDocument) => Promise<void>,
517+
): Promise<void> {
518+
await this.started.promise;
519+
return next(document);
520+
}
521+
522+
public async didChange(
523+
event: vscode.TextDocumentChangeEvent,
524+
next: (event: vscode.TextDocumentChangeEvent) => Promise<void>,
525+
): Promise<void> {
526+
await this.started.promise;
527+
return next(event);
503528
}
504529

505530
// TODO: Is this used by the magic of "Middleware" in the client library?

0 commit comments

Comments
 (0)