@codingame/monaco-vscode-bulk-edit-service-override 1.83.8 → 1.83.10-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bulkEdit.js +11 -0
- package/external/tslib/tslib.es6.js +11 -0
- package/index.js +1 -1
- package/override/vs/platform/dialogs/common/dialogs.js +8 -0
- package/package.json +2 -2
- package/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.js +360 -0
- package/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.js +364 -0
- package/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.js +267 -0
package/bulkEdit.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SyncDescriptor } from 'monaco-editor/esm/vs/platform/instantiation/common/descriptors.js';
|
|
2
|
+
import { IBulkEditService } from 'monaco-editor/esm/vs/editor/browser/services/bulkEditService.js';
|
|
3
|
+
import { BulkEditService } from './vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.js';
|
|
4
|
+
|
|
5
|
+
function getServiceOverride() {
|
|
6
|
+
return {
|
|
7
|
+
[( IBulkEditService.toString())]: new SyncDescriptor(BulkEditService, [], true)
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { getServiceOverride as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
function __decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function __param(paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { __decorate, __param };
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from '
|
|
1
|
+
export { default } from './bulkEdit.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var ConfirmResult;
|
|
2
|
+
( (function(ConfirmResult) {
|
|
3
|
+
ConfirmResult[ConfirmResult["SAVE"] = 0] = "SAVE";
|
|
4
|
+
ConfirmResult[ConfirmResult["DONT_SAVE"] = 1] = "DONT_SAVE";
|
|
5
|
+
ConfirmResult[ConfirmResult["CANCEL"] = 2] = "CANCEL";
|
|
6
|
+
})(ConfirmResult || (ConfirmResult = {})));
|
|
7
|
+
|
|
8
|
+
export { ConfirmResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codingame/monaco-vscode-bulk-edit-service-override",
|
|
3
|
-
"version": "1.83.
|
|
3
|
+
"version": "1.83.10-next.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodinGame",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"module": "index.js",
|
|
19
19
|
"types": "index.d.ts",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"vscode": "npm:@codingame/monaco-vscode-api@1.83.
|
|
21
|
+
"vscode": "npm:@codingame/monaco-vscode-api@1.83.10-next.0",
|
|
22
22
|
"monaco-editor": "0.44.0"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
|
|
2
|
+
import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation.js';
|
|
3
|
+
import { toDisposable } from 'monaco-editor/esm/vs/base/common/lifecycle.js';
|
|
4
|
+
import { LinkedList } from 'monaco-editor/esm/vs/base/common/linkedList.js';
|
|
5
|
+
import { ResourceMap, ResourceSet } from 'monaco-editor/esm/vs/base/common/map.js';
|
|
6
|
+
import { isCodeEditor, isDiffEditor } from 'monaco-editor/esm/vs/editor/browser/editorBrowser.js';
|
|
7
|
+
import { ResourceTextEdit, ResourceFileEdit } from 'monaco-editor/esm/vs/editor/browser/services/bulkEditService.js';
|
|
8
|
+
import { localizeWithPath } from 'monaco-editor/esm/vs/nls.js';
|
|
9
|
+
import { IConfigurationService } from 'monaco-editor/esm/vs/platform/configuration/common/configuration.js';
|
|
10
|
+
import { Extensions } from 'monaco-editor/esm/vs/platform/configuration/common/configurationRegistry.js';
|
|
11
|
+
import '../../../../../../../override/vs/platform/dialogs/common/dialogs.js';
|
|
12
|
+
import { IInstantiationService } from 'monaco-editor/esm/vs/platform/instantiation/common/instantiation.js';
|
|
13
|
+
import { ILogService } from 'monaco-editor/esm/vs/platform/log/common/log.js';
|
|
14
|
+
import { Progress } from 'monaco-editor/esm/vs/platform/progress/common/progress.js';
|
|
15
|
+
import { Registry } from 'monaco-editor/esm/vs/platform/registry/common/platform.js';
|
|
16
|
+
import { UndoRedoGroup } from 'monaco-editor/esm/vs/platform/undoRedo/common/undoRedo.js';
|
|
17
|
+
import { ResourceNotebookCellEdit, BulkCellEdits } from 'vscode/vscode/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits';
|
|
18
|
+
import { BulkFileEdits } from './bulkFileEdits.js';
|
|
19
|
+
import { BulkTextEdits } from './bulkTextEdits.js';
|
|
20
|
+
import { IEditorService } from 'vscode/vscode/vs/workbench/services/editor/common/editorService';
|
|
21
|
+
import { ILifecycleService } from 'vscode/vscode/vs/workbench/services/lifecycle/common/lifecycle';
|
|
22
|
+
import { IWorkingCopyService } from 'vscode/vscode/vs/workbench/services/workingCopy/common/workingCopyService';
|
|
23
|
+
import { IDialogService } from 'vscode/vscode/vs/platform/dialogs/common/dialogs';
|
|
24
|
+
|
|
25
|
+
function liftEdits(edits) {
|
|
26
|
+
return ( edits.map(edit => {
|
|
27
|
+
if (ResourceTextEdit.is(edit)) {
|
|
28
|
+
return ResourceTextEdit.lift(edit);
|
|
29
|
+
}
|
|
30
|
+
if (ResourceFileEdit.is(edit)) {
|
|
31
|
+
return ResourceFileEdit.lift(edit);
|
|
32
|
+
}
|
|
33
|
+
if (ResourceNotebookCellEdit.is(edit)) {
|
|
34
|
+
return ResourceNotebookCellEdit.lift(edit);
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Unsupported edit');
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
let BulkEdit = class BulkEdit {
|
|
40
|
+
constructor(_label, _code, _editor, _progress, _token, _edits, _undoRedoGroup, _undoRedoSource, _confirmBeforeUndo, _instaService, _logService) {
|
|
41
|
+
this._label = _label;
|
|
42
|
+
this._code = _code;
|
|
43
|
+
this._editor = _editor;
|
|
44
|
+
this._progress = _progress;
|
|
45
|
+
this._token = _token;
|
|
46
|
+
this._edits = _edits;
|
|
47
|
+
this._undoRedoGroup = _undoRedoGroup;
|
|
48
|
+
this._undoRedoSource = _undoRedoSource;
|
|
49
|
+
this._confirmBeforeUndo = _confirmBeforeUndo;
|
|
50
|
+
this._instaService = _instaService;
|
|
51
|
+
this._logService = _logService;
|
|
52
|
+
}
|
|
53
|
+
ariaMessage() {
|
|
54
|
+
const otherResources = ( new ResourceMap());
|
|
55
|
+
const textEditResources = ( new ResourceMap());
|
|
56
|
+
let textEditCount = 0;
|
|
57
|
+
for (const edit of this._edits) {
|
|
58
|
+
if (edit instanceof ResourceTextEdit) {
|
|
59
|
+
textEditCount += 1;
|
|
60
|
+
textEditResources.set(edit.resource, true);
|
|
61
|
+
}
|
|
62
|
+
else if (edit instanceof ResourceFileEdit) {
|
|
63
|
+
otherResources.set(edit.oldResource ?? edit.newResource, true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (this._edits.length === 0) {
|
|
67
|
+
return ( localizeWithPath(
|
|
68
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
69
|
+
'summary.0',
|
|
70
|
+
"Made no edits"
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
else if (otherResources.size === 0) {
|
|
74
|
+
if (textEditCount > 1 && textEditResources.size > 1) {
|
|
75
|
+
return ( localizeWithPath(
|
|
76
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
77
|
+
'summary.nm',
|
|
78
|
+
"Made {0} text edits in {1} files",
|
|
79
|
+
textEditCount,
|
|
80
|
+
textEditResources.size
|
|
81
|
+
));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
return ( localizeWithPath(
|
|
85
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
86
|
+
'summary.n0',
|
|
87
|
+
"Made {0} text edits in one file",
|
|
88
|
+
textEditCount
|
|
89
|
+
));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return ( localizeWithPath(
|
|
94
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
95
|
+
'summary.textFiles',
|
|
96
|
+
"Made {0} text edits in {1} files, also created or deleted {2} files",
|
|
97
|
+
textEditCount,
|
|
98
|
+
textEditResources.size,
|
|
99
|
+
otherResources.size
|
|
100
|
+
));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async perform() {
|
|
104
|
+
if (this._edits.length === 0) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const ranges = [1];
|
|
108
|
+
for (let i = 1; i < this._edits.length; i++) {
|
|
109
|
+
if (Object.getPrototypeOf(this._edits[i - 1]) === Object.getPrototypeOf(this._edits[i])) {
|
|
110
|
+
ranges[ranges.length - 1]++;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
ranges.push(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const increment = this._edits.length > 1 ? 0 : undefined;
|
|
117
|
+
this._progress.report({ increment, total: 100 });
|
|
118
|
+
const progress = { report: _ => this._progress.report({ increment: 100 / this._edits.length }) };
|
|
119
|
+
const resources = [];
|
|
120
|
+
let index = 0;
|
|
121
|
+
for (const range of ranges) {
|
|
122
|
+
if (this._token.isCancellationRequested) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
const group = this._edits.slice(index, index + range);
|
|
126
|
+
if (group[0] instanceof ResourceFileEdit) {
|
|
127
|
+
resources.push(await this._performFileEdits(group, this._undoRedoGroup, this._undoRedoSource, this._confirmBeforeUndo, progress));
|
|
128
|
+
}
|
|
129
|
+
else if (group[0] instanceof ResourceTextEdit) {
|
|
130
|
+
resources.push(await this._performTextEdits(group, this._undoRedoGroup, this._undoRedoSource, progress));
|
|
131
|
+
}
|
|
132
|
+
else if (group[0] instanceof ResourceNotebookCellEdit) {
|
|
133
|
+
resources.push(await this._performCellEdits(group, this._undoRedoGroup, this._undoRedoSource, progress));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log('UNKNOWN EDIT');
|
|
137
|
+
}
|
|
138
|
+
index = index + range;
|
|
139
|
+
}
|
|
140
|
+
return resources.flat();
|
|
141
|
+
}
|
|
142
|
+
async _performFileEdits(edits, undoRedoGroup, undoRedoSource, confirmBeforeUndo, progress) {
|
|
143
|
+
this._logService.debug('_performFileEdits', JSON.stringify(edits));
|
|
144
|
+
const model = this._instaService.createInstance(BulkFileEdits, this._label || ( localizeWithPath(
|
|
145
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
146
|
+
'workspaceEdit',
|
|
147
|
+
"Workspace Edit"
|
|
148
|
+
)), this._code || 'undoredo.workspaceEdit', undoRedoGroup, undoRedoSource, confirmBeforeUndo, progress, this._token, edits);
|
|
149
|
+
return await model.apply();
|
|
150
|
+
}
|
|
151
|
+
async _performTextEdits(edits, undoRedoGroup, undoRedoSource, progress) {
|
|
152
|
+
this._logService.debug('_performTextEdits', JSON.stringify(edits));
|
|
153
|
+
const model = this._instaService.createInstance(BulkTextEdits, this._label || ( localizeWithPath(
|
|
154
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
155
|
+
'workspaceEdit',
|
|
156
|
+
"Workspace Edit"
|
|
157
|
+
)), this._code || 'undoredo.workspaceEdit', this._editor, undoRedoGroup, undoRedoSource, progress, this._token, edits);
|
|
158
|
+
return await model.apply();
|
|
159
|
+
}
|
|
160
|
+
async _performCellEdits(edits, undoRedoGroup, undoRedoSource, progress) {
|
|
161
|
+
this._logService.debug('_performCellEdits', JSON.stringify(edits));
|
|
162
|
+
const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, undoRedoSource, progress, this._token, edits);
|
|
163
|
+
return await model.apply();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
BulkEdit = ( __decorate([
|
|
167
|
+
( __param(9, IInstantiationService)),
|
|
168
|
+
( __param(10, ILogService))
|
|
169
|
+
], BulkEdit));
|
|
170
|
+
let BulkEditService = class BulkEditService {
|
|
171
|
+
constructor(_instaService, _logService, _editorService, _lifecycleService, _dialogService, _workingCopyService, _configService) {
|
|
172
|
+
this._instaService = _instaService;
|
|
173
|
+
this._logService = _logService;
|
|
174
|
+
this._editorService = _editorService;
|
|
175
|
+
this._lifecycleService = _lifecycleService;
|
|
176
|
+
this._dialogService = _dialogService;
|
|
177
|
+
this._workingCopyService = _workingCopyService;
|
|
178
|
+
this._configService = _configService;
|
|
179
|
+
this._activeUndoRedoGroups = ( new LinkedList());
|
|
180
|
+
}
|
|
181
|
+
setPreviewHandler(handler) {
|
|
182
|
+
this._previewHandler = handler;
|
|
183
|
+
return toDisposable(() => {
|
|
184
|
+
if (this._previewHandler === handler) {
|
|
185
|
+
this._previewHandler = undefined;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
hasPreviewHandler() {
|
|
190
|
+
return Boolean(this._previewHandler);
|
|
191
|
+
}
|
|
192
|
+
async apply(editsIn, options) {
|
|
193
|
+
let edits = liftEdits(Array.isArray(editsIn) ? editsIn : editsIn.edits);
|
|
194
|
+
if (edits.length === 0) {
|
|
195
|
+
return { ariaSummary: ( localizeWithPath(
|
|
196
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
197
|
+
'nothing',
|
|
198
|
+
"Made no edits"
|
|
199
|
+
)), isApplied: false };
|
|
200
|
+
}
|
|
201
|
+
if (this._previewHandler && (options?.showPreview || ( edits.some(value => value.metadata?.needsConfirmation)))) {
|
|
202
|
+
edits = await this._previewHandler(edits, options);
|
|
203
|
+
}
|
|
204
|
+
let codeEditor = options?.editor;
|
|
205
|
+
if (!codeEditor) {
|
|
206
|
+
const candidate = this._editorService.activeTextEditorControl;
|
|
207
|
+
if (isCodeEditor(candidate)) {
|
|
208
|
+
codeEditor = candidate;
|
|
209
|
+
}
|
|
210
|
+
else if (isDiffEditor(candidate)) {
|
|
211
|
+
codeEditor = candidate.getModifiedEditor();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (codeEditor && codeEditor.getOption(90 )) {
|
|
215
|
+
codeEditor = undefined;
|
|
216
|
+
}
|
|
217
|
+
let undoRedoGroup;
|
|
218
|
+
let undoRedoGroupRemove = () => { };
|
|
219
|
+
if (typeof options?.undoRedoGroupId === 'number') {
|
|
220
|
+
for (const candidate of this._activeUndoRedoGroups) {
|
|
221
|
+
if (candidate.id === options.undoRedoGroupId) {
|
|
222
|
+
undoRedoGroup = candidate;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (!undoRedoGroup) {
|
|
228
|
+
undoRedoGroup = ( new UndoRedoGroup());
|
|
229
|
+
undoRedoGroupRemove = this._activeUndoRedoGroups.push(undoRedoGroup);
|
|
230
|
+
}
|
|
231
|
+
const label = options?.quotableLabel || options?.label;
|
|
232
|
+
const bulkEdit = this._instaService.createInstance(BulkEdit, label, options?.code, codeEditor, options?.progress ?? Progress.None, options?.token ?? CancellationToken.None, edits, undoRedoGroup, options?.undoRedoSource, !!options?.confirmBeforeUndo);
|
|
233
|
+
let listener;
|
|
234
|
+
try {
|
|
235
|
+
listener = this._lifecycleService.onBeforeShutdown(e => e.veto(this._shouldVeto(label, e.reason), 'veto.blukEditService'));
|
|
236
|
+
const resources = await bulkEdit.perform();
|
|
237
|
+
if (options?.respectAutoSaveConfig && this._configService.getValue(autoSaveSetting) === true && resources.length > 1) {
|
|
238
|
+
await this._saveAll(resources);
|
|
239
|
+
}
|
|
240
|
+
return { ariaSummary: bulkEdit.ariaMessage(), isApplied: edits.length > 0 };
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
this._logService.error(err);
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
listener?.dispose();
|
|
248
|
+
undoRedoGroupRemove();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async _saveAll(resources) {
|
|
252
|
+
const set = ( new ResourceSet(resources));
|
|
253
|
+
const saves = ( this._workingCopyService.dirtyWorkingCopies.map(async (copy) => {
|
|
254
|
+
if (( set.has(copy.resource))) {
|
|
255
|
+
await copy.save();
|
|
256
|
+
}
|
|
257
|
+
}));
|
|
258
|
+
const result = await Promise.allSettled(saves);
|
|
259
|
+
for (const item of result) {
|
|
260
|
+
if (item.status === 'rejected') {
|
|
261
|
+
this._logService.warn(item.reason);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async _shouldVeto(label, reason) {
|
|
266
|
+
let message;
|
|
267
|
+
let primaryButton;
|
|
268
|
+
switch (reason) {
|
|
269
|
+
case 1 :
|
|
270
|
+
message = ( localizeWithPath(
|
|
271
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
272
|
+
'closeTheWindow.message',
|
|
273
|
+
"Are you sure you want to close the window?"
|
|
274
|
+
));
|
|
275
|
+
primaryButton = ( localizeWithPath(
|
|
276
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
277
|
+
{ key: 'closeTheWindow', comment: ['&& denotes a mnemonic'] },
|
|
278
|
+
"&&Close Window"
|
|
279
|
+
));
|
|
280
|
+
break;
|
|
281
|
+
case 4 :
|
|
282
|
+
message = ( localizeWithPath(
|
|
283
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
284
|
+
'changeWorkspace.message',
|
|
285
|
+
"Are you sure you want to change the workspace?"
|
|
286
|
+
));
|
|
287
|
+
primaryButton = ( localizeWithPath(
|
|
288
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
289
|
+
{ key: 'changeWorkspace', comment: ['&& denotes a mnemonic'] },
|
|
290
|
+
"Change &&Workspace"
|
|
291
|
+
));
|
|
292
|
+
break;
|
|
293
|
+
case 3 :
|
|
294
|
+
message = ( localizeWithPath(
|
|
295
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
296
|
+
'reloadTheWindow.message',
|
|
297
|
+
"Are you sure you want to reload the window?"
|
|
298
|
+
));
|
|
299
|
+
primaryButton = ( localizeWithPath(
|
|
300
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
301
|
+
{ key: 'reloadTheWindow', comment: ['&& denotes a mnemonic'] },
|
|
302
|
+
"&&Reload Window"
|
|
303
|
+
));
|
|
304
|
+
break;
|
|
305
|
+
default:
|
|
306
|
+
message = ( localizeWithPath(
|
|
307
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
308
|
+
'quit.message',
|
|
309
|
+
"Are you sure you want to quit?"
|
|
310
|
+
));
|
|
311
|
+
primaryButton = ( localizeWithPath(
|
|
312
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
313
|
+
{ key: 'quit', comment: ['&& denotes a mnemonic'] },
|
|
314
|
+
"&&Quit"
|
|
315
|
+
));
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
const result = await this._dialogService.confirm({
|
|
319
|
+
message,
|
|
320
|
+
detail: ( localizeWithPath(
|
|
321
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
322
|
+
'areYouSureQuiteBulkEdit.detail',
|
|
323
|
+
"'{0}' is in progress.",
|
|
324
|
+
label || ( localizeWithPath(
|
|
325
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
326
|
+
'fileOperation',
|
|
327
|
+
"File operation"
|
|
328
|
+
))
|
|
329
|
+
)),
|
|
330
|
+
primaryButton
|
|
331
|
+
});
|
|
332
|
+
return !result.confirmed;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
BulkEditService = ( __decorate([
|
|
336
|
+
( __param(0, IInstantiationService)),
|
|
337
|
+
( __param(1, ILogService)),
|
|
338
|
+
( __param(2, IEditorService)),
|
|
339
|
+
( __param(3, ILifecycleService)),
|
|
340
|
+
( __param(4, IDialogService)),
|
|
341
|
+
( __param(5, IWorkingCopyService)),
|
|
342
|
+
( __param(6, IConfigurationService))
|
|
343
|
+
], BulkEditService));
|
|
344
|
+
const autoSaveSetting = 'files.refactoring.autoSave';
|
|
345
|
+
( Registry.as(Extensions.Configuration)).registerConfiguration({
|
|
346
|
+
id: 'files',
|
|
347
|
+
properties: {
|
|
348
|
+
[autoSaveSetting]: {
|
|
349
|
+
description: ( localizeWithPath(
|
|
350
|
+
'vs/workbench/contrib/bulkEdit/browser/bulkEditService',
|
|
351
|
+
'refactoring.autoSave',
|
|
352
|
+
"Controls if files that were part of a refactoring are saved automatically"
|
|
353
|
+
)),
|
|
354
|
+
default: true,
|
|
355
|
+
type: 'boolean'
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
export { BulkEditService };
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
|
|
2
|
+
import { IFileService } from 'monaco-editor/esm/vs/platform/files/common/files.js';
|
|
3
|
+
import { IConfigurationService } from 'monaco-editor/esm/vs/platform/configuration/common/configuration.js';
|
|
4
|
+
import { IWorkingCopyFileService } from 'vscode/vscode/vs/workbench/services/workingCopy/common/workingCopyFileService';
|
|
5
|
+
import { IUndoRedoService } from 'monaco-editor/esm/vs/platform/undoRedo/common/undoRedo.js';
|
|
6
|
+
import { IInstantiationService } from 'monaco-editor/esm/vs/platform/instantiation/common/instantiation.js';
|
|
7
|
+
import { ILogService } from 'monaco-editor/esm/vs/platform/log/common/log.js';
|
|
8
|
+
import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation.js';
|
|
9
|
+
import { tail } from 'monaco-editor/esm/vs/base/common/arrays.js';
|
|
10
|
+
import { ITextFileService } from 'vscode/vscode/vs/workbench/services/textfile/common/textfiles';
|
|
11
|
+
import { Schemas } from 'monaco-editor/esm/vs/base/common/network.js';
|
|
12
|
+
|
|
13
|
+
var RenameOperation_1;
|
|
14
|
+
class Noop {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.uris = [];
|
|
17
|
+
}
|
|
18
|
+
async perform() { return this; }
|
|
19
|
+
toString() {
|
|
20
|
+
return '(noop)';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class RenameEdit {
|
|
24
|
+
constructor(newUri, oldUri, options) {
|
|
25
|
+
this.newUri = newUri;
|
|
26
|
+
this.oldUri = oldUri;
|
|
27
|
+
this.options = options;
|
|
28
|
+
this.type = 'rename';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let RenameOperation = RenameOperation_1 = class RenameOperation {
|
|
32
|
+
constructor(_edits, _undoRedoInfo, _workingCopyFileService, _fileService) {
|
|
33
|
+
this._edits = _edits;
|
|
34
|
+
this._undoRedoInfo = _undoRedoInfo;
|
|
35
|
+
this._workingCopyFileService = _workingCopyFileService;
|
|
36
|
+
this._fileService = _fileService;
|
|
37
|
+
}
|
|
38
|
+
get uris() {
|
|
39
|
+
return ( this._edits.map(edit => [edit.newUri, edit.oldUri])).flat();
|
|
40
|
+
}
|
|
41
|
+
async perform(token) {
|
|
42
|
+
const moves = [];
|
|
43
|
+
const undoes = [];
|
|
44
|
+
for (const edit of this._edits) {
|
|
45
|
+
const skip = edit.options.overwrite === undefined && edit.options.ignoreIfExists && (await this._fileService.exists(edit.newUri));
|
|
46
|
+
if (!skip) {
|
|
47
|
+
moves.push({
|
|
48
|
+
file: { source: edit.oldUri, target: edit.newUri },
|
|
49
|
+
overwrite: edit.options.overwrite
|
|
50
|
+
});
|
|
51
|
+
undoes.push(( new RenameEdit(edit.oldUri, edit.newUri, edit.options)));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (moves.length === 0) {
|
|
55
|
+
return ( new Noop());
|
|
56
|
+
}
|
|
57
|
+
await this._workingCopyFileService.move(moves, token, this._undoRedoInfo);
|
|
58
|
+
return ( new RenameOperation_1(
|
|
59
|
+
undoes,
|
|
60
|
+
{ isUndoing: true },
|
|
61
|
+
this._workingCopyFileService,
|
|
62
|
+
this._fileService
|
|
63
|
+
));
|
|
64
|
+
}
|
|
65
|
+
toString() {
|
|
66
|
+
return `(rename ${( this._edits.map(edit => `${edit.oldUri} to ${edit.newUri}`)).join(', ')})`;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
RenameOperation = RenameOperation_1 = ( __decorate([
|
|
70
|
+
( __param(2, IWorkingCopyFileService)),
|
|
71
|
+
( __param(3, IFileService))
|
|
72
|
+
], RenameOperation));
|
|
73
|
+
class CopyEdit {
|
|
74
|
+
constructor(newUri, oldUri, options) {
|
|
75
|
+
this.newUri = newUri;
|
|
76
|
+
this.oldUri = oldUri;
|
|
77
|
+
this.options = options;
|
|
78
|
+
this.type = 'copy';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let CopyOperation = class CopyOperation {
|
|
82
|
+
constructor(_edits, _undoRedoInfo, _workingCopyFileService, _fileService, _instaService) {
|
|
83
|
+
this._edits = _edits;
|
|
84
|
+
this._undoRedoInfo = _undoRedoInfo;
|
|
85
|
+
this._workingCopyFileService = _workingCopyFileService;
|
|
86
|
+
this._fileService = _fileService;
|
|
87
|
+
this._instaService = _instaService;
|
|
88
|
+
}
|
|
89
|
+
get uris() {
|
|
90
|
+
return ( this._edits.map(edit => [edit.newUri, edit.oldUri])).flat();
|
|
91
|
+
}
|
|
92
|
+
async perform(token) {
|
|
93
|
+
const copies = [];
|
|
94
|
+
for (const edit of this._edits) {
|
|
95
|
+
const skip = edit.options.overwrite === undefined && edit.options.ignoreIfExists && (await this._fileService.exists(edit.newUri));
|
|
96
|
+
if (!skip) {
|
|
97
|
+
copies.push({ file: { source: edit.oldUri, target: edit.newUri }, overwrite: edit.options.overwrite });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (copies.length === 0) {
|
|
101
|
+
return ( new Noop());
|
|
102
|
+
}
|
|
103
|
+
const stats = await this._workingCopyFileService.copy(copies, token, this._undoRedoInfo);
|
|
104
|
+
const undoes = [];
|
|
105
|
+
for (let i = 0; i < stats.length; i++) {
|
|
106
|
+
const stat = stats[i];
|
|
107
|
+
const edit = this._edits[i];
|
|
108
|
+
undoes.push(( new DeleteEdit(
|
|
109
|
+
stat.resource,
|
|
110
|
+
{ recursive: true, folder: this._edits[i].options.folder || stat.isDirectory, ...edit.options },
|
|
111
|
+
false
|
|
112
|
+
)));
|
|
113
|
+
}
|
|
114
|
+
return this._instaService.createInstance(DeleteOperation, undoes, { isUndoing: true });
|
|
115
|
+
}
|
|
116
|
+
toString() {
|
|
117
|
+
return `(copy ${( this._edits.map(edit => `${edit.oldUri} to ${edit.newUri}`)).join(', ')})`;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
CopyOperation = ( __decorate([
|
|
121
|
+
( __param(2, IWorkingCopyFileService)),
|
|
122
|
+
( __param(3, IFileService)),
|
|
123
|
+
( __param(4, IInstantiationService))
|
|
124
|
+
], CopyOperation));
|
|
125
|
+
class CreateEdit {
|
|
126
|
+
constructor(newUri, options, contents) {
|
|
127
|
+
this.newUri = newUri;
|
|
128
|
+
this.options = options;
|
|
129
|
+
this.contents = contents;
|
|
130
|
+
this.type = 'create';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
let CreateOperation = class CreateOperation {
|
|
134
|
+
constructor(_edits, _undoRedoInfo, _fileService, _workingCopyFileService, _instaService, _textFileService) {
|
|
135
|
+
this._edits = _edits;
|
|
136
|
+
this._undoRedoInfo = _undoRedoInfo;
|
|
137
|
+
this._fileService = _fileService;
|
|
138
|
+
this._workingCopyFileService = _workingCopyFileService;
|
|
139
|
+
this._instaService = _instaService;
|
|
140
|
+
this._textFileService = _textFileService;
|
|
141
|
+
}
|
|
142
|
+
get uris() {
|
|
143
|
+
return ( this._edits.map(edit => edit.newUri));
|
|
144
|
+
}
|
|
145
|
+
async perform(token) {
|
|
146
|
+
const folderCreates = [];
|
|
147
|
+
const fileCreates = [];
|
|
148
|
+
const undoes = [];
|
|
149
|
+
for (const edit of this._edits) {
|
|
150
|
+
if (edit.newUri.scheme === Schemas.untitled) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (edit.options.overwrite === undefined && edit.options.ignoreIfExists && (await this._fileService.exists(edit.newUri))) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (edit.options.folder) {
|
|
157
|
+
folderCreates.push({ resource: edit.newUri });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const encodedReadable = typeof edit.contents !== 'undefined' ? edit.contents : await this._textFileService.getEncodedReadable(edit.newUri);
|
|
161
|
+
fileCreates.push({ resource: edit.newUri, contents: encodedReadable, overwrite: edit.options.overwrite });
|
|
162
|
+
}
|
|
163
|
+
undoes.push(( new DeleteEdit(edit.newUri, edit.options, !edit.options.folder && !edit.contents)));
|
|
164
|
+
}
|
|
165
|
+
if (folderCreates.length === 0 && fileCreates.length === 0) {
|
|
166
|
+
return ( new Noop());
|
|
167
|
+
}
|
|
168
|
+
await this._workingCopyFileService.createFolder(folderCreates, token, this._undoRedoInfo);
|
|
169
|
+
await this._workingCopyFileService.create(fileCreates, token, this._undoRedoInfo);
|
|
170
|
+
return this._instaService.createInstance(DeleteOperation, undoes, { isUndoing: true });
|
|
171
|
+
}
|
|
172
|
+
toString() {
|
|
173
|
+
return `(create ${( this._edits.map(
|
|
174
|
+
edit => edit.options.folder ? `folder ${edit.newUri}` : `file ${edit.newUri} with ${edit.contents?.byteLength || 0} bytes`
|
|
175
|
+
)).join(', ')})`;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
CreateOperation = ( __decorate([
|
|
179
|
+
( __param(2, IFileService)),
|
|
180
|
+
( __param(3, IWorkingCopyFileService)),
|
|
181
|
+
( __param(4, IInstantiationService)),
|
|
182
|
+
( __param(5, ITextFileService))
|
|
183
|
+
], CreateOperation));
|
|
184
|
+
class DeleteEdit {
|
|
185
|
+
constructor(oldUri, options, undoesCreate) {
|
|
186
|
+
this.oldUri = oldUri;
|
|
187
|
+
this.options = options;
|
|
188
|
+
this.undoesCreate = undoesCreate;
|
|
189
|
+
this.type = 'delete';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
let DeleteOperation = class DeleteOperation {
|
|
193
|
+
constructor(_edits, _undoRedoInfo, _workingCopyFileService, _fileService, _configurationService, _instaService, _logService) {
|
|
194
|
+
this._edits = _edits;
|
|
195
|
+
this._undoRedoInfo = _undoRedoInfo;
|
|
196
|
+
this._workingCopyFileService = _workingCopyFileService;
|
|
197
|
+
this._fileService = _fileService;
|
|
198
|
+
this._configurationService = _configurationService;
|
|
199
|
+
this._instaService = _instaService;
|
|
200
|
+
this._logService = _logService;
|
|
201
|
+
}
|
|
202
|
+
get uris() {
|
|
203
|
+
return ( this._edits.map(edit => edit.oldUri));
|
|
204
|
+
}
|
|
205
|
+
async perform(token) {
|
|
206
|
+
const deletes = [];
|
|
207
|
+
const undoes = [];
|
|
208
|
+
for (const edit of this._edits) {
|
|
209
|
+
let fileStat;
|
|
210
|
+
try {
|
|
211
|
+
fileStat = await this._fileService.resolve(edit.oldUri, { resolveMetadata: true });
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
if (!edit.options.ignoreIfNotExists) {
|
|
215
|
+
throw new Error(`${edit.oldUri} does not exist and can not be deleted`);
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
deletes.push({
|
|
220
|
+
resource: edit.oldUri,
|
|
221
|
+
recursive: edit.options.recursive,
|
|
222
|
+
useTrash: !edit.options.skipTrashBin && this._fileService.hasCapability(edit.oldUri, 4096 ) && this._configurationService.getValue('files.enableTrash')
|
|
223
|
+
});
|
|
224
|
+
let fileContent;
|
|
225
|
+
if (!edit.undoesCreate && !edit.options.folder && !(typeof edit.options.maxSize === 'number' && fileStat.size > edit.options.maxSize)) {
|
|
226
|
+
try {
|
|
227
|
+
fileContent = await this._fileService.readFile(edit.oldUri);
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
this._logService.error(err);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (fileContent !== undefined) {
|
|
234
|
+
undoes.push(( new CreateEdit(edit.oldUri, edit.options, fileContent.value)));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (deletes.length === 0) {
|
|
238
|
+
return ( new Noop());
|
|
239
|
+
}
|
|
240
|
+
await this._workingCopyFileService.delete(deletes, token, this._undoRedoInfo);
|
|
241
|
+
if (undoes.length === 0) {
|
|
242
|
+
return ( new Noop());
|
|
243
|
+
}
|
|
244
|
+
return this._instaService.createInstance(CreateOperation, undoes, { isUndoing: true });
|
|
245
|
+
}
|
|
246
|
+
toString() {
|
|
247
|
+
return `(delete ${( this._edits.map(edit => edit.oldUri)).join(', ')})`;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
DeleteOperation = ( __decorate([
|
|
251
|
+
( __param(2, IWorkingCopyFileService)),
|
|
252
|
+
( __param(3, IFileService)),
|
|
253
|
+
( __param(4, IConfigurationService)),
|
|
254
|
+
( __param(5, IInstantiationService)),
|
|
255
|
+
( __param(6, ILogService))
|
|
256
|
+
], DeleteOperation));
|
|
257
|
+
class FileUndoRedoElement {
|
|
258
|
+
constructor(label, code, operations, confirmBeforeUndo) {
|
|
259
|
+
this.label = label;
|
|
260
|
+
this.code = code;
|
|
261
|
+
this.operations = operations;
|
|
262
|
+
this.confirmBeforeUndo = confirmBeforeUndo;
|
|
263
|
+
this.type = 1 ;
|
|
264
|
+
this.resources = ( operations.map(op => op.uris)).flat();
|
|
265
|
+
}
|
|
266
|
+
async undo() {
|
|
267
|
+
await this._reverse();
|
|
268
|
+
}
|
|
269
|
+
async redo() {
|
|
270
|
+
await this._reverse();
|
|
271
|
+
}
|
|
272
|
+
async _reverse() {
|
|
273
|
+
for (let i = 0; i < this.operations.length; i++) {
|
|
274
|
+
const op = this.operations[i];
|
|
275
|
+
const undo = await op.perform(CancellationToken.None);
|
|
276
|
+
this.operations[i] = undo;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
toString() {
|
|
280
|
+
return ( this.operations.map(op => String(op))).join(', ');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
let BulkFileEdits = class BulkFileEdits {
|
|
284
|
+
constructor(_label, _code, _undoRedoGroup, _undoRedoSource, _confirmBeforeUndo, _progress, _token, _edits, _instaService, _undoRedoService) {
|
|
285
|
+
this._label = _label;
|
|
286
|
+
this._code = _code;
|
|
287
|
+
this._undoRedoGroup = _undoRedoGroup;
|
|
288
|
+
this._undoRedoSource = _undoRedoSource;
|
|
289
|
+
this._confirmBeforeUndo = _confirmBeforeUndo;
|
|
290
|
+
this._progress = _progress;
|
|
291
|
+
this._token = _token;
|
|
292
|
+
this._edits = _edits;
|
|
293
|
+
this._instaService = _instaService;
|
|
294
|
+
this._undoRedoService = _undoRedoService;
|
|
295
|
+
}
|
|
296
|
+
async apply() {
|
|
297
|
+
const undoOperations = [];
|
|
298
|
+
const undoRedoInfo = { undoRedoGroupId: this._undoRedoGroup.id };
|
|
299
|
+
const edits = [];
|
|
300
|
+
for (const edit of this._edits) {
|
|
301
|
+
if (edit.newResource && edit.oldResource && !edit.options?.copy) {
|
|
302
|
+
edits.push(( new RenameEdit(edit.newResource, edit.oldResource, edit.options ?? {})));
|
|
303
|
+
}
|
|
304
|
+
else if (edit.newResource && edit.oldResource && edit.options?.copy) {
|
|
305
|
+
edits.push(( new CopyEdit(edit.newResource, edit.oldResource, edit.options ?? {})));
|
|
306
|
+
}
|
|
307
|
+
else if (!edit.newResource && edit.oldResource) {
|
|
308
|
+
edits.push(( new DeleteEdit(edit.oldResource, edit.options ?? {}, false)));
|
|
309
|
+
}
|
|
310
|
+
else if (edit.newResource && !edit.oldResource) {
|
|
311
|
+
edits.push(( new CreateEdit(edit.newResource, edit.options ?? {}, await edit.options.contents)));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (edits.length === 0) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
const groups = [];
|
|
318
|
+
groups[0] = [edits[0]];
|
|
319
|
+
for (let i = 1; i < edits.length; i++) {
|
|
320
|
+
const edit = edits[i];
|
|
321
|
+
const lastGroup = tail(groups);
|
|
322
|
+
if (lastGroup[0].type === edit.type) {
|
|
323
|
+
lastGroup.push(edit);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
groups.push([edit]);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const group of groups) {
|
|
330
|
+
if (this._token.isCancellationRequested) {
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
let op;
|
|
334
|
+
switch (group[0].type) {
|
|
335
|
+
case 'rename':
|
|
336
|
+
op = this._instaService.createInstance(RenameOperation, group, undoRedoInfo);
|
|
337
|
+
break;
|
|
338
|
+
case 'copy':
|
|
339
|
+
op = this._instaService.createInstance(CopyOperation, group, undoRedoInfo);
|
|
340
|
+
break;
|
|
341
|
+
case 'delete':
|
|
342
|
+
op = this._instaService.createInstance(DeleteOperation, group, undoRedoInfo);
|
|
343
|
+
break;
|
|
344
|
+
case 'create':
|
|
345
|
+
op = this._instaService.createInstance(CreateOperation, group, undoRedoInfo);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (op) {
|
|
349
|
+
const undoOp = await op.perform(this._token);
|
|
350
|
+
undoOperations.push(undoOp);
|
|
351
|
+
}
|
|
352
|
+
this._progress.report(undefined);
|
|
353
|
+
}
|
|
354
|
+
const undoRedoElement = ( new FileUndoRedoElement(this._label, this._code, undoOperations, this._confirmBeforeUndo));
|
|
355
|
+
this._undoRedoService.pushElement(undoRedoElement, this._undoRedoGroup, this._undoRedoSource);
|
|
356
|
+
return undoRedoElement.resources;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
BulkFileEdits = ( __decorate([
|
|
360
|
+
( __param(8, IInstantiationService)),
|
|
361
|
+
( __param(9, IUndoRedoService))
|
|
362
|
+
], BulkFileEdits));
|
|
363
|
+
|
|
364
|
+
export { BulkFileEdits };
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
|
|
2
|
+
import { dispose } from 'monaco-editor/esm/vs/base/common/lifecycle.js';
|
|
3
|
+
import { EditOperation } from 'monaco-editor/esm/vs/editor/common/core/editOperation.js';
|
|
4
|
+
import { Range } from 'monaco-editor/esm/vs/editor/common/core/range.js';
|
|
5
|
+
import { ITextModelService } from 'monaco-editor/esm/vs/editor/common/services/resolverService.js';
|
|
6
|
+
import { IEditorWorkerService } from 'monaco-editor/esm/vs/editor/common/services/editorWorker.js';
|
|
7
|
+
import { IUndoRedoService } from 'monaco-editor/esm/vs/platform/undoRedo/common/undoRedo.js';
|
|
8
|
+
import { SingleModelEditStackElement, MultiModelEditStackElement } from 'monaco-editor/esm/vs/editor/common/model/editStack.js';
|
|
9
|
+
import { ResourceMap } from 'monaco-editor/esm/vs/base/common/map.js';
|
|
10
|
+
import { IModelService } from 'monaco-editor/esm/vs/editor/common/services/model.js';
|
|
11
|
+
import { ResourceTextEdit } from 'monaco-editor/esm/vs/editor/browser/services/bulkEditService.js';
|
|
12
|
+
import { SnippetController2 } from 'monaco-editor/esm/vs/editor/contrib/snippet/browser/snippetController2.js';
|
|
13
|
+
import { SnippetParser } from 'monaco-editor/esm/vs/editor/contrib/snippet/browser/snippetParser.js';
|
|
14
|
+
|
|
15
|
+
class ModelEditTask {
|
|
16
|
+
constructor(_modelReference) {
|
|
17
|
+
this._modelReference = _modelReference;
|
|
18
|
+
this.model = this._modelReference.object.textEditorModel;
|
|
19
|
+
this._edits = [];
|
|
20
|
+
}
|
|
21
|
+
dispose() {
|
|
22
|
+
this._modelReference.dispose();
|
|
23
|
+
}
|
|
24
|
+
isNoOp() {
|
|
25
|
+
if (this._edits.length > 0) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (this._newEol !== undefined && this._newEol !== this.model.getEndOfLineSequence()) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
addEdit(resourceEdit) {
|
|
34
|
+
this._expectedModelVersionId = resourceEdit.versionId;
|
|
35
|
+
const { textEdit } = resourceEdit;
|
|
36
|
+
if (typeof textEdit.eol === 'number') {
|
|
37
|
+
this._newEol = textEdit.eol;
|
|
38
|
+
}
|
|
39
|
+
if (!textEdit.range && !textEdit.text) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (Range.isEmpty(textEdit.range) && !textEdit.text) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let range;
|
|
46
|
+
if (!textEdit.range) {
|
|
47
|
+
range = this.model.getFullModelRange();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
range = Range.lift(textEdit.range);
|
|
51
|
+
}
|
|
52
|
+
this._edits.push({ ...EditOperation.replaceMove(range, textEdit.text), insertAsSnippet: textEdit.insertAsSnippet });
|
|
53
|
+
}
|
|
54
|
+
validate() {
|
|
55
|
+
if (typeof this._expectedModelVersionId === 'undefined' || this.model.getVersionId() === this._expectedModelVersionId) {
|
|
56
|
+
return { canApply: true };
|
|
57
|
+
}
|
|
58
|
+
return { canApply: false, reason: this.model.uri };
|
|
59
|
+
}
|
|
60
|
+
getBeforeCursorState() {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
apply() {
|
|
64
|
+
if (this._edits.length > 0) {
|
|
65
|
+
this._edits = (
|
|
66
|
+
this._edits
|
|
67
|
+
.map(this._transformSnippetStringToInsertText, this))
|
|
68
|
+
.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
|
69
|
+
this.model.pushEditOperations(null, this._edits, () => null);
|
|
70
|
+
}
|
|
71
|
+
if (this._newEol !== undefined) {
|
|
72
|
+
this.model.pushEOL(this._newEol);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
_transformSnippetStringToInsertText(edit) {
|
|
76
|
+
if (!edit.insertAsSnippet) {
|
|
77
|
+
return edit;
|
|
78
|
+
}
|
|
79
|
+
if (!edit.text) {
|
|
80
|
+
return edit;
|
|
81
|
+
}
|
|
82
|
+
const text = SnippetParser.asInsertText(edit.text);
|
|
83
|
+
return { ...edit, insertAsSnippet: false, text };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
class EditorEditTask extends ModelEditTask {
|
|
87
|
+
constructor(modelReference, editor) {
|
|
88
|
+
super(modelReference);
|
|
89
|
+
this._editor = editor;
|
|
90
|
+
}
|
|
91
|
+
getBeforeCursorState() {
|
|
92
|
+
return this._canUseEditor() ? this._editor.getSelections() : null;
|
|
93
|
+
}
|
|
94
|
+
apply() {
|
|
95
|
+
if (!this._canUseEditor()) {
|
|
96
|
+
super.apply();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (this._edits.length > 0) {
|
|
100
|
+
const snippetCtrl = SnippetController2.get(this._editor);
|
|
101
|
+
if (snippetCtrl && ( this._edits.some(edit => edit.insertAsSnippet))) {
|
|
102
|
+
const snippetEdits = [];
|
|
103
|
+
for (const edit of this._edits) {
|
|
104
|
+
if (edit.range && edit.text !== null) {
|
|
105
|
+
snippetEdits.push({
|
|
106
|
+
range: Range.lift(edit.range),
|
|
107
|
+
template: edit.insertAsSnippet ? edit.text : SnippetParser.escape(edit.text)
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
snippetCtrl.apply(snippetEdits, { undoStopBefore: false, undoStopAfter: false });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this._edits = (
|
|
115
|
+
this._edits
|
|
116
|
+
.map(this._transformSnippetStringToInsertText, this))
|
|
117
|
+
.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
|
118
|
+
this._editor.executeEdits('', this._edits);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (this._newEol !== undefined) {
|
|
122
|
+
if (this._editor.hasModel()) {
|
|
123
|
+
this._editor.getModel().pushEOL(this._newEol);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
_canUseEditor() {
|
|
128
|
+
return this._editor?.getModel()?.uri.toString() === ( this.model.uri.toString());
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
let BulkTextEdits = class BulkTextEdits {
|
|
132
|
+
constructor(_label, _code, _editor, _undoRedoGroup, _undoRedoSource, _progress, _token, edits, _editorWorker, _modelService, _textModelResolverService, _undoRedoService) {
|
|
133
|
+
this._label = _label;
|
|
134
|
+
this._code = _code;
|
|
135
|
+
this._editor = _editor;
|
|
136
|
+
this._undoRedoGroup = _undoRedoGroup;
|
|
137
|
+
this._undoRedoSource = _undoRedoSource;
|
|
138
|
+
this._progress = _progress;
|
|
139
|
+
this._token = _token;
|
|
140
|
+
this._editorWorker = _editorWorker;
|
|
141
|
+
this._modelService = _modelService;
|
|
142
|
+
this._textModelResolverService = _textModelResolverService;
|
|
143
|
+
this._undoRedoService = _undoRedoService;
|
|
144
|
+
this._edits = ( new ResourceMap());
|
|
145
|
+
for (const edit of edits) {
|
|
146
|
+
let array = this._edits.get(edit.resource);
|
|
147
|
+
if (!array) {
|
|
148
|
+
array = [];
|
|
149
|
+
this._edits.set(edit.resource, array);
|
|
150
|
+
}
|
|
151
|
+
array.push(edit);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
_validateBeforePrepare() {
|
|
155
|
+
for (const array of ( this._edits.values())) {
|
|
156
|
+
for (const edit of array) {
|
|
157
|
+
if (typeof edit.versionId === 'number') {
|
|
158
|
+
const model = this._modelService.getModel(edit.resource);
|
|
159
|
+
if (model && model.getVersionId() !== edit.versionId) {
|
|
160
|
+
throw new Error(`${model.uri.toString()} has changed in the meantime`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async _createEditsTasks() {
|
|
167
|
+
const tasks = [];
|
|
168
|
+
const promises = [];
|
|
169
|
+
for (const [key, edits] of this._edits) {
|
|
170
|
+
const promise = this._textModelResolverService.createModelReference(key).then(async (ref) => {
|
|
171
|
+
let task;
|
|
172
|
+
let makeMinimal = false;
|
|
173
|
+
if (this._editor?.getModel()?.uri.toString() === ( ref.object.textEditorModel.uri.toString())) {
|
|
174
|
+
task = ( new EditorEditTask(ref, this._editor));
|
|
175
|
+
makeMinimal = true;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
task = ( new ModelEditTask(ref));
|
|
179
|
+
}
|
|
180
|
+
tasks.push(task);
|
|
181
|
+
if (!makeMinimal) {
|
|
182
|
+
edits.forEach(task.addEdit, task);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const makeGroupMoreMinimal = async (start, end) => {
|
|
186
|
+
const oldEdits = edits.slice(start, end);
|
|
187
|
+
const newEdits = await this._editorWorker.computeMoreMinimalEdits(ref.object.textEditorModel.uri, ( oldEdits.map(e => e.textEdit)), false);
|
|
188
|
+
if (!newEdits) {
|
|
189
|
+
oldEdits.forEach(task.addEdit, task);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
newEdits.forEach(edit => task.addEdit(( new ResourceTextEdit(ref.object.textEditorModel.uri, edit, undefined, undefined))));
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
let start = 0;
|
|
196
|
+
let i = 0;
|
|
197
|
+
for (; i < edits.length; i++) {
|
|
198
|
+
if (edits[i].textEdit.insertAsSnippet || edits[i].metadata) {
|
|
199
|
+
await makeGroupMoreMinimal(start, i);
|
|
200
|
+
task.addEdit(edits[i]);
|
|
201
|
+
start = i + 1;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
await makeGroupMoreMinimal(start, i);
|
|
205
|
+
});
|
|
206
|
+
promises.push(promise);
|
|
207
|
+
}
|
|
208
|
+
await Promise.all(promises);
|
|
209
|
+
return tasks;
|
|
210
|
+
}
|
|
211
|
+
_validateTasks(tasks) {
|
|
212
|
+
for (const task of tasks) {
|
|
213
|
+
const result = task.validate();
|
|
214
|
+
if (!result.canApply) {
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { canApply: true };
|
|
219
|
+
}
|
|
220
|
+
async apply() {
|
|
221
|
+
this._validateBeforePrepare();
|
|
222
|
+
const tasks = await this._createEditsTasks();
|
|
223
|
+
try {
|
|
224
|
+
if (this._token.isCancellationRequested) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
const resources = [];
|
|
228
|
+
const validation = this._validateTasks(tasks);
|
|
229
|
+
if (!validation.canApply) {
|
|
230
|
+
throw new Error(`${validation.reason.toString()} has changed in the meantime`);
|
|
231
|
+
}
|
|
232
|
+
if (tasks.length === 1) {
|
|
233
|
+
const task = tasks[0];
|
|
234
|
+
if (!task.isNoOp()) {
|
|
235
|
+
const singleModelEditStackElement = ( new SingleModelEditStackElement(this._label, this._code, task.model, task.getBeforeCursorState()));
|
|
236
|
+
this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
|
|
237
|
+
task.apply();
|
|
238
|
+
singleModelEditStackElement.close();
|
|
239
|
+
resources.push(task.model.uri);
|
|
240
|
+
}
|
|
241
|
+
this._progress.report(undefined);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const multiModelEditStackElement = ( new MultiModelEditStackElement(this._label, this._code, ( tasks.map(t => ( new SingleModelEditStackElement(this._label, this._code, t.model, t.getBeforeCursorState()))))));
|
|
245
|
+
this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
|
|
246
|
+
for (const task of tasks) {
|
|
247
|
+
task.apply();
|
|
248
|
+
this._progress.report(undefined);
|
|
249
|
+
resources.push(task.model.uri);
|
|
250
|
+
}
|
|
251
|
+
multiModelEditStackElement.close();
|
|
252
|
+
}
|
|
253
|
+
return resources;
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
dispose(tasks);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
BulkTextEdits = ( __decorate([
|
|
261
|
+
( __param(8, IEditorWorkerService)),
|
|
262
|
+
( __param(9, IModelService)),
|
|
263
|
+
( __param(10, ITextModelService)),
|
|
264
|
+
( __param(11, IUndoRedoService))
|
|
265
|
+
], BulkTextEdits));
|
|
266
|
+
|
|
267
|
+
export { BulkTextEdits };
|