Skip to content

Commit

Permalink
proposed extension: selectionRange
Browse files Browse the repository at this point in the history
  • Loading branch information
matklad committed Jan 11, 2019
1 parent eba5131 commit 54480ec
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 7 deletions.
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.

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

0 comments on commit 54480ec

Please sign in to comment.