/
rename.ts
151 lines (123 loc) · 4.73 KB
/
rename.ts
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import type * as ts from 'typescript/lib/tsserverlibrary';
import * as vscode from 'vscode-languageserver-protocol';
import * as shared from '@volar/shared';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { posix as path } from 'path';
import { renameInfoOptions } from './prepareRename';
import type { GetConfiguration } from '../createLanguageService';
import { URI } from 'vscode-uri';
import { getFormatCodeSettings } from '../configs/getFormatCodeSettings';
import { getUserPreferences } from '../configs/getUserPreferences';
export function register(
rootUri: URI,
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
getConfiguration: GetConfiguration,
) {
return async (uri: string, position: vscode.Position, newName: string): Promise<vscode.WorkspaceEdit | undefined> => {
const document = getTextDocument(uri);
if (!document) return;
const fileName = shared.getPathOfUri(document.uri);
const offset = document.offsetAt(position);
let renameInfo: ReturnType<typeof languageService.getRenameInfo> | undefined;
try { renameInfo = languageService.getRenameInfo(fileName, offset, renameInfoOptions); } catch { }
if (!renameInfo?.canRename) return;
if (renameInfo.fileToRename) {
const [formatOptions, preferences] = await Promise.all([
getFormatCodeSettings(getConfiguration, document.uri),
getUserPreferences(getConfiguration, document.uri, rootUri),
]);
return renameFile(renameInfo.fileToRename, newName, formatOptions, preferences);
}
const { providePrefixAndSuffixTextForRename } = await getUserPreferences(getConfiguration, document.uri, rootUri);
const entries = languageService.findRenameLocations(fileName, offset, false, false, providePrefixAndSuffixTextForRename);
if (!entries)
return;
const locations = locationsToWorkspaceEdit(rootUri, newName, entries, getTextDocument);
return locations;
};
function renameFile(
fileToRename: string,
newName: string,
formatOptions: ts.FormatCodeSettings,
preferences: ts.UserPreferences,
): vscode.WorkspaceEdit | undefined {
// Make sure we preserve file extension if none provided
if (!path.extname(newName)) {
newName += path.extname(fileToRename);
}
const dirname = path.dirname(fileToRename);
const newFilePath = path.join(dirname, newName);
const response = languageService.getEditsForFileRename(fileToRename, newFilePath, formatOptions, preferences);
const edits = fileTextChangesToWorkspaceEdit(rootUri, response, getTextDocument);
if (!edits.documentChanges) {
edits.documentChanges = [];
}
edits.documentChanges.push(vscode.RenameFile.create(
shared.getUriByPath(rootUri, fileToRename),
shared.getUriByPath(rootUri, newFilePath),
));
return edits;
}
}
export function fileTextChangesToWorkspaceEdit(rootUri: URI, changes: readonly ts.FileTextChanges[], getTextDocument: (uri: string) => TextDocument | undefined) {
const workspaceEdit: vscode.WorkspaceEdit = {};
for (const change of changes) {
if (!workspaceEdit.documentChanges) {
workspaceEdit.documentChanges = [];
}
const uri = shared.getUriByPath(rootUri, change.fileName);
let doc = getTextDocument(uri);
if (change.isNewFile) {
workspaceEdit.documentChanges.push(vscode.CreateFile.create(uri));
doc = TextDocument.create(uri, 'typescript', 0, '');
}
if (!doc)
continue;
const docEdit = vscode.TextDocumentEdit.create(
{
uri,
version: null, // fix https://github.com/johnsoncodehk/volar/issues/2025
},
[],
);
for (const textChange of change.textChanges) {
docEdit.edits.push({
newText: textChange.newText,
range: {
start: doc.positionAt(textChange.span.start),
end: doc.positionAt(textChange.span.start + textChange.span.length),
},
});
}
workspaceEdit.documentChanges.push(docEdit);
}
return workspaceEdit;
}
function locationsToWorkspaceEdit(rootUri: URI, newText: string, locations: readonly ts.RenameLocation[], getTextDocument: (uri: string) => TextDocument | undefined) {
const workspaceEdit: vscode.WorkspaceEdit = {};
for (const location of locations) {
if (!workspaceEdit.changes) {
workspaceEdit.changes = {};
}
const uri = shared.getUriByPath(rootUri, location.fileName);
const doc = getTextDocument(uri);
if (!doc) continue;
if (!workspaceEdit.changes[uri]) {
workspaceEdit.changes[uri] = [];
}
let _newText = newText;
if (location.prefixText)
_newText = location.prefixText + _newText;
if (location.suffixText)
_newText = _newText + location.suffixText;
workspaceEdit.changes[uri].push({
newText: _newText,
range: {
start: doc.positionAt(location.textSpan.start),
end: doc.positionAt(location.textSpan.start + location.textSpan.length),
},
});
}
return workspaceEdit;
}