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

adjust selectionRange API #474

Closed
wants to merge 1 commit into from
Closed
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
111 changes: 58 additions & 53 deletions client/src/selectionRange.proposed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import * as UUID from './utils/uuid';
import * as Is from './utils/is';

import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, SelectionRange as VSelectionRange, SelectionRangeKind as VSelectionRangeKind } from 'vscode';
import { languages as Languages, Disposable, TextDocument, ProviderResult, Position as VPosition, SelectionRange as VSelectionRange } from 'vscode';

import {
ClientCapabilities, CancellationToken, ServerCapabilities, TextDocumentRegistrationOptions, DocumentSelector, StaticRegistrationOptions,
SelectionRangeRequest, SelectionRangeProviderOptions, SelectionRangeKind, SelectionRange, SelectionRangeParams
SelectionRangeRequest, SelectionRangeProviderOptions, SelectionRange, SelectionRangeParams
} from 'vscode-languageserver-protocol';

import { TextDocumentFeature, BaseLanguageClient } from './client';
Expand All @@ -24,12 +24,12 @@ function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
}

export interface ProvideSelectionRangeSignature {
(document: TextDocument, positions: VPosition[], token: CancellationToken): ProviderResult<VSelectionRange[][]>;
(document: TextDocument, positions: VPosition[], token: CancellationToken): ProviderResult<VSelectionRange[]>;
}


export interface SelectionRangeProviderMiddleware {
provideSelectionRanges?: (this: void, document: TextDocument, positions: VPosition[], token: CancellationToken, next: ProvideSelectionRangeSignature) => ProviderResult<VSelectionRange[][]>;
provideSelectionRanges?: (this: void, document: TextDocument, positions: VPosition[], token: CancellationToken, next: ProvideSelectionRangeSignature) => ProviderResult<VSelectionRange[]>;
}

export class SelectionRangeFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {
Expand Down Expand Up @@ -75,7 +75,7 @@ export class SelectionRangeFeature extends TextDocumentFeature<TextDocumentRegis
};
let middleware = client.clientOptions.middleware!;
return Languages.registerSelectionRangeProvider(options.documentSelector!, {
provideSelectionRanges(document: TextDocument, positions: VPosition[], token: CancellationToken): ProviderResult<VSelectionRange[][]> {
provideSelectionRanges(document: TextDocument, positions: VPosition[], token: CancellationToken): ProviderResult<VSelectionRange[]> {
return middleware.provideSelectionRanges
? middleware.provideSelectionRanges(document, positions, token, provideSelectionRanges)
: provideSelectionRanges(document, positions, token);
Expand All @@ -84,84 +84,89 @@ export class SelectionRangeFeature extends TextDocumentFeature<TextDocumentRegis
});
}

private asSelectionRanges(selectionRanges: SelectionRange[][] | null): VSelectionRange[][] {
private asSelectionRanges(selectionRanges: SelectionRange[] | null): VSelectionRange[] {
if (!Array.isArray(selectionRanges)) {
return [];
}
let result: VSelectionRange[][] = [];
for (let ranges of selectionRanges) {
if (!Array.isArray(ranges)) {
let result: VSelectionRange[] = [];
for (let range of selectionRanges) {
let vrange = this.asSelectionRange(range);
if (vrange == null) {
return [];
}
let inner: VSelectionRange[] = [];
result.push(inner);
for (let range of ranges) {
inner.push(new VSelectionRange(
this._client.protocol2CodeConverter.asRange(range.range),
this.asSelectionRangeKind(range.kind),
));
}
result.push(vrange);
}
return result;
}

private asSelectionRangeKind(kind?: string): VSelectionRangeKind {
switch (kind) {
case SelectionRangeKind.Empty:
return VSelectionRangeKind.Empty;
case SelectionRangeKind.Statement:
return VSelectionRangeKind.Statement;
case SelectionRangeKind.Declaration:
return VSelectionRangeKind.Declaration;
default:
return VSelectionRangeKind.Empty
private asSelectionRange(selectionRange: SelectionRange): VSelectionRange | undefined {
let range = this._client.protocol2CodeConverter.asRange(selectionRange.range);
let parent = undefined;
if (selectionRange.parent) {
parent = this.asSelectionRange(selectionRange.parent);
if (!parent || !(parent.range.contains(range) && !parent.range.isEqual(range))) {
return undefined
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the proper way to report invalid data? eg, when parent is smaller than a child?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrieken any recommendation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, child must be contained by parents

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, and this code checks this conditions. The question is, what to do if the server implementation returns wrong data?

The behavior should be such that:

  • server implementer ideally should see a loud error and fix the problem, instead of spending time with debugger to figure out where exactly the info is dropped
  • editor user should ideally get some message (such that they can open an issue for the corresponding extension), but it should not be distracting, and it should not be repeating.

Is there some function like show_error_dialog_if_in_dev_mode_otherwise_log_a_warning?

}
}
return new VSelectionRange(range, parent);
}
}

declare module 'vscode' {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that the API is not proposed in insiders, so we should remove this once we update vscode dependency


export class SelectionRangeKind {
/**
* 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 {

/**
* Empty Kind.
* The [range](#Range) of this selection range.
*/
static readonly Empty: SelectionRangeKind;
range: Range;

/**
* The statment kind, its value is `statement`, possible extensions can be
* `statement.if` etc
* The parent selection range containing this range.
*/
static readonly Statement: SelectionRangeKind;
parent?: SelectionRange;

/**
* The declaration kind, its value is `declaration`, possible extensions can be
* `declaration.function`, `declaration.class` etc.
* Creates a new selection range.
*
* @param range The range of the selection range.
* @param parent The parent of the selection range.
*/
static readonly Declaration: SelectionRangeKind;

readonly value: string;

private constructor(value: string);

append(value: string): SelectionRangeKind;
}

export class SelectionRange {
kind: SelectionRangeKind;
range: Range;
constructor(range: Range, kind: SelectionRangeKind);
constructor(range: Range, parent?: SelectionRange);
}

export interface SelectionRangeProvider {
/**
* Provide selection ranges starting at a given position. The first range must [contain](#Range.contains)
* position and subsequent ranges must contain the previous range.
* Provide selection ranges for the given positions.
*
* Selection ranges should be computed individually and independend for each postion. 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, position: Position[], token: CancellationToken): ProviderResult<SelectionRange[][]>;
provideSelectionRanges(document: TextDocument, positions: Position[], token: CancellationToken): ProviderResult<SelectionRange[]>;
}

export namespace languages {
/**
* 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;
}
}
}
8 changes: 4 additions & 4 deletions protocol/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions protocol/src/protocol.selectionRange.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#### Selection Range Request (:leftwards_arrow_with_hook:)

The selection range request is sent from the client to the server to return suggested selection ranges at a given position.
The selection range request is sent from the client to the server to return suggested selection ranges at given positions.
A selection range is a range around the cursor position which the user might be interested in selecting.
Typically, but not neccessary, selection ranges correspond to the nodes of the syntax tree.
The first range must contain the given position.
Position can coincide with the start or end of the first range.
Subsequent ranges must contain the previous range.
Typically, but not necessary, selection ranges correspond to the nodes of the syntax tree.

Selection ranges should be computed independently for each position. Ranges for
a specific position should form hierarchy: each range has an optional, strictly
larger, parent range.

_Request_:

* method: 'textDocument/selectionRange'
* params: `TextDocumentPositionParams`
* params: `SelectionRangeParams`

_Response_:
* result: `SelectionRange[] | null`: a list of selection ranges
Expand Down
30 changes: 5 additions & 25 deletions protocol/src/protocol.selectionRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,6 @@ export interface SelectionRangeServerCapabilities {
selectionRangeProvider?: boolean | SelectionRangeProviderOptions | (SelectionRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
}

/**
* Enum of known selection range kinds
*/
export enum SelectionRangeKind {
/**
* Empty Kind.
*/
Empty = '',
/**
* The statment kind, its value is `statement`, possible extensions can be
* `statement.if` etc
*/
Statement = 'statement',
/**
* The declaration kind, its value is `declaration`, possible extensions can be
* `declaration.function`, `declaration.class` etc.
*/
Declaration = 'declaration',
}

/**
* Represents a selection range
*/
Expand All @@ -66,11 +46,11 @@ export interface SelectionRange {
* Range of the selection.
*/
range: Range;

/**
* Describes the kind of the selection range such as `statemet' or 'declaration'. See
* [SelectionRangeKind](#SelectionRangeKind) for an enumeration of standardized kinds.
* The parent selection range containing this range.
*/
kind: string;
parent?: SelectionRange;
}

/**
Expand All @@ -92,9 +72,9 @@ export interface SelectionRangeParams {
/**
* A request to provide selection ranges in a document. The request's
* parameter is of type [TextDocumentPositionParams](#TextDocumentPositionParams), the
* response is of type [SelectionRange[][]](#SelectionRange[][]) or a Thenable
* response is of type [SelectionRange[]](#SelectionRange[]) or a Thenable
* that resolves to such.
*/
export namespace SelectionRangeRequest {
export const type: RequestType<SelectionRangeParams, SelectionRange[][] | null, any, any> = new RequestType('textDocument/selectionRange');
export const type: RequestType<SelectionRangeParams, SelectionRange[] | null, any, any> = new RequestType('textDocument/selectionRange');
}
4 changes: 2 additions & 2 deletions protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
} from './protocol.declaration';
import {
SelectionRangeClientCapabilities, SelectionRangeProviderOptions, SelectionRangeRequest, SelectionRangeServerCapabilities,
SelectionRangeKind, SelectionRange, SelectionRangeParams
SelectionRange, SelectionRangeParams
} from './protocol.selectionRange';


Expand Down Expand Up @@ -1993,5 +1993,5 @@ export {
FoldingRangeClientCapabilities, FoldingRangeProviderOptions, FoldingRangeRequest, FoldingRangeParams, FoldingRangeServerCapabilities,
DeclarationClientCapabilities, DeclarationRequest, DeclarationServerCapabilities,
SelectionRangeClientCapabilities, SelectionRangeProviderOptions, SelectionRangeRequest, SelectionRangeServerCapabilities,
SelectionRangeKind, SelectionRange, SelectionRangeParams
SelectionRange, SelectionRangeParams
};