Skip to content

Commit 69ac1f4

Browse files
authored
Open all closed workspaces on startup (#780)
1 parent a20899f commit 69ac1f4

File tree

2 files changed

+98
-8
lines changed

2 files changed

+98
-8
lines changed

arduino-ide-extension/src/browser/arduino-workspace-resolver.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { toUnix } from 'upath';
2-
import URI from '@theia/core/lib/common/uri';
1+
import { URI } from '@theia/core/shared/vscode-uri';
32
import { isWindows } from '@theia/core/lib/common/os';
43
import { notEmpty } from '@theia/core/lib/common/objects';
54
import { MaybePromise } from '@theia/core/lib/common/types';
@@ -61,12 +60,8 @@ export class ArduinoWorkspaceRootResolver {
6160
// - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L423
6261
protected hashToUri(hash: string | undefined): string | undefined {
6362
if (hash && hash.length > 1 && hash.startsWith('#')) {
64-
const path = hash.slice(1); // Trim the leading `#`.
65-
return new URI(
66-
toUnix(path.slice(isWindows && hash.startsWith('/') ? 1 : 0))
67-
)
68-
.withScheme('file')
69-
.toString();
63+
const path = decodeURI(hash.slice(1)).replace(/\\/g, '/'); // Trim the leading `#`, decode the URI and replace Windows separators
64+
return URI.file(path.slice(isWindows && hash.startsWith('/') ? 1 : 0)).toString();
7065
}
7166
return undefined;
7267
}

arduino-ide-extension/src/electron-main/theia/electron-main-application.ts

+95
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,39 @@ import {
88
import { fork } from 'child_process';
99
import { AddressInfo } from 'net';
1010
import { join } from 'path';
11+
import * as fs from 'fs-extra';
1112
import { initSplashScreen } from '../splash/splash-screen';
1213
import { MaybePromise } from '@theia/core/lib/common/types';
1314
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
1415
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
1516
import {
1617
ElectronMainApplication as TheiaElectronMainApplication,
18+
ElectronMainExecutionParams,
1719
TheiaBrowserWindowOptions,
1820
} from '@theia/core/lib/electron-main/electron-main-application';
1921
import { SplashServiceImpl } from '../splash/splash-service-impl';
2022
import { ipcMain } from '@theia/core/shared/electron';
23+
import { URI } from '@theia/core/shared/vscode-uri';
2124

2225
app.commandLine.appendSwitch('disable-http-cache');
2326

27+
interface WorkspaceOptions {
28+
file: string
29+
x: number
30+
y: number
31+
width: number
32+
height: number
33+
isMaximized: boolean
34+
isFullScreen: boolean
35+
time: number
36+
}
37+
38+
const WORKSPACES = 'workspaces';
39+
2440
@injectable()
2541
export class ElectronMainApplication extends TheiaElectronMainApplication {
2642
protected _windows: BrowserWindow[] = [];
43+
protected startup = false;
2744

2845
@inject(SplashServiceImpl)
2946
protected readonly splashService: SplashServiceImpl;
@@ -36,6 +53,45 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
3653
return super.start(config);
3754
}
3855

56+
protected async launch(params: ElectronMainExecutionParams): Promise<void> {
57+
this.startup = true;
58+
const workspaces: WorkspaceOptions[] | undefined = this.electronStore.get(WORKSPACES);
59+
let useDefault = true;
60+
if (workspaces && workspaces.length > 0) {
61+
for (const workspace of workspaces) {
62+
const file = workspace.file;
63+
if (typeof file === 'string' && await fs.pathExists(file)) {
64+
useDefault = false;
65+
await this.openSketch(workspace);
66+
}
67+
}
68+
}
69+
this.startup = false;
70+
if (useDefault) {
71+
super.launch(params);
72+
}
73+
}
74+
75+
protected async openSketch(workspace: WorkspaceOptions): Promise<BrowserWindow> {
76+
const options = await this.getLastWindowOptions();
77+
options.x = workspace.x;
78+
options.y = workspace.y;
79+
options.width = workspace.width;
80+
options.height = workspace.height;
81+
options.isMaximized = workspace.isMaximized;
82+
options.isFullScreen = workspace.isFullScreen;
83+
const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow(options)]);
84+
electronWindow.loadURL(uri.withFragment(encodeURI(workspace.file)).toString(true));
85+
return electronWindow;
86+
}
87+
88+
protected avoidOverlap(options: TheiaBrowserWindowOptions): TheiaBrowserWindowOptions {
89+
if (this.startup) {
90+
return options;
91+
}
92+
return super.avoidOverlap(options);
93+
}
94+
3995
protected getTitleBarStyle(): 'native' | 'custom' {
4096
return 'native';
4197
}
@@ -148,6 +204,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
148204
}
149205
}
150206
});
207+
this.attachClosedWorkspace(electronWindow);
151208
this.attachReadyToShow(electronWindow);
152209
this.attachSaveWindowState(electronWindow);
153210
this.attachGlobalShortcuts(electronWindow);
@@ -218,6 +275,44 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
218275
}
219276
}
220277

278+
protected closedWorkspaces: WorkspaceOptions[] = [];
279+
280+
protected attachClosedWorkspace(window: BrowserWindow): void {
281+
// Since the `before-quit` event is only fired when closing the *last* window
282+
// We need to keep track of recently closed windows/workspaces manually
283+
window.on('close', () => {
284+
const url = window.webContents.getURL();
285+
const workspace = URI.parse(url).fragment;
286+
if (workspace) {
287+
const workspaceUri = URI.file(workspace);
288+
const bounds = window.getNormalBounds();
289+
this.closedWorkspaces.push({
290+
...bounds,
291+
isMaximized: window.isMaximized(),
292+
isFullScreen: window.isFullScreen(),
293+
file: workspaceUri.fsPath,
294+
time: Date.now()
295+
})
296+
}
297+
});
298+
}
299+
300+
protected onWillQuit(event: Electron.Event): void {
301+
// Only add workspaces which were closed within the last second (1000 milliseconds)
302+
const threshold = Date.now() - 1000;
303+
const visited = new Set<string>();
304+
const workspaces = this.closedWorkspaces.filter(e => {
305+
if (e.time < threshold || visited.has(e.file)) {
306+
return false;
307+
}
308+
visited.add(e.file);
309+
return true;
310+
}).sort((a, b) => a.file.localeCompare(b.file));
311+
this.electronStore.set(WORKSPACES, workspaces);
312+
313+
super.onWillQuit(event);
314+
}
315+
221316
get windows(): BrowserWindow[] {
222317
return this._windows.slice();
223318
}

0 commit comments

Comments
 (0)