Skip to content

Commit ff606a5

Browse files
author
Akos Kitta
committed
s
Signed-off-by: Akos Kitta <[email protected]>
1 parent 6562c15 commit ff606a5

File tree

3 files changed

+133
-8
lines changed

3 files changed

+133
-8
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
141141
app.on('open-file', async (event, uri) => {
142142
event.preventDefault();
143143
if (uri.endsWith('.ino') && (await fs.pathExists(uri))) {
144-
this.openFilePromise.reject();
144+
this.openFilePromise.reject(new InterruptWorkspaceRestoreError());
145145
await this.openSketch(dirname(uri));
146146
}
147147
});
@@ -163,9 +163,12 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
163163
// 1. The `open-file` command has been received by the app, rejecting the promise
164164
// 2. A short timeout resolves the promise automatically, falling back to the usual app launch
165165
await this.openFilePromise.promise;
166-
} catch {
167-
// Application has received the `open-file` event and will skip the default application launch
168-
return;
166+
} catch (err) {
167+
if (err instanceof InterruptWorkspaceRestoreError) {
168+
// Application has received the `open-file` event and will skip the default application launch
169+
return;
170+
}
171+
throw err;
169172
}
170173

171174
if (!os.isOSX && (await this.launchFromArgs(params))) {
@@ -486,3 +489,12 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
486489
return this._firstWindowId;
487490
}
488491
}
492+
493+
class InterruptWorkspaceRestoreError extends Error {
494+
constructor() {
495+
super(
496+
"Received 'open-file' event. Interrupting the default launch workflow."
497+
);
498+
Object.setPrototypeOf(this, InterruptWorkspaceRestoreError.prototype);
499+
}
500+
}

arduino-ide-extension/src/node/arduino-ide-backend-module.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ import { CoreService, CoreServicePath } from '../common/protocol/core-service';
2525
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
2626
import { CoreClientProvider } from './core-client-provider';
2727
import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core';
28-
import { DefaultWorkspaceServer } from './theia/workspace/default-workspace-server';
29-
import { WorkspaceServer as TheiaWorkspaceServer } from '@theia/workspace/lib/common';
28+
import {
29+
DefaultWorkspaceServer,
30+
WorkspaceCliContribution,
31+
} from './theia/workspace/default-workspace-server';
32+
import { WorkspaceCliContribution as TheiaWorkspaceCliContribution } from '@theia/workspace/lib/node/default-workspace-server';
33+
import { WorkspaceServer as TheiaWorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
3034
import { SketchesServiceImpl } from './sketches-service-impl';
3135
import {
3236
SketchesService,
@@ -227,6 +231,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
227231

228232
bind(DefaultWorkspaceServer).toSelf().inSingletonScope();
229233
rebind(TheiaWorkspaceServer).toService(DefaultWorkspaceServer);
234+
bind(WorkspaceCliContribution).toSelf().inSingletonScope();
235+
rebind(TheiaWorkspaceCliContribution).toService(WorkspaceCliContribution);
230236

231237
bind(EnvVariablesServer).toSelf().inSingletonScope();
232238
rebind(TheiaEnvVariablesServer).toService(EnvVariablesServer);

arduino-ide-extension/src/node/theia/workspace/default-workspace-server.ts

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,116 @@
11
import { promises as fs, constants } from 'fs';
22
import { injectable, inject } from '@theia/core/shared/inversify';
3-
import { DefaultWorkspaceServer as TheiaDefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
3+
import {
4+
DefaultWorkspaceServer as TheiaDefaultWorkspaceServer,
5+
WorkspaceCliContribution as TheiaWorkspaceCliContribution,
6+
} from '@theia/workspace/lib/node/default-workspace-server';
47
import { SketchesService } from '../../../common/protocol';
5-
import { FileUri } from '@theia/core/lib/node';
8+
import { FileUri } from '@theia/core/lib/node/file-uri';
69
import { IsTempSketch } from '../../is-temp-sketch';
10+
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
11+
import { Deferred } from '@theia/core/lib/common/promise-util';
12+
import { MaybePromise } from '@theia/core/lib/common/types';
13+
import URI from '@theia/core/lib/common/uri';
14+
import * as yargs from '@theia/core/shared/yargs';
15+
import { THEIA_EXT } from '@theia/workspace/lib/common/utils';
16+
import * as fsExtra from 'fs-extra';
17+
import * as path from 'path';
18+
19+
@injectable()
20+
export class WorkspaceCliContribution extends TheiaWorkspaceCliContribution {
21+
@inject(EnvVariablesServer)
22+
private readonly envVariablesServer: EnvVariablesServer;
23+
24+
override workspaceRoot = new Deferred<string | undefined>();
25+
26+
override configure(conf: yargs.Argv): void {
27+
conf.usage('$0 [workspace-directories] [options]');
28+
conf.option('root-dir', {
29+
description: 'DEPRECATED: Sets the workspace directory.',
30+
});
31+
}
32+
33+
override async setArguments(args: yargs.Arguments): Promise<void> {
34+
const workspaceArguments = args._.slice(2).map((probablyAlreadyString) =>
35+
String(probablyAlreadyString)
36+
);
37+
if (workspaceArguments.length === 0 && args['root-dir']) {
38+
workspaceArguments.push(String(args['root-dir']));
39+
}
40+
if (workspaceArguments.length === 0) {
41+
this.workspaceRoot.resolve(undefined);
42+
} else if (workspaceArguments.length === 1) {
43+
this.workspaceRoot.resolve(
44+
this.normalizeWorkspaceArg(workspaceArguments[0])
45+
);
46+
} else {
47+
this.workspaceRoot.resolve(
48+
await this.buildWorkspaceForMultipleArguments(workspaceArguments)
49+
);
50+
}
51+
}
52+
53+
protected normalizeWorkspaceArg(raw: string): string {
54+
return path.resolve(raw).replace(/\/$/, '');
55+
}
56+
57+
protected async buildWorkspaceForMultipleArguments(
58+
workspaceArguments: string[]
59+
): Promise<string | undefined> {
60+
try {
61+
const dirs = await Promise.all(
62+
workspaceArguments.map(async (maybeDir) =>
63+
(await fs.stat(maybeDir).catch(() => undefined))?.isDirectory()
64+
)
65+
);
66+
const folders = workspaceArguments
67+
.filter((_, index) => dirs[index])
68+
.map((dir) => ({ path: this.normalizeWorkspaceArg(dir) }));
69+
if (folders.length < 2) {
70+
return folders[0]?.path;
71+
}
72+
const untitledWorkspaceUri = await this.getUntitledWorkspaceUri(
73+
new URI(await this.envVariablesServer.getConfigDirUri()),
74+
async (uri) => !(await fsExtra.pathExists(uri.path.fsPath()))
75+
);
76+
const untitledWorkspacePath = untitledWorkspaceUri.path.fsPath();
77+
78+
await fsExtra.ensureDir(path.dirname(untitledWorkspacePath));
79+
await fs.writeFile(
80+
untitledWorkspacePath,
81+
JSON.stringify({ folders }, undefined, 4)
82+
);
83+
return untitledWorkspacePath;
84+
} catch {
85+
return undefined;
86+
}
87+
}
88+
89+
async getUntitledWorkspaceUri(
90+
configDirUri: URI,
91+
isAcceptable: (candidate: URI) => MaybePromise<boolean>,
92+
warnOnHits?: () => unknown
93+
): Promise<URI> {
94+
const parentDir = configDirUri.resolve('workspaces');
95+
let uri;
96+
let attempts = 0;
97+
do {
98+
attempts++;
99+
uri = parentDir.resolve(
100+
`Untitled-${Math.round(Math.random() * 1000)}.${THEIA_EXT}`
101+
);
102+
if (attempts === 10) {
103+
warnOnHits?.();
104+
}
105+
if (attempts === 50) {
106+
throw new Error(
107+
'Workspace Service: too many attempts to find unused filename.'
108+
);
109+
}
110+
} while (!(await isAcceptable(uri)));
111+
return uri;
112+
}
113+
}
7114

8115
@injectable()
9116
export class DefaultWorkspaceServer extends TheiaDefaultWorkspaceServer {

0 commit comments

Comments
 (0)