-
-
Notifications
You must be signed in to change notification settings - Fork 431
/
Copy pathsave-as-sketch.ts
188 lines (176 loc) · 6.44 KB
/
save-as-sketch.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import { inject, injectable } from '@theia/core/shared/inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dateFormat from 'dateformat';
import { ArduinoMenus } from '../menu/arduino-menus';
import {
SketchContribution,
URI,
Command,
CommandRegistry,
MenuModelRegistry,
KeybindingRegistry,
} from './contribution';
import { nls } from '@theia/core/lib/common';
import { ApplicationShell, NavigatableWidget, Saveable } from '@theia/core/lib/browser';
import { EditorManager } from '@theia/editor/lib/browser';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable()
export class SaveAsSketch extends SketchContribution {
@inject(ApplicationShell)
protected readonly applicationShell: ApplicationShell;
@inject(EditorManager)
protected override readonly editorManager: EditorManager;
@inject(WindowService)
protected readonly windowService: WindowService;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, {
execute: (args) => this.saveAs(args),
});
}
override registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'),
order: '7',
});
}
override registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({
command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
keybinding: 'CtrlCmd+Shift+S',
});
}
/**
* Resolves `true` if the sketch was successfully saved as something.
*/
async saveAs(
{
execOnlyIfTemp,
openAfterMove,
wipeOriginal,
markAsRecentlyOpened,
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
): Promise<boolean> {
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return false;
}
const isTemp = await this.sketchService.isTemp(sketch);
if (!isTemp && !!execOnlyIfTemp) {
return false;
}
// If target does not exist, propose a `directories.user`/${sketch.name} path
// If target exists, propose `directories.user`/${sketch.name}_copy_${yyyymmddHHMMss}
const sketchDirUri = new URI(
(await this.configService.getConfiguration()).sketchDirUri
);
const exists = await this.fileService.exists(
sketchDirUri.resolve(sketch.name)
);
const defaultUri = sketchDirUri.resolve(
exists
? `${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`
: sketch.name
);
const defaultPath = await this.fileService.fsPath(defaultUri);
const { filePath, canceled } = await remote.dialog.showSaveDialog({
title: nls.localize(
'arduino/sketch/saveFolderAs',
'Save sketch folder as...'
),
defaultPath,
});
if (!filePath || canceled) {
return false;
}
const destinationUri = await this.fileSystemExt.getUri(filePath);
if (!destinationUri) {
return false;
}
const workspaceUri = await this.sketchService.copy(sketch, {
destinationUri,
});
if (workspaceUri) {
await this.saveOntoCopiedSketch(sketch.mainFileUri, sketch.uri, workspaceUri);
if (markAsRecentlyOpened) {
this.sketchService.markAsRecentlyOpened(workspaceUri);
}
}
if (workspaceUri && openAfterMove) {
this.windowService.setSafeToShutDown();
if (wipeOriginal || (openAfterMove && execOnlyIfTemp)) {
// This window will navigate away.
// Explicitly stop the contribution to dispose the file watcher before deleting the temp sketch.
// Otherwise, users might see irrelevant _Unable to watch for file changes in this large workspace._ notification.
// https://github.com/arduino/arduino-ide/issues/39.
this.sketchServiceClient.onStop();
// TODO: consider implementing the temp sketch deletion the following way:
// Open the other sketch with a `delete the temp sketch` startup-task.
this.sketchService.notifyDeleteSketch(sketch); // This is a notification and will execute on the backend.
}
this.workspaceService.open(new URI(workspaceUri), {
preserveWindow: true,
});
}
return !!workspaceUri;
}
private async saveOntoCopiedSketch(mainFileUri: string, sketchUri: string, newSketchUri: string): Promise<void> {
const widgets = this.applicationShell.widgets;
const snapshots = new Map<string, object>();
for (const widget of widgets) {
const saveable = Saveable.getDirty(widget);
const uri = NavigatableWidget.getUri(widget);
const uriString = uri?.toString();
let relativePath: string;
if (uri && uriString!.includes(sketchUri) && saveable && saveable.createSnapshot) {
// The main file will change its name during the copy process
// We need to store the new name in the map
if (mainFileUri === uriString) {
const lastPart = new URI(newSketchUri).path.base + uri.path.ext;
relativePath = '/' + lastPart;
} else {
relativePath = uri.toString().substring(sketchUri.length);
}
snapshots.set(relativePath, saveable.createSnapshot());
}
}
await Promise.all(Array.from(snapshots.entries()).map(async ([path, snapshot]) => {
const widgetUri = new URI(newSketchUri + path);
try {
const widget = await this.editorManager.getOrCreateByUri(widgetUri);
const saveable = Saveable.get(widget);
if (saveable && saveable.applySnapshot) {
saveable.applySnapshot(snapshot);
await saveable.save();
}
} catch (e) {
console.error(e);
}
}));
}
}
export namespace SaveAsSketch {
export namespace Commands {
export const SAVE_AS_SKETCH: Command = {
id: 'arduino-save-as-sketch',
};
}
export interface Options {
readonly execOnlyIfTemp?: boolean;
readonly openAfterMove?: boolean;
/**
* Ignored if `openAfterMove` is `false`.
*/
readonly wipeOriginal?: boolean;
readonly markAsRecentlyOpened?: boolean;
}
export namespace Options {
export const DEFAULT: Options = {
execOnlyIfTemp: false,
openAfterMove: true,
wipeOriginal: false,
markAsRecentlyOpened: false,
};
}
}