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

View File

@ -3,8 +3,10 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { import {
ArduinoDaemon, ArduinoDaemon,
assertSanitizedFqbn,
BoardsService, BoardsService,
ExecutableService, ExecutableService,
sanitizeFqbn,
} from '../../common/protocol'; } from '../../common/protocol';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
import { BoardsConfig } from '../boards/boards-config'; import { BoardsConfig } from '../boards/boards-config';
@ -12,6 +14,7 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { HostedPluginEvents } from '../hosted-plugin-events'; import { HostedPluginEvents } from '../hosted-plugin-events';
import { NotificationCenter } from '../notification-center'; import { NotificationCenter } from '../notification-center';
import { SketchContribution, URI } from './contribution'; import { SketchContribution, URI } from './contribution';
import { BoardsDataStore } from '../boards/boards-data-store';
@injectable() @injectable()
export class InoLanguage extends SketchContribution { export class InoLanguage extends SketchContribution {
@ -33,6 +36,9 @@ export class InoLanguage extends SketchContribution {
@inject(NotificationCenter) @inject(NotificationCenter)
private readonly notificationCenter: NotificationCenter; private readonly notificationCenter: NotificationCenter;
@inject(BoardsDataStore)
private readonly boardDataStore: BoardsDataStore;
private readonly toDispose = new DisposableCollection(); private readonly toDispose = new DisposableCollection();
private readonly languageServerStartMutex = new Mutex(); private readonly languageServerStartMutex = new Mutex();
private languageServerFqbn?: string; private languageServerFqbn?: string;
@ -76,6 +82,26 @@ export class InoLanguage extends SketchContribution {
this.notificationCenter.onPlatformDidInstall(() => forceRestart()), this.notificationCenter.onPlatformDidInstall(() => forceRestart()),
this.notificationCenter.onPlatformDidUninstall(() => forceRestart()), this.notificationCenter.onPlatformDidUninstall(() => forceRestart()),
this.notificationCenter.onDidReinitialize(() => 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); start(this.boardsServiceProvider.boardsConfig);
} }
@ -121,11 +147,18 @@ export class InoLanguage extends SketchContribution {
} }
return; 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 // NOOP
return; return;
} }
this.logger.info(`Starting language server: ${fqbn}`); this.logger.info(`Starting language server: ${fqbnWithConfig}`);
const log = this.preferences.get('arduino.language.log'); const log = this.preferences.get('arduino.language.log');
const realTimeDiagnostics = this.preferences.get( const realTimeDiagnostics = this.preferences.get(
'arduino.language.realTimeDiagnostics' 'arduino.language.realTimeDiagnostics'
@ -161,7 +194,7 @@ export class InoLanguage extends SketchContribution {
log: currentSketchPath ? currentSketchPath : log, log: currentSketchPath ? currentSketchPath : log,
cliDaemonInstance: '1', cliDaemonInstance: '1',
board: { board: {
fqbn, fqbn: fqbnWithConfig,
name: name ? `"${name}"` : undefined, name: name ? `"${name}"` : undefined,
}, },
realTimeDiagnostics, realTimeDiagnostics,
@ -170,7 +203,7 @@ export class InoLanguage extends SketchContribution {
), ),
]); ]);
} catch (e) { } 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; this.languageServerFqbn = undefined;
} finally { } finally {
release(); release();

View File

@ -1,6 +1,6 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event'; 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 { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { import {
@ -170,7 +170,7 @@ export class UploadSketch extends CoreServiceContribution {
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] = const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
await Promise.all([ await Promise.all([
verifyOptions.fqbn, // already decorated FQBN 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.verify'),
this.preferences.get('arduino.upload.verbose'), this.preferences.get('arduino.upload.verbose'),
]); ]);
@ -207,19 +207,6 @@ export class UploadSketch extends CoreServiceContribution {
} }
return port; 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 { 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}`;
}