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

Implementation of SelectionRange and SelectionRangeProvider API #7534

Merged
merged 1 commit into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ export class FoldingRangeKind {
public constructor(public value: string) { }
}

export interface SelectionRange {
range: Range;
}

export interface Color {
readonly red: number;
readonly green: number;
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
CodeActionContext,
FoldingContext,
FoldingRange,
SelectionRange,
CallHierarchyDefinition,
CallHierarchyReference
} from './plugin-api-rpc-model';
Expand Down Expand Up @@ -1196,6 +1197,7 @@ export interface LanguagesExt {
context: FoldingContext,
token: CancellationToken
): PromiseLike<FoldingRange[] | undefined>;
$provideSelectionRanges(handle: number, resource: UriComponents, positions: Position[], token: CancellationToken): PromiseLike<SelectionRange[][]>;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): PromiseLike<RawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: RawColorInfo, token: CancellationToken): PromiseLike<ColorPresentation[]>;
$provideRenameEdits(handle: number, resource: UriComponents, position: Position, newName: string, token: CancellationToken): PromiseLike<WorkspaceEditDto | undefined>;
Expand Down Expand Up @@ -1241,6 +1243,7 @@ export interface LanguagesMain {
$registerOutlineSupport(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerWorkspaceSymbolProvider(handle: number, pluginInfo: PluginInfo): void;
$registerFoldingRangeProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerSelectionRangeProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerDocumentColorProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerRenameProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerCallHierarchyProvider(handle: number, selector: SerializedDocumentFilter[]): void;
Expand Down
17 changes: 17 additions & 0 deletions packages/plugin-ext/src/main/browser/languages-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,23 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
return this.proxy.$provideFoldingRange(handle, model.uri, context, token);
}

$registerSelectionRangeProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const provider = this.createSelectionRangeProvider(handle);
this.register(handle, monaco.languages.registerSelectionRangeProvider(languageSelector, provider));
}

protected createSelectionRangeProvider(handle: number): monaco.languages.SelectionRangeProvider {
return {
provideSelectionRanges: (model, positions, token) => this.provideSelectionRanges(handle, model, positions, token)
};
}

protected provideSelectionRanges(handle: number, model: monaco.editor.ITextModel,
positions: monaco.Position[], token: monaco.CancellationToken): monaco.languages.ProviderResult<monaco.languages.SelectionRange[][]> {
return this.proxy.$provideSelectionRanges(handle, model.uri, positions, token);
}

$registerDocumentColorProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const colorProvider = this.createColorProvider(handle);
Expand Down
13 changes: 13 additions & 0 deletions packages/plugin-ext/src/plugin/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
CodeActionContext,
CodeAction,
FoldingRange,
SelectionRange,
CallHierarchyDefinition,
CallHierarchyReference
} from '../common/plugin-api-rpc-model';
Expand All @@ -80,6 +81,7 @@ import { ReferenceAdapter } from './languages/reference';
import { WorkspaceSymbolAdapter } from './languages/workspace-symbol';
import { SymbolInformation } from 'vscode-languageserver-types';
import { FoldingProviderAdapter } from './languages/folding';
import { SelectionRangeProviderAdapter } from './languages/selection-range';
import { ColorProviderAdapter } from './languages/color';
import { RenameAdapter } from './languages/rename';
import { Event } from '@theia/core/lib/common/event';
Expand All @@ -106,6 +108,7 @@ type Adapter = CompletionAdapter |
ReferenceAdapter |
WorkspaceSymbolAdapter |
FoldingProviderAdapter |
SelectionRangeProviderAdapter |
ColorProviderAdapter |
RenameAdapter |
CallHierarchyAdapter;
Expand Down Expand Up @@ -544,6 +547,16 @@ export class LanguagesExtImpl implements LanguagesExt {
}
// ### Folding Range Provider end

registerSelectionRangeProvider(selector: theia.DocumentSelector, provider: theia.SelectionRangeProvider, pluginInfo: PluginInfo): theia.Disposable {
const callId = this.addNewAdapter(new SelectionRangeProviderAdapter(provider, this.documents));
this.proxy.$registerSelectionRangeProvider(callId, pluginInfo, this.transformDocumentSelector(selector));
return this.createDisposable(callId);
}

$provideSelectionRanges(handle: number, resource: UriComponents, positions: Position[], token: theia.CancellationToken): Promise<SelectionRange[][]> {
return this.withAdapter(handle, SelectionRangeProviderAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), positions, token), []);
}

// ### Rename Provider begin
registerRenameProvider(selector: theia.DocumentSelector, provider: theia.RenameProvider, pluginInfo: PluginInfo): theia.Disposable {
const callId = this.addNewAdapter(new RenameAdapter(provider, this.documents));
Expand Down
79 changes: 79 additions & 0 deletions packages/plugin-ext/src/plugin/languages/selection-range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/********************************************************************************
* Copyright (C) 2020 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// copied and modified from https://github.com/microsoft/vscode/blob/standalone/0.19.x/src/vs/workbench/api/common/extHostLanguageFeatures.ts#L1107-L1151

import * as theia from '@theia/plugin';
import { DocumentsExtImpl } from '../documents';
import { URI } from 'vscode-uri/lib/umd';
import * as model from '../../common/plugin-api-rpc-model';
import * as Converter from '../type-converters';
import * as types from '../types-impl';

export class SelectionRangeProviderAdapter {

constructor(
private readonly provider: theia.SelectionRangeProvider,
private readonly documents: DocumentsExtImpl
) { }

provideSelectionRanges(resource: URI, position: monaco.IPosition[], token: theia.CancellationToken): Promise<model.SelectionRange[][]> {
const documentData = this.documents.getDocumentData(resource);

if (!documentData) {
return Promise.reject(new Error(`There are no document for ${resource}`));
}

const document = documentData.document;
const positions = position.map(pos => Converter.toPosition(pos));

return Promise.resolve(this.provider.provideSelectionRanges(document, positions, token)).then(allProviderRanges => {
if (!Array.isArray(allProviderRanges) || allProviderRanges.length === 0) {
return [];
}

if (allProviderRanges.length !== positions.length) {
return [];
}

const allResults: model.SelectionRange[][] = [];
for (let i = 0; i < positions.length; i++) {
const oneResult: model.SelectionRange[] = [];
allResults.push(oneResult);

let last: types.Position | theia.Range = positions[i];
let selectionRange = allProviderRanges[i];

while (true) {
if (!selectionRange.range.contains(last)) {
return Promise.reject('INVALID selection range, must contain the previous range');
}
oneResult.push(Converter.fromSelectionRange(selectionRange));
if (!selectionRange.parent) {
break;
}
last = selectionRange.range;
selectionRange = selectionRange.parent;
}
}
return allResults;
});
}
}
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ import {
FunctionBreakpoint,
FoldingRange,
FoldingRangeKind,
SelectionRange,
Color,
ColorInformation,
ColorPresentation,
Expand Down Expand Up @@ -626,6 +627,9 @@ export function createAPIFactory(
registerFoldingRangeProvider(selector: theia.DocumentSelector, provider: theia.FoldingRangeProvider): theia.Disposable {
return languagesExt.registerFoldingRangeProvider(selector, provider, pluginToPluginInfo(plugin));
},
registerSelectionRangeProvider(selector: theia.DocumentSelector, provider: theia.SelectionRangeProvider): theia.Disposable {
return languagesExt.registerSelectionRangeProvider(selector, provider, pluginToPluginInfo(plugin));
},
registerRenameProvider(selector: theia.DocumentSelector, provider: theia.RenameProvider): theia.Disposable {
return languagesExt.registerRenameProvider(selector, provider, pluginToPluginInfo(plugin));
},
Expand Down Expand Up @@ -855,6 +859,7 @@ export function createAPIFactory(
ColorInformation,
ColorPresentation,
FoldingRange,
SelectionRange,
FoldingRangeKind,
OperatingSystem,
WebviewPanelTargetArea,
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/plugin/type-converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,10 @@ export function toSymbolInformation(symbolInformation: SymbolInformation): theia
};
}

export function fromSelectionRange(selectionRange: theia.SelectionRange): model.SelectionRange {
return { range: fromRange(selectionRange.range) };
}

export function fromFoldingRange(foldingRange: theia.FoldingRange): model.FoldingRange {
const range: model.FoldingRange = {
start: foldingRange.start + 1,
Expand Down
15 changes: 15 additions & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,21 @@ export enum FoldingRangeKind {
Region = 3
}

export class SelectionRange {

range: Range;
parent?: SelectionRange;

constructor(range: Range, parent?: SelectionRange) {
this.range = range;
this.parent = parent;

if (parent && !parent.range.contains(this.range)) {
throw new Error('Invalid argument: parent must contain this range');
}
}
}

/**
* Enumeration of the supported operating systems.
*/
Expand Down
55 changes: 55 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7288,6 +7288,19 @@ declare module '@theia/plugin' {
*/
export function registerFoldingRangeProvider(selector: DocumentSelector, provider: FoldingRangeProvider): Disposable;

/**
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
* Register a selection range provider.
*
* Multiple providers can be registered for a language. In that case providers are asked in
* parallel and the results are merged. A failing provider (rejected promise or exception) will
* not cause a failure of the whole operation.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A selection range provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable;

/**
* Register a reference provider.
*
Expand Down Expand Up @@ -9040,6 +9053,48 @@ declare module '@theia/plugin' {
export function createCommentController(id: string, label: string): CommentController;
}

/**
* A selection range represents a part of a selection hierarchy. A selection range
* may have a parent selection range that contains it.
*/
export class SelectionRange {

/**
* The [range](#Range) of this selection range.
*/
range: Range;

/**
* The parent selection range containing this range.
*/
parent?: SelectionRange;

/**
* Creates a new selection range.
*
* @param range The range of the selection range.
* @param parent The parent of the selection range.
*/
constructor(range: Range, parent?: SelectionRange);
}

export interface SelectionRangeProvider {
/**
* Provide selection ranges for the given positions.
*
* Selection ranges should be computed individually and independent for each position. The editor will merge
* and deduplicate ranges but providers must return hierarchies of selection ranges so that a range
* is [contained](#Range.contains) by its parent.
*
* @param document The document in which the command was invoked.
* @param positions The positions at which the command was invoked.
* @param token A cancellation token.
* @return Selection ranges or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideSelectionRanges(document: TextDocument, positions: Position[], token: CancellationToken): ProviderResult<SelectionRange[]>;
}

/**
* Represents programming constructs like functions or constructors in the context
* of call hierarchy.
Expand Down