Skip to content

Commit 458ef30

Browse files
authored
Rework custom config requests (#12727)
1 parent 970f95b commit 458ef30

File tree

2 files changed

+51
-94
lines changed

2 files changed

+51
-94
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 50 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,6 @@ interface CompileCommandsPaths extends WorkspaceFolderParams {
246246
paths: string[];
247247
}
248248

249-
interface QueryTranslationUnitSourceParams extends WorkspaceFolderParams {
250-
uri: string;
251-
ignoreExisting: boolean;
252-
}
253-
254-
interface QueryTranslationUnitSourceResult {
255-
candidates: string[];
256-
}
257-
258249
interface GetDiagnosticsResult {
259250
diagnostics: string;
260251
}
@@ -554,7 +545,6 @@ export interface ChatContextResult {
554545
const PreInitializationRequest: RequestType<void, string, void> = new RequestType<void, string, void>('cpptools/preinitialize');
555546
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
556547
const QueryCompilerDefaultsRequest: RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void> = new RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void>('cpptools/queryCompilerDefaults');
557-
const QueryTranslationUnitSourceRequest: RequestType<QueryTranslationUnitSourceParams, QueryTranslationUnitSourceResult, void> = new RequestType<QueryTranslationUnitSourceParams, QueryTranslationUnitSourceResult, void>('cpptools/queryTranslationUnitSource');
558548
const SwitchHeaderSourceRequest: RequestType<SwitchHeaderSourceParams, string, void> = new RequestType<SwitchHeaderSourceParams, string, void>('cpptools/didSwitchHeaderSource');
559549
const GetDiagnosticsRequest: RequestType<void, GetDiagnosticsResult, void> = new RequestType<void, GetDiagnosticsResult, void>('cpptools/getDiagnostics');
560550
export const GetDocumentSymbolRequest: RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void> = new RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void>('cpptools/getDocumentSymbols');
@@ -744,7 +734,7 @@ export interface Client {
744734
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void>;
745735
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
746736
updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
747-
provideCustomConfiguration(docUri: vscode.Uri, requestFile?: string, replaceExisting?: boolean): Promise<void>;
737+
provideCustomConfiguration(docUri: vscode.Uri): Promise<void>;
748738
logDiagnostics(): Promise<void>;
749739
rescanFolder(): Promise<void>;
750740
toggleReferenceResultsView(): void;
@@ -1871,9 +1861,6 @@ export class DefaultClient implements Client {
18711861
}
18721862

18731863
await this.clearCustomConfigurations();
1874-
await Promise.all([
1875-
...[...this.trackedDocuments].map(([_uri, document]) => this.provideCustomConfiguration(document.uri, undefined, true))
1876-
]);
18771864
}
18781865

18791866
public async updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Promise<void> {
@@ -2023,68 +2010,42 @@ export class DefaultClient implements Client {
20232010
return this.languageClient.sendNotification(RescanFolderNotification);
20242011
}
20252012

2026-
public async provideCustomConfiguration(docUri: vscode.Uri, requestFile?: string, replaceExisting?: boolean): Promise<void> {
2013+
public async provideCustomConfiguration(docUri: vscode.Uri): Promise<void> {
20272014
const onFinished: () => void = () => {
2028-
if (requestFile) {
2029-
void this.languageClient.sendNotification(FinishedRequestCustomConfig, { uri: requestFile });
2030-
}
2015+
void this.languageClient.sendNotification(FinishedRequestCustomConfig, { uri: docUri.toString() });
20312016
};
2032-
const providerId: string | undefined = this.configurationProvider;
2033-
if (!providerId) {
2034-
onFinished();
2035-
return;
2036-
}
2037-
const provider: CustomConfigurationProvider1 | undefined = getCustomConfigProviders().get(providerId);
2038-
if (!provider || !provider.isReady) {
2039-
onFinished();
2040-
return;
2041-
}
20422017
try {
2043-
DefaultClient.isStarted.reset();
2044-
const resultCode = await this.provideCustomConfigurationAsync(docUri, requestFile, replaceExisting, provider);
2018+
const providerId: string | undefined = this.configurationProvider;
2019+
if (!providerId) {
2020+
return;
2021+
}
2022+
const provider: CustomConfigurationProvider1 | undefined = getCustomConfigProviders().get(providerId);
2023+
if (!provider || !provider.isReady) {
2024+
return;
2025+
}
2026+
const resultCode = await this.provideCustomConfigurationAsync(docUri, provider);
20452027
telemetry.logLanguageServerEvent('provideCustomConfiguration', { providerId, resultCode });
20462028
} finally {
20472029
onFinished();
2048-
DefaultClient.isStarted.resolve();
20492030
}
20502031
}
20512032

2052-
private async provideCustomConfigurationAsync(docUri: vscode.Uri, requestFile: string | undefined, replaceExisting: boolean | undefined, provider: CustomConfigurationProvider1): Promise<string> {
2033+
private async provideCustomConfigurationAsync(docUri: vscode.Uri, provider: CustomConfigurationProvider1): Promise<string> {
20532034
const tokenSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
20542035

2055-
const params: QueryTranslationUnitSourceParams = {
2056-
uri: docUri.toString(),
2057-
ignoreExisting: !!replaceExisting,
2058-
workspaceFolderUri: this.RootUri?.toString()
2059-
};
2060-
2061-
const response: QueryTranslationUnitSourceResult = await this.languageClient.sendRequest(QueryTranslationUnitSourceRequest, params);
2062-
if (!response.candidates || response.candidates.length === 0) {
2063-
// If we didn't receive any candidates, no configuration is needed.
2064-
return "noCandidates";
2065-
}
2066-
20672036
// Need to loop through candidates, to see if we can get a custom configuration from any of them.
20682037
// Wrap all lookups in a single task, so we can apply a timeout to the entire duration.
20692038
const provideConfigurationAsync: () => Thenable<SourceFileConfigurationItem[] | undefined> = async () => {
2070-
const uris: vscode.Uri[] = [];
2071-
for (let i: number = 0; i < response.candidates.length; ++i) {
2072-
const candidate: string = response.candidates[i];
2073-
const tuUri: vscode.Uri = vscode.Uri.parse(candidate);
2074-
try {
2075-
if (await provider.canProvideConfiguration(tuUri, tokenSource.token)) {
2076-
uris.push(tuUri);
2077-
}
2078-
} catch (err) {
2079-
console.warn("Caught exception from canProvideConfiguration");
2039+
try {
2040+
if (!await provider.canProvideConfiguration(docUri, tokenSource.token)) {
2041+
return [];
20802042
}
2081-
}
2082-
if (!uris.length) {
2083-
return [];
2043+
} catch (err) {
2044+
console.warn("Caught exception from canProvideConfiguration");
20842045
}
20852046
let configs: util.Mutable<SourceFileConfigurationItem>[] = [];
20862047
try {
2087-
configs = await provider.provideConfigurations(uris, tokenSource.token);
2048+
configs = await provider.provideConfigurations([docUri], tokenSource.token);
20882049
} catch (err) {
20892050
console.warn("Caught exception from provideConfigurations");
20902051
}
@@ -2132,39 +2093,42 @@ export class DefaultClient implements Client {
21322093
try {
21332094
const configs: SourceFileConfigurationItem[] | undefined = await this.callTaskWithTimeout(provideConfigurationAsync, configProviderTimeout, tokenSource);
21342095
if (configs && configs.length > 0) {
2135-
this.sendCustomConfigurations(configs, provider.version, requestFile !== undefined);
2096+
this.sendCustomConfigurations(configs, provider.version);
21362097
} else {
21372098
result = "noConfigurations";
21382099
}
21392100
} catch (err) {
21402101
result = "timeout";
2141-
if (!requestFile) {
2142-
const settings: CppSettings = new CppSettings(this.RootUri);
2143-
if (settings.isConfigurationWarningsEnabled && !this.isExternalHeader(docUri) && !vscode.debug.activeDebugSession) {
2144-
const dismiss: string = localize("dismiss.button", "Dismiss");
2145-
const disable: string = localize("disable.warnings.button", "Disable Warnings");
2146-
const configName: string | undefined = this.configuration.CurrentConfiguration?.name;
2147-
if (!configName) {
2148-
return "noConfigName";
2149-
}
2150-
let message: string = localize("unable.to.provide.configuration",
2151-
"{0} is unable to provide IntelliSense configuration information for '{1}'. Settings from the '{2}' configuration will be used instead.",
2152-
provider.name, docUri.fsPath, configName);
2153-
if (err) {
2154-
message += ` (${err})`;
2155-
}
2102+
const settings: CppSettings = new CppSettings(this.RootUri);
2103+
if (settings.isConfigurationWarningsEnabled && !this.isExternalHeader(docUri) && !vscode.debug.activeDebugSession) {
2104+
const dismiss: string = localize("dismiss.button", "Dismiss");
2105+
const disable: string = localize("disable.warnings.button", "Disable Warnings");
2106+
const configName: string | undefined = this.configuration.CurrentConfiguration?.name;
2107+
if (!configName) {
2108+
return "noConfigName";
2109+
}
2110+
let message: string = localize("unable.to.provide.configuration",
2111+
"{0} is unable to provide IntelliSense configuration information for '{1}'. Settings from the '{2}' configuration will be used instead.",
2112+
provider.name, docUri.fsPath, configName);
2113+
if (err) {
2114+
message += ` (${err})`;
2115+
}
21562116

2157-
if (await vscode.window.showInformationMessage(message, dismiss, disable) === disable) {
2158-
settings.toggleSetting("configurationWarnings", "enabled", "disabled");
2159-
}
2117+
if (await vscode.window.showInformationMessage(message, dismiss, disable) === disable) {
2118+
settings.toggleSetting("configurationWarnings", "enabled", "disabled");
21602119
}
21612120
}
21622121
}
21632122
return result;
21642123
}
21652124

2166-
private async handleRequestCustomConfig(requestFile: string): Promise<void> {
2167-
await this.provideCustomConfiguration(vscode.Uri.file(requestFile), requestFile);
2125+
private handleRequestCustomConfig(file: string): void {
2126+
const uri: vscode.Uri = vscode.Uri.file(file);
2127+
const client: Client = clients.getClientFor(uri);
2128+
if (client instanceof DefaultClient) {
2129+
const defaultClient: DefaultClient = client as DefaultClient;
2130+
void defaultClient.provideCustomConfiguration(uri).catch(logAndReturn.undefined);
2131+
}
21682132
}
21692133

21702134
private isExternalHeader(uri: vscode.Uri): boolean {
@@ -2386,13 +2350,7 @@ export class DefaultClient implements Client {
23862350
this.languageClient.onNotification(CompileCommandsPathsNotification, (e) => void this.promptCompileCommands(e));
23872351
this.languageClient.onNotification(ReferencesNotification, (e) => this.processReferencesPreview(e));
23882352
this.languageClient.onNotification(ReportReferencesProgressNotification, (e) => this.handleReferencesProgress(e));
2389-
this.languageClient.onNotification(RequestCustomConfig, (requestFile: string) => {
2390-
const client: Client = clients.getClientFor(vscode.Uri.file(requestFile));
2391-
if (client instanceof DefaultClient) {
2392-
const defaultClient: DefaultClient = client as DefaultClient;
2393-
void defaultClient.handleRequestCustomConfig(requestFile);
2394-
}
2395-
});
2353+
this.languageClient.onNotification(RequestCustomConfig, (e) => this.handleRequestCustomConfig(e));
23962354
this.languageClient.onNotification(IntelliSenseResultNotification, (e) => this.handleIntelliSenseResult(e));
23972355
this.languageClient.onNotification(PublishRefactorDiagnosticsNotification, publishRefactorDiagnostics);
23982356
RegisterCodeAnalysisNotifications(this.languageClient);
@@ -3078,7 +3036,7 @@ export class DefaultClient implements Client {
30783036
util.isOptionalArrayOfString(input.configuration.forcedInclude);
30793037
}
30803038

3081-
private sendCustomConfigurations(configs: any, providerVersion: Version, wasRequested: boolean): void {
3039+
private sendCustomConfigurations(configs: any, providerVersion: Version): void {
30823040
// configs is marked as 'any' because it is untrusted data coming from a 3rd-party. We need to sanitize it before sending it to the language server.
30833041
if (!configs || !(configs instanceof Array)) {
30843042
console.warn("discarding invalid SourceFileConfigurationItems[]: " + configs);
@@ -3144,9 +3102,10 @@ export class DefaultClient implements Client {
31443102
workspaceFolderUri: this.RootUri?.toString()
31453103
};
31463104

3147-
if (wasRequested) {
3148-
void this.languageClient.sendNotification(CustomConfigurationHighPriorityNotification, params).catch(logAndReturn.undefined);
3149-
}
3105+
// We send the higher priority notification to ensure we don't deadlock if the request is blocking the queue.
3106+
// We send the normal priority notification to avoid a race that could result in a redundant request when racing with
3107+
// the reset of custom configurations.
3108+
void this.languageClient.sendNotification(CustomConfigurationHighPriorityNotification, params).catch(logAndReturn.undefined);
31503109
void this.languageClient.sendNotification(CustomConfigurationNotification, params).catch(logAndReturn.undefined);
31513110
}
31523111

@@ -4092,7 +4051,7 @@ class NullClient implements Client {
40924051
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
40934052
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
40944053
updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
4095-
provideCustomConfiguration(docUri: vscode.Uri, requestFile?: string, replaceExisting?: boolean): Promise<void> { return Promise.resolve(); }
4054+
provideCustomConfiguration(docUri: vscode.Uri): Promise<void> { return Promise.resolve(); }
40964055
logDiagnostics(): Promise<void> { return Promise.resolve(); }
40974056
rescanFolder(): Promise<void> { return Promise.resolve(); }
40984057
toggleReferenceResultsView(): void { }

Extension/src/LanguageServer/protocolFilter.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export function createProtocolFilter(): Middleware {
4343
client.sendDidChangeSettings();
4444
document = await vscode.languages.setTextDocumentLanguage(document, "cpp");
4545
}
46-
await client.provideCustomConfiguration(document.uri, undefined);
4746
// client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set.
4847
client.onDidOpenTextDocument(document);
4948
client.takeOwnership(document);
@@ -52,8 +51,7 @@ export function createProtocolFilter(): Middleware {
5251
// For a file already open when we activate, sometimes we don't get any notifications about visible
5352
// or active text editors, visible ranges, or text selection. As a workaround, we trigger
5453
// onDidChangeVisibleTextEditors here, only for the first file opened.
55-
if (!anyFileOpened)
56-
{
54+
if (!anyFileOpened) {
5755
anyFileOpened = true;
5856
const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
5957
await client.onDidChangeVisibleTextEditors(cppEditors);

0 commit comments

Comments
 (0)