Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable progressive IntelliSense updates #11735

Merged
merged 47 commits into from
Dec 20, 2023
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4cb5abc
Work in progress
Colengms Sep 13, 2023
ee0c5a5
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Sep 18, 2023
35309a5
Work in progress
Colengms Sep 19, 2023
df61b30
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Sep 26, 2023
efc0455
Work in progress
Colengms Oct 3, 2023
39056d3
Work in progress
Colengms Oct 4, 2023
df5c4db
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Oct 9, 2023
5fe880e
Work in progress
Colengms Oct 9, 2023
149c9a3
Work in progress
Colengms Oct 10, 2023
9183bee
Work in progress
Colengms Oct 11, 2023
a318c37
Work in progress
Colengms Oct 12, 2023
4fa2d00
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Oct 18, 2023
7aee0e9
Work in progress
Colengms Oct 19, 2023
26f5f7b
Work in progress
Colengms Oct 20, 2023
e04a6d9
Work in progress
Colengms Oct 24, 2023
191a1e3
Work in progress
Colengms Oct 25, 2023
b62cd1f
Work in progress
Colengms Oct 27, 2023
779705d
Work in progress
Colengms Nov 1, 2023
f204072
Work in progress
Colengms Nov 3, 2023
516b587
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 3, 2023
8a092d1
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 9, 2023
b2fe443
Work in progress
Colengms Nov 10, 2023
bc24730
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 11, 2023
088ba13
Fix position fields
Colengms Nov 14, 2023
284b4bf
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 14, 2023
77e121a
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 16, 2023
a2884e0
Cleanup
Colengms Nov 17, 2023
5550e6a
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 18, 2023
be3c07b
Work in progress
Colengms Nov 22, 2023
cd34a67
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Nov 22, 2023
44a9870
Cleanup
Colengms Nov 22, 2023
e82c0f0
Revert changes to inactive region setting scopes
Colengms Nov 22, 2023
34586ce
Bug fixes
Colengms Nov 23, 2023
8ccc7f7
Bug fixes, make some providers even simpler
Colengms Nov 28, 2023
cffb922
Potential fix for strange FAR issue
Colengms Nov 29, 2023
a6519d8
Address issues with crash recovery and excessive updating on activate
Colengms Nov 29, 2023
c43765b
Fix some issues with updates being cleared between edits
Colengms Nov 30, 2023
39bc387
Fix assert due to forwarding an invalid uri
Colengms Dec 1, 2023
0a46721
Mitigate effects of https://github.com/microsoft/vscode/issues/199724
Colengms Dec 1, 2023
ab8332a
Fix version tracking issue in semantic token and inlay hint providers
Colengms Dec 2, 2023
cc9d6c3
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
Colengms Dec 2, 2023
8fde8b9
Fix issue with results not getting cleared
Colengms Dec 4, 2023
7cfdfe2
Fix regression in publishRefactorDiagnostics
Colengms Dec 9, 2023
a721373
Address issue with unnecessary updates of folding ranges
Colengms Dec 13, 2023
1c0ed83
Fix issue with failing to clear squiggles from not currently visible …
Colengms Dec 14, 2023
5673a75
Work around apparent VS Code Insiders bug resulting in hundreds of su…
Colengms Dec 15, 2023
ace2773
Fix lint issue
Colengms Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import * as vscode from 'vscode';
import { Position, Range, RequestType, TextDocumentIdentifier } from 'vscode-languageclient';
import * as Telemetry from '../../telemetry';
import { DefaultClient, workspaceReferences } from '../client';
import { processDelayedDidOpen } from '../extension';
import { CancellationSender } from '../references';
import { makeVscodeRange } from '../utils';

@@ -104,7 +103,7 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
}

public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CallHierarchyItem | undefined> {
await this.client.enqueue(() => processDelayedDidOpen(document));
await this.client.ready;

workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
workspaceReferences.clearViews();
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Client, DefaultClient, GetDocumentSymbolRequest, GetDocumentSymbolRequestParams, GetDocumentSymbolResult, LocalizeDocumentSymbol, SymbolScope } from '../client';
import { clients, processDelayedDidOpen } from '../extension';
import { clients } from '../extension';
import { getLocalizedString, getLocalizedSymbolScope } from '../localization';
import { makeVscodeRange } from '../utils';

@@ -57,7 +57,7 @@ export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
const client: Client = clients.getClientFor(document.uri);
if (client instanceof DefaultClient) {
const defaultClient: DefaultClient = <DefaultClient>client;
await client.enqueue(() => processDelayedDidOpen(document));
await client.ready;
const params: GetDocumentSymbolRequestParams = {
uri: document.uri.toString()
};
53 changes: 49 additions & 4 deletions Extension/src/LanguageServer/Providers/foldingRangeProvider.ts
Original file line number Diff line number Diff line change
@@ -3,28 +3,65 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ManualPromise } from '../../Utility/Async/manualPromise';
import { CppFoldingRange, DefaultClient, FoldingRangeKind, GetFoldingRangesParams, GetFoldingRangesRequest, GetFoldingRangesResult } from '../client';
import { processDelayedDidOpen } from '../extension';
import { CppSettings } from '../settings';

interface FoldingRangeRequestInfo {
promise: ManualPromise<vscode.FoldingRange[] | undefined> | undefined;
}

export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
private client: DefaultClient;
public onDidChangeFoldingRangesEvent = new vscode.EventEmitter<void>();
public onDidChangeFoldingRanges?: vscode.Event<void>;

// Mitigate an issue where VS Code sends us an inordinate number of requests
// for the same file without waiting for the prior request to complete or cancelling them.
private pendingRequests: Map<string, FoldingRangeRequestInfo> = new Map<string, FoldingRangeRequestInfo>();

constructor(client: DefaultClient) {
this.client = client;
this.onDidChangeFoldingRanges = this.onDidChangeFoldingRangesEvent.event;
}
async provideFoldingRanges(document: vscode.TextDocument, context: vscode.FoldingContext, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined> {
await this.client.ready;
const settings: CppSettings = new CppSettings();
if (!settings.codeFolding) {
return [];
}
const params: GetFoldingRangesParams = {
uri: document.uri.toString()

const pendingRequest: FoldingRangeRequestInfo | undefined = this.pendingRequests.get(document.uri.toString());
if (pendingRequest !== undefined) {
if (pendingRequest.promise === undefined) {
pendingRequest.promise = new ManualPromise<vscode.FoldingRange[] | undefined>();
}
console.log("Redundant folding ranges request received for: " + document.uri.toString());
return pendingRequest.promise;
}
const foldingRangeRequestInfo: FoldingRangeRequestInfo = {
promise: undefined
};
this.pendingRequests.set(document.uri.toString(), foldingRangeRequestInfo);

await this.client.enqueue(() => processDelayedDidOpen(document));
const promise: Promise<vscode.FoldingRange[] | undefined> = this.requestRanges(document.uri.toString(), token);
await promise;
this.pendingRequests.delete(document.uri.toString());
if (foldingRangeRequestInfo.promise !== undefined) {
promise.then(() => {
foldingRangeRequestInfo.promise?.resolve(promise);
}, () => {
foldingRangeRequestInfo.promise?.reject(new vscode.CancellationError());
});
}
return promise;
}

private async requestRanges(uri: string, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined>
{
const params: GetFoldingRangesParams = {
uri
};

const response: GetFoldingRangesResult = await this.client.languageClient.sendRequest(GetFoldingRangesRequest, params, token);
if (token.isCancellationRequested || response.ranges === undefined) {
@@ -55,6 +92,14 @@ export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
}

public refresh(): void {
// Consider all pending requests as being outdated. Cancel them all.
const oldPendingRequests: Map<string, FoldingRangeRequestInfo> = this.pendingRequests;
this.pendingRequests = new Map<string, FoldingRangeRequestInfo>();
this.onDidChangeFoldingRangesEvent.fire();
oldPendingRequests.forEach((value: FoldingRangeRequestInfo | undefined, _key: string) => {
if (value !== undefined && value.promise !== undefined) {
value.promise.reject(new vscode.CancellationError());
}
});
}
}
189 changes: 112 additions & 77 deletions Extension/src/LanguageServer/Providers/inlayHintProvider.ts
Original file line number Diff line number Diff line change
@@ -3,22 +3,19 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Position, RequestType } from 'vscode-languageclient';
import { DefaultClient, openFileVersions } from '../client';
import { processDelayedDidOpen } from '../extension';
import { ManualPromise } from '../../Utility/Async/manualPromise';
import { CppSettings } from '../settings';

interface GetInlayHintsParams {
uri: string;
interface FileData
{
version: number;
promise: ManualPromise<vscode.InlayHint[]>;
inlayHints: vscode.InlayHint[];
}

enum InlayHintKind {
Type = 0,
Parameter = 1,
}

interface CppInlayHint {
position: Position;
export interface CppInlayHint {
line: number;
character: number;
label: string;
inlayHintKind: InlayHintKind;
isValueRef: boolean;
@@ -28,81 +25,131 @@ interface CppInlayHint {
identifierLength: number;
}

interface GetInlayHintsResult {
fileVersion: number;
inlayHints: CppInlayHint[];
enum InlayHintKind {
Type = 0,
Parameter = 1,
}

type InlayHintsCacheEntry = {
FileVersion: number;
TypeHints: CppInlayHint[];
ParameterHints: CppInlayHint[];
};

const GetInlayHintsRequest: RequestType<GetInlayHintsParams, GetInlayHintsResult, void> =
new RequestType<GetInlayHintsParams, GetInlayHintsResult, void>('cpptools/getInlayHints');

export class InlayHintsProvider implements vscode.InlayHintsProvider {
private client: DefaultClient;
public onDidChangeInlayHintsEvent = new vscode.EventEmitter<void>();
public onDidChangeInlayHints?: vscode.Event<void>;
private cache: Map<string, InlayHintsCacheEntry> = new Map<string, InlayHintsCacheEntry>();
public onDidChangeInlayHints?: vscode.Event<void> = this.onDidChangeInlayHintsEvent.event;
private allFileData: Map<string, FileData> = new Map<string, FileData>();

public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.InlayHint[]> {
const uri: vscode.Uri = document.uri;
const uriString: string = uri.toString();
let fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData) {
if (fileData.promise.isCompleted) {
// Make sure file hasn't been changed since the last set of results.
// If a complete promise is present, there should also be a cache.
if (fileData.version === document.version) {
return fileData.promise;
}
} else {
// A new request requires a new ManualPromise, as each promise returned needs
// to be associated with the cancellation token provided at the time.
fileData.promise.reject(new vscode.CancellationError());
}
}

constructor(client: DefaultClient) {
this.client = client;
this.onDidChangeInlayHints = this.onDidChangeInlayHintsEvent.event;
fileData = {
version: document.version,
promise: new ManualPromise<vscode.InlayHint[]>(),
inlayHints: []
};
this.allFileData.set(uriString, fileData);

// Capture a local variable instead of referring to the member variable directly,
// to avoid race conditions where the member variable is changed before the
// cancallation token is triggered.
const currentPromise = fileData.promise;
token.onCancellationRequested(() => {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData && currentPromise === fileData.promise) {
this.allFileData.delete(uriString);
currentPromise.reject(new vscode.CancellationError());
}
});
return currentPromise;
}

public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range,
token: vscode.CancellationToken): Promise<vscode.InlayHint[] | undefined> {
await this.client.enqueue(() => processDelayedDidOpen(document));
const uriString: string = document.uri.toString();
public deliverInlayHints(uriString: string, cppInlayHints: CppInlayHint[], startNewSet: boolean): void {
if (!startNewSet && cppInlayHints.length === 0) {
return;
}

// Get results from cache if available.
let cacheEntry: InlayHintsCacheEntry | undefined = this.cache.get(uriString);
if (cacheEntry?.FileVersion === document.version) {
return this.buildVSCodeHints(document.uri, cacheEntry);
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document.uri.toString() === uriString);
if (!editor) {
this.allFileData.get(uriString)?.promise.resolve([]);
return;
}

// Get new results from the language server
const params: GetInlayHintsParams = { uri: uriString };
const inlayHintsResult: GetInlayHintsResult = await this.client.languageClient.sendRequest(GetInlayHintsRequest, params, token);
if (token.isCancellationRequested || inlayHintsResult.inlayHints === undefined || inlayHintsResult.fileVersion !== openFileVersions.get(uriString)) {
throw new vscode.CancellationError();
// Use a lambda to remove ambiguity about whether fileData may be undefined.
const [fileData, wasNewPromiseCreated]: [FileData, boolean] = (() => {
let fileData = this.allFileData.get(uriString);
let newPromiseCreated = false;
if (!fileData) {
fileData = {
version: editor.document.version,
promise: new ManualPromise<vscode.InlayHint[]>(),
inlayHints: []
};
newPromiseCreated = true;
this.allFileData.set(uriString, fileData);
} else {
if (!fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
fileData.promise = new ManualPromise<vscode.InlayHint[]>();
newPromiseCreated = true;
}
if (fileData.version !== editor.document.version) {
fileData.version = editor.document.version;
fileData.inlayHints = [];
}
}
return [fileData, newPromiseCreated];
})();
if (startNewSet) {
fileData.inlayHints = [];
}

cacheEntry = this.createCacheEntry(inlayHintsResult);
this.cache.set(uriString, cacheEntry);
return this.buildVSCodeHints(document.uri, cacheEntry);
const typeHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
const paramHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);

}
const settings: CppSettings = new CppSettings(vscode.Uri.parse(uriString));
if (settings.inlayHintsAutoDeclarationTypes) {
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(settings, typeHints);
Array.prototype.push.apply(fileData.inlayHints, resolvedTypeHints);
}
if (settings.inlayHintsParameterNames || settings.inlayHintsReferenceOperator) {
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(settings, paramHints);
Array.prototype.push.apply(fileData.inlayHints, resolvedParameterHints);
}

public invalidateFile(uri: string): void {
this.cache.delete(uri);
this.onDidChangeInlayHintsEvent.fire();
fileData?.promise.resolve(fileData.inlayHints);
if (wasNewPromiseCreated) {
this.onDidChangeInlayHintsEvent.fire();
}
}

private buildVSCodeHints(uri: vscode.Uri, cacheEntry: InlayHintsCacheEntry): vscode.InlayHint[] {
let result: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
if (settings.inlayHintsAutoDeclarationTypes) {
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(uri, cacheEntry.TypeHints);
result = result.concat(resolvedTypeHints);
public removeFile(uriString: string): void {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (!fileData) {
return;
}
if (settings.inlayHintsParameterNames || settings.inlayHintsReferenceOperator) {
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(uri, cacheEntry.ParameterHints);
result = result.concat(resolvedParameterHints);
if (fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
}
return result;
this.allFileData.delete(uriString);
}

private resolveTypeHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
private resolveTypeHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
const resolvedHints: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
for (const hint of hints) {
const showOnLeft: boolean = settings.inlayHintsAutoDeclarationTypesShowOnLeft && hint.identifierLength > 0;
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
new vscode.Position(hint.position.line, hint.position.character +
new vscode.Position(hint.line, hint.character +
(showOnLeft ? 0 : hint.identifierLength)),
showOnLeft ? hint.label : ": " + hint.label,
vscode.InlayHintKind.Type);
@@ -113,9 +160,8 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
return resolvedHints;
}

private resolveParameterHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
private resolveParameterHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
const resolvedHints: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
for (const hint of hints) {
// Build parameter label based on settings.
let paramHintLabel: string = "";
@@ -144,23 +190,12 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
}

const inlayHint: vscode.InlayHint = new vscode.InlayHint(
new vscode.Position(hint.position.line, hint.position.character),
new vscode.Position(hint.line, hint.character),
refOperatorString + paramHintLabel + ":",
vscode.InlayHintKind.Parameter);
inlayHint.paddingRight = true;
resolvedHints.push(inlayHint);
}
return resolvedHints;
}

private createCacheEntry(inlayHintsResults: GetInlayHintsResult): InlayHintsCacheEntry {
const typeHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
const paramHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
const cacheEntry: InlayHintsCacheEntry = {
FileVersion: inlayHintsResults.fileVersion,
TypeHints: typeHints,
ParameterHints: paramHints
};
return cacheEntry;
}
}
Loading