Compare commits

...
5 changed files with 211 additions and 173 deletions
@@ -220,93 +220,99 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
this.client = client;
}
public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CallHierarchyItem | undefined> {
await this.client.ready;
public prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CallHierarchyItem | undefined> {
return this.client.enqueue(async () => {
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
workspaceReferences.clearViews();
workspaceReferences.clearViews();
const range: vscode.Range | undefined = document.getWordRangeAtPosition(position);
if (range === undefined) {
return undefined;
}
const range: vscode.Range | undefined = document.getWordRangeAtPosition(position);
if (range === undefined) {
return undefined;
}
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => {
cancelSource.cancel();
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => {
cancelSource.cancel();
});
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => {
cancelSource.cancel();
});
let result: vscode.CallHierarchyItem[] | undefined;
try {
result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token);
} finally {
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
if (cancelSource.token.isCancellationRequested) {
throw new vscode.CancellationError();
}
if (!result || result.length === 0) {
return undefined;
}
this.isEntryRootNodeTelemetry = true;
return result[0];
});
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => {
cancelSource.cancel();
});
let result: vscode.CallHierarchyItem[] | undefined;
try {
result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token);
} finally {
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
if (cancelSource.token.isCancellationRequested) {
throw new vscode.CancellationError();
}
if (!result || result.length === 0) {
return undefined;
}
this.isEntryRootNodeTelemetry = true;
return result[0];
}
public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise<vscode.CallHierarchyIncomingCall[] | undefined> {
await this.client.ready;
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
public provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise<vscode.CallHierarchyIncomingCall[] | undefined> {
return this.client.enqueue(async () => {
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo";
if (item === undefined) {
this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Failed);
return undefined;
}
const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo";
if (item === undefined) {
this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Failed);
return undefined;
}
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
let requestCanceled: CancellationSender | undefined;
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => {
requestCanceled = CancellationSender.ProviderToken;
cancelSource.cancel();
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
let requestCanceled: CancellationSender | undefined;
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => {
requestCanceled = CancellationSender.ProviderToken;
cancelSource.cancel();
});
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(sender => {
requestCanceled = sender;
cancelSource.cancel();
});
// Send the request to the language server.
let result: vscode.CallHierarchyIncomingCall[] | undefined;
let progressBarDuration: number | undefined;
try {
result = await sendCallHierarchyCallsToRequest(this.client, item, cancelSource.token);
} finally {
// Reset anything that can be cleared before processing the result.
progressBarDuration = workspaceReferences.getCallHierarchyProgressBarDuration();
workspaceReferences.resetProgressBar();
workspaceReferences.resetReferences();
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || result === undefined || requestCanceled !== undefined) {
const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ?
CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled;
this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration);
throw new vscode.CancellationError();
}
this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration);
return result.length !== 0 ? result : undefined;
});
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(sender => {
requestCanceled = sender;
cancelSource.cancel();
});
// Send the request to the language server.
let result: vscode.CallHierarchyIncomingCall[] | undefined;
let progressBarDuration: number | undefined;
try {
result = await sendCallHierarchyCallsToRequest(this.client, item, cancelSource.token);
} finally {
// Reset anything that can be cleared before processing the result.
progressBarDuration = workspaceReferences.getCallHierarchyProgressBarDuration();
workspaceReferences.resetProgressBar();
workspaceReferences.resetReferences();
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || result === undefined || requestCanceled !== undefined) {
const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ?
CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled;
this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration);
throw new vscode.CancellationError();
}
this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration);
return result.length !== 0 ? result : undefined;
}
public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise<vscode.CallHierarchyOutgoingCall[] | undefined> {
@@ -6,7 +6,7 @@ import * as vscode from 'vscode';
import { Position, RequestType, ResponseError } from 'vscode-languageclient';
import { DefaultClient, workspaceReferences } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CancellationSender, ReferenceInfo, ReferenceType, ReferencesParams, ReferencesResult } from '../references';
import { ReferenceInfo, ReferenceType, ReferencesParams, ReferencesResult } from '../references';
const FindAllReferencesRequest: RequestType<ReferencesParams, ReferencesResult, void> =
new RequestType<ReferencesParams, ReferencesResult, void>('cpptools/findAllReferences');
@@ -62,43 +62,46 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider {
this.client = client;
}
public async provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise<vscode.Location[] | undefined> {
await this.client.ready;
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
public provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise<vscode.Location[] | undefined> {
return this.client.enqueue(async () => {
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { cancelSource.cancel(); });
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); });
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { cancelSource.cancel(); });
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); });
// Send the request to the language server.
let result: FindAllReferencesResult | undefined;
try {
result = await sendFindAllReferencesRequest(this.client, document.uri, position, cancelSource.token);
} finally {
// Reset anything that can be cleared before processing the result.
workspaceReferences.resetProgressBar();
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
// Send the request to the language server.
let result: FindAllReferencesResult | undefined;
try {
result = await sendFindAllReferencesRequest(this.client, document.uri, position, cancelSource.token);
} finally {
// Reset anything that can be cleared before processing the result.
workspaceReferences.resetProgressBar();
cancellationTokenListener.dispose();
requestCanceledListener.dispose();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || !result) {
// Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code:
// "Cannot destructure property 'range' of 'e.location' as it is undefined."
// TODO: per issue https://github.com/microsoft/vscode/issues/169698
// vscode.CancellationError is expected, so when VS Code fixes the error use vscode.CancellationError again.
workspaceReferences.resetReferences();
return undefined;
} else if (result.referencesResult.referenceInfos.length > 0) {
// Display other reference types in panel or channel view.
// Note: ReferencesManager.resetReferences is called in ReferencesManager.showResultsInPanelView
workspaceReferences.showResultsInPanelView(result.referencesResult);
} else {
workspaceReferences.resetReferences();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || !result) {
// Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code:
// "Cannot destructure property 'range' of 'e.location' as it is undefined."
// TODO: per issue https://github.com/microsoft/vscode/issues/169698
// vscode.CancellationError is expected, so when VS Code fixes the error use vscode.CancellationError again.
workspaceReferences.resetReferences();
return undefined;
} else if (result.referencesResult.referenceInfos.length > 0) {
// Display other reference types in panel or channel view.
// Note: ReferencesManager.resetReferences is called in ReferencesManager.showResultsInPanelView
workspaceReferences.showResultsInPanelView(result.referencesResult);
} else {
workspaceReferences.resetReferences();
}
return result.locations;
return result.locations;
});
}
}
@@ -24,65 +24,71 @@ export class RenameProvider implements vscode.RenameProvider {
this.client = client;
}
public async provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, _token: vscode.CancellationToken): Promise<vscode.WorkspaceEdit | undefined> {
await this.client.ready;
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
public provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, _token: vscode.CancellationToken): Promise<vscode.WorkspaceEdit | undefined> {
const settings: CppSettings = new CppSettings();
if (settings.renameRequiresIdentifier && !util.isValidIdentifier(newName)) {
void vscode.window.showErrorMessage(localize("invalid.identifier.for.rename", "Invalid identifier provided for the Rename Symbol operation."));
return undefined;
return Promise.resolve(undefined);
}
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
// Don't listen to the token from the provider, as it will cancel when the cursor is moved to a different position.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); });
return this.client.enqueue(async () => {
// Listen to a cancellation for this request. When this request is cancelled,
// use a local cancellation source to explicitly cancel a token.
// Don't listen to the token from the provider, as it will cancel when the cursor is moved to a different position.
const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); });
const renameCanceledListener: vscode.Disposable = workspaceReferences.onRenameCancellationRequested((sender) => {
if (sender === CancellationSender.User) {
cancelSource.cancel();
}
});
const renameRequestId: number = workspaceReferences.createRenameRequest();
// Send the request to the language server.
workspaceReferences.startRename();
const workspaceEditResult: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
const params: ReferencesParams = {
newName: newName,
position: Position.create(position.line, position.character),
textDocument: { uri: document.uri.toString() }
};
let response: ReferencesResult;
try {
response = await this.client.languageClient.sendRequest(RenameRequest, params, cancelSource.token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
// Send the request to the language server.
const workspaceEditResult: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
const params: ReferencesParams = {
newName: newName,
position: Position.create(position.line, position.character),
textDocument: { uri: document.uri.toString() }
};
let response: ReferencesResult;
try {
response = await this.client.languageClient.sendRequest(RenameRequest, params, cancelSource.token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
finally {
// Reset anything that can be cleared before processing the result.
workspaceReferences.finishRenameRequest(renameRequestId);
workspaceReferences.resetProgressBar();
workspaceReferences.resetReferences();
requestCanceledListener.dispose();
renameCanceledListener.dispose();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || response.isCanceled) {
throw new vscode.CancellationError();
} else if (response.referenceInfos.length === 0) {
void vscode.window.showErrorMessage(localize("unable.to.locate.selected.symbol", "A definition for the selected symbol could not be located."));
} else {
for (const reference of response.referenceInfos) {
const uri: vscode.Uri = vscode.Uri.file(reference.file);
const range: vscode.Range = new vscode.Range(reference.position.line, reference.position.character,
reference.position.line, reference.position.character + response.text.length);
const metadata: vscode.WorkspaceEditEntryMetadata = {
needsConfirmation: reference.type !== ReferenceType.Confirmed,
label: getReferenceTagString(reference.type, false, true),
iconPath: getReferenceItemIconPath(reference.type, false)
};
workspaceEditResult.replace(uri, range, newName, metadata);
}
}
throw e;
}
finally {
// Reset anything that can be cleared before processing the result.
workspaceReferences.resetProgressBar();
workspaceReferences.resetReferences();
requestCanceledListener.dispose();
}
// Process the result.
if (cancelSource.token.isCancellationRequested || response.isCanceled) {
throw new vscode.CancellationError();
} else if (response.referenceInfos.length === 0) {
void vscode.window.showErrorMessage(localize("unable.to.locate.selected.symbol", "A definition for the selected symbol could not be located."));
} else {
for (const reference of response.referenceInfos) {
const uri: vscode.Uri = vscode.Uri.file(reference.file);
const range: vscode.Range = new vscode.Range(reference.position.line, reference.position.character,
reference.position.line, reference.position.character + response.text.length);
const metadata: vscode.WorkspaceEditEntryMetadata = {
needsConfirmation: reference.type !== ReferenceType.Confirmed,
label: getReferenceTagString(reference.type, false, true),
iconPath: getReferenceItemIconPath(reference.type, false)
};
workspaceEditResult.replace(uri, range, newName, metadata);
}
}
return workspaceEditResult;
return workspaceEditResult;
});
}
}
+2 -2
View File
@@ -2015,10 +2015,10 @@ export class DefaultClient implements Client {
public onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void {
if (util.isCpp(textDocumentChangeEvent.document)) {
// If any file has changed, we need to abort the current rename operation
// If any file has changed, abort any pending rename operations.
if (workspaceReferences !== undefined // Occurs when a document changes before cpptools starts.
&& workspaceReferences.renamePending) {
workspaceReferences.cancelCurrentReferenceRequest(refs.CancellationSender.User);
workspaceReferences.cancelPendingRenameRequests(refs.CancellationSender.User);
}
const oldVersion: number | undefined = openFileVersions.get(textDocumentChangeEvent.document.uri.toString());
+30 -7
View File
@@ -196,8 +196,10 @@ export class ReferencesManager {
private findAllRefsView?: FindAllRefsView;
private viewsInitialized: boolean = false;
public renamePending: boolean = false;
private nextRenameRequestId: number = 0;
private pendingRenameRequests: Set<number> = new Set<number>();
private referenceRequestCanceled = new vscode.EventEmitter<CancellationSender>();
private renameRequestCanceled = new vscode.EventEmitter<CancellationSender>();
private referencesCurrentProgress?: ReportReferencesProgressNotification;
private referencesPrevProgressIncrement: number = 0;
@@ -225,7 +227,7 @@ export class ReferencesManager {
constructor(client: DefaultClient) {
this.client = client;
this.disposables.push(vscode.Disposable.from(this.referenceRequestCanceled));
this.disposables.push(vscode.Disposable.from(this.referenceRequestCanceled, this.renameRequestCanceled));
}
initializeViews(): void {
@@ -239,11 +241,37 @@ export class ReferencesManager {
return this.referenceRequestCanceled.event;
}
public get onRenameCancellationRequested(): vscode.Event<CancellationSender> {
return this.renameRequestCanceled.event;
}
public get renamePending(): boolean {
return this.pendingRenameRequests.size !== 0;
}
public cancelCurrentReferenceRequest(sender: CancellationSender): void {
// Notify the current listener to cancel its request.
this.referenceRequestCanceled.fire(sender);
}
public createRenameRequest(): number {
const requestId: number = ++this.nextRenameRequestId;
this.pendingRenameRequests.add(requestId);
return requestId;
}
public cancelPendingRenameRequests(sender: CancellationSender): void {
if (this.pendingRenameRequests.size === 0) {
return;
}
this.pendingRenameRequests.clear();
this.renameRequestCanceled.fire(sender);
}
public finishRenameRequest(requestId: number): void {
this.pendingRenameRequests.delete(requestId);
}
public dispose(): void {
this.disposables.forEach((d) => d.dispose());
this.disposables = [];
@@ -450,12 +478,7 @@ export class ReferencesManager {
this.referencesProgressBarStartTime = 0;
}
public startRename(): void {
this.renamePending = true;
}
public resetReferences(): void {
this.renamePending = false;
this.initializeViews();
this.client.setReferencesCommandMode(ReferencesCommandMode.None);
}