提交 e69d768e 编写于 作者: M Martin Aeschlimann

adopt feedback for #109923

上级 776193fd
......@@ -824,12 +824,27 @@ export interface DocumentHighlightProvider {
*/
export interface OnTypeRenameProvider {
wordPattern?: RegExp;
/**
* Provide a list of ranges that can be live-renamed together.
*/
provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>;
provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<OnTypeRenameRanges>;
}
/**
* Represents a list of ranges that can be renamed together along with a word pattern to describe valid range contents.
*/
export interface OnTypeRenameRanges {
/**
* A list of ranges that can be renamed together. The ranges must have
* identical length and contain identical text content. The ranges cannot overlap
*/
ranges: IRange[];
/**
* An optional word pattern that describes valid contents for the given ranges.
* If no pattern is provided, the language configuration's word pattern will be used.
*/
wordPattern?: RegExp;
}
/**
......
......@@ -15,7 +15,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRange, Range } from 'vs/editor/common/core/range';
import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes';
import { OnTypeRenameProviderRegistry, OnTypeRenameRanges } from 'vs/editor/common/modes';
import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
......@@ -419,33 +419,19 @@ registerEditorCommand(new OnTypeRenameCommand({
}));
export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{
ranges: IRange[],
wordPattern?: RegExp
} | undefined | null> {
function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<OnTypeRenameRanges | undefined | null> {
const orderedByScore = OnTypeRenameProviderRegistry.ordered(model);
// in order of score ask the occurrences provider
// in order of score ask the on type rename provider
// until someone response with a good result
// (good = none empty array)
return first<{
ranges: IRange[],
wordPattern?: RegExp
} | undefined>(orderedByScore.map(provider => () => {
return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => {
if (!res) {
return undefined;
}
return {
ranges: res.ranges,
wordPattern: res.wordPattern || provider.wordPattern
};
}, (err) => {
onUnexpectedExternalError(err);
// (good = not null)
return first<OnTypeRenameRanges | undefined | null>(orderedByScore.map(provider => async () => {
try {
return await provider.provideOnTypeRenameRanges(model, position, token);
} catch (e) {
onUnexpectedExternalError(e);
return undefined;
});
}
}), result => !!result && arrays.isNonEmptyArray(result?.ranges));
}
......
......@@ -16,6 +16,7 @@ import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { ITextModel } from 'vs/editor/common/model';
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
const mockFile = URI.parse('test:somefile.ttt');
const mockFileSelector = { scheme: 'test' };
......@@ -29,6 +30,11 @@ interface TestEditor {
redo(): void;
}
const languageIdentifier = new modes.LanguageIdentifier('onTypeRenameTestLangage', 74);
LanguageConfigurationRegistry.register(languageIdentifier, {
wordPattern: /[a-zA-Z]+/
});
suite('On type rename', () => {
const disposables = new DisposableStore();
......@@ -42,8 +48,8 @@ suite('On type rename', () => {
function createMockEditor(text: string | string[]): ITestCodeEditor {
const model = typeof text === 'string'
? createTextModel(text, undefined, undefined, mockFile)
: createTextModel(text.join('\n'), undefined, undefined, mockFile);
? createTextModel(text, undefined, languageIdentifier, mockFile)
: createTextModel(text.join('\n'), undefined, languageIdentifier, mockFile);
const editor = createTestCodeEditor({ model });
disposables.add(model);
......@@ -55,18 +61,16 @@ suite('On type rename', () => {
function testCase(
name: string,
initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp },
initialState: { text: string | string[], responseWordPattern?: RegExp },
operations: (editor: TestEditor) => Promise<void>,
expectedEndText: string | string[]
) {
test(name, async () => {
disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, {
wordPattern: initialState.providerWordPattern,
provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) {
const wordAtPos = model.getWordAtPosition(pos);
if (wordAtPos) {
const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false);
assert.ok(matches.length > 0);
return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern };
}
return { ranges: [], wordPattern: initialState.responseWordPattern };
......@@ -299,7 +303,7 @@ suite('On type rename', () => {
const state3 = {
...state,
providerWordPattern: /[a-yA-Y]+/
responseWordPattern: /[a-yA-Y]+/
};
testCase('Breakout with stop pattern - insert', state3, async (editor) => {
......@@ -334,7 +338,6 @@ suite('On type rename', () => {
const state4 = {
...state,
providerWordPattern: /[a-yA-Y]+/,
responseWordPattern: /[a-eA-E]+/
};
......
......@@ -5829,14 +5829,26 @@ declare namespace monaco.languages {
* the live-rename feature.
*/
export interface OnTypeRenameProvider {
wordPattern?: RegExp;
/**
* Provide a list of ranges that can be live-renamed together.
*/
provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<{
ranges: IRange[];
wordPattern?: RegExp;
}>;
provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<OnTypeRenameRanges>;
}
/**
* Represents a list of ranges that can be renamed together along with a word pattern to describe valid range contents.
*/
export interface OnTypeRenameRanges {
/**
* A list of ranges that can be renamed together. The ranges must have
* identical length and contain identical text content. The ranges cannot overlap.
*/
ranges: IRange[];
/**
* An optional word pattern that describes valid contents for the given ranges.
* If no pattern is provided, the language configuration's word pattern will be used.
*/
wordPattern?: RegExp;
}
/**
......
......@@ -1115,19 +1115,17 @@ declare module 'vscode' {
export interface OnTypeRenameProvider {
/**
* For a given position in a document, returns the range of the symbol at the position and all ranges
* that have the same content and can be renamed together. Optionally a result specific word pattern can be returned as well
* that describes valid contents. A rename to one of the ranges can be applied to all other ranges if the new content
* matches the word pattern.
* If no result-specific word pattern is provided, the word pattern defined when registering the provider is used.
* that have the same content and can be renamed together. Optionally a word pattern can be returned
* to describe valid contents. A rename to one of the ranges can be applied to all other ranges if the new content
* is valid.
* If no result-specific word pattern is provided, the word pattern from the language configuration is used.
*
* @param document The document in which the provider was invoked.
* @param position The position at which the provider was invoked.
* @param token A cancellation token.
* @return A list of ranges that can be renamed together. The ranges must have
* identical length and contain identical text content. The ranges cannot overlap. Optionally a word pattern
* that overrides the word pattern defined when registering the provider can be provided.
* @return A list of ranges that can be renamed together
*/
provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<{ ranges: Range[]; wordPattern?: RegExp; }>;
provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<OnTypeRenameRanges>;
}
namespace languages {
......@@ -1140,10 +1138,28 @@ declare module 'vscode' {
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider An 'on type' rename provider.
* @param wordPattern A word pattern to describes valid contents of renamed ranges.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, wordPattern?: RegExp): Disposable;
export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider): Disposable;
}
/**
* Represents a list of ranges that can be renamed together along with a word pattern to describe valid range contents.
*/
export class OnTypeRenameRanges {
constructor(ranges: Range[], wordPattern?: RegExp);
/**
* A list of ranges that can be renamed together. The ranges must have
* identical length and contain identical text content. The ranges cannot overlap.
*/
readonly ranges: Range[];
/**
* An optional word pattern that describes valid contents for the given ranges.
* If no pattern is provided, the language configuration's word pattern will be used.
*/
readonly wordPattern?: RegExp;
}
//#endregion
......
......@@ -262,11 +262,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- on type rename
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], wordPattern?: IRegExpDto): void {
const revivedWordPattern = wordPattern ? MainThreadLanguageFeatures._reviveRegExp(wordPattern) : undefined;
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.OnTypeRenameProviderRegistry.register(selector, <modes.OnTypeRenameProvider>{
wordPattern: revivedWordPattern,
provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> => {
provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.OnTypeRenameRanges | undefined> => {
const res = await this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token);
if (res) {
return {
......
......@@ -397,9 +397,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
},
registerOnTypeRenameProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, stopPattern?: RegExp): vscode.Disposable {
registerOnTypeRenameProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerOnTypeRenameProvider(extension, checkSelector(selector), provider, stopPattern);
return extHostLanguageFeatures.registerOnTypeRenameProvider(extension, checkSelector(selector), provider);
},
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider);
......@@ -1197,6 +1197,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
NotebookCellOutput: extHostTypes.NotebookCellOutput,
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
OnTypeRenameRanges: extHostTypes.OnTypeRenameRanges
};
};
}
......
......@@ -384,7 +384,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], stopPattern: IRegExpDto | undefined): void;
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
......@@ -1395,6 +1395,11 @@ export interface ILanguageWordDefinitionDto {
regexFlags: string
}
export interface IOnTypeRenameRangesDto {
ranges: IRange[];
wordPattern?: IRegExpDto;
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
......@@ -1407,7 +1412,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: IRegExpDto; } | undefined>;
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IOnTypeRenameRangesDto | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<ILocationDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined>;
$resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
......
......@@ -317,7 +317,7 @@ class OnTypeRenameAdapter {
private readonly _provider: vscode.OnTypeRenameProvider
) { }
provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> {
provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.OnTypeRenameRanges | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
......@@ -1564,14 +1564,13 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- on type rename
registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, wordPattern?: RegExp): vscode.Disposable {
registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider): vscode.Disposable {
const handle = this._addNewAdapter(new OnTypeRenameAdapter(this._documents, provider), extension);
const serializedWordPattern = wordPattern ? ExtHostLanguageFeatures._serializeRegExp(wordPattern) : undefined;
this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedWordPattern);
this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: extHostProtocol.IRegExpDto; } | undefined> {
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.IOnTypeRenameRangesDto | undefined> {
return this._withAdapter(handle, OnTypeRenameAdapter, async adapter => {
const res = await adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token);
if (res) {
......
......@@ -2891,3 +2891,9 @@ export enum StandardTokenType {
String = 2,
RegEx = 4
}
export class OnTypeRenameRanges {
constructor(public readonly ranges: Range[], public readonly wordPattern?: RegExp) {
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册