Skip to content

Commit

Permalink
Fix LSP cancellation (#12592)
Browse files Browse the repository at this point in the history
  • Loading branch information
Colengms authored Aug 20, 2024
1 parent 818f7c6 commit a0f863c
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 48 deletions.
46 changes: 35 additions & 11 deletions Extension/src/LanguageServer/Providers/callHierarchyProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
* ------------------------------------------------------------------------------------------ */
import * as path from 'path';
import * as vscode from 'vscode';
import { Position, Range, RequestType, TextDocumentIdentifier } from 'vscode-languageclient';
import { Position, Range, RequestType, ResponseError, TextDocumentIdentifier } from 'vscode-languageclient';
import * as Telemetry from '../../telemetry';
import { DefaultClient, workspaceReferences } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CancellationSender } from '../references';
import { makeVscodeRange } from '../utils';

Expand Down Expand Up @@ -127,8 +128,15 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
textDocument: { uri: document.uri.toString() },
position: Position.create(position.line, position.character)
};
const response: CallHierarchyItemResult = await this.client.languageClient.sendRequest(CallHierarchyItemRequest, params, cancelSource.token);

let response: CallHierarchyItemResult;
try {
response = await this.client.languageClient.sendRequest(CallHierarchyItemRequest, params, cancelSource.token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
return undefined;
}
throw e;
}
cancellationTokenListener.dispose();
requestCanceledListener.dispose();

Expand Down Expand Up @@ -176,8 +184,16 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
textDocument: { uri: item.uri.toString() },
position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character)
};
const response: CallHierarchyCallsItemResult = await this.client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, cancelSource.token);

let response: CallHierarchyCallsItemResult | undefined;
let cancelled: boolean = false;
try {
response = await this.client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, cancelSource.token);
} catch (e: any) {
cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled);
if (!cancelled) {
throw e;
}
}
// Reset anything that can be cleared before processing the result.
const progressBarDuration: number | undefined = workspaceReferences.getCallHierarchyProgressBarDuration();
workspaceReferences.resetProgressBar();
Expand All @@ -186,12 +202,12 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
requestCanceledListener.dispose();

// Process the result.
if (cancelSource.token.isCancellationRequested || response.calls === undefined || requestCanceled !== undefined) {
if (cancelSource.token.isCancellationRequested || cancelled || requestCanceled !== undefined) {
const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ?
CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled;
this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration);
throw new vscode.CancellationError();
} else if (response.calls.length !== 0) {
} else if (response && response.calls.length !== 0) {
result = this.createIncomingCalls(response.calls);
}

Expand All @@ -214,12 +230,20 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
textDocument: { uri: item.uri.toString() },
position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character)
};
const response: CallHierarchyCallsItemResult = await this.client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token);

if (token.isCancellationRequested || response.calls === undefined) {
let response: CallHierarchyCallsItemResult | undefined;
let cancelled: boolean = false;
try {
await this.client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token);
} catch (e: any) {
cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled);
if (!cancelled) {
throw e;
}
}
if (token.isCancellationRequested || cancelled) {
this.logTelemetry(CallHierarchyCallsFromEvent, CallHierarchyRequestStatus.Canceled);
throw new vscode.CancellationError();
} else if (response.calls.length !== 0) {
} else if (response && response.calls.length !== 0) {
result = this.createOutgoingCalls(response.calls);
}

Expand Down
29 changes: 22 additions & 7 deletions Extension/src/LanguageServer/Providers/codeActionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Position, Range, RequestType, TextEdit } from 'vscode-languageclient';
import { Position, Range, RequestType, ResponseError, TextEdit } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { DefaultClient } from '../client';
import {
CodeActionCodeInfo, CodeActionDiagnosticInfo, codeAnalysisAllFixes, codeAnalysisCodeToFixes, codeAnalysisFileToCodeActions
} from '../codeAnalysis';
import { LocalizeStringParams, getLocalizedString } from '../localization';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CppSettings } from '../settings';
import { makeVscodeRange } from '../utils';

Expand Down Expand Up @@ -70,14 +71,21 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
uri: document.uri.toString()
};

let response: GetCodeActionsResult = await this.client.languageClient.sendRequest(
GetCodeActionsRequest, params, token);
let response: GetCodeActionsResult;
try {
response = await this.client.languageClient.sendRequest(GetCodeActionsRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}

const resultCodeActions: vscode.CodeAction[] = [];
if (token.isCancellationRequested || response.commands === undefined) {
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}

const resultCodeActions: vscode.CodeAction[] = [];
let hasSelectIntelliSenseConfiguration: boolean = false;
const settings: CppSettings = new CppSettings(this.client.RootUri);
const hasConfigurationSet: boolean = settings.defaultCompilerPath !== null ||
Expand Down Expand Up @@ -254,8 +262,15 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
if (!hoverResult.value.includes(localize("expands.to", "Expands to:"))) {
return false;
}
response = await this.client.languageClient.sendRequest(GetCodeActionsRequest, params, token);
if (token.isCancellationRequested || response.commands === undefined) {
try {
response = await this.client.languageClient.sendRequest(GetCodeActionsRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
return false;
}
throw e;
}
if (token.isCancellationRequested) {
return false;
}
for (const command of response.commands) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ResponseError } from 'vscode-languageclient';
import { DefaultClient, FormatDocumentRequest, FormatParams, FormatResult } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CppSettings, OtherSettings, getEditorConfigSettings } from '../settings';
import { makeVscodeTextEdits } from '../utils';

Expand Down Expand Up @@ -66,12 +68,16 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting
},
onChanges: options.onChanges === true
};
// We do not currently pass the CancellationToken to sendRequest
// because there is not currently cancellation logic for formatting
// in the native process. Formatting is currently done directly in
// message handling thread.
const response: FormatResult = await this.client.languageClient.sendRequest(FormatDocumentRequest, params, token);
if (token.isCancellationRequested || response.edits === undefined) {
let response: FormatResult;
try {
response = await this.client.languageClient.sendRequest(FormatDocumentRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
const results: vscode.TextEdit[] = makeVscodeTextEdits(response.edits);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ResponseError } from 'vscode-languageclient';
import { DefaultClient, FormatParams, FormatRangeRequest, FormatResult } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CppSettings, getEditorConfigSettings } from '../settings';
import { makeVscodeTextEdits } from '../utils';

Expand Down Expand Up @@ -42,12 +44,16 @@ export class DocumentRangeFormattingEditProvider implements vscode.DocumentRange
},
onChanges: false
};
// We do not currently pass the CancellationToken to sendRequest
// because there is not currently cancellation logic for formatting
// in the native process. Formatting is currently done directly in
// message handling thread.
const response: FormatResult = await this.client.languageClient.sendRequest(FormatRangeRequest, params, token);
if (token.isCancellationRequested || response.edits === undefined) {
let response: FormatResult;
try {
response = await this.client.languageClient.sendRequest(FormatRangeRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
return makeVscodeTextEdits(response.edits);
Expand Down
14 changes: 12 additions & 2 deletions Extension/src/LanguageServer/Providers/documentSymbolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ResponseError } from 'vscode-languageclient';
import { Client, DefaultClient, GetDocumentSymbolRequest, GetDocumentSymbolRequestParams, GetDocumentSymbolResult, LocalizeDocumentSymbol, SymbolScope } from '../client';
import { clients } from '../extension';
import { getLocalizedString, getLocalizedSymbolScope } from '../localization';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { makeVscodeRange } from '../utils';

export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
Expand Down Expand Up @@ -61,8 +63,16 @@ export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
const params: GetDocumentSymbolRequestParams = {
uri: document.uri.toString()
};
const response: GetDocumentSymbolResult = await defaultClient.languageClient.sendRequest(GetDocumentSymbolRequest, params, token);
if (token.isCancellationRequested || response.symbols === undefined) {
let response: GetDocumentSymbolResult;
try {
response = await defaultClient.languageClient.sendRequest(GetDocumentSymbolRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
const resultSymbols: vscode.DocumentSymbol[] = this.getChildrenSymbols(response.symbols);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Position, RequestType } from 'vscode-languageclient';
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';

const FindAllReferencesRequest: RequestType<ReferencesParams, ReferencesResult, void> =
Expand Down Expand Up @@ -34,22 +35,31 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider {
position: Position.create(position.line, position.character),
textDocument: { uri: document.uri.toString() }
};
const response: ReferencesResult = await this.client.languageClient.sendRequest(FindAllReferencesRequest, params, cancelSource.token);
let response: ReferencesResult | undefined;
let cancelled: boolean = false;
try {
response = await this.client.languageClient.sendRequest(FindAllReferencesRequest, params, cancelSource.token);
} catch (e: any) {
cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled);
if (!cancelled) {
throw e;
}
}

// Reset anything that can be cleared before processing the result.
workspaceReferences.resetProgressBar();
cancellationTokenListener.dispose();
requestCanceledListener.dispose();

// Process the result.
if (cancelSource.token.isCancellationRequested || response.referenceInfos === null || response.isCanceled) {
if (cancelSource.token.isCancellationRequested || cancelled || (response && response.isCanceled)) {
// 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 (response.referenceInfos.length > 0) {
} else if (response && response.referenceInfos.length > 0) {
response.referenceInfos.forEach((referenceInfo: ReferenceInfo) => {
if (referenceInfo.type === ReferenceType.Confirmed) {
const uri: vscode.Uri = vscode.Uri.file(referenceInfo.file);
Expand Down
14 changes: 12 additions & 2 deletions Extension/src/LanguageServer/Providers/foldingRangeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ResponseError } from 'vscode-languageclient';
import { ManualPromise } from '../../Utility/Async/manualPromise';
import { CppFoldingRange, DefaultClient, FoldingRangeKind, GetFoldingRangesParams, GetFoldingRangesRequest, GetFoldingRangesResult } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CppSettings } from '../settings';

interface FoldingRangeRequestInfo {
Expand Down Expand Up @@ -62,8 +64,16 @@ export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
uri
};

const response: GetFoldingRangesResult = await this.client.languageClient.sendRequest(GetFoldingRangesRequest, params, token);
if (token.isCancellationRequested || response.ranges === undefined) {
let response: GetFoldingRangesResult;
try {
response = await this.client.languageClient.sendRequest(GetFoldingRangesRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
const result: vscode.FoldingRange[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ResponseError } from 'vscode-languageclient';
import { DefaultClient, FormatOnTypeRequest, FormatParams, FormatResult } from '../client';
import { RequestCancelled, ServerCancelled } from '../protocolFilter';
import { CppSettings, getEditorConfigSettings } from '../settings';
import { makeVscodeTextEdits } from '../utils';

Expand Down Expand Up @@ -41,12 +43,16 @@ export class OnTypeFormattingEditProvider implements vscode.OnTypeFormattingEdit
},
onChanges: false
};
// We do not currently pass the CancellationToken to sendRequest
// because there is not currently cancellation logic for formatting
// in the native process. Formatting is currently done directly in
// message handling thread.
const response: FormatResult = await this.client.languageClient.sendRequest(FormatOnTypeRequest, params, token);
if (token.isCancellationRequested || response.edits === undefined) {
let response: FormatResult;
try {
response = await this.client.languageClient.sendRequest(FormatOnTypeRequest, params, token);
} catch (e: any) {
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
throw new vscode.CancellationError();
}
throw e;
}
if (token.isCancellationRequested) {
throw new vscode.CancellationError();
}
return makeVscodeTextEdits(response.edits);
Expand Down
Loading

0 comments on commit a0f863c

Please sign in to comment.