提交 514ec620 编写于 作者: J Johannes Rieken

add SelectionRange and SelectionRangeKind, #63935

上级 f5f4ad9d
......@@ -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 {
......
......@@ -3,7 +3,7 @@
* 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';
......@@ -11,8 +11,8 @@ 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)))
......@@ -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) {
......@@ -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);
}
}
......@@ -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;
}
......@@ -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
......@@ -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' });
}
}
}
......
......@@ -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 });
}
}
}
......
......@@ -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);
......@@ -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}`);
}
}
......
......@@ -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[] = [];
......@@ -26,7 +26,7 @@ export class TokenTreeSelectionRangeProvider implements SelectionRangeProvider {
lastRange = node.range;
node = node.parent;
}
return ranges;
return ranges.map(range => ({ range, kind: '' }));
}
}
......
......@@ -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);
......@@ -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' });
}
}
......@@ -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())) {
......@@ -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;
......
......@@ -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 {
......
......@@ -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)
......@@ -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 {
......
......@@ -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,
......
......@@ -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 {
......
......@@ -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 {
......@@ -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 [];
});
......
......@@ -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;
});
......@@ -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));
}
......
......@@ -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 {
......
......@@ -1036,6 +1036,37 @@ export class CodeActionKind {
}
}
export class SelectionRangeKind {
private static readonly _sep = '.';
static readonly Empty = new SelectionRangeKind('');
static readonly Statement = SelectionRangeKind.Empty.append('statement');
static readonly Expression = SelectionRangeKind.Empty.append('expression');
static readonly Block = SelectionRangeKind.Empty.append('block');
readonly value: string;
constructor(value: string) {
this.value = value;
}
append(value: string): SelectionRangeKind {
return new SelectionRangeKind(this.value ? this.value + SelectionRangeKind._sep + value : value);
}
}
export class SelectionRange {
kind: SelectionRangeKind;
range: Range;
constructor(kind: SelectionRangeKind, range: Range) {
this.kind = kind;
this.range = range;
}
}
export class CodeLens {
......
......@@ -772,7 +772,10 @@ suite('ExtHostLanguageFeatureCommands', function () {
disposables.push(extHost.registerSelectionRangeProvider(nullExtensionDescription, defaultSelector, <vscode.SelectionRangeProvider>{
provideSelectionRanges() {
return [new types.Range(0, 10, 0, 18), new types.Range(0, 2, 0, 20)];
return [
new types.SelectionRange(types.SelectionRangeKind.Block, new types.Range(0, 10, 0, 18)),
new types.SelectionRange(types.SelectionRangeKind.Block, new types.Range(0, 2, 0, 20))
];
}
}));
......
......@@ -1229,7 +1229,10 @@ suite('ExtHostLanguageFeatures', function () {
test('Selection Ranges, data conversion', async function () {
disposables.push(extHost.registerSelectionRangeProvider(defaultExtension, defaultSelector, <vscode.SelectionRangeProvider>{
provideSelectionRanges() {
return [new types.Range(0, 10, 0, 18), new types.Range(0, 2, 0, 20)];
return [
new types.SelectionRange(types.SelectionRangeKind.Block, new types.Range(0, 10, 0, 18)),
new types.SelectionRange(types.SelectionRangeKind.Block, new types.Range(0, 2, 0, 20))
];
}
}));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册