fix: start the LS with the board specific settings

restart the LS when board settings of the running LS has changed

Closes #1029

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2023-01-10 14:00:21 +01:00 committed by Akos Kitta
parent a15a94a339
commit 40e797966f
4 changed files with 73 additions and 29 deletions

View File

@ -30,11 +30,11 @@ export class BoardsDataStore implements FrontendApplicationContribution {
@inject(LocalStorageService)
protected readonly storageService: LocalStorageService;
protected readonly onChangedEmitter = new Emitter<void>();
protected readonly onChangedEmitter = new Emitter<string[]>();
onStart(): void {
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
let shouldFireChanged = false;
const dataDidChangePerFqbn: string[] = [];
for (const fqbn of item.boards
.map(({ fqbn }) => fqbn)
.filter(notEmpty)
@ -49,18 +49,18 @@ export class BoardsDataStore implements FrontendApplicationContribution {
data = details.configOptions;
if (data.length) {
await this.storageService.setData(key, data);
shouldFireChanged = true;
dataDidChangePerFqbn.push(fqbn);
}
}
}
}
if (shouldFireChanged) {
this.fireChanged();
if (dataDidChangePerFqbn.length) {
this.fireChanged(...dataDidChangePerFqbn);
}
});
}
get onChanged(): Event<void> {
get onChanged(): Event<string[]> {
return this.onChangedEmitter.event;
}
@ -116,7 +116,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
fqbn,
data: { ...data, selectedProgrammer },
});
this.fireChanged();
this.fireChanged(fqbn);
return true;
}
@ -146,7 +146,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
return false;
}
await this.setData({ fqbn, data });
this.fireChanged();
this.fireChanged(fqbn);
return true;
}
@ -190,8 +190,8 @@ export class BoardsDataStore implements FrontendApplicationContribution {
}
}
protected fireChanged(): void {
this.onChangedEmitter.fire();
protected fireChanged(...fqbn: string[]): void {
this.onChangedEmitter.fire(fqbn);
}
}

View File

@ -3,8 +3,10 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { Mutex } from 'async-mutex';
import {
ArduinoDaemon,
assertSanitizedFqbn,
BoardsService,
ExecutableService,
sanitizeFqbn,
} from '../../common/protocol';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
import { BoardsConfig } from '../boards/boards-config';
@ -12,6 +14,7 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { HostedPluginEvents } from '../hosted-plugin-events';
import { NotificationCenter } from '../notification-center';
import { SketchContribution, URI } from './contribution';
import { BoardsDataStore } from '../boards/boards-data-store';
@injectable()
export class InoLanguage extends SketchContribution {
@ -33,6 +36,9 @@ export class InoLanguage extends SketchContribution {
@inject(NotificationCenter)
private readonly notificationCenter: NotificationCenter;
@inject(BoardsDataStore)
private readonly boardDataStore: BoardsDataStore;
private readonly toDispose = new DisposableCollection();
private readonly languageServerStartMutex = new Mutex();
private languageServerFqbn?: string;
@ -76,6 +82,26 @@ export class InoLanguage extends SketchContribution {
this.notificationCenter.onPlatformDidInstall(() => forceRestart()),
this.notificationCenter.onPlatformDidUninstall(() => forceRestart()),
this.notificationCenter.onDidReinitialize(() => forceRestart()),
this.boardDataStore.onChanged((dataChangePerFqbn) => {
if (this.languageServerFqbn) {
const sanitizedFqbn = sanitizeFqbn(this.languageServerFqbn);
if (!sanitizeFqbn) {
throw new Error(
`Failed to sanitize the FQBN of the running language server. FQBN with the board settings was: ${this.languageServerFqbn}`
);
}
const matchingFqbn = dataChangePerFqbn.find(
(fqbn) => sanitizedFqbn === fqbn
);
const { boardsConfig } = this.boardsServiceProvider;
if (
matchingFqbn &&
boardsConfig.selectedBoard?.fqbn === matchingFqbn
) {
start(boardsConfig);
}
}
}),
]);
start(this.boardsServiceProvider.boardsConfig);
}
@ -121,11 +147,18 @@ export class InoLanguage extends SketchContribution {
}
return;
}
if (!forceStart && fqbn === this.languageServerFqbn) {
assertSanitizedFqbn(fqbn);
const fqbnWithConfig = await this.boardDataStore.appendConfigToFqbn(fqbn);
if (!fqbnWithConfig) {
throw new Error(
`Failed to append boards config to the FQBN. Original FQBN was: ${fqbn}`
);
}
if (!forceStart && fqbnWithConfig === this.languageServerFqbn) {
// NOOP
return;
}
this.logger.info(`Starting language server: ${fqbn}`);
this.logger.info(`Starting language server: ${fqbnWithConfig}`);
const log = this.preferences.get('arduino.language.log');
const realTimeDiagnostics = this.preferences.get(
'arduino.language.realTimeDiagnostics'
@ -161,7 +194,7 @@ export class InoLanguage extends SketchContribution {
log: currentSketchPath ? currentSketchPath : log,
cliDaemonInstance: '1',
board: {
fqbn,
fqbn: fqbnWithConfig,
name: name ? `"${name}"` : undefined,
},
realTimeDiagnostics,
@ -170,7 +203,7 @@ export class InoLanguage extends SketchContribution {
),
]);
} catch (e) {
console.log(`Failed to start language server for ${fqbn}`, e);
console.log(`Failed to start language server. Original FQBN: ${fqbn}`, e);
this.languageServerFqbn = undefined;
} finally {
release();

View File

@ -1,6 +1,6 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
import { CoreService, Port } from '../../common/protocol';
import { CoreService, Port, sanitizeFqbn } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import {
@ -170,7 +170,7 @@ export class UploadSketch extends CoreServiceContribution {
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
await Promise.all([
verifyOptions.fqbn, // already decorated FQBN
this.boardsDataStore.getData(this.sanitizeFqbn(verifyOptions.fqbn)),
this.boardsDataStore.getData(sanitizeFqbn(verifyOptions.fqbn)),
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
@ -207,19 +207,6 @@ export class UploadSketch extends CoreServiceContribution {
}
return port;
}
/**
* Converts the `VENDOR:ARCHITECTURE:BOARD_ID[:MENU_ID=OPTION_ID[,MENU2_ID=OPTION_ID ...]]` FQBN to
* `VENDOR:ARCHITECTURE:BOARD_ID` format.
* See the details of the `{build.fqbn}` entry in the [specs](https://arduino.github.io/arduino-cli/latest/platform-specification/#global-predefined-properties).
*/
private sanitizeFqbn(fqbn: string | undefined): string | undefined {
if (!fqbn) {
return undefined;
}
const [vendor, arch, id] = fqbn.split(':');
return `${vendor}:${arch}:${id}`;
}
}
export namespace UploadSketch {

View File

@ -623,3 +623,27 @@ export namespace Board {
}));
}
}
/**
* Throws an error if the `fqbn` argument is not sanitized. A sanitized FQBN has the `VENDOR:ARCHITECTURE:BOARD_ID` construct.
*/
export function assertSanitizedFqbn(fqbn: string): void {
if (fqbn.split(':').length !== 3) {
throw new Error(
`Expected a sanitized FQBN with three segments in the following format: 'VENDOR:ARCHITECTURE:BOARD_ID'. Got ${fqbn} instead.`
);
}
}
/**
* Converts the `VENDOR:ARCHITECTURE:BOARD_ID[:MENU_ID=OPTION_ID[,MENU2_ID=OPTION_ID ...]]` FQBN to
* `VENDOR:ARCHITECTURE:BOARD_ID` format.
* See the details of the `{build.fqbn}` entry in the [specs](https://arduino.github.io/arduino-cli/latest/platform-specification/#global-predefined-properties).
*/
export function sanitizeFqbn(fqbn: string | undefined): string | undefined {
if (!fqbn) {
return undefined;
}
const [vendor, arch, id] = fqbn.split(':');
return `${vendor}:${arch}:${id}`;
}