Skip to content

Commit

Permalink
add SelectionRange and SelectionRangeKind, #63935
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Dec 17, 2018
1 parent f5f4ad9 commit 514ec62
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 54 deletions.
7 changes: 6 additions & 1 deletion src/vs/editor/common/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1025,11 +1025,16 @@ export interface DocumentColorProvider {
provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): ProviderResult<IColorPresentation[]>;
}

export interface SelectionRange {
kind: string;
range: IRange;
}

export interface SelectionRangeProvider {
/**
* Provide ranges that should be selected from the given position.
*/
provideSelectionRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<IRange[]>;
provideSelectionRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<SelectionRange[]>;
}

export interface FoldingContext {
Expand Down
22 changes: 11 additions & 11 deletions src/vs/editor/contrib/smartSelect/bracketSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SelectionRangeProvider } from 'vs/editor/common/modes';
import { SelectionRangeProvider, SelectionRange } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { LinkedList } from 'vs/base/common/linkedList';

export class BracketSelectionRangeProvider implements SelectionRangeProvider {

provideSelectionRanges(model: ITextModel, position: Position): Promise<Range[]> {
const bucket: Range[] = [];
provideSelectionRanges(model: ITextModel, position: Position): Promise<SelectionRange[]> {
const bucket: SelectionRange[] = [];
const ranges = new Map<string, LinkedList<Range>>();
return new Promise(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges))
.then(() => new Promise(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket)))
Expand Down Expand Up @@ -67,7 +67,7 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
}
}

private static _bracketsLeftYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>, bucket: Range[]): void {
private static _bracketsLeftYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>, bucket: SelectionRange[]): void {
const counts = new Map<string, number>();
const t1 = Date.now();
while (true) {
Expand Down Expand Up @@ -108,8 +108,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
}
const innerBracket = Range.fromPositions(bracket.range.getEndPosition(), closing!.getStartPosition());
const outerBracket = Range.fromPositions(bracket.range.getStartPosition(), closing!.getEndPosition());
bucket.push(innerBracket);
bucket.push(outerBracket);
bucket.push({ range: innerBracket, kind: 'block.bracket' });
bucket.push({ range: outerBracket, kind: 'block.bracket' });
BracketSelectionRangeProvider._addBracketLeading(model, outerBracket, bucket);
}
}
Expand All @@ -118,7 +118,7 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
}
}

private static _addBracketLeading(model: ITextModel, bracket: Range, bucket: Range[]): void {
private static _addBracketLeading(model: ITextModel, bracket: Range, bucket: SelectionRange[]): void {
if (bracket.startLineNumber === bracket.endLineNumber) {
return;
}
Expand All @@ -128,8 +128,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
const startLine = bracket.startLineNumber;
const column = model.getLineFirstNonWhitespaceColumn(startLine);
if (column !== 0 && column !== bracket.startColumn) {
bucket.push(Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()));
bucket.push(Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()));
bucket.push({ range: Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()), kind: 'block.bracket.leading' });
bucket.push({ range: Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()), kind: 'block.bracket.leading' });
}

// xxxxxxxx
Expand All @@ -140,8 +140,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
if (aboveLine > 0) {
const column = model.getLineFirstNonWhitespaceColumn(aboveLine);
if (column === bracket.startColumn && column !== model.getLineLastNonWhitespaceColumn(aboveLine)) {
bucket.push(Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()));
bucket.push(Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()));
bucket.push({ range: Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()), kind: 'block.bracket.leading' });
bucket.push({ range: Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()), kind: 'block.bracket.leading' });
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/vs/editor/contrib/smartSelect/smartSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ export function provideSelectionRanges(model: ITextModel, position: Position, to
for (const group of provider) {
rank += 1;
for (const prov of group) {
work.push(Promise.resolve(prov.provideSelectionRanges(model, position, token)).then(res => {
if (arrays.isNonEmptyArray(res)) {
for (const range of res) {
if (Range.isIRange(range) && Range.containsPosition(range, position)) {
ranges.push({ range: Range.lift(range), rank });
work.push(Promise.resolve(prov.provideSelectionRanges(model, position, token)).then(selectionRanges => {
if (arrays.isNonEmptyArray(selectionRanges)) {
for (const sel of selectionRanges) {
if (Range.isIRange(sel.range) && Range.containsPosition(sel.range, position)) {
ranges.push({ range: Range.lift(sel.range), rank });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ suite('SmartSelect', () => {
let actual = new TokenTreeSelectionRangeProvider().provideSelectionRanges(model, new Position(lineNumber, column));


let actualStr = actual.map(r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn).toString());
let actualStr = actual.map(r => new Range(r.range.startLineNumber, r.range.startColumn, r.range.endLineNumber, r.range.endColumn).toString());
let desiredStr = ranges.reverse().map(r => String(r));

assert.deepEqual(actualStr, desiredStr);
Expand Down Expand Up @@ -199,7 +199,7 @@ suite('SmartSelect', () => {
assert.equal(expected.length, ranges.length);
for (const range of ranges) {
let exp = expected.shift() || null;
assert.ok(Range.equalsRange(range, exp), `A=${range} <> E=${exp}`);
assert.ok(Range.equalsRange(range.range, exp), `A=${range} <> E=${exp}`);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/vs/editor/contrib/smartSelect/tokenTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageId, StandardTokenType, SelectionRangeProvider } from 'vs/editor/common/modes';
import { LanguageId, StandardTokenType, SelectionRangeProvider, SelectionRange } from 'vs/editor/common/modes';

export class TokenTreeSelectionRangeProvider implements SelectionRangeProvider {

provideSelectionRanges(model: ITextModel, position: Position): Range[] {
provideSelectionRanges(model: ITextModel, position: Position): SelectionRange[] {
let tree = new TokenTreeBuilder(model).build();
let node = find(tree, position);
let ranges: Range[] = [];
Expand All @@ -26,7 +26,7 @@ export class TokenTreeSelectionRangeProvider implements SelectionRangeProvider {
lastRange = node.range;
node = node.parent;
}
return ranges;
return ranges.map(range => ({ range, kind: '' }));
}
}

Expand Down
24 changes: 12 additions & 12 deletions src/vs/editor/contrib/smartSelect/wordSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SelectionRangeProvider, StandardTokenType } from 'vs/editor/common/modes';
import { SelectionRangeProvider, StandardTokenType, SelectionRange } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';

export class WordSelectionRangeProvider implements SelectionRangeProvider {

provideSelectionRanges(model: ITextModel, position: Position): Range[] {
let result: Range[] = [];
provideSelectionRanges(model: ITextModel, position: Position): SelectionRange[] {
let result: SelectionRange[] = [];
this._addWordRanges(result, model, position);
this._addTokenRange(result, model, position);
this._addLineRanges(result, model, position);
return result;
}

private _addWordRanges(bucket: Range[], model: ITextModel, pos: Position): void {
private _addWordRanges(bucket: SelectionRange[], model: ITextModel, pos: Position): void {
const word = model.getWordAtPosition(pos);
if (word) {
bucket.push(new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn));
bucket.push({ range: new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn), kind: 'simple.word' });
}
}

private _addTokenRange(bucket: Range[], model: ITextModel, pos: Position): void {
private _addTokenRange(bucket: SelectionRange[], model: ITextModel, pos: Position): void {
const tokens = model.getLineTokens(pos.lineNumber);
const index = tokens.findTokenIndexAtOffset(pos.column - 1);
const type = tokens.getStandardTokenType(index);
Expand Down Expand Up @@ -98,15 +98,15 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider {

if (type === StandardTokenType.String) {
// just assume that quotation marks are length=1
bucket.push(Range.fromPositions(left.delta(0, 1), right.delta(0, -1)));
bucket.push(Range.fromPositions(left, right));
bucket.push({ range: Range.fromPositions(left.delta(0, 1), right.delta(0, -1)), kind: 'simple.string' });
bucket.push({ range: Range.fromPositions(left, right), kind: 'simple.string' });
} else {
bucket.push(Range.fromPositions(left, right));
bucket.push({ range: Range.fromPositions(left, right), kind: 'simple.comment' });
}
}

private _addLineRanges(bucket: Range[], model: ITextModel, pos: Position): void {
bucket.push(new Range(pos.lineNumber, model.getLineFirstNonWhitespaceColumn(pos.lineNumber), pos.lineNumber, model.getLineLastNonWhitespaceColumn(pos.lineNumber)));
bucket.push(new Range(pos.lineNumber, model.getLineMinColumn(pos.lineNumber), pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)));
private _addLineRanges(bucket: SelectionRange[], model: ITextModel, pos: Position): void {
bucket.push({ range: new Range(pos.lineNumber, model.getLineFirstNonWhitespaceColumn(pos.lineNumber), pos.lineNumber, model.getLineLastNonWhitespaceColumn(pos.lineNumber)), kind: 'simple.line' });
bucket.push({ range: new Range(pos.lineNumber, model.getLineMinColumn(pos.lineNumber), pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)), kind: 'simple.line' });
}
}
4 changes: 2 additions & 2 deletions src/vs/editor/contrib/suggest/wordDistance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export abstract class WordDistance {
if (!ranges || ranges.length === 0) {
return WordDistance.None;
}
return service.computeWordRanges(model.uri, ranges[0]).then(wordRanges => {
return service.computeWordRanges(model.uri, ranges[0].range).then(wordRanges => {
return new class extends WordDistance {
distance(anchor: IPosition, suggestion: CompletionItem) {
if (!wordRanges || !position.equals(editor.getPosition())) {
Expand All @@ -56,7 +56,7 @@ export abstract class WordDistance {
let bestWordRange = idx >= 0 ? wordLines[idx] : wordLines[Math.max(0, ~idx - 1)];
let blockDistance = ranges.length;
for (const range of ranges) {
if (!Range.containsRange(range, bestWordRange)) {
if (!Range.containsRange(range.range, bestWordRange)) {
break;
}
blockDistance -= 1;
Expand Down
7 changes: 6 additions & 1 deletion src/vs/monaco.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5277,11 +5277,16 @@ declare namespace monaco.languages {
provideColorPresentations(model: editor.ITextModel, colorInfo: IColorInformation, token: CancellationToken): ProviderResult<IColorPresentation[]>;
}

export interface SelectionRange {
kind: string;
range: IRange;
}

export interface SelectionRangeProvider {
/**
* Provide ranges that should be selected from the given position.
*/
provideSelectionRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<IRange[]>;
provideSelectionRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<SelectionRange[]>;
}

export interface FoldingContext {
Expand Down
22 changes: 21 additions & 1 deletion src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ declare module 'vscode' {

//#region Joh - selection range provider

export class SelectionRangeKind {

static readonly Empty: SelectionRangeKind;
static readonly Statement: SelectionRangeKind;
static readonly Expression: SelectionRangeKind;
static readonly Block: SelectionRangeKind;

readonly value: string;

private constructor(value: string);

append(value: string): SelectionRangeKind;
}

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

export interface SelectionRangeProvider {
/**
* Provide selection ranges starting at a given position. The first range must [contain](#Range.contains)
Expand All @@ -26,7 +46,7 @@ declare module 'vscode' {
* @param position
* @param token
*/
provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Range[]>;
provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<SelectionRange[]>;
}

export namespace languages {
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,8 @@ export function createApiFactory(
Range: extHostTypes.Range,
RelativePattern: extHostTypes.RelativePattern,
Selection: extHostTypes.Selection,
SelectionRange: extHostTypes.SelectionRange,
SelectionRangeKind: extHostTypes.SelectionRangeKind,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[]>;
$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IRange[]>;
$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.SelectionRange[]>;
}

export interface ExtHostQuickOpenShape {
Expand Down
7 changes: 3 additions & 4 deletions src/vs/workbench/api/node/extHostApiCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures'
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';

export class ExtHostApiCommands {

Expand Down Expand Up @@ -421,14 +420,14 @@ export class ExtHostApiCommands {
});
}

private _executeSelectionRangeProvider(resource: URI, position: types.Position): Promise<types.Range[]> {
private _executeSelectionRangeProvider(resource: URI, position: types.Position): Promise<vscode.SelectionRange[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<IRange[]>('_executeSelectionRangeProvider', args).then(result => {
return this._commands.executeCommand<modes.SelectionRange[]>('_executeSelectionRangeProvider', args).then(result => {
if (isNonEmptyArray(result)) {
return result.map(typeConverters.Range.to);
return result.map(typeConverters.SelectionRange.to);
}
return [];
});
Expand Down
18 changes: 9 additions & 9 deletions src/vs/workbench/api/node/extHostLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,21 +853,21 @@ class SelectionRangeAdapter {
private readonly _provider: vscode.SelectionRangeProvider
) { }

provideSelectionRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<IRange[]> {
provideSelectionRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.SelectionRange[]> {
const { document } = this._documents.getDocumentData(resource);
const pos = typeConvert.Position.to(position);
return asPromise(() => this._provider.provideSelectionRanges(document, pos, token)).then(ranges => {
if (isFalsyOrEmpty(ranges)) {
return asPromise(() => this._provider.provideSelectionRanges(document, pos, token)).then(selectionRanges => {
if (isFalsyOrEmpty(selectionRanges)) {
return undefined;
}
let result: IRange[] = [];
let result: modes.SelectionRange[] = [];
let last: vscode.Position | vscode.Range = pos;
for (const range of ranges) {
if (!range.contains(last)) {
for (const sel of selectionRanges) {
if (!sel.range.contains(last)) {
throw new Error('INVALID selection range, must contain the previous range');
}
result.push(typeConvert.Range.from(range));
last = range;
result.push(typeConvert.SelectionRange.from(sel));
last = sel.range;
}
return result;
});
Expand Down Expand Up @@ -1280,7 +1280,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}

$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IRange[]> {
$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.SelectionRange[]> {
return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), position, token));
}

Expand Down
24 changes: 24 additions & 0 deletions src/vs/workbench/api/node/extHostTypeConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,30 @@ export namespace Color {
}
}

export namespace SelectionRangeKind {

export function from(kind: vscode.SelectionRangeKind): string {
return kind.value;
}

export function to(value: string): vscode.SelectionRangeKind {
return new types.SelectionRangeKind(value);
}
}

export namespace SelectionRange {
export function from(obj: vscode.SelectionRange): modes.SelectionRange {
return {
kind: SelectionRangeKind.from(obj.kind),
range: Range.from(obj.range)
};
}

export function to(obj: modes.SelectionRange): vscode.SelectionRange {
return new types.SelectionRange(SelectionRangeKind.to(obj.kind), Range.to(obj.range));
}
}

export namespace TextDocumentSaveReason {

export function to(reason: SaveReason): vscode.TextDocumentSaveReason {
Expand Down
Loading

0 comments on commit 514ec62

Please sign in to comment.