Skip to content

Commit b435e56

Browse files
authored
Adding additional parse checking logic (#14407)
Adding additional parse checking logic to determine if idle state has no pending calls and finished workspace parsing, file parsing, and intellisense updates.
1 parent 25cc9b7 commit b435e56

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

Extension/src/LanguageServer/client.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ interface ConfigStateReceived {
104104
timeout: boolean;
105105
}
106106

107+
interface PendingTagParsingCall {
108+
promise: ManualPromise<boolean>;
109+
timer: NodeJS.Timeout;
110+
cancellationListener: vscode.Disposable;
111+
}
112+
107113
let workspaceHash: string = "";
108114

109115
let workspaceDisposables: vscode.Disposable[] = [];
@@ -805,6 +811,7 @@ export interface Client {
805811
setCurrentConfigName(configurationName: string): Thenable<void>;
806812
getCurrentConfigName(): Thenable<string | undefined>;
807813
getCurrentConfigCustomVariable(variableName: string): Thenable<string>;
814+
waitForTagParsing(timeout: number, token: vscode.CancellationToken): Promise<boolean>;
808815
getVcpkgInstalled(): Thenable<boolean>;
809816
getVcpkgEnabled(): Thenable<boolean>;
810817
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined>;
@@ -919,6 +926,7 @@ export class DefaultClient implements Client {
919926

920927
private configStateReceived: ConfigStateReceived = { compilers: false, compileCommands: false, configProviders: undefined, timeout: false };
921928
private showConfigureIntelliSenseButton: boolean = false;
929+
private pendingTagParsingCalls: PendingTagParsingCall[] = [];
922930

923931
/** A queue of asynchronous tasks that need to be processed befofe ready is considered active. */
924932
private static queue = new Array<[ManualPromise<unknown>, () => Promise<unknown>] | [ManualPromise<unknown>]>();
@@ -1008,6 +1016,62 @@ export class DefaultClient implements Client {
10081016
};
10091017
}
10101018

1019+
// If there are any pending calls that were waiting for tag parsing to complete, we can resolve them since it's finished. If there are no pending calls, this does nothing.
1020+
private resolvePendingTagParsingCallsIfReady(): void {
1021+
if (!this.pendingTagParsingCalls.length || this.IsTagParsing) {
1022+
return;
1023+
}
1024+
1025+
const pendingCalls: PendingTagParsingCall[] = this.pendingTagParsingCalls;
1026+
this.pendingTagParsingCalls = [];
1027+
pendingCalls.forEach(pendingCall => {
1028+
if (pendingCall.timer) {
1029+
clearTimeout(pendingCall.timer);
1030+
}
1031+
pendingCall.cancellationListener.dispose();
1032+
pendingCall.promise.resolve(true);
1033+
});
1034+
}
1035+
1036+
public async waitForTagParsing(timeout: number, token: vscode.CancellationToken): Promise<boolean> {
1037+
// On initialization, the client has UI bools all set to false which could cause an early return. We want to ensure it's ready first.
1038+
await this.ready;
1039+
1040+
if (!this.IsTagParsing) {
1041+
return true;
1042+
}
1043+
1044+
if (token.isCancellationRequested) {
1045+
throw new vscode.CancellationError();
1046+
}
1047+
1048+
const pendingCall: PendingTagParsingCall = {
1049+
promise: new ManualPromise<boolean>(),
1050+
1051+
timer: global.setTimeout(() => {
1052+
const index: number = this.pendingTagParsingCalls.indexOf(pendingCall);
1053+
if (index !== -1) {
1054+
this.pendingTagParsingCalls.splice(index, 1);
1055+
}
1056+
pendingCall.cancellationListener.dispose();
1057+
pendingCall.promise.resolve(false);
1058+
}, timeout),
1059+
1060+
cancellationListener: token.onCancellationRequested(() => {
1061+
const index: number = this.pendingTagParsingCalls.indexOf(pendingCall);
1062+
if (index !== -1) {
1063+
this.pendingTagParsingCalls.splice(index, 1);
1064+
}
1065+
clearTimeout(pendingCall.timer);
1066+
pendingCall.cancellationListener.dispose();
1067+
pendingCall.promise.reject(new vscode.CancellationError());
1068+
})
1069+
};
1070+
1071+
this.pendingTagParsingCalls.push(pendingCall);
1072+
return pendingCall.promise;
1073+
}
1074+
10111075
private getName(workspaceFolder?: vscode.WorkspaceFolder): string {
10121076
return workspaceFolder ? workspaceFolder.name : "untitled";
10131077
}
@@ -2893,6 +2957,8 @@ export class DefaultClient implements Client {
28932957
} else if (message.includes("/")) {
28942958
this.lastInvokedLspMessage = message;
28952959
}
2960+
2961+
this.resolvePendingTagParsingCallsIfReady();
28962962
}
28972963

28982964
private updateTagParseStatus(tagParseStatus: TagParseStatus): void {
@@ -4208,6 +4274,14 @@ export class DefaultClient implements Client {
42084274
}
42094275

42104276
public dispose(): void {
4277+
this.pendingTagParsingCalls.forEach(pendingCall => {
4278+
if (pendingCall.timer) {
4279+
clearTimeout(pendingCall.timer);
4280+
}
4281+
pendingCall.cancellationListener.dispose();
4282+
pendingCall.promise.resolve(false);
4283+
});
4284+
this.pendingTagParsingCalls = [];
42114285
this.disposables.forEach((d) => d.dispose());
42124286
this.disposables = [];
42134287
if (this.documentFormattingProviderDisposable) {
@@ -4360,6 +4434,7 @@ class NullClient implements Client {
43604434
setCurrentConfigName(configurationName: string): Thenable<void> { return Promise.resolve(); }
43614435
getCurrentConfigName(): Thenable<string> { return Promise.resolve(""); }
43624436
getCurrentConfigCustomVariable(variableName: string): Thenable<string> { return Promise.resolve(""); }
4437+
waitForTagParsing(timeout: number, token: vscode.CancellationToken): Promise<boolean> { return Promise.resolve(true); }
43634438
getVcpkgInstalled(): Thenable<boolean> { return Promise.resolve(false); }
43644439
getVcpkgEnabled(): Thenable<boolean> { return Promise.resolve(false); }
43654440
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined> { return Promise.resolve(undefined); }

Extension/src/LanguageServer/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ export async function registerCommands(enabled: boolean): Promise<void> {
430430
commandDisposables.push(vscode.commands.registerCommand('cpptools.activeConfigName', enabled ? onGetActiveConfigName : onDisabledCommand));
431431
commandDisposables.push(vscode.commands.registerCommand('cpptools.activeConfigCustomVariable', enabled ? onGetActiveConfigCustomVariable : onDisabledCommand));
432432
commandDisposables.push(vscode.commands.registerCommand('cpptools.setActiveConfigName', enabled ? onSetActiveConfigName : onDisabledCommand));
433+
commandDisposables.push(vscode.commands.registerCommand('cpptools.waitForTagParsing', enabled ? onWaitForTagParsing : () => true));
433434
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.RestartIntelliSenseForFile', enabled ? onRestartIntelliSenseForFile : onDisabledCommand));
434435
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.GenerateDoxygenComment', enabled ? onGenerateDoxygenComment : onDisabledCommand));
435436
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CreateDeclarationOrDefinition', enabled ? onCreateDeclarationOrDefinition : onDisabledCommand));
@@ -977,6 +978,10 @@ function onGetActiveConfigCustomVariable(variableName: string): Thenable<string>
977978
return clients.ActiveClient.getCurrentConfigCustomVariable(variableName);
978979
}
979980

981+
async function onWaitForTagParsing(timeout: number, token: vscode.CancellationToken): Promise<boolean> {
982+
return clients.getDefaultClient().waitForTagParsing(timeout, token);
983+
}
984+
980985
function onLogDiagnostics(): Promise<void> {
981986
return clients.ActiveClient.logDiagnostics();
982987
}

0 commit comments

Comments
 (0)