diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index a7c4fac148d7a4db4e44cc534fe160ef217b8019..8d8542f5a1e36ee765b85d75635ca59b47ecb511 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -81,16 +81,17 @@ export function activate(context: ExtensionContext) { documentSelector.forEach(selector => { context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { - async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); if (Array.isArray(rawResult)) { return rawResult.map(rawSelectionRanges => { - return rawSelectionRanges.map(selectionRange => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { return { - range: client.protocol2CodeConverter.asRange(selectionRange.range) + range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent }; - }); + }, undefined)!; }); } return []; diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index efbfd2b319565a1465c4fa0a3048dbd4cce15969..77799b561ba72c3ff4a00660081e1440a4f0d7c7 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -90,16 +90,17 @@ export function activate(context: ExtensionContext) { documentSelector.forEach(selector => { context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { - async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); if (Array.isArray(rawResult)) { return rawResult.map(rawSelectionRanges => { - return rawSelectionRanges.map(selectionRange => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { return { range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent }; - }); + }, undefined)!; }); } return []; diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index a5532004431441ee01f4caaa53a0659a857b8556..73a6f9bbf33878c639d8975a57a6d89294af3790 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -214,16 +214,17 @@ export function activate(context: ExtensionContext) { documentSelector.forEach(selector => { toDispose.push(languages.registerSelectionRangeProvider(selector, { - async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); if (Array.isArray(rawResult)) { return rawResult.map(rawSelectionRanges => { - return rawSelectionRanges.map(selectionRange => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { return { range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent, }; - }); + }, undefined)!; }); } return []; diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 63e1579a3486f0f5993cea6bacd43169e099fc2f..f8edd2156ee3b8114d8f3fb8c0aca7a06595534f 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -22,6 +22,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; class SelectionRanges { @@ -237,7 +238,7 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], } } } - })); + }, onUnexpectedExternalError)); } return Promise.all(work).then(() => { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 846714de7bca7fac402e72464eb19481eba0a926..e67cf1512f217a653c4103176371c4c222307fa4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -86,7 +86,8 @@ declare module 'vscode' { export class SelectionRange { range: Range; - constructor(range: Range); + parent?: SelectionRange; + constructor(range: Range, parent?: SelectionRange); } export interface SelectionRangeProvider { @@ -97,7 +98,7 @@ declare module 'vscode' { * * todo@joh */ - provideSelectionRanges(document: TextDocument, positions: Position[], token: CancellationToken): ProviderResult; + provideSelectionRanges(document: TextDocument, positions: Position[], token: CancellationToken): ProviderResult; } export namespace languages { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 5d902dd3def5122921ee247bf26ca73ebf6040b0..19cf2adb0148d9a70b073f4f9a33e87a1791fcbc 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -944,14 +944,19 @@ class SelectionRangeAdapter { const oneResult: modes.SelectionRange[] = []; allResults.push(oneResult); - const oneProviderRanges = allProviderRanges[i]; let last: vscode.Position | vscode.Range = positions[i]; - for (const selectionRange of oneProviderRanges) { + let selectionRange = allProviderRanges[i]; + + while (true) { if (!selectionRange.range.contains(last)) { throw new Error('INVALID selection range, must contain the previous range'); } oneResult.push(typeConvert.SelectionRange.from(selectionRange)); + if (!selectionRange.parent) { + break; + } last = selectionRange.range; + selectionRange = selectionRange.parent; } } return allResults; diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 76446d107e90da42473f455928a0bdbac1ab81ab..7edb9a9045c5fd272a2c14bb174e5bdd53bcfe98 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1098,9 +1098,11 @@ CodeActionKind.SourceFixAll = CodeActionKind.Source.append('fixAll'); export class SelectionRange { range: Range; + parent?: SelectionRange; - constructor(range: Range) { + constructor(range: Range, parent?: SelectionRange) { this.range = range; + this.parent = parent; } } diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index fa2d483f30aad1e883085cb2bffe7c709566b2d3..99ef60172844a6208f3eb2290af447b55eb45dd5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -799,10 +799,9 @@ suite('ExtHostLanguageFeatureCommands', function () { disposables.push(extHost.registerSelectionRangeProvider(nullExtensionDescription, defaultSelector, { provideSelectionRanges() { - return [[ - new types.SelectionRange(new types.Range(0, 10, 0, 18)), - new types.SelectionRange(new types.Range(0, 2, 0, 20)) - ]]; + return [ + new types.SelectionRange(new types.Range(0, 10, 0, 18), new types.SelectionRange(new types.Range(0, 2, 0, 20))), + ]; } })); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index d2d9fc6f6c89effaeccdb3060f9aa40fa22e8e08..c25d55334b8b7988859da12adb194a244612b4ce 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -1104,10 +1104,9 @@ suite('ExtHostLanguageFeatures', function () { test('Selection Ranges, data conversion', async () => { disposables.push(extHost.registerSelectionRangeProvider(defaultExtension, defaultSelector, new class implements vscode.SelectionRangeProvider { provideSelectionRanges() { - return [[ - new types.SelectionRange(new types.Range(0, 10, 0, 18)), - new types.SelectionRange(new types.Range(0, 2, 0, 20)) - ]]; + return [ + new types.SelectionRange(new types.Range(0, 10, 0, 18), new types.SelectionRange(new types.Range(0, 2, 0, 20))), + ]; } })); @@ -1118,4 +1117,21 @@ suite('ExtHostLanguageFeatures', function () { assert.ok(ranges[0].length >= 2); }); }); + + test('Selection Ranges, bad data', async () => { + disposables.push(extHost.registerSelectionRangeProvider(defaultExtension, defaultSelector, new class implements vscode.SelectionRangeProvider { + provideSelectionRanges() { + return [ + new types.SelectionRange(new types.Range(0, 10, 0, 18), + new types.SelectionRange(new types.Range(0, 11, 0, 18))), + ]; + } + })); + + await rpcProtocol.sync(); + + provideSelectionRanges(model, [new Position(1, 17)], CancellationToken.None).then(ranges => { + assert.equal(ranges.length, 0); + }); + }); });