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

Proposed extensions: textDocument/selectionRange #441

Merged
merged 1 commit into from
Feb 8, 2019
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
3 changes: 2 additions & 1 deletion client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { ConfigurationWorkspaceMiddleware } from './configuration';
import { WorkspaceFolderWorkspaceMiddleware } from './workspaceFolders';
import { FoldingRangeProviderMiddleware } from './foldingRange';
import { DeclarationMiddleware } from './declaration';
import { SelectionRangeProviderMiddleware } from './selectionRange.proposed';

import * as c2p from './codeConverter';
import * as p2c from './protocolConverter';
Expand Down Expand Up @@ -444,7 +445,7 @@ export interface _Middleware {
}

export type Middleware = _Middleware & TypeDefinitionMiddleware & ImplementationMiddleware & ColorProviderMiddleware &
FoldingRangeProviderMiddleware & DeclarationMiddleware;
FoldingRangeProviderMiddleware & DeclarationMiddleware & SelectionRangeProviderMiddleware;

export interface LanguageClientOptions {
documentSelector?: DocumentSelector | string[];
Expand Down
7 changes: 5 additions & 2 deletions client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { TypeDefinitionFeature } from './typeDefinition';
import { WorkspaceFoldersFeature } from './workspaceFolders';
import { FoldingRangeFeature } from './foldingRange';
import { DeclarationFeature } from './declaration';
import { SelectionRangeFeature } from './selectionRange.proposed';

import * as Is from './utils/is';
import { terminate } from './utils/processes';
Expand Down Expand Up @@ -507,8 +508,10 @@ export class SettingMonitor {
// Exporting proposed protocol.

export namespace ProposedFeatures {
export function createAll(_client: BaseLanguageClient): (StaticFeature | DynamicFeature<any>)[] {
let result: (StaticFeature | DynamicFeature<any>)[] = [];
export function createAll(client: BaseLanguageClient): (StaticFeature | DynamicFeature<any>)[] {
let result: (StaticFeature | DynamicFeature<any>)[] = [
new SelectionRangeFeature(client)
];
return result;
}
}
2 changes: 1 addition & 1 deletion client/src/protocolConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -880,4 +880,4 @@ export function createConverter(uriConverter?: URIConverter): Converter {
asColorPresentation,
asColorPresentations
}
}
}
156 changes: 156 additions & 0 deletions client/src/selectionRange.proposed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
'use strict';

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 {
ClientCapabilities, CancellationToken, ServerCapabilities, TextDocumentRegistrationOptions, DocumentSelector, StaticRegistrationOptions,
TextDocumentPositionParams,
SelectionRangeRequest, SelectionRangeProviderOptions, SelectionRangeKind, SelectionRange
} from 'vscode-languageserver-protocol';

import { TextDocumentFeature, BaseLanguageClient } from './client';

function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
if (target[key] === void 0) {
target[key] = {} as any;
}
return target[key];
}

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


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

export class SelectionRangeFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {
constructor(client: BaseLanguageClient) {
super(client, SelectionRangeRequest.type);
}

public fillClientCapabilities(capabilites: ClientCapabilities): void {
let capability = ensure(ensure(capabilites, 'textDocument')!, 'selectionRange')!;
capability.dynamicRegistration = true;
}

public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
if (!capabilities.selectionRangeProvider) {
return;
}

const implCapabilities = capabilities.selectionRangeProvider as TextDocumentRegistrationOptions & StaticRegistrationOptions & SelectionRangeProviderOptions;
const id = Is.string(implCapabilities.id) && implCapabilities.id.length > 0 ? implCapabilities.id : UUID.generateUuid();
const selector = implCapabilities.documentSelector || documentSelector;
if (selector) {
this.register(this.messages, {
id,
registerOptions: Object.assign({}, { documentSelector: selector })
});
}
}

protected registerLanguageProvider(options: TextDocumentRegistrationOptions): Disposable {
let client = this._client;
let provideSelectionRanges: ProvideSelectionRangeSignature = (document, position, token) => {
const requestParams: TextDocumentPositionParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position),
};
return client.sendRequest(SelectionRangeRequest.type, requestParams, token).then(
(ranges) => this.asSelectionRanges(ranges),
(error: any) => {
client.logFailedRequest(SelectionRangeRequest.type, error);
return Promise.resolve(null);
}
);
};
let middleware = client.clientOptions.middleware!;
return Languages.registerSelectionRangeProvider(options.documentSelector!, {
provideSelectionRanges(document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VSelectionRange[]> {
return middleware.provideSelectionRanges
? middleware.provideSelectionRanges(document, position, token, provideSelectionRanges)
: provideSelectionRanges(document, position, token);

}
});
}

private asSelectionRanges(selectionRanges: SelectionRange[] | null): VSelectionRange[] {
if (!Array.isArray(selectionRanges)) return [];
return selectionRanges.map(r => {
return new VSelectionRange(
this._client.protocol2CodeConverter.asRange(r.range),
this.asSelectionRangeKind(r.kind),
);
});
}

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
}
}
}

declare module 'vscode' {

export class SelectionRangeKind {

/**
* Empty Kind.
*/
static readonly Empty: SelectionRangeKind;

/**
* The statment kind, its value is `statement`, possible extensions can be
* `statement.if` etc
*/
static readonly Statement: SelectionRangeKind;

/**
* The declaration kind, its value is `declaration`, possible extensions can be
* `declaration.function`, `declaration.class` etc.
*/
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);
}

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.
*/
provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<SelectionRange[]>;
}

export namespace languages {
export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable;
}}
18 changes: 18 additions & 0 deletions protocol/src/protocol.selectionRange.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#### 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.
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.
matklad marked this conversation as resolved.
Show resolved Hide resolved

_Request_:

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

_Response_:
* result: `SelectionRange[] | null`: a list of selection ranges
* error: code and message set in case an exception happens during the 'textDocument/selectionRange' request

84 changes: 84 additions & 0 deletions protocol/src/protocol.selectionRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { RequestType } from 'vscode-jsonrpc';
import { Range } from 'vscode-languageserver-types';
import { TextDocumentRegistrationOptions, StaticRegistrationOptions, TextDocumentPositionParams } from './protocol';

// ---- capabilities

export interface SelectionRangeClientCapabilities {
/**
* The text document client capabilities
*/
textDocument?: {
/**
* Capabilities specific to `textDocument/selectionRange` requests
*/
selectionRange?: {
/**
* Whether implementation supports dynamic registration for selection range providers. If this is set to `true`
* the client supports the new `(SelectionRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
* return value for the corresponding server capability as well.
*/
dynamicRegistration?: boolean;
};
};
}

export interface SelectionRangeProviderOptions {
}

export interface SelectionRangeServerCapabilities {
/**
* The server provides selection range support.
*/
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
*/
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.
*/
kind: string;
}

/**
* 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
* that resolves to such.
*/
export namespace SelectionRangeRequest {
export const type: RequestType<TextDocumentPositionParams, SelectionRange[] | null, any, any> = new RequestType('textDocument/selectionRange');
}
12 changes: 9 additions & 3 deletions protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import {
import {
DeclarationClientCapabilities, DeclarationRequest, DeclarationServerCapabilities
} from './protocol.declaration';
import {
SelectionRangeClientCapabilities, SelectionRangeProviderOptions, SelectionRangeRequest, SelectionRangeServerCapabilities,
SelectionRangeKind, SelectionRange,
} from './protocol.selectionRange';


// @ts-ignore: to avoid inlining LocatioLink as dynamic import
Expand Down Expand Up @@ -668,7 +672,7 @@ export interface _ClientCapabilities {

export type ClientCapabilities = _ClientCapabilities & ImplementationClientCapabilities & TypeDefinitionClientCapabilities &
WorkspaceFoldersClientCapabilities & ConfigurationClientCapabilities & ColorClientCapabilities & FoldingRangeClientCapabilities &
DeclarationClientCapabilities;
DeclarationClientCapabilities & SelectionRangeClientCapabilities;

/**
* Defines how the host (editor) should sync
Expand Down Expand Up @@ -955,7 +959,7 @@ export interface _ServerCapabilities {
}

export type ServerCapabilities = _ServerCapabilities & ImplementationServerCapabilities & TypeDefinitionServerCapabilities & WorkspaceFoldersServerCapabilities &
ColorServerCapabilities & FoldingRangeServerCapabilities & DeclarationServerCapabilities;
ColorServerCapabilities & FoldingRangeServerCapabilities & DeclarationServerCapabilities & SelectionRangeServerCapabilities;

/**
* The initialize request is sent from the client to the server.
Expand Down Expand Up @@ -1987,5 +1991,7 @@ export {
ConfigurationRequest, ConfigurationParams, ConfigurationItem,
DocumentColorRequest, ColorPresentationRequest, ColorProviderOptions, DocumentColorParams, ColorPresentationParams,
FoldingRangeClientCapabilities, FoldingRangeProviderOptions, FoldingRangeRequest, FoldingRangeParams, FoldingRangeServerCapabilities,
DeclarationClientCapabilities, DeclarationRequest, DeclarationServerCapabilities
DeclarationClientCapabilities, DeclarationRequest, DeclarationServerCapabilities,
SelectionRangeClientCapabilities, SelectionRangeProviderOptions, SelectionRangeRequest, SelectionRangeServerCapabilities,
SelectionRangeKind, SelectionRange
};