mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-13 06:16:33 +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 { AddZipLibrary } from './contributions/add-zip-library';
|
||||||
import { WorkspaceVariableContribution as TheiaWorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
import { WorkspaceVariableContribution as TheiaWorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
||||||
import { WorkspaceVariableContribution } from './theia/workspace/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');
|
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.
|
// To remove the `Run` menu item from the application menu.
|
||||||
bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope();
|
bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope();
|
||||||
rebind(TheiaDebugFrontendApplicationContribution).toService(DebugFrontendApplicationContribution);
|
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
|
// Preferences
|
||||||
bindArduinoPreferences(bind);
|
bindArduinoPreferences(bind);
|
||||||
|
@ -106,9 +106,11 @@ export class Debug extends SketchContribution {
|
|||||||
if (!sketch) {
|
if (!sketch) {
|
||||||
return;
|
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(executables.cliUri)),
|
||||||
this.fileService.fsPath(new URI(sketch.uri))
|
this.fileService.fsPath(new URI(sketch.uri)),
|
||||||
|
this.fileService.fsPath(new URI(ideTempFolderUri)),
|
||||||
])
|
])
|
||||||
const config = {
|
const config = {
|
||||||
cliPath,
|
cliPath,
|
||||||
@ -116,7 +118,8 @@ export class Debug extends SketchContribution {
|
|||||||
fqbn,
|
fqbn,
|
||||||
name
|
name
|
||||||
},
|
},
|
||||||
sketchPath
|
sketchPath,
|
||||||
|
configPath
|
||||||
};
|
};
|
||||||
return this.commandService.executeCommand('arduino.debug.start', config);
|
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>;
|
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 {
|
export interface Sketch {
|
||||||
|
@ -3,6 +3,7 @@ import * as fs from 'fs';
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as temp from 'temp';
|
import * as temp from 'temp';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import { ncp } from 'ncp';
|
import { ncp } from 'ncp';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import URI from '@theia/core/lib/common/uri';
|
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 { firstToLowerCase } from '../common/utils';
|
||||||
import { NotificationServiceServerImpl } from './notification-service-server';
|
import { NotificationServiceServerImpl } from './notification-service-server';
|
||||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
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';
|
import { LoadSketchReq, ArchiveSketchReq } from './cli-protocol/commands/commands_pb';
|
||||||
|
|
||||||
const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
||||||
@ -21,14 +22,11 @@ const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
|||||||
const prefix = '.arduinoIDE-unsaved';
|
const prefix = '.arduinoIDE-unsaved';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SketchesServiceImpl implements SketchesService {
|
export class SketchesServiceImpl extends CoreClientAware implements SketchesService {
|
||||||
|
|
||||||
@inject(ConfigService)
|
@inject(ConfigService)
|
||||||
protected readonly configService: ConfigService;
|
protected readonly configService: ConfigService;
|
||||||
|
|
||||||
@inject(CoreClientProvider)
|
|
||||||
protected readonly coreClientProvider: CoreClientProvider;
|
|
||||||
|
|
||||||
@inject(NotificationServiceServerImpl)
|
@inject(NotificationServiceServerImpl)
|
||||||
protected readonly notificationService: NotificationServiceServerImpl;
|
protected readonly notificationService: NotificationServiceServerImpl;
|
||||||
|
|
||||||
@ -348,23 +346,16 @@ void loop() {
|
|||||||
return destinationUri;
|
return destinationUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async coreClient(): Promise<CoreClientProvider.Client> {
|
async getIdeTempFolderUri(sketch: Sketch): Promise<string> {
|
||||||
const coreClient = await new Promise<CoreClientProvider.Client>(async resolve => {
|
const genBuildPath = await this.getIdeTempFolderPath(sketch);
|
||||||
const client = await this.coreClientProvider.client();
|
return FileUri.create(genBuildPath).toString();
|
||||||
if (client) {
|
|
||||||
resolve(client);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const toDispose = this.coreClientProvider.onClientReady(async () => {
|
|
||||||
const client = await this.coreClientProvider.client();
|
async getIdeTempFolderPath(sketch: Sketch): Promise<string> {
|
||||||
if (client) {
|
const sketchPath = FileUri.fsPath(sketch.uri);
|
||||||
toDispose.dispose();
|
await fs.promises.readdir(sketchPath); // Validates the sketch folder and rejects if not accessible.
|
||||||
resolve(client);
|
const suffix = crypto.createHash('md5').update(sketchPath).digest('hex');
|
||||||
return;
|
return path.join(os.tmpdir(), `arduino-ide2-${suffix}`);
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return coreClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user