mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-12 05:46:32 +00:00
ATL-972: Moved the './theia/launch.json' config into a temp folder.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
acbd98d0f8
commit
d648159f43
@ -145,6 +145,8 @@ import { OutputToolbarContribution } from './theia/output/output-toolbar-contrib
|
||||
import { AddZipLibrary } from './contributions/add-zip-library';
|
||||
import { WorkspaceVariableContribution as TheiaWorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
||||
import { WorkspaceVariableContribution } from './theia/workspace/workspace-variable-contribution';
|
||||
import { DebugConfigurationManager } from './theia/debug/debug-configuration-manager';
|
||||
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
@ -394,6 +396,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// To remove the `Run` menu item from the application menu.
|
||||
bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugFrontendApplicationContribution).toService(DebugFrontendApplicationContribution);
|
||||
// To be able to use a `launch.json` from outside of the workspace.
|
||||
bind(DebugConfigurationManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
|
||||
|
||||
// Preferences
|
||||
bindArduinoPreferences(bind);
|
||||
|
@ -106,9 +106,11 @@ export class Debug extends SketchContribution {
|
||||
if (!sketch) {
|
||||
return;
|
||||
}
|
||||
const [cliPath, sketchPath] = await Promise.all([
|
||||
const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri(sketch);
|
||||
const [cliPath, sketchPath, configPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(executables.cliUri)),
|
||||
this.fileService.fsPath(new URI(sketch.uri))
|
||||
this.fileService.fsPath(new URI(sketch.uri)),
|
||||
this.fileService.fsPath(new URI(ideTempFolderUri)),
|
||||
])
|
||||
const config = {
|
||||
cliPath,
|
||||
@ -116,7 +118,8 @@ export class Debug extends SketchContribution {
|
||||
fqbn,
|
||||
name
|
||||
},
|
||||
sketchPath
|
||||
sketchPath,
|
||||
configPath
|
||||
};
|
||||
return this.commandService.executeCommand('arduino.debug.start', config);
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
import debounce = require('p-debounce');
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
|
||||
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
|
||||
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
||||
import { SketchesService } from '../../../common/protocol';
|
||||
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
|
||||
import { DebugConfigurationModel } from './debug-configuration-model';
|
||||
import { FileOperationError, FileOperationResult } from '@theia/filesystem/lib/common/files';
|
||||
|
||||
@injectable()
|
||||
export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchesService: SketchesService;
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected onTempContentDidChangeEmitter = new Emitter<TheiaDebugConfigurationModel.JsonContent>();
|
||||
get onTempContentDidChange(): Event<TheiaDebugConfigurationModel.JsonContent> {
|
||||
return this.onTempContentDidChangeEmitter.event;
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
super.init();
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const tempContent = await this.getTempLaunchJsonContent();
|
||||
if (!tempContent) {
|
||||
// No active sketch.
|
||||
return;
|
||||
}
|
||||
// Watch the file of the container folder.
|
||||
this.fileService.watch(tempContent instanceof URI ? tempContent : tempContent.uri);
|
||||
// Use the normalized temp folder name. We cannot compare Theia URIs here.
|
||||
// /var/folders/k3/d2fkvv1j16v3_rz93k7f74180000gn/T/arduino-ide2-a0337d47f86b24a51df3dbcf2cc17925/launch.json
|
||||
// /private/var/folders/k3/d2fkvv1j16v3_rz93k7f74180000gn/T/arduino-ide2-A0337D47F86B24A51DF3DBCF2CC17925/launch.json
|
||||
const tempFolderName = (tempContent instanceof URI ? tempContent : tempContent.uri.parent).path.base.toLowerCase();
|
||||
this.fileService.onDidFilesChange(event => {
|
||||
for (const { resource } of event.changes) {
|
||||
if (resource.path.base === 'launch.json' && resource.parent.path.base.toLowerCase() === tempFolderName) {
|
||||
this.getTempLaunchJsonContent().then(config => {
|
||||
if (config && !(config instanceof URI)) {
|
||||
this.onTempContentDidChangeEmitter.fire(config);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.updateModels();
|
||||
});
|
||||
}
|
||||
|
||||
protected updateModels = debounce(async () => {
|
||||
await this.appStateService.reachedState('ready');
|
||||
const roots = await this.workspaceService.roots;
|
||||
const toDelete = new Set(this.models.keys());
|
||||
for (const rootStat of roots) {
|
||||
const key = rootStat.resource.toString();
|
||||
toDelete.delete(key);
|
||||
if (!this.models.has(key)) {
|
||||
const tempContent = await this.getTempLaunchJsonContent();
|
||||
if (!tempContent) {
|
||||
continue;
|
||||
}
|
||||
const configurations: DebugConfiguration[] = tempContent instanceof URI ? [] : tempContent.configurations;
|
||||
const uri = tempContent instanceof URI ? undefined : tempContent.uri;
|
||||
const model = new DebugConfigurationModel(key, this.preferences, configurations, uri, this.onTempContentDidChange);
|
||||
model.onDidChange(() => this.updateCurrent());
|
||||
model.onDispose(() => this.models.delete(key));
|
||||
this.models.set(key, model);
|
||||
}
|
||||
}
|
||||
for (const uri of toDelete) {
|
||||
const model = this.models.get(uri);
|
||||
if (model) {
|
||||
model.dispose();
|
||||
}
|
||||
}
|
||||
this.updateCurrent();
|
||||
}, 500);
|
||||
|
||||
protected async getTempLaunchJsonContent(): Promise<TheiaDebugConfigurationModel.JsonContent & { uri: URI } | URI | undefined> {
|
||||
const sketch = await this.sketchesServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
return undefined;
|
||||
}
|
||||
const uri = await this.sketchesService.getIdeTempFolderUri(sketch);
|
||||
const tempFolderUri = new URI(uri);
|
||||
await this.fileService.createFolder(tempFolderUri);
|
||||
try {
|
||||
const uri = tempFolderUri.resolve('launch.json');
|
||||
const { value } = await this.fileService.read(uri);
|
||||
const configurations = DebugConfigurationModel.parse(JSON.parse(value));
|
||||
return { uri, configurations };
|
||||
} catch (err) {
|
||||
if (err instanceof FileOperationError && err.fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
|
||||
return tempFolderUri;
|
||||
}
|
||||
console.error('Could not load debug configuration from IDE2 temp folder.', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
|
||||
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
|
||||
|
||||
export class DebugConfigurationModel extends TheiaDebugConfigurationModel {
|
||||
|
||||
constructor(
|
||||
readonly workspaceFolderUri: string,
|
||||
protected readonly preferences: PreferenceService,
|
||||
protected readonly config: DebugConfiguration[],
|
||||
protected configUri: URI | undefined,
|
||||
protected readonly onConfigDidChange: Event<TheiaDebugConfigurationModel.JsonContent>) {
|
||||
|
||||
super(workspaceFolderUri, preferences);
|
||||
this.toDispose.push(onConfigDidChange(content => {
|
||||
const { uri, configurations } = content;
|
||||
this.configUri = uri;
|
||||
this.config.length = 0;
|
||||
this.config.push(...configurations);
|
||||
this.reconcile();
|
||||
}));
|
||||
this.reconcile();
|
||||
}
|
||||
|
||||
protected parseConfigurations(): TheiaDebugConfigurationModel.JsonContent {
|
||||
return {
|
||||
uri: this.configUri,
|
||||
configurations: this.config
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace DebugConfigurationModel {
|
||||
export function parse(launchConfig: any): DebugConfiguration[] {
|
||||
const configurations: DebugConfiguration[] = [];
|
||||
if (launchConfig && typeof launchConfig === 'object' && 'configurations' in launchConfig) {
|
||||
if (Array.isArray(launchConfig.configurations)) {
|
||||
for (const configuration of launchConfig.configurations) {
|
||||
if (DebugConfiguration.is(configuration)) {
|
||||
configurations.push(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return configurations;
|
||||
}
|
||||
}
|
@ -63,6 +63,12 @@ export interface SketchesService {
|
||||
*/
|
||||
archive(sketch: Sketch, destinationUri: string): Promise<string>;
|
||||
|
||||
/**
|
||||
* Counterpart of the CLI's `genBuildPath` functionality.
|
||||
* Based on https://github.com/arduino/arduino-cli/blob/550179eefd2d2bca299d50a4af9e9bfcfebec649/arduino/builder/builder.go#L30-L38
|
||||
*/
|
||||
getIdeTempFolderUri(sketch: Sketch): Promise<string>;
|
||||
|
||||
}
|
||||
|
||||
export interface Sketch {
|
||||
|
@ -3,6 +3,7 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as temp from 'temp';
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import { ncp } from 'ncp';
|
||||
import { promisify } from 'util';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
@ -13,7 +14,7 @@ import { SketchesService, Sketch } from '../common/protocol/sketches-service';
|
||||
import { firstToLowerCase } from '../common/utils';
|
||||
import { NotificationServiceServerImpl } from './notification-service-server';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import { CoreClientAware } from './core-client-provider';
|
||||
import { LoadSketchReq, ArchiveSketchReq } from './cli-protocol/commands/commands_pb';
|
||||
|
||||
const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
||||
@ -21,14 +22,11 @@ const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
||||
const prefix = '.arduinoIDE-unsaved';
|
||||
|
||||
@injectable()
|
||||
export class SketchesServiceImpl implements SketchesService {
|
||||
export class SketchesServiceImpl extends CoreClientAware implements SketchesService {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(CoreClientProvider)
|
||||
protected readonly coreClientProvider: CoreClientProvider;
|
||||
|
||||
@inject(NotificationServiceServerImpl)
|
||||
protected readonly notificationService: NotificationServiceServerImpl;
|
||||
|
||||
@ -348,23 +346,16 @@ void loop() {
|
||||
return destinationUri;
|
||||
}
|
||||
|
||||
private async coreClient(): Promise<CoreClientProvider.Client> {
|
||||
const coreClient = await new Promise<CoreClientProvider.Client>(async resolve => {
|
||||
const client = await this.coreClientProvider.client();
|
||||
if (client) {
|
||||
resolve(client);
|
||||
return;
|
||||
}
|
||||
const toDispose = this.coreClientProvider.onClientReady(async () => {
|
||||
const client = await this.coreClientProvider.client();
|
||||
if (client) {
|
||||
toDispose.dispose();
|
||||
resolve(client);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
return coreClient;
|
||||
async getIdeTempFolderUri(sketch: Sketch): Promise<string> {
|
||||
const genBuildPath = await this.getIdeTempFolderPath(sketch);
|
||||
return FileUri.create(genBuildPath).toString();
|
||||
}
|
||||
|
||||
async getIdeTempFolderPath(sketch: Sketch): Promise<string> {
|
||||
const sketchPath = FileUri.fsPath(sketch.uri);
|
||||
await fs.promises.readdir(sketchPath); // Validates the sketch folder and rejects if not accessible.
|
||||
const suffix = crypto.createHash('md5').update(sketchPath).digest('hex');
|
||||
return path.join(os.tmpdir(), `arduino-ide2-${suffix}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user