Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 76f9f63

Browse filesBrowse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
feat: configure sketchbook location without restart
Closes #1764 Closes #796 Closes #569 Closes #655 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 3f05396 commit 76f9f63
Copy full SHA for 76f9f63
Expand file treeCollapse file tree

28 files changed

+651
-262
lines changed

‎arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
343343
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
344344
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
345345
import { DebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
346+
import { ConfigServiceClient } from './config/config-service-client';
346347

347348
export default new ContainerModule((bind, unbind, isBound, rebind) => {
348349
// Commands and toolbar items
@@ -404,6 +405,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
404405
)
405406
)
406407
.inSingletonScope();
408+
bind(ConfigServiceClient).toSelf().inSingletonScope();
409+
bind(FrontendApplicationContribution).toService(ConfigServiceClient);
407410

408411
// Boards service
409412
bind(BoardsService)
+106Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
2+
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
3+
import { DisposableCollection } from '@theia/core/lib/common/disposable';
4+
import { Emitter, Event } from '@theia/core/lib/common/event';
5+
import { MessageService } from '@theia/core/lib/common/message-service';
6+
import { deepClone } from '@theia/core/lib/common/objects';
7+
import URI from '@theia/core/lib/common/uri';
8+
import {
9+
inject,
10+
injectable,
11+
postConstruct,
12+
} from '@theia/core/shared/inversify';
13+
import { ConfigService, ConfigState } from '../../common/protocol';
14+
import { NotificationCenter } from '../notification-center';
15+
16+
@injectable()
17+
export class ConfigServiceClient implements FrontendApplicationContribution {
18+
@inject(ConfigService)
19+
private readonly delegate: ConfigService;
20+
@inject(NotificationCenter)
21+
private readonly notificationCenter: NotificationCenter;
22+
@inject(FrontendApplicationStateService)
23+
private readonly appStateService: FrontendApplicationStateService;
24+
@inject(MessageService)
25+
private readonly messageService: MessageService;
26+
27+
private readonly didChangeSketchDirUriEmitter = new Emitter<
28+
URI | undefined
29+
>();
30+
private readonly didChangeDataDirUriEmitter = new Emitter<URI | undefined>();
31+
private readonly toDispose = new DisposableCollection(
32+
this.didChangeSketchDirUriEmitter,
33+
this.didChangeDataDirUriEmitter
34+
);
35+
36+
private config: ConfigState | undefined;
37+
38+
@postConstruct()
39+
protected init(): void {
40+
this.appStateService.reachedState('ready').then(async () => {
41+
const config = await this.fetchConfig();
42+
this.use(config);
43+
});
44+
}
45+
46+
onStart(): void {
47+
this.notificationCenter.onConfigDidChange((config) => this.use(config));
48+
}
49+
50+
onStop(): void {
51+
this.toDispose.dispose();
52+
}
53+
54+
get onDidChangeSketchDirUri(): Event<URI | undefined> {
55+
return this.didChangeSketchDirUriEmitter.event;
56+
}
57+
58+
get onDidChangeDataDirUri(): Event<URI | undefined> {
59+
return this.didChangeDataDirUriEmitter.event;
60+
}
61+
62+
async fetchConfig(): Promise<ConfigState> {
63+
return this.delegate.getConfiguration();
64+
}
65+
66+
/**
67+
* CLI config related error messages if any.
68+
*/
69+
tryGetMessages(): string[] | undefined {
70+
return this.config?.messages;
71+
}
72+
73+
/**
74+
* `directories.user`
75+
*/
76+
tryGetSketchDirUri(): URI | undefined {
77+
return this.config?.config?.sketchDirUri
78+
? new URI(this.config?.config?.sketchDirUri)
79+
: undefined;
80+
}
81+
82+
/**
83+
* `directories.data`
84+
*/
85+
tryGetDataDirUri(): URI | undefined {
86+
return this.config?.config?.dataDirUri
87+
? new URI(this.config?.config?.dataDirUri)
88+
: undefined;
89+
}
90+
91+
private use(config: ConfigState): void {
92+
const oldConfig = deepClone(this.config);
93+
this.config = config;
94+
if (oldConfig?.config?.sketchDirUri !== this.config?.config?.sketchDirUri) {
95+
this.didChangeSketchDirUriEmitter.fire(this.tryGetSketchDirUri());
96+
}
97+
if (oldConfig?.config?.dataDirUri !== this.config?.config?.dataDirUri) {
98+
this.didChangeDataDirUriEmitter.fire(this.tryGetDataDirUri());
99+
}
100+
if (this.config.messages?.length) {
101+
const message = this.config.messages.join(' ');
102+
// toast the error later otherwise it might not show up in IDE2
103+
setTimeout(() => this.messageService.error(message), 1_000);
104+
}
105+
}
106+
}

‎arduino-ide-extension/src/browser/contributions/add-zip-library.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/add-zip-library.ts
-4Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
22
import * as remote from '@theia/core/electron-shared/@electron/remote';
33
import URI from '@theia/core/lib/common/uri';
44
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
5-
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
65
import { ArduinoMenus } from '../menu/arduino-menus';
76
import { LibraryService, ResponseServiceClient } from '../../common/protocol';
87
import { ExecuteWithProgress } from '../../common/protocol/progressible';
@@ -16,9 +15,6 @@ import { nls } from '@theia/core/lib/common';
1615

1716
@injectable()
1817
export class AddZipLibrary extends SketchContribution {
19-
@inject(EnvVariablesServer)
20-
private readonly envVariableServer: EnvVariablesServer;
21-
2218
@inject(ResponseServiceClient)
2319
private readonly responseService: ResponseServiceClient;
2420

‎arduino-ide-extension/src/browser/contributions/archive-sketch.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/archive-sketch.ts
+4-8Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { injectable } from '@theia/core/shared/inversify';
22
import * as remote from '@theia/core/electron-shared/@electron/remote';
33
import * as dateFormat from 'dateformat';
4-
import URI from '@theia/core/lib/common/uri';
54
import { ArduinoMenus } from '../menu/arduino-menus';
65
import {
76
SketchContribution,
@@ -29,20 +28,17 @@ export class ArchiveSketch extends SketchContribution {
2928
}
3029

3130
private async archiveSketch(): Promise<void> {
32-
const [sketch, config] = await Promise.all([
33-
this.sketchServiceClient.currentSketch(),
34-
this.configService.getConfiguration(),
35-
]);
31+
const sketch = await this.sketchServiceClient.currentSketch();
3632
if (!CurrentSketch.isValid(sketch)) {
3733
return;
3834
}
3935
const archiveBasename = `${sketch.name}-${dateFormat(
4036
new Date(),
4137
'yymmdd'
4238
)}a.zip`;
43-
const defaultPath = await this.fileService.fsPath(
44-
new URI(config.sketchDirUri).resolve(archiveBasename)
45-
);
39+
const defaultContainerUri = await this.defaultUri();
40+
const defaultUri = defaultContainerUri.resolve(archiveBasename);
41+
const defaultPath = await this.fileService.fsPath(defaultUri);
4642
const { filePath, canceled } = await remote.dialog.showSaveDialog(
4743
remote.getCurrentWindow(),
4844
{

‎arduino-ide-extension/src/browser/contributions/board-selection.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/board-selection.ts
+1-4Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,7 @@ PID: ${PID}`;
155155
);
156156

157157
// Ports submenu
158-
const portsSubmenuPath = [
159-
...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP,
160-
'2_ports',
161-
];
158+
const portsSubmenuPath = ArduinoMenus.TOOLS__PORTS_SUBMENU;
162159
const portsSubmenuLabel = config.selectedPort?.address;
163160
this.menuModelRegistry.registerSubmenu(
164161
portsSubmenuPath,

‎arduino-ide-extension/src/browser/contributions/contribution.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/contribution.ts
+26-3Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { MaybePromise } from '@theia/core/lib/common/types';
1212
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
1313
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
1414
import { MessageService } from '@theia/core/lib/common/message-service';
15+
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
1516
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
1617

1718
import {
@@ -43,7 +44,6 @@ import {
4344
} from '../../common/protocol/sketches-service-client-impl';
4445
import {
4546
SketchesService,
46-
ConfigService,
4747
FileSystemExt,
4848
Sketch,
4949
CoreService,
@@ -62,6 +62,7 @@ import { NotificationManager } from '../theia/messages/notifications-manager';
6262
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
6363
import { WorkspaceService } from '../theia/workspace/workspace-service';
6464
import { MainMenuManager } from '../../common/main-menu-manager';
65+
import { ConfigServiceClient } from '../config/config-service-client';
6566

6667
export {
6768
Command,
@@ -142,8 +143,8 @@ export abstract class SketchContribution extends Contribution {
142143
@inject(FileSystemExt)
143144
protected readonly fileSystemExt: FileSystemExt;
144145

145-
@inject(ConfigService)
146-
protected readonly configService: ConfigService;
146+
@inject(ConfigServiceClient)
147+
protected readonly configService: ConfigServiceClient;
147148

148149
@inject(SketchesService)
149150
protected readonly sketchService: SketchesService;
@@ -160,6 +161,9 @@ export abstract class SketchContribution extends Contribution {
160161
@inject(OutputChannelManager)
161162
protected readonly outputChannelManager: OutputChannelManager;
162163

164+
@inject(EnvVariablesServer)
165+
protected readonly envVariableServer: EnvVariablesServer;
166+
163167
protected async sourceOverride(): Promise<Record<string, string>> {
164168
const override: Record<string, string> = {};
165169
const sketch = await this.sketchServiceClient.currentSketch();
@@ -173,6 +177,25 @@ export abstract class SketchContribution extends Contribution {
173177
}
174178
return override;
175179
}
180+
181+
/**
182+
* Defaults to `directories.user` if defined and not CLI config errors were detected.
183+
* Otherwise, the URI of the user home directory.
184+
*/
185+
protected async defaultUri(): Promise<URI> {
186+
const errors = this.configService.tryGetMessages();
187+
let defaultUri = this.configService.tryGetSketchDirUri();
188+
if (!defaultUri || errors?.length) {
189+
// Fall back to user home when the `directories.user` is not available or there are known CLI config errors
190+
defaultUri = new URI(await this.envVariableServer.getHomeDirUri());
191+
}
192+
return defaultUri;
193+
}
194+
195+
protected async defaultPath(): Promise<string> {
196+
const defaultUri = await this.defaultUri();
197+
return this.fileService.fsPath(defaultUri);
198+
}
176199
}
177200

178201
@injectable()

‎arduino-ide-extension/src/browser/contributions/examples.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/examples.ts
+16-4Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ import {
2929
CoreService,
3030
} from '../../common/protocol';
3131
import { nls } from '@theia/core/lib/common';
32+
import { unregisterSubmenu } from '../menu/arduino-menus';
3233

3334
@injectable()
3435
export abstract class Examples extends SketchContribution {
3536
@inject(CommandRegistry)
3637
private readonly commandRegistry: CommandRegistry;
3738

3839
@inject(MenuModelRegistry)
39-
private readonly menuRegistry: MenuModelRegistry;
40+
protected readonly menuRegistry: MenuModelRegistry;
4041

4142
@inject(ExamplesService)
4243
protected readonly examplesService: ExamplesService;
@@ -47,13 +48,22 @@ export abstract class Examples extends SketchContribution {
4748
@inject(BoardsServiceProvider)
4849
protected readonly boardsServiceClient: BoardsServiceProvider;
4950

51+
@inject(NotificationCenter)
52+
protected readonly notificationCenter: NotificationCenter;
53+
5054
protected readonly toDispose = new DisposableCollection();
5155

5256
protected override init(): void {
5357
super.init();
5458
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
5559
this.handleBoardChanged(selectedBoard)
5660
);
61+
this.notificationCenter.onDidReinitialize(() =>
62+
this.update({
63+
board: this.boardsServiceClient.boardsConfig.selectedBoard,
64+
// No force refresh. The core client was already refreshed.
65+
})
66+
);
5767
}
5868

5969
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
@@ -120,6 +130,11 @@ export abstract class Examples extends SketchContribution {
120130
const { label } = sketchContainerOrPlaceholder;
121131
submenuPath = [...menuPath, label];
122132
this.menuRegistry.registerSubmenu(submenuPath, label, subMenuOptions);
133+
this.toDispose.push(
134+
Disposable.create(() =>
135+
unregisterSubmenu(submenuPath, this.menuRegistry)
136+
)
137+
);
123138
sketches.push(...sketchContainerOrPlaceholder.sketches);
124139
children.push(...sketchContainerOrPlaceholder.children);
125140
} else {
@@ -239,9 +254,6 @@ export class BuiltInExamples extends Examples {
239254

240255
@injectable()
241256
export class LibraryExamples extends Examples {
242-
@inject(NotificationCenter)
243-
private readonly notificationCenter: NotificationCenter;
244-
245257
private readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
246258

247259
override onStart(): void {

‎arduino-ide-extension/src/browser/contributions/include-library.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/include-library.ts
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class IncludeLibrary extends SketchContribution {
5353
this.notificationCenter.onLibraryDidUninstall(() =>
5454
this.updateMenuActions()
5555
);
56+
this.notificationCenter.onDidReinitialize(() => this.updateMenuActions());
5657
}
5758

5859
override async onReady(): Promise<void> {

‎arduino-ide-extension/src/browser/contributions/open-sketch.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/open-sketch.ts
+1-4Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,7 @@ export class OpenSketch extends SketchContribution {
8282
}
8383

8484
private async selectSketch(): Promise<Sketch | undefined> {
85-
const config = await this.configService.getConfiguration();
86-
const defaultPath = await this.fileService.fsPath(
87-
new URI(config.sketchDirUri)
88-
);
85+
const defaultPath = await this.defaultPath();
8986
const { filePaths } = await remote.dialog.showOpenDialog(
9087
remote.getCurrentWindow(),
9188
{

‎arduino-ide-extension/src/browser/contributions/save-as-sketch.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/save-as-sketch.ts
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ export class SaveAsSketch extends SketchContribution {
5858
markAsRecentlyOpened,
5959
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
6060
): Promise<boolean> {
61-
const [sketch, configuration] = await Promise.all([
62-
this.sketchServiceClient.currentSketch(),
63-
this.configService.getConfiguration(),
64-
]);
61+
const sketch = await this.sketchServiceClient.currentSketch();
6562
if (!CurrentSketch.isValid(sketch)) {
6663
return false;
6764
}
@@ -72,7 +69,7 @@ export class SaveAsSketch extends SketchContribution {
7269
}
7370

7471
const sketchUri = new URI(sketch.uri);
75-
const sketchbookDirUri = new URI(configuration.sketchDirUri);
72+
const sketchbookDirUri = await this.defaultUri();
7673
// If the sketch is temp, IDE2 proposes the default sketchbook folder URI.
7774
// If the sketch is not temp, but not contained in the default sketchbook folder, IDE2 proposes the default location.
7875
// Otherwise, it proposes the parent folder of the current sketch.

‎arduino-ide-extension/src/browser/contributions/sketchbook.ts

Copy file name to clipboardExpand all lines: arduino-ide-extension/src/browser/contributions/sketchbook.ts
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { nls } from '@theia/core/lib/common/nls';
1111
export class Sketchbook extends Examples {
1212
override onStart(): void {
1313
this.sketchServiceClient.onSketchbookDidChange(() => this.update());
14+
this.configService.onDidChangeSketchDirUri(() => this.update());
1415
}
1516

1617
override async onReady(): Promise<void> {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.