mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-12 03:39:27 +00:00
feat: configure sketchbook location without restart
Closes #1764 Closes #796 Closes #569 Closes #655 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
@@ -343,6 +343,7 @@ import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
|
||||
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
|
||||
import { DebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
||||
import { ConfigServiceClient } from './config/config-service-client';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// Commands and toolbar items
|
||||
@@ -404,6 +405,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
bind(ConfigServiceClient).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(ConfigServiceClient);
|
||||
|
||||
// Boards service
|
||||
bind(BoardsService)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { ConfigService, ConfigState } from '../../common/protocol';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
|
||||
@injectable()
|
||||
export class ConfigServiceClient implements FrontendApplicationContribution {
|
||||
@inject(ConfigService)
|
||||
private readonly delegate: ConfigService;
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
@inject(MessageService)
|
||||
private readonly messageService: MessageService;
|
||||
|
||||
private readonly didChangeSketchDirUriEmitter = new Emitter<
|
||||
URI | undefined
|
||||
>();
|
||||
private readonly didChangeDataDirUriEmitter = new Emitter<URI | undefined>();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.didChangeSketchDirUriEmitter,
|
||||
this.didChangeDataDirUriEmitter
|
||||
);
|
||||
|
||||
private config: ConfigState | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const config = await this.fetchConfig();
|
||||
this.use(config);
|
||||
});
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.notificationCenter.onConfigDidChange((config) => this.use(config));
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
get onDidChangeSketchDirUri(): Event<URI | undefined> {
|
||||
return this.didChangeSketchDirUriEmitter.event;
|
||||
}
|
||||
|
||||
get onDidChangeDataDirUri(): Event<URI | undefined> {
|
||||
return this.didChangeDataDirUriEmitter.event;
|
||||
}
|
||||
|
||||
async fetchConfig(): Promise<ConfigState> {
|
||||
return this.delegate.getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI config related error messages if any.
|
||||
*/
|
||||
tryGetMessages(): string[] | undefined {
|
||||
return this.config?.messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* `directories.user`
|
||||
*/
|
||||
tryGetSketchDirUri(): URI | undefined {
|
||||
return this.config?.config?.sketchDirUri
|
||||
? new URI(this.config?.config?.sketchDirUri)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* `directories.data`
|
||||
*/
|
||||
tryGetDataDirUri(): URI | undefined {
|
||||
return this.config?.config?.dataDirUri
|
||||
? new URI(this.config?.config?.dataDirUri)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private use(config: ConfigState): void {
|
||||
const oldConfig = deepClone(this.config);
|
||||
this.config = config;
|
||||
if (oldConfig?.config?.sketchDirUri !== this.config?.config?.sketchDirUri) {
|
||||
this.didChangeSketchDirUriEmitter.fire(this.tryGetSketchDirUri());
|
||||
}
|
||||
if (oldConfig?.config?.dataDirUri !== this.config?.config?.dataDirUri) {
|
||||
this.didChangeDataDirUriEmitter.fire(this.tryGetDataDirUri());
|
||||
}
|
||||
if (this.config.messages?.length) {
|
||||
const message = this.config.messages.join(' ');
|
||||
// toast the error later otherwise it might not show up in IDE2
|
||||
setTimeout(() => this.messageService.error(message), 1_000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { LibraryService, ResponseServiceClient } from '../../common/protocol';
|
||||
import { ExecuteWithProgress } from '../../common/protocol/progressible';
|
||||
@@ -16,9 +15,6 @@ import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class AddZipLibrary extends SketchContribution {
|
||||
@inject(EnvVariablesServer)
|
||||
private readonly envVariableServer: EnvVariablesServer;
|
||||
|
||||
@inject(ResponseServiceClient)
|
||||
private readonly responseService: ResponseServiceClient;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -29,10 +28,7 @@ export class ArchiveSketch extends SketchContribution {
|
||||
}
|
||||
|
||||
private async archiveSketch(): Promise<void> {
|
||||
const [sketch, config] = await Promise.all([
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
@@ -40,9 +36,9 @@ export class ArchiveSketch extends SketchContribution {
|
||||
new Date(),
|
||||
'yymmdd'
|
||||
)}a.zip`;
|
||||
const defaultPath = await this.fileService.fsPath(
|
||||
new URI(config.sketchDirUri).resolve(archiveBasename)
|
||||
);
|
||||
const defaultContainerUri = await this.defaultUri();
|
||||
const defaultUri = defaultContainerUri.resolve(archiveBasename);
|
||||
const defaultPath = await this.fileService.fsPath(defaultUri);
|
||||
const { filePath, canceled } = await remote.dialog.showSaveDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
|
||||
@@ -155,10 +155,7 @@ PID: ${PID}`;
|
||||
);
|
||||
|
||||
// Ports submenu
|
||||
const portsSubmenuPath = [
|
||||
...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP,
|
||||
'2_ports',
|
||||
];
|
||||
const portsSubmenuPath = ArduinoMenus.TOOLS__PORTS_SUBMENU;
|
||||
const portsSubmenuLabel = config.selectedPort?.address;
|
||||
this.menuModelRegistry.registerSubmenu(
|
||||
portsSubmenuPath,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
|
||||
|
||||
import {
|
||||
@@ -43,7 +44,6 @@ import {
|
||||
} from '../../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
SketchesService,
|
||||
ConfigService,
|
||||
FileSystemExt,
|
||||
Sketch,
|
||||
CoreService,
|
||||
@@ -62,6 +62,7 @@ import { NotificationManager } from '../theia/messages/notifications-manager';
|
||||
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { WorkspaceService } from '../theia/workspace/workspace-service';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
|
||||
export {
|
||||
Command,
|
||||
@@ -142,8 +143,8 @@ export abstract class SketchContribution extends Contribution {
|
||||
@inject(FileSystemExt)
|
||||
protected readonly fileSystemExt: FileSystemExt;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigServiceClient)
|
||||
protected readonly configService: ConfigServiceClient;
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchService: SketchesService;
|
||||
@@ -160,6 +161,9 @@ export abstract class SketchContribution extends Contribution {
|
||||
@inject(OutputChannelManager)
|
||||
protected readonly outputChannelManager: OutputChannelManager;
|
||||
|
||||
@inject(EnvVariablesServer)
|
||||
protected readonly envVariableServer: EnvVariablesServer;
|
||||
|
||||
protected async sourceOverride(): Promise<Record<string, string>> {
|
||||
const override: Record<string, string> = {};
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
@@ -173,6 +177,25 @@ export abstract class SketchContribution extends Contribution {
|
||||
}
|
||||
return override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults to `directories.user` if defined and not CLI config errors were detected.
|
||||
* Otherwise, the URI of the user home directory.
|
||||
*/
|
||||
protected async defaultUri(): Promise<URI> {
|
||||
const errors = this.configService.tryGetMessages();
|
||||
let defaultUri = this.configService.tryGetSketchDirUri();
|
||||
if (!defaultUri || errors?.length) {
|
||||
// Fall back to user home when the `directories.user` is not available or there are known CLI config errors
|
||||
defaultUri = new URI(await this.envVariableServer.getHomeDirUri());
|
||||
}
|
||||
return defaultUri;
|
||||
}
|
||||
|
||||
protected async defaultPath(): Promise<string> {
|
||||
const defaultUri = await this.defaultUri();
|
||||
return this.fileService.fsPath(defaultUri);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
CoreService,
|
||||
} from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { unregisterSubmenu } from '../menu/arduino-menus';
|
||||
|
||||
@injectable()
|
||||
export abstract class Examples extends SketchContribution {
|
||||
@@ -36,7 +37,7 @@ export abstract class Examples extends SketchContribution {
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuRegistry: MenuModelRegistry;
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(ExamplesService)
|
||||
protected readonly examplesService: ExamplesService;
|
||||
@@ -47,6 +48,9 @@ export abstract class Examples extends SketchContribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
protected override init(): void {
|
||||
@@ -54,6 +58,12 @@ export abstract class Examples extends SketchContribution {
|
||||
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
|
||||
this.handleBoardChanged(selectedBoard)
|
||||
);
|
||||
this.notificationCenter.onDidReinitialize(() =>
|
||||
this.update({
|
||||
board: this.boardsServiceClient.boardsConfig.selectedBoard,
|
||||
// No force refresh. The core client was already refreshed.
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
|
||||
@@ -120,6 +130,11 @@ export abstract class Examples extends SketchContribution {
|
||||
const { label } = sketchContainerOrPlaceholder;
|
||||
submenuPath = [...menuPath, label];
|
||||
this.menuRegistry.registerSubmenu(submenuPath, label, subMenuOptions);
|
||||
this.toDispose.push(
|
||||
Disposable.create(() =>
|
||||
unregisterSubmenu(submenuPath, this.menuRegistry)
|
||||
)
|
||||
);
|
||||
sketches.push(...sketchContainerOrPlaceholder.sketches);
|
||||
children.push(...sketchContainerOrPlaceholder.children);
|
||||
} else {
|
||||
@@ -239,9 +254,6 @@ export class BuiltInExamples extends Examples {
|
||||
|
||||
@injectable()
|
||||
export class LibraryExamples extends Examples {
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
private readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
|
||||
override onStart(): void {
|
||||
|
||||
@@ -53,6 +53,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
this.notificationCenter.onLibraryDidUninstall(() =>
|
||||
this.updateMenuActions()
|
||||
);
|
||||
this.notificationCenter.onDidReinitialize(() => this.updateMenuActions());
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
|
||||
@@ -82,10 +82,7 @@ export class OpenSketch extends SketchContribution {
|
||||
}
|
||||
|
||||
private async selectSketch(): Promise<Sketch | undefined> {
|
||||
const config = await this.configService.getConfiguration();
|
||||
const defaultPath = await this.fileService.fsPath(
|
||||
new URI(config.sketchDirUri)
|
||||
);
|
||||
const defaultPath = await this.defaultPath();
|
||||
const { filePaths } = await remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
|
||||
@@ -58,10 +58,7 @@ export class SaveAsSketch extends SketchContribution {
|
||||
markAsRecentlyOpened,
|
||||
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
|
||||
): Promise<boolean> {
|
||||
const [sketch, configuration] = await Promise.all([
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return false;
|
||||
}
|
||||
@@ -72,7 +69,7 @@ export class SaveAsSketch extends SketchContribution {
|
||||
}
|
||||
|
||||
const sketchUri = new URI(sketch.uri);
|
||||
const sketchbookDirUri = new URI(configuration.sketchDirUri);
|
||||
const sketchbookDirUri = await this.defaultUri();
|
||||
// If the sketch is temp, IDE2 proposes the default sketchbook folder URI.
|
||||
// If the sketch is not temp, but not contained in the default sketchbook folder, IDE2 proposes the default location.
|
||||
// Otherwise, it proposes the parent folder of the current sketch.
|
||||
|
||||
@@ -11,6 +11,7 @@ import { nls } from '@theia/core/lib/common/nls';
|
||||
export class Sketchbook extends Examples {
|
||||
override onStart(): void {
|
||||
this.sketchServiceClient.onSketchbookDidChange(() => this.update());
|
||||
this.configService.onDidChangeSketchDirUri(() => this.update());
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
import { DefaultTheme } from '@theia/application-package/lib/application-props';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import type { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
|
||||
export const WINDOW_SETTING = 'window';
|
||||
export const EDITOR_SETTING = 'editor';
|
||||
@@ -171,7 +172,15 @@ export class SettingsService {
|
||||
this.preferenceService.get<boolean>(SHOW_ALL_FILES_SETTING, false),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
const { additionalUrls, sketchDirUri, network } = cliConfig;
|
||||
const {
|
||||
config = {
|
||||
additionalUrls: [],
|
||||
sketchDirUri: '',
|
||||
network: Network.Default(),
|
||||
},
|
||||
} = cliConfig;
|
||||
const { additionalUrls, sketchDirUri, network } = config;
|
||||
|
||||
const sketchbookPath = await this.fileService.fsPath(new URI(sketchDirUri));
|
||||
return {
|
||||
editorFontSize,
|
||||
@@ -223,7 +232,11 @@ export class SettingsService {
|
||||
try {
|
||||
const { sketchbookPath, editorFontSize, themeId } = await settings;
|
||||
const sketchbookDir = await this.fileSystemExt.getUri(sketchbookPath);
|
||||
if (!(await this.fileService.exists(new URI(sketchbookDir)))) {
|
||||
let sketchbookStat: FileStat | undefined = undefined;
|
||||
try {
|
||||
sketchbookStat = await this.fileService.resolve(new URI(sketchbookDir));
|
||||
} catch {}
|
||||
if (!sketchbookStat || !sketchbookStat.isDirectory) {
|
||||
return nls.localize(
|
||||
'arduino/preferences/invalid.sketchbook.location',
|
||||
'Invalid sketchbook location: {0}',
|
||||
@@ -274,10 +287,19 @@ export class SettingsService {
|
||||
network,
|
||||
sketchbookShowAllFiles,
|
||||
} = this._settings;
|
||||
const [config, sketchDirUri] = await Promise.all([
|
||||
const [cliConfig, sketchDirUri] = await Promise.all([
|
||||
this.configService.getConfiguration(),
|
||||
this.fileSystemExt.getUri(sketchbookPath),
|
||||
]);
|
||||
const { config } = cliConfig;
|
||||
if (!config) {
|
||||
// Do not check for any error messages. The config might has errors (such as invalid directories.user) right before saving the new values.
|
||||
return nls.localize(
|
||||
'arduino/preferences/noCliConfig',
|
||||
'Could not load the CLI configuration'
|
||||
);
|
||||
}
|
||||
|
||||
(config as any).additionalUrls = additionalUrls;
|
||||
(config as any).sketchDirUri = sketchDirUri;
|
||||
(config as any).network = network;
|
||||
|
||||
@@ -88,8 +88,25 @@ export class LocalCacheFsProvider
|
||||
}
|
||||
|
||||
protected async init(fileService: FileService): Promise<void> {
|
||||
const config = await this.configService.getConfiguration();
|
||||
this._localCacheRoot = new URI(config.dataDirUri);
|
||||
const { config } = await this.configService.getConfiguration();
|
||||
// Any possible CLI config errors are ignored here. IDE2 does not verify the `directories.data` folder.
|
||||
// If the data dir is accessible, IDE2 creates the cache folder for the remote sketches. Otherwise, it does not.
|
||||
// The data folder can be configured outside of the IDE2, and the new data folder will be picked up with a
|
||||
// subsequent IDE2 start.
|
||||
if (!config?.dataDirUri) {
|
||||
return; // the deferred promise will never resolve
|
||||
}
|
||||
const localCacheUri = new URI(config.dataDirUri);
|
||||
try {
|
||||
await fileService.access(localCacheUri);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`'directories.data' location is inaccessible at ${config.dataDirUri}`,
|
||||
err
|
||||
);
|
||||
return;
|
||||
}
|
||||
this._localCacheRoot = localCacheUri;
|
||||
for (const segment of ['RemoteSketchbook', 'ArduinoCloud']) {
|
||||
this._localCacheRoot = this._localCacheRoot.resolve(segment);
|
||||
await fileService.createFolder(this._localCacheRoot);
|
||||
|
||||
@@ -97,6 +97,11 @@ export namespace ArduinoMenus {
|
||||
export const TOOLS__BOARD_SELECTION_GROUP = [...TOOLS, '2_board_selection'];
|
||||
// Core settings, such as `Processor` and `Programmers` for the board and `Burn Bootloader`
|
||||
export const TOOLS__BOARD_SETTINGS_GROUP = [...TOOLS, '3_board_settings'];
|
||||
// `Tool` > `Ports` (always visible https://github.com/arduino/arduino-ide/issues/655)
|
||||
export const TOOLS__PORTS_SUBMENU = [
|
||||
...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP,
|
||||
'2_ports',
|
||||
];
|
||||
|
||||
// -- Help
|
||||
// `Getting Started`, `Environment`, `Troubleshooting`, etc.
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
AttachedBoardsChangeEvent,
|
||||
BoardsPackage,
|
||||
LibraryPackage,
|
||||
Config,
|
||||
ConfigState,
|
||||
Sketch,
|
||||
ProgressMessage,
|
||||
} from '../common/protocol';
|
||||
@@ -37,6 +37,7 @@ export class NotificationCenter
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
private readonly didReinitializeEmitter = new Emitter<void>();
|
||||
private readonly indexUpdateDidCompleteEmitter =
|
||||
new Emitter<IndexUpdateDidCompleteParams>();
|
||||
private readonly indexUpdateWillStartEmitter =
|
||||
@@ -47,9 +48,7 @@ export class NotificationCenter
|
||||
new Emitter<IndexUpdateDidFailParams>();
|
||||
private readonly daemonDidStartEmitter = new Emitter<string>();
|
||||
private readonly daemonDidStopEmitter = new Emitter<void>();
|
||||
private readonly configDidChangeEmitter = new Emitter<{
|
||||
config: Config | undefined;
|
||||
}>();
|
||||
private readonly configDidChangeEmitter = new Emitter<ConfigState>();
|
||||
private readonly platformDidInstallEmitter = new Emitter<{
|
||||
item: BoardsPackage;
|
||||
}>();
|
||||
@@ -71,6 +70,7 @@ export class NotificationCenter
|
||||
new Emitter<FrontendApplicationState>();
|
||||
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.didReinitializeEmitter,
|
||||
this.indexUpdateWillStartEmitter,
|
||||
this.indexUpdateDidProgressEmitter,
|
||||
this.indexUpdateDidCompleteEmitter,
|
||||
@@ -85,6 +85,7 @@ export class NotificationCenter
|
||||
this.attachedBoardsDidChangeEmitter
|
||||
);
|
||||
|
||||
readonly onDidReinitialize = this.didReinitializeEmitter.event;
|
||||
readonly onIndexUpdateDidComplete = this.indexUpdateDidCompleteEmitter.event;
|
||||
readonly onIndexUpdateWillStart = this.indexUpdateWillStartEmitter.event;
|
||||
readonly onIndexUpdateDidProgress = this.indexUpdateDidProgressEmitter.event;
|
||||
@@ -115,6 +116,10 @@ export class NotificationCenter
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
notifyDidReinitialize(): void {
|
||||
this.didReinitializeEmitter.fire();
|
||||
}
|
||||
|
||||
notifyIndexUpdateWillStart(params: IndexUpdateWillStartParams): void {
|
||||
this.indexUpdateWillStartEmitter.fire(params);
|
||||
}
|
||||
@@ -139,7 +144,7 @@ export class NotificationCenter
|
||||
this.daemonDidStopEmitter.fire();
|
||||
}
|
||||
|
||||
notifyConfigDidChange(event: { config: Config | undefined }): void {
|
||||
notifyConfigDidChange(event: ConfigState): void {
|
||||
this.configDidChangeEmitter.fire(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Title, Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { WidgetDecoration } from '@theia/core/lib/browser/widget-decoration';
|
||||
import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
|
||||
import { ConfigService } from '../../../common/protocol/config-service';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class TabBarDecoratorService extends TheiaTabBarDecoratorService {
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configService: ConfigServiceClient;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(ILogger)
|
||||
protected readonly logger: ILogger;
|
||||
|
||||
protected dataDirUri: URI | undefined;
|
||||
private dataDirUri: URI | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.configService
|
||||
.getConfiguration()
|
||||
.then(({ dataDirUri }) => (this.dataDirUri = new URI(dataDirUri)))
|
||||
.catch((err) =>
|
||||
this.logger.error(`Failed to determine the data directory: ${err}`)
|
||||
);
|
||||
const fireDidChange = () =>
|
||||
this.appStateService
|
||||
.reachedState('ready')
|
||||
.then(() => this.fireDidChangeDecorations());
|
||||
this.dataDirUri = this.configService.tryGetDataDirUri();
|
||||
this.configService.onDidChangeDataDirUri((dataDirUri) => {
|
||||
this.dataDirUri = dataDirUri;
|
||||
fireDidChange();
|
||||
});
|
||||
if (this.dataDirUri) {
|
||||
fireDidChange();
|
||||
}
|
||||
}
|
||||
|
||||
override getDecorations(title: Title<Widget>): WidgetDecoration.Data[] {
|
||||
|
||||
@@ -5,31 +5,23 @@ import {
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Diagnostic } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||
import { ConfigService } from '../../../common/protocol/config-service';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import debounce = require('lodash.debounce');
|
||||
|
||||
@injectable()
|
||||
export class ProblemManager extends TheiaProblemManager {
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configService: ConfigServiceClient;
|
||||
|
||||
@inject(ILogger)
|
||||
protected readonly logger: ILogger;
|
||||
|
||||
protected dataDirUri: URI | undefined;
|
||||
private dataDirUri: URI | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.configService
|
||||
.getConfiguration()
|
||||
.then(({ dataDirUri }) => (this.dataDirUri = new URI(dataDirUri)))
|
||||
.catch((err) =>
|
||||
this.logger.error(`Failed to determine the data directory: ${err}`)
|
||||
);
|
||||
this.dataDirUri = this.configService.tryGetDataDirUri();
|
||||
this.configService.onDidChangeDataDirUri((uri) => (this.dataDirUri = uri));
|
||||
}
|
||||
|
||||
override setMarkers(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { FileNode, FileTreeModel } from '@theia/filesystem/lib/browser';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ConfigService } from '../../../common/protocol';
|
||||
import { ConfigServiceClient } from './../../config/config-service-client';
|
||||
import { SketchbookTree } from './sketchbook-tree';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import {
|
||||
@@ -36,8 +36,8 @@ export class SketchbookTreeModel extends FileTreeModel {
|
||||
@inject(CommandRegistry)
|
||||
public readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigServiceClient)
|
||||
protected readonly configService: ConfigServiceClient;
|
||||
|
||||
@inject(OpenerService)
|
||||
protected readonly openerService: OpenerService;
|
||||
@@ -59,6 +59,12 @@ export class SketchbookTreeModel extends FileTreeModel {
|
||||
super.init();
|
||||
this.reportBusyProgress();
|
||||
this.initializeRoot();
|
||||
this.toDispose.push(
|
||||
this.configService.onDidChangeSketchDirUri(async () => {
|
||||
await this.updateRoot();
|
||||
this.selectRoot(this.root);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected readonly pendingBusyProgress = new Map<string, Deferred<void>>();
|
||||
@@ -121,6 +127,10 @@ export class SketchbookTreeModel extends FileTreeModel {
|
||||
return;
|
||||
}
|
||||
const root = this.root;
|
||||
this.selectRoot(root);
|
||||
}
|
||||
|
||||
private selectRoot(root: TreeNode | undefined) {
|
||||
if (CompositeTreeNode.is(root) && root.children.length === 1) {
|
||||
const child = root.children[0];
|
||||
if (
|
||||
@@ -161,10 +171,12 @@ export class SketchbookTreeModel extends FileTreeModel {
|
||||
}
|
||||
|
||||
protected async createRoot(): Promise<TreeNode | undefined> {
|
||||
const config = await this.configService.getConfiguration();
|
||||
const rootFileStats = await this.fileService.resolve(
|
||||
new URI(config.sketchDirUri)
|
||||
);
|
||||
const sketchDirUri = this.configService.tryGetSketchDirUri();
|
||||
const errors = this.configService.tryGetMessages();
|
||||
if (!sketchDirUri || errors?.length) {
|
||||
return undefined;
|
||||
}
|
||||
const rootFileStats = await this.fileService.resolve(sketchDirUri);
|
||||
|
||||
if (this.workspaceService.opened && rootFileStats.children) {
|
||||
// filter out libraries and hardware
|
||||
|
||||
Reference in New Issue
Block a user