提交 9ce3f2cf 编写于 作者: J Johannes Rieken

rename provider as support

上级 a7ff9cdb
......@@ -25,7 +25,7 @@ import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/
import {IEventService} from 'vs/platform/event/common/event';
import {IEditorService} from 'vs/platform/editor/common/editor';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {RenameRegistry} from '../common/rename';
import {RenameRegistry, rename} from '../common/rename';
export class RenameAction extends EditorAction {
......@@ -145,42 +145,12 @@ export class RenameAction extends EditorAction {
// start recording of file changes so that we can figure out if a file that
// is to be renamed conflicts with another (concurrent) modification
let sourceModel = this.editor.getModel().getAssociatedResource();
let sourceSelections = this.editor.getSelections();
let supports = RenameRegistry.ordered(this.editor.getModel());
let hasResult = false;
let rejects: string[] = [];
let factory = supports.map(support => {
return () => {
if (!hasResult) {
return support.rename(sourceModel, this.editor.getPosition(), newName).then(result => {
if (!result) {
// ignore
} else if (!result.rejectReason) {
hasResult = true;
return result;
} else {
rejects.push(result.rejectReason);
}
});
}
};
});
let edit = createBulkEdit(this._eventService, this._editorService,
<EditorBrowser.ICodeEditor>this.editor);
let edit = createBulkEdit(this._eventService, this._editorService, <EditorBrowser.ICodeEditor>this.editor);
return sequence(factory).then(values => {
let result = values[0];
if (rejects.length > 0) {
return TPromise.wrapError(rejects.join('\n'));
} else if (!result) {
return TPromise.wrapError(nls.localize('no result', "No result."));
return rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => {
if (result.rejectReason) {
return TPromise.wrapError(result.rejectReason);
}
edit.add(result.edits);
return edit;
});
......
......@@ -5,7 +5,56 @@
'use strict';
import {IRenameSupport} from 'vs/editor/common/modes';
import {IRenameSupport, IRenameResult} from 'vs/editor/common/modes';
import {IModel, IPosition} from 'vs/editor/common/editorCommon';
import {TPromise} from 'vs/base/common/winjs.base';
import {localize} from 'vs/nls';
import {sequence} from 'vs/base/common/async';
import {onUnexpectedError} from 'vs/base/common/errors';
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
export const RenameRegistry = new LanguageFeatureRegistry<IRenameSupport>('renameSupport');
export function rename(model: IModel, position: IPosition, newName: string): TPromise<IRenameResult> {
const supports = RenameRegistry.ordered(model);
const resource = model.getAssociatedResource();
const rejects: string[] = [];
let hasResult = false;
const factory = supports.map(support => {
return () => {
if (!hasResult) {
return support.rename(resource, position, newName).then(result => {
if (!result) {
// ignore
} else if (!result.rejectReason) {
hasResult = true;
return result;
} else {
rejects.push(result.rejectReason);
}
});
}
};
});
return sequence(factory).then(values => {
let result = values[0];
if (rejects.length > 0) {
return <IRenameResult>{
currentName: undefined,
edits: undefined,
rejectReason: rejects.join('\n')
};
} else if (!result) {
return <IRenameResult>{
currentName: undefined,
edits: undefined,
rejectReason: localize('no result', "No result.")
};
} else {
return result;
}
});
}
\ No newline at end of file
......@@ -283,7 +283,7 @@ export class PluginHostAPIImplementation {
return languageFeatures.registerReferenceProvider(selector, provider);
},
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
return features.rename.register(selector, provider);
return languageFeatures.registerRenameProvider(selector, provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return languageFeatures.registerDocumentSymbolProvider(selector, provider);
......
......@@ -454,8 +454,58 @@ class NavigateTypeAdapter implements INavigateTypesSupport {
}
}
class RenameAdapter implements modes.IRenameSupport {
private _documents: PluginHostModelService;
private _provider: vscode.RenameProvider;
constructor(documents: PluginHostModelService, provider: vscode.RenameProvider) {
this._documents = documents;
this._provider = provider;
}
rename(resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> {
let doc = this._documents.getDocument(resource);
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
if (!value) {
return;
}
let result = <modes.IRenameResult>{
currentName: undefined,
edits: []
};
for (let entry of value.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: <URI>uri,
newText: textEdit.newText,
range: TypeConverters.fromRange(textEdit.range)
});
}
}
return result;
}, err => {
if (typeof err === 'string') {
return <modes.IRenameResult>{
currentName: undefined,
edits: undefined,
rejectReason: err
};
}
return TPromise.wrapError(err);
});
}
}
type Adapter = OutlineAdapter | CodeLensAdapter | DeclarationAdapter | ExtraInfoAdapter | OccurrencesAdapter | ReferenceAdapter | QuickFixAdapter
| DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter;
| DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter;
@Remotable.PluginHostContext('ExtHostLanguageFeatures')
export class ExtHostLanguageFeatures {
......@@ -638,6 +688,19 @@ export class ExtHostLanguageFeatures {
$getNavigateToItems(handle: number, search: string): TPromise<ITypeBearing[]> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.getNavigateToItems(search));
}
// --- rename
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
const handle = this._nextHandle();
this._adapter[handle] = new RenameAdapter(this._documents, provider);
this._proxy.$registerRenameSupport(handle, selector);
return this._createDisposable(handle);
}
$rename(handle: number, resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.rename(resource, position, newName));
}
}
@Remotable.MainContext('MainThreadLanguageFeatures')
......@@ -798,4 +861,15 @@ export class MainThreadLanguageFeatures {
});
return undefined;
}
// --- rename
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = RenameRegistry.register(selector, <modes.IRenameSupport>{
rename: (resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> => {
return this._proxy.$rename(handle, resource, position, newName);
}
});
return undefined;
}
}
\ No newline at end of file
......@@ -131,79 +131,6 @@ export abstract class AbstractExtensionHostFeature<T, P extends AbstractMainThre
}
// -- Rename provider
export class ExtensionHostRename extends AbstractExtensionHostFeature<vscode.RenameProvider, MainThreadRename> {
constructor( @IThreadService threadService: IThreadService) {
super(threadService.getRemotable(MainThreadRename), threadService);
}
_runAsCommand(resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> {
let document = this._models.getDocument(resource);
let pos = TypeConverters.toPosition(position);
let hasResult = false;
let rejects: string[] = [];
let factory = this._getOrderedFor(document).map(provider => {
return () => {
if (!hasResult) {
return asWinJsPromise(token => provider.provideRenameEdits(document, pos, newName, token)).then(result => {
if (result && result.size > 0) {
hasResult = true;
return result;
}
}, err => {
if (typeof err === 'string') {
rejects.push(err);
}
});
}
};
});
return sequence(factory).then(results => {
let rename = results[0];
if (!rename) {
return <modes.IRenameResult>{
rejectReason: rejects.join('\n'),
edits: undefined,
currentName: undefined
};
}
let result = <modes.IRenameResult>{
currentName: undefined,
edits: []
};
for (let entry of rename.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: <URI> uri,
newText: textEdit.newText,
range: TypeConverters.fromRange(textEdit.range)
});
}
}
return result;
});
}
}
@Remotable.MainContext('MainThreadRename')
export class MainThreadRename extends AbstractMainThreadFeature<modes.IRenameSupport> implements modes.IRenameSupport {
constructor( @IThreadService threadService: IThreadService) {
super('vscode.executeDocumentRenameProvider', RenameRegistry, threadService);
}
rename(resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> {
return this._executeCommand(resource, position, newName);
}
}
// --- format
export class ExtHostFormatDocument extends AbstractExtensionHostFeature<vscode.DocumentFormattingEditProvider, MainThreadFormatDocument> {
......@@ -628,14 +555,12 @@ export class MainThreadCompletions extends AbstractMainThreadFeature<modes.ISugg
export namespace LanguageFeatures {
export function createMainThreadInstances(threadService: IThreadService): void {
threadService.getRemotable(MainThreadRename);
threadService.getRemotable(MainThreadSignatureHelp);
threadService.getRemotable(MainThreadCompletions);
}
export function createExtensionHostInstances(threadService: IThreadService) {
return {
rename: new ExtensionHostRename(threadService),
signatureHelp: new ExtHostSignatureHelp(threadService),
completions: threadService.getRemotable(ExtHostCompletions)
};
......
......@@ -34,6 +34,7 @@ import {OccurrencesRegistry, getOccurrencesAtPosition} from 'vs/editor/contrib/w
import {ReferenceRegistry, findReferences} from 'vs/editor/contrib/referenceSearch/common/referenceSearch';
import {getQuickFixes} from 'vs/editor/contrib/quickFix/common/quickFix';
import {getNavigateToItems} from 'vs/workbench/parts/search/common/search';
import {rename} from 'vs/editor/contrib/rename/common/rename';
const defaultSelector = { scheme: 'far' };
const model: EditorCommon.IModel = new EditorModel(
......@@ -706,5 +707,76 @@ suite('ExtHostLanguageFeatures', function() {
done();
});
});
})
});
// --- rename
test('Rename, evil provider 1/2', function(done) {
disposables.push(extHost.registerRenameProvider(defaultSelector, <vscode.RenameProvider>{
provideRenameEdits(): any {
throw Error('evil');
}
}));
threadService.sync().then(() => {
rename(model, { lineNumber: 1, column: 1 }, 'newName').then(value => {
done(new Error(''));
}, err => {
done(); // expected
});
});
});
test('Rename, evil provider 2/2', function(done) {
disposables.push(extHost.registerRenameProvider('*', <vscode.RenameProvider>{
provideRenameEdits(): any {
throw Error('evil');
}
}));
disposables.push(extHost.registerRenameProvider(defaultSelector, <vscode.RenameProvider>{
provideRenameEdits(): any {
let edit = new types.WorkspaceEdit();
edit.replace(model.getAssociatedResource(), new types.Range(0, 0, 0, 0), 'testing');
return edit;
}
}));
threadService.sync().then(() => {
rename(model, { lineNumber: 1, column: 1 }, 'newName').then(value => {
assert.equal(value.edits.length, 1);
done();
});
});
});
test('Rename, ordering', function(done) {
disposables.push(extHost.registerRenameProvider('*', <vscode.RenameProvider>{
provideRenameEdits(): any {
let edit = new types.WorkspaceEdit();
edit.replace(model.getAssociatedResource(), new types.Range(0, 0, 0, 0), 'testing');
edit.replace(model.getAssociatedResource(), new types.Range(1, 0, 1, 0), 'testing');
return edit;
}
}));
disposables.push(extHost.registerRenameProvider(defaultSelector, <vscode.RenameProvider>{
provideRenameEdits(): any {
return;
}
}));
threadService.sync().then(() => {
rename(model, { lineNumber: 1, column: 1 }, 'newName').then(value => {
assert.equal(value.edits.length, 2); // least relevant renamer
done();
});
});
});
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册