-
Notifications
You must be signed in to change notification settings - Fork 385
Expand file tree
/
Copy pathrename.ts
More file actions
137 lines (122 loc) · 6.16 KB
/
rename.ts
File metadata and controls
137 lines (122 loc) · 6.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import {
languages as Languages, Disposable, TextDocument, ProviderResult, Range as VRange, Position as VPosition, WorkspaceEdit as VWorkspaceEdit, RenameProvider
} from 'vscode';
import {
ClientCapabilities, CancellationToken, ServerCapabilities, DocumentSelector, RenameOptions, RenameRegistrationOptions, RenameRequest, PrepareSupportDefaultBehavior, RenameParams, ResponseError, TextDocumentPositionParams, PrepareRenameRequest, Range} from 'vscode-languageserver-protocol';
import * as UUID from './utils/uuid';
import * as Is from './utils/is';
import { TextDocumentLanguageFeature, FeatureClient, ensure, DocumentSelectorOptions } from './features';
export interface ProvideRenameEditsSignature {
(this: void, document: TextDocument, position: VPosition, newName: string, token: CancellationToken): ProviderResult<VWorkspaceEdit>;
}
export interface PrepareRenameSignature {
(this: void, document: TextDocument, position: VPosition, token: CancellationToken): ProviderResult<VRange | { range: VRange; placeholder: string }>;
}
export interface RenameMiddleware {
provideRenameEdits?: (this: void, document: TextDocument, position: VPosition, newName: string, token: CancellationToken, next: ProvideRenameEditsSignature) => ProviderResult<VWorkspaceEdit>;
prepareRename?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: PrepareRenameSignature) => ProviderResult<VRange | { range: VRange; placeholder: string }>;
}
type DefaultBehavior = {
defaultBehavior: boolean;
};
export class RenameFeature extends TextDocumentLanguageFeature<boolean | RenameOptions, RenameRegistrationOptions, RenameProvider, RenameMiddleware> {
constructor(client: FeatureClient<RenameMiddleware>) {
super(client, RenameRequest.type);
}
public fillClientCapabilities(capabilities: ClientCapabilities): void {
const rename = ensure(ensure(capabilities, 'textDocument')!, 'rename')!;
rename.dynamicRegistration = true;
rename.prepareSupport = true;
rename.prepareSupportDefaultBehavior = PrepareSupportDefaultBehavior.Identifier;
rename.honorsChangeAnnotations = true;
}
public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
const options = this.getRegistrationOptions(documentSelector, capabilities.renameProvider);
if (!options) {
return;
}
if (Is.boolean(capabilities.renameProvider)) {
options.prepareProvider = false;
}
this.register({ id: UUID.generateUuid(), registerOptions: options });
}
protected registerLanguageProvider(options: RenameRegistrationOptions & DocumentSelectorOptions): [Disposable, RenameProvider] {
const selector = options.documentSelector;
const provider: RenameProvider = {
provideRenameEdits: (document, position, newName, token) => {
const client = this._client;
const provideRenameEdits: ProvideRenameEditsSignature = (document, position, newName, token) => {
const params: RenameParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position),
newName: newName
};
return client.sendRequest(RenameRequest.type, params, token).then((result) => {
if (token.isCancellationRequested) {
return null;
}
return client.protocol2CodeConverter.asWorkspaceEdit(result, token);
}, (error: ResponseError<void>) => {
return client.handleFailedRequest(RenameRequest.type, token, error, null, false);
});
};
const middleware = client.middleware;
return middleware.provideRenameEdits
? middleware.provideRenameEdits(document, position, newName, token, provideRenameEdits)
: provideRenameEdits(document, position, newName, token);
},
prepareRename: options.prepareProvider
? (document, position, token) => {
const client = this._client;
const prepareRename: PrepareRenameSignature = (document, position, token) => {
const params: TextDocumentPositionParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position),
};
return client.sendRequest(PrepareRenameRequest.type, params, token).then((result) => {
if (token.isCancellationRequested) {
return null;
}
if (Range.is(result)) {
return client.protocol2CodeConverter.asRange(result);
} else if (this.isDefaultBehavior(result)) {
return result.defaultBehavior === true
? document.getWordRangeAtPosition(position)
: Promise.reject(new Error(`The element can't be renamed.`));
} else if (result && Range.is(result.range)) {
return {
range: client.protocol2CodeConverter.asRange(result.range),
placeholder: result.placeholder
};
}
// To cancel the rename vscode API expects a rejected promise.
return Promise.reject(new Error(`The element can't be renamed.`));
}, (error: ResponseError<void>) => {
if (typeof error.message === 'string') {
throw new Error(error.message);
} else {
throw new Error(`The element can't be renamed.`);
}
});
};
const middleware = client.middleware;
return middleware.prepareRename
? middleware.prepareRename(document, position, token, prepareRename)
: prepareRename(document, position, token);
}
: undefined
};
return [this.registerProvider(selector, provider), provider];
}
private registerProvider(selector: RenameOptions & DocumentSelector, provider: RenameProvider): Disposable {
return Languages.registerRenameProvider(this._client.protocol2CodeConverter.asDocumentSelector(selector), provider);
}
private isDefaultBehavior(value: any): value is DefaultBehavior {
const candidate: DefaultBehavior = value;
return candidate && Is.boolean(candidate.defaultBehavior);
}
}