mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-10 12:56:32 +00:00
IDE2 falls back to a new sketch if the opening fails. Closes #1089 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
fe31d15b9f
commit
8ad10b5adf
@ -1,21 +0,0 @@
|
||||
import { Command } from '@theia/core/lib/common/command';
|
||||
|
||||
/**
|
||||
* @deprecated all these commands should go under contributions and have their command, menu, keybinding, and toolbar contributions.
|
||||
*/
|
||||
export namespace ArduinoCommands {
|
||||
export const TOGGLE_COMPILE_FOR_DEBUG: Command = {
|
||||
id: 'arduino-toggle-compile-for-debug',
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlike `OPEN_SKETCH`, it opens all files from a sketch folder. (ino, cpp, etc...)
|
||||
*/
|
||||
export const OPEN_SKETCH_FILES: Command = {
|
||||
id: 'arduino-open-sketch-files',
|
||||
};
|
||||
|
||||
export const OPEN_BOARDS_DIALOG: Command = {
|
||||
id: 'arduino-open-boards-dialog',
|
||||
};
|
||||
}
|
@ -1,36 +1,22 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import {
|
||||
BoardsService,
|
||||
SketchesService,
|
||||
ExecutableService,
|
||||
Sketch,
|
||||
ArduinoDaemon,
|
||||
} from '../common/protocol';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { SketchesService } from '../common/protocol';
|
||||
import {
|
||||
MAIN_MENU_BAR,
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
ILogger,
|
||||
DisposableCollection,
|
||||
} from '@theia/core';
|
||||
import {
|
||||
Dialog,
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
OnWillStopAction,
|
||||
SaveableWidget,
|
||||
StatusBar,
|
||||
StatusBarAlignment,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
|
||||
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
@ -38,45 +24,28 @@ import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import {
|
||||
CommandContribution,
|
||||
CommandRegistry,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import {
|
||||
EditorCommands,
|
||||
EditorMainMenu,
|
||||
EditorManager,
|
||||
EditorOpenerOptions,
|
||||
} from '@theia/editor/lib/browser';
|
||||
import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser';
|
||||
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/browser';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { BoardsConfigDialog } from './boards/boards-config-dialog';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { EditorMode } from './editor-mode';
|
||||
import { ArduinoMenus } from './menu/arduino-menus';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { ArduinoPreferences } from './arduino-preferences';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../common/protocol/sketches-service-client-impl';
|
||||
import { ArduinoPreferences } from './arduino-preferences';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { SaveAsSketch } from './contributions/save-as-sketch';
|
||||
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
|
||||
import { IDEUpdater } from '../common/protocol/ide-updater';
|
||||
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
|
||||
import { HostedPluginEvents } from './hosted-plugin-events';
|
||||
|
||||
export const SKIP_IDE_VERSION = 'skipIDEVersion';
|
||||
import { ArduinoMenus } from './menu/arduino-menus';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution
|
||||
@ -87,45 +56,18 @@ export class ArduinoFrontendContribution
|
||||
MenuContribution,
|
||||
ColorContribution
|
||||
{
|
||||
@inject(ILogger)
|
||||
private readonly logger: ILogger;
|
||||
|
||||
@inject(MessageService)
|
||||
private readonly messageService: MessageService;
|
||||
|
||||
@inject(BoardsService)
|
||||
private readonly boardsService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
|
||||
@inject(EditorManager)
|
||||
private readonly editorManager: EditorManager;
|
||||
|
||||
@inject(FileService)
|
||||
private readonly fileService: FileService;
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(SketchesService)
|
||||
private readonly sketchService: SketchesService;
|
||||
|
||||
@inject(BoardsConfigDialog)
|
||||
private readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
|
||||
@inject(CommandRegistry)
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(StatusBar)
|
||||
private readonly statusBar: StatusBar;
|
||||
|
||||
@inject(EditorMode)
|
||||
private readonly editorMode: EditorMode;
|
||||
|
||||
@inject(HostedPluginEvents)
|
||||
private readonly hostedPluginEvents: HostedPluginEvents;
|
||||
|
||||
@inject(ExecutableService)
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
private readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
@ -135,26 +77,6 @@ export class ArduinoFrontendContribution
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
private readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(FileSystemFrontendContribution)
|
||||
private readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
|
||||
|
||||
@inject(IDEUpdater)
|
||||
private readonly updater: IDEUpdater;
|
||||
|
||||
@inject(IDEUpdaterDialog)
|
||||
private readonly updaterDialog: IDEUpdaterDialog;
|
||||
|
||||
@inject(ArduinoDaemon)
|
||||
private readonly daemon: ArduinoDaemon;
|
||||
|
||||
protected invalidConfigPopup:
|
||||
| Promise<void | 'No' | 'Yes' | undefined>
|
||||
| undefined;
|
||||
protected toDisposeOnStop = new DisposableCollection();
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
if (!window.navigator.onLine) {
|
||||
@ -166,250 +88,32 @@ export class ArduinoFrontendContribution
|
||||
)
|
||||
);
|
||||
}
|
||||
const updateStatusBar = ({
|
||||
selectedBoard,
|
||||
selectedPort,
|
||||
}: BoardsConfig.Config) => {
|
||||
this.statusBar.setElement('arduino-selected-board', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedBoard
|
||||
? `$(microchip) ${selectedBoard.name}`
|
||||
: `$(close) ${nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
)}`,
|
||||
className: 'arduino-selected-board',
|
||||
});
|
||||
if (selectedBoard) {
|
||||
this.statusBar.setElement('arduino-selected-port', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedPort
|
||||
? nls.localize(
|
||||
'arduino/common/selectedOn',
|
||||
'on {0}',
|
||||
selectedPort.address
|
||||
)
|
||||
: nls.localize('arduino/common/notConnected', '[not connected]'),
|
||||
className: 'arduino-selected-port',
|
||||
});
|
||||
}
|
||||
};
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(updateStatusBar);
|
||||
updateStatusBar(this.boardsServiceClientImpl.boardsConfig);
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (
|
||||
CurrentSketch.isValid(sketch) &&
|
||||
!(await this.sketchService.isTemp(sketch))
|
||||
) {
|
||||
this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri)));
|
||||
this.toDisposeOnStop.push(
|
||||
this.fileService.onDidFilesChange(async (event) => {
|
||||
for (const { type, resource } of event.changes) {
|
||||
if (
|
||||
type === FileChangeType.ADDED &&
|
||||
resource.parent.toString() === sketch.uri
|
||||
) {
|
||||
const reloadedSketch = await this.sketchService.loadSketch(
|
||||
sketch.uri
|
||||
);
|
||||
if (Sketch.isInSketch(resource, reloadedSketch)) {
|
||||
this.ensureOpened(resource.toString(), true, {
|
||||
mode: 'open',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async onStart(app: FrontendApplication): Promise<void> {
|
||||
this.updater
|
||||
.init(
|
||||
this.arduinoPreferences.get('arduino.ide.updateChannel'),
|
||||
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
|
||||
)
|
||||
.then(() => this.updater.checkForUpdates(true))
|
||||
.then(async (updateInfo) => {
|
||||
if (!updateInfo) return;
|
||||
const versionToSkip = await this.localStorageService.getData<string>(
|
||||
SKIP_IDE_VERSION
|
||||
);
|
||||
if (versionToSkip === updateInfo.version) return;
|
||||
this.updaterDialog.open(updateInfo);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/ide-updater/errorCheckingForUpdates',
|
||||
'Error while checking for Arduino IDE updates.\n{0}',
|
||||
e.message
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const start = async (
|
||||
{ selectedBoard }: BoardsConfig.Config,
|
||||
forceStart = false
|
||||
) => {
|
||||
if (selectedBoard) {
|
||||
const { name, fqbn } = selectedBoard;
|
||||
if (fqbn) {
|
||||
this.startLanguageServer(fqbn, name, forceStart);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
|
||||
this.hostedPluginEvents.onPluginsDidStart(() =>
|
||||
start(this.boardsServiceClientImpl.boardsConfig)
|
||||
);
|
||||
this.hostedPluginEvents.onPluginsWillUnload(
|
||||
() => (this.languageServerFqbn = undefined)
|
||||
);
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (event.newValue !== event.oldValue) {
|
||||
switch (event.preferenceName) {
|
||||
case 'arduino.language.log':
|
||||
case 'arduino.language.realTimeDiagnostics':
|
||||
start(this.boardsServiceClientImpl.boardsConfig, true);
|
||||
break;
|
||||
case 'arduino.window.zoomLevel':
|
||||
if (typeof event.newValue === 'number') {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
}
|
||||
break;
|
||||
case 'arduino.ide.updateChannel':
|
||||
case 'arduino.ide.updateBaseUrl':
|
||||
this.updater.init(
|
||||
this.arduinoPreferences.get('arduino.ide.updateChannel'),
|
||||
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.appStateService.reachedState('initialized_layout').then(() =>
|
||||
this.arduinoPreferences.ready.then(() => {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel');
|
||||
const zoomLevel = this.arduinoPreferences.get(
|
||||
'arduino.window.zoomLevel'
|
||||
);
|
||||
webContents.setZoomLevel(zoomLevel);
|
||||
});
|
||||
|
||||
})
|
||||
);
|
||||
// Removes the _Settings_ (cog) icon from the left sidebar
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
|
||||
this.fileSystemFrontendContribution.onDidChangeEditorFile(
|
||||
({ type, editor }) => {
|
||||
if (type === FileChangeType.DELETED) {
|
||||
const editorWidget = editor;
|
||||
if (SaveableWidget.is(editorWidget)) {
|
||||
editorWidget.closeWithoutSaving();
|
||||
} else {
|
||||
editorWidget.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDisposeOnStop.dispose();
|
||||
}
|
||||
|
||||
protected languageServerFqbn?: string;
|
||||
protected languageServerStartMutex = new Mutex();
|
||||
protected async startLanguageServer(
|
||||
fqbn: string,
|
||||
name: string | undefined,
|
||||
forceStart = false
|
||||
): Promise<void> {
|
||||
const port = await this.daemon.tryGetPort();
|
||||
if (!port) {
|
||||
return;
|
||||
}
|
||||
const release = await this.languageServerStartMutex.acquire();
|
||||
try {
|
||||
await this.hostedPluginEvents.didStart;
|
||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
// Core is not installed for the selected board.
|
||||
console.info(
|
||||
`Could not start language server for ${fqbn}. The core is not installed for the board.`
|
||||
);
|
||||
if (this.languageServerFqbn) {
|
||||
try {
|
||||
await this.commandRegistry.executeCommand(
|
||||
'arduino.languageserver.stop'
|
||||
);
|
||||
console.info(
|
||||
`Stopped language server process for ${this.languageServerFqbn}.`
|
||||
);
|
||||
this.languageServerFqbn = undefined;
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Failed to start language server process for ${this.languageServerFqbn}`,
|
||||
e
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!forceStart && fqbn === this.languageServerFqbn) {
|
||||
// NOOP
|
||||
return;
|
||||
}
|
||||
this.logger.info(`Starting language server: ${fqbn}`);
|
||||
const log = this.arduinoPreferences.get('arduino.language.log');
|
||||
const realTimeDiagnostics = this.arduinoPreferences.get(
|
||||
'arduino.language.realTimeDiagnostics'
|
||||
);
|
||||
let currentSketchPath: string | undefined = undefined;
|
||||
if (log) {
|
||||
const currentSketch = await this.sketchServiceClient.currentSketch();
|
||||
if (CurrentSketch.isValid(currentSketch)) {
|
||||
currentSketchPath = await this.fileService.fsPath(
|
||||
new URI(currentSketch.uri)
|
||||
);
|
||||
}
|
||||
}
|
||||
const { clangdUri, lsUri } = await this.executableService.list();
|
||||
const [clangdPath, lsPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(clangdUri)),
|
||||
this.fileService.fsPath(new URI(lsUri)),
|
||||
]);
|
||||
|
||||
this.languageServerFqbn = await Promise.race([
|
||||
new Promise<undefined>((_, reject) =>
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timeout after ${20_000} ms.`)),
|
||||
20_000
|
||||
)
|
||||
),
|
||||
this.commandRegistry.executeCommand<string>(
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
lsPath,
|
||||
cliDaemonAddr: `localhost:${port}`,
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
cliDaemonInstance: '1',
|
||||
realTimeDiagnostics,
|
||||
board: {
|
||||
fqbn,
|
||||
name: name ? `"${name}"` : undefined,
|
||||
},
|
||||
}
|
||||
),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Failed to start language server for ${fqbn}`, e);
|
||||
this.languageServerFqbn = undefined;
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
@ -419,7 +123,7 @@ export class ArduinoFrontendContribution
|
||||
<BoardsToolBarItem
|
||||
key="boardsToolbarItem"
|
||||
commands={this.commandRegistry}
|
||||
boardsServiceClient={this.boardsServiceClientImpl}
|
||||
boardsServiceProvider={this.boardsServiceProvider}
|
||||
/>
|
||||
),
|
||||
isVisible: (widget) =>
|
||||
@ -434,24 +138,6 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG, {
|
||||
execute: () => this.editorMode.toggleCompileForDebug(),
|
||||
isToggled: () => this.editorMode.compileForDebug,
|
||||
});
|
||||
registry.registerCommand(ArduinoCommands.OPEN_SKETCH_FILES, {
|
||||
execute: async (uri: URI) => {
|
||||
this.openSketchFiles(uri);
|
||||
},
|
||||
});
|
||||
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
|
||||
execute: async (query?: string | undefined) => {
|
||||
const boardsConfig = await this.boardsConfigDialog.open(query);
|
||||
if (boardsConfig) {
|
||||
this.boardsServiceClientImpl.boardsConfig = boardsConfig;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
for (const command of [
|
||||
EditorCommands.SPLIT_EDITOR_DOWN,
|
||||
EditorCommands.SPLIT_EDITOR_LEFT,
|
||||
@ -484,70 +170,6 @@ export class ArduinoFrontendContribution
|
||||
ArduinoMenus.TOOLS,
|
||||
nls.localize('arduino/menu/tools', 'Tools')
|
||||
);
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id,
|
||||
label: nls.localize(
|
||||
'arduino/debug/optimizeForDebugging',
|
||||
'Optimize for Debugging'
|
||||
),
|
||||
order: '5',
|
||||
});
|
||||
}
|
||||
|
||||
protected async openSketchFiles(uri: URI): Promise<void> {
|
||||
try {
|
||||
const sketch = await this.sketchService.loadSketch(uri.toString());
|
||||
const { mainFileUri, rootFolderFileUris } = sketch;
|
||||
for (const uri of [mainFileUri, ...rootFolderFileUris]) {
|
||||
await this.ensureOpened(uri);
|
||||
}
|
||||
if (mainFileUri.endsWith('.pde')) {
|
||||
const message = nls.localize(
|
||||
'arduino/common/oldFormat',
|
||||
"The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
|
||||
sketch.name
|
||||
);
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
this.messageService
|
||||
.info(message, nls.localize('arduino/common/later', 'Later'), yes)
|
||||
.then(async (answer) => {
|
||||
if (answer === yes) {
|
||||
this.commandRegistry.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{
|
||||
execOnlyIfTemp: false,
|
||||
openAfterMove: true,
|
||||
wipeOriginal: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
const message = e instanceof Error ? e.message : JSON.stringify(e);
|
||||
this.messageService.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
protected async ensureOpened(
|
||||
uri: string,
|
||||
forceOpen = false,
|
||||
options?: EditorOpenerOptions | undefined
|
||||
): Promise<unknown> {
|
||||
const widget = this.editorManager.all.find(
|
||||
(widget) => widget.editor.uri.toString() === uri
|
||||
);
|
||||
if (!widget || forceOpen) {
|
||||
return this.editorManager.open(
|
||||
new URI(uri),
|
||||
options ?? {
|
||||
mode: 'reveal',
|
||||
preview: false,
|
||||
counter: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
registerColors(colors: ColorRegistry): void {
|
||||
@ -699,6 +321,7 @@ export class ArduinoFrontendContribution
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: should be handled by `Close` contribution. https://github.com/arduino/arduino-ide/issues/1016
|
||||
onWillStop(): OnWillStopAction {
|
||||
return {
|
||||
reason: 'temp-sketch',
|
||||
|
@ -80,7 +80,6 @@ import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browse
|
||||
import { ProblemManager } from './theia/markers/problem-manager';
|
||||
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
||||
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
|
||||
import { EditorMode } from './editor-mode';
|
||||
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
@ -301,10 +300,16 @@ import { CoreErrorHandler } from './contributions/core-error-handler';
|
||||
import { CompilerErrors } from './contributions/compiler-errors';
|
||||
import { WidgetManager } from './theia/core/widget-manager';
|
||||
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
|
||||
import { StartupTask } from './widgets/sketchbook/startup-task';
|
||||
import { StartupTasks } from './widgets/sketchbook/startup-task';
|
||||
import { IndexesUpdateProgress } from './contributions/indexes-update-progress';
|
||||
import { Daemon } from './contributions/daemon';
|
||||
import { FirstStartupInstaller } from './contributions/first-startup-installer';
|
||||
import { OpenSketchFiles } from './contributions/open-sketch-files';
|
||||
import { InoLanguage } from './contributions/ino-language';
|
||||
import { SelectedBoard } from './contributions/selected-board';
|
||||
import { CheckForUpdates } from './contributions/check-for-updates';
|
||||
import { OpenBoardsConfig } from './contributions/open-boards-config';
|
||||
import { SketchFilesTracker } from './contributions/sketch-files-tracker';
|
||||
|
||||
MonacoThemingService.register({
|
||||
id: 'arduino-theme',
|
||||
@ -486,10 +491,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
WorkspaceVariableContribution
|
||||
);
|
||||
|
||||
// Customizing default Theia layout based on the editor mode: `pro-mode` or `classic`.
|
||||
bind(EditorMode).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(EditorMode);
|
||||
|
||||
bind(SurveyNotificationService)
|
||||
.toDynamicValue((context) => {
|
||||
return ElectronIpcConnectionProvider.createProxy(
|
||||
@ -697,10 +698,16 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, PlotterFrontendContribution);
|
||||
Contribution.configure(bind, Format);
|
||||
Contribution.configure(bind, CompilerErrors);
|
||||
Contribution.configure(bind, StartupTask);
|
||||
Contribution.configure(bind, StartupTasks);
|
||||
Contribution.configure(bind, IndexesUpdateProgress);
|
||||
Contribution.configure(bind, Daemon);
|
||||
Contribution.configure(bind, FirstStartupInstaller);
|
||||
Contribution.configure(bind, OpenSketchFiles);
|
||||
Contribution.configure(bind, InoLanguage);
|
||||
Contribution.configure(bind, SelectedBoard);
|
||||
Contribution.configure(bind, CheckForUpdates);
|
||||
Contribution.configure(bind, OpenBoardsConfig);
|
||||
Contribution.configure(bind, SketchFilesTracker);
|
||||
|
||||
// Disabled the quick-pick customization from Theia when multiple formatters are available.
|
||||
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
|
||||
|
@ -92,6 +92,14 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
),
|
||||
default: 'None',
|
||||
},
|
||||
'arduino.compile.optimizeForDebug': {
|
||||
type: 'boolean',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/compile.optimizeForDebug',
|
||||
"Optimize compile output for debug, not for release. It's 'false' by default."
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.upload.verbose': {
|
||||
type: 'boolean',
|
||||
description: nls.localize(
|
||||
@ -185,10 +193,10 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.cloud.sketchSyncEnpoint': {
|
||||
'arduino.cloud.sketchSyncEndpoint': {
|
||||
type: 'string',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.sketchSyncEnpoint',
|
||||
'arduino/preferences/cloud.sketchSyncEndpoint',
|
||||
'The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.'
|
||||
),
|
||||
default: 'https://api2.arduino.cc/create',
|
||||
@ -251,6 +259,7 @@ export interface ArduinoConfiguration {
|
||||
'arduino.compile.experimental': boolean;
|
||||
'arduino.compile.revealRange': ErrorRevealStrategy;
|
||||
'arduino.compile.warnings': CompilerWarnings;
|
||||
'arduino.compile.optimizeForDebug': boolean;
|
||||
'arduino.upload.verbose': boolean;
|
||||
'arduino.upload.verify': boolean;
|
||||
'arduino.window.autoScale': boolean;
|
||||
@ -263,7 +272,7 @@ export interface ArduinoConfiguration {
|
||||
'arduino.cloud.pull.warn': boolean;
|
||||
'arduino.cloud.push.warn': boolean;
|
||||
'arduino.cloud.pushpublic.warn': boolean;
|
||||
'arduino.cloud.sketchSyncEnpoint': string;
|
||||
'arduino.cloud.sketchSyncEndpoint': string;
|
||||
'arduino.auth.clientID': string;
|
||||
'arduino.auth.domain': string;
|
||||
'arduino.auth.audience': string;
|
||||
|
@ -17,10 +17,10 @@ import {
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { naturalCompare } from '../../common/utils';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
import { StorageWrapper } from '../storage-wrapper';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
@ -39,6 +39,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
@inject(NotificationCenter)
|
||||
protected notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected readonly onBoardsConfigChangedEmitter =
|
||||
new Emitter<BoardsConfig.Config>();
|
||||
protected readonly onAvailableBoardsChangedEmitter = new Emitter<
|
||||
@ -87,11 +90,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
this.notifyPlatformUninstalled.bind(this)
|
||||
);
|
||||
|
||||
Promise.all([
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const [attachedBoards, availablePorts] = await Promise.all([
|
||||
this.boardsService.getAttachedBoards(),
|
||||
this.boardsService.getAvailablePorts(),
|
||||
this.loadState(),
|
||||
]).then(async ([attachedBoards, availablePorts]) => {
|
||||
]);
|
||||
this._attachedBoards = attachedBoards;
|
||||
this._availablePorts = availablePorts;
|
||||
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
||||
@ -166,7 +170,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
.then(async (answer) => {
|
||||
if (answer === yes) {
|
||||
this.commandService.executeCommand(
|
||||
ArduinoCommands.OPEN_BOARDS_DIALOG.id,
|
||||
'arduino-open-boards-dialog',
|
||||
selectedBoard.name
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Port } from '../../common/protocol';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
import { OpenBoardsConfig } from '../contributions/open-boards-config';
|
||||
import {
|
||||
BoardsServiceProvider,
|
||||
AvailableBoard,
|
||||
@ -155,7 +155,7 @@ export class BoardsToolBarItem extends React.Component<
|
||||
constructor(props: BoardsToolBarItem.Props) {
|
||||
super(props);
|
||||
|
||||
const { availableBoards } = props.boardsServiceClient;
|
||||
const { availableBoards } = props.boardsServiceProvider;
|
||||
this.state = {
|
||||
availableBoards,
|
||||
coords: 'hidden',
|
||||
@ -167,8 +167,8 @@ export class BoardsToolBarItem extends React.Component<
|
||||
}
|
||||
|
||||
override componentDidMount(): void {
|
||||
this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) =>
|
||||
this.setState({ availableBoards })
|
||||
this.props.boardsServiceProvider.onAvailableBoardsChanged(
|
||||
(availableBoards) => this.setState({ availableBoards })
|
||||
);
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ export class BoardsToolBarItem extends React.Component<
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
protected readonly show = (event: React.MouseEvent<HTMLElement>) => {
|
||||
protected readonly show = (event: React.MouseEvent<HTMLElement>): void => {
|
||||
const { currentTarget: element } = event;
|
||||
if (element instanceof HTMLElement) {
|
||||
if (this.state.coords === 'hidden') {
|
||||
@ -212,7 +212,7 @@ export class BoardsToolBarItem extends React.Component<
|
||||
const protocolIcon = isConnected
|
||||
? iconNameFromProtocol(selectedBoard?.port?.protocol || '')
|
||||
: null;
|
||||
const procolIconClassNames = classNames(
|
||||
const protocolIconClassNames = classNames(
|
||||
'arduino-boards-toolbar-item--protocol',
|
||||
'fa',
|
||||
protocolIcon
|
||||
@ -225,7 +225,7 @@ export class BoardsToolBarItem extends React.Component<
|
||||
title={selectedPortLabel}
|
||||
onClick={this.show}
|
||||
>
|
||||
{protocolIcon && <div className={procolIconClassNames} />}
|
||||
{protocolIcon && <div className={protocolIconClassNames} />}
|
||||
<div
|
||||
className={classNames(
|
||||
'arduino-boards-toolbar-item--label',
|
||||
@ -245,12 +245,12 @@ export class BoardsToolBarItem extends React.Component<
|
||||
...board,
|
||||
onClick: () => {
|
||||
if (board.state === AvailableBoard.State.incomplete) {
|
||||
this.props.boardsServiceClient.boardsConfig = {
|
||||
this.props.boardsServiceProvider.boardsConfig = {
|
||||
selectedPort: board.port,
|
||||
};
|
||||
this.openDialog();
|
||||
} else {
|
||||
this.props.boardsServiceClient.boardsConfig = {
|
||||
this.props.boardsServiceProvider.boardsConfig = {
|
||||
selectedBoard: board,
|
||||
selectedPort: board.port,
|
||||
};
|
||||
@ -264,13 +264,15 @@ export class BoardsToolBarItem extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
protected openDialog = () => {
|
||||
this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id);
|
||||
protected openDialog = (): void => {
|
||||
this.props.commands.executeCommand(
|
||||
OpenBoardsConfig.Commands.OPEN_DIALOG.id
|
||||
);
|
||||
};
|
||||
}
|
||||
export namespace BoardsToolBarItem {
|
||||
export interface Props {
|
||||
readonly boardsServiceClient: BoardsServiceProvider;
|
||||
readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
readonly commands: CommandRegistry;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,64 @@
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
IDEUpdater,
|
||||
SKIP_IDE_VERSION,
|
||||
} from '../../common/protocol/ide-updater';
|
||||
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
|
||||
import { Contribution } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class CheckForUpdates extends Contribution {
|
||||
@inject(IDEUpdater)
|
||||
private readonly updater: IDEUpdater;
|
||||
|
||||
@inject(IDEUpdaterDialog)
|
||||
private readonly updaterDialog: IDEUpdaterDialog;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
private readonly localStorage: LocalStorageService;
|
||||
|
||||
override onStart(): void {
|
||||
this.preferences.onPreferenceChanged(
|
||||
({ preferenceName, newValue, oldValue }) => {
|
||||
if (newValue !== oldValue) {
|
||||
switch (preferenceName) {
|
||||
case 'arduino.ide.updateChannel':
|
||||
case 'arduino.ide.updateBaseUrl':
|
||||
this.updater.init(
|
||||
this.preferences.get('arduino.ide.updateChannel'),
|
||||
this.preferences.get('arduino.ide.updateBaseUrl')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.updater
|
||||
.init(
|
||||
this.preferences.get('arduino.ide.updateChannel'),
|
||||
this.preferences.get('arduino.ide.updateBaseUrl')
|
||||
)
|
||||
.then(() => this.updater.checkForUpdates(true))
|
||||
.then(async (updateInfo) => {
|
||||
if (!updateInfo) return;
|
||||
const versionToSkip = await this.localStorage.getData<string>(
|
||||
SKIP_IDE_VERSION
|
||||
);
|
||||
if (versionToSkip === updateInfo.version) return;
|
||||
this.updaterDialog.open(updateInfo);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/ide-updater/errorCheckingForUpdates',
|
||||
'Error while checking for Arduino IDE updates.\n{0}',
|
||||
e.message
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
@ -29,10 +29,7 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter';
|
||||
import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter';
|
||||
import { CoreError } from '../../common/protocol/core-service';
|
||||
import {
|
||||
ArduinoPreferences,
|
||||
ErrorRevealStrategy,
|
||||
} from '../arduino-preferences';
|
||||
import { ErrorRevealStrategy } from '../arduino-preferences';
|
||||
import { InoSelector } from '../ino-selectors';
|
||||
import { fullRange } from '../utils/monaco';
|
||||
import { Contribution } from './contribution';
|
||||
@ -127,9 +124,6 @@ export class CompilerErrors
|
||||
@inject(CoreErrorHandler)
|
||||
private readonly coreErrorHandler: CoreErrorHandler;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
private readonly preferences: ArduinoPreferences;
|
||||
|
||||
private readonly errors: ErrorDecoration[] = [];
|
||||
private readonly onDidChangeEmitter = new monaco.Emitter<this>();
|
||||
private readonly currentErrorDidChangEmitter = new Emitter<ErrorDecoration>();
|
||||
|
@ -37,7 +37,6 @@ import {
|
||||
CommandContribution,
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
import { SettingsService } from '../dialogs/settings/settings';
|
||||
import {
|
||||
CurrentSketch,
|
||||
@ -90,15 +89,15 @@ export abstract class Contribution
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
@inject(LabelProvider)
|
||||
protected readonly labelProvider: LabelProvider;
|
||||
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly preferences: ArduinoPreferences;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@ -146,9 +145,6 @@ export abstract class SketchContribution extends Contribution {
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly preferences: ArduinoPreferences;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
|
||||
|
@ -12,46 +12,54 @@ import {
|
||||
SketchContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { MaybePromise, nls } from '@theia/core/lib/common';
|
||||
import { MaybePromise, MenuModelRegistry, nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
PreferenceScope,
|
||||
PreferenceService,
|
||||
} from '@theia/core/lib/browser/preferences/preference-service';
|
||||
|
||||
@injectable()
|
||||
export class Debug extends SketchContribution {
|
||||
@inject(HostedPluginSupport)
|
||||
protected hostedPluginSupport: HostedPluginSupport;
|
||||
private readonly hostedPluginSupport: HostedPluginSupport;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(ExecutableService)
|
||||
protected readonly executableService: ExecutableService;
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardService: BoardsService;
|
||||
private readonly boardService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(PreferenceService)
|
||||
private readonly preferenceService: PreferenceService;
|
||||
|
||||
/**
|
||||
* If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled.
|
||||
*/
|
||||
protected _disabledMessages?: string = nls.localize(
|
||||
private _disabledMessages?: string = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
); // Initial pessimism.
|
||||
protected disabledMessageDidChangeEmitter = new Emitter<string | undefined>();
|
||||
protected onDisabledMessageDidChange =
|
||||
private disabledMessageDidChangeEmitter = new Emitter<string | undefined>();
|
||||
private onDisabledMessageDidChange =
|
||||
this.disabledMessageDidChangeEmitter.event;
|
||||
|
||||
protected get disabledMessage(): string | undefined {
|
||||
private get disabledMessage(): string | undefined {
|
||||
return this._disabledMessages;
|
||||
}
|
||||
protected set disabledMessage(message: string | undefined) {
|
||||
private set disabledMessage(message: string | undefined) {
|
||||
this._disabledMessages = message;
|
||||
this.disabledMessageDidChangeEmitter.fire(this._disabledMessages);
|
||||
}
|
||||
|
||||
protected readonly debugToolbarItem = {
|
||||
private readonly debugToolbarItem = {
|
||||
id: Debug.Commands.START_DEBUGGING.id,
|
||||
command: Debug.Commands.START_DEBUGGING.id,
|
||||
tooltip: `${
|
||||
@ -98,12 +106,24 @@ export class Debug extends SketchContribution {
|
||||
ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||
isEnabled: () => !this.disabledMessage,
|
||||
});
|
||||
registry.registerCommand(Debug.Commands.OPTIMIZE_FOR_DEBUG, {
|
||||
execute: () => this.toggleOptimizeForDebug(),
|
||||
isToggled: () => this.isOptimizeForDebug(),
|
||||
});
|
||||
}
|
||||
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem(this.debugToolbarItem);
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: Debug.Commands.OPTIMIZE_FOR_DEBUG.id,
|
||||
label: Debug.Commands.OPTIMIZE_FOR_DEBUG.label,
|
||||
order: '5',
|
||||
});
|
||||
}
|
||||
|
||||
private async refreshState(
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
@ -145,7 +165,7 @@ export class Debug extends SketchContribution {
|
||||
}
|
||||
}
|
||||
|
||||
protected async startDebug(
|
||||
private async startDebug(
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
): Promise<void> {
|
||||
@ -183,8 +203,19 @@ export class Debug extends SketchContribution {
|
||||
};
|
||||
return this.commandService.executeCommand('arduino.debug.start', config);
|
||||
}
|
||||
}
|
||||
|
||||
private isOptimizeForDebug(): boolean {
|
||||
return this.preferences.get('arduino.compile.optimizeForDebug');
|
||||
}
|
||||
|
||||
private async toggleOptimizeForDebug(): Promise<void> {
|
||||
return this.preferenceService.set(
|
||||
'arduino.compile.optimizeForDebug',
|
||||
!this.isOptimizeForDebug(),
|
||||
PreferenceScope.User
|
||||
);
|
||||
}
|
||||
}
|
||||
export namespace Debug {
|
||||
export namespace Commands {
|
||||
export const START_DEBUGGING = Command.toLocalizedCommand(
|
||||
@ -195,5 +226,13 @@ export namespace Debug {
|
||||
},
|
||||
'vscode/debug.contribution/startDebuggingHelp'
|
||||
);
|
||||
export const OPTIMIZE_FOR_DEBUG = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-optimize-for-debug',
|
||||
label: 'Optimize for Debugging',
|
||||
category: 'Arduino',
|
||||
},
|
||||
'arduino/debug/optimizeForDebugging'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
|
||||
import {
|
||||
Contribution,
|
||||
@ -20,13 +19,10 @@ import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/edit
|
||||
@injectable()
|
||||
export class EditContributions extends Contribution {
|
||||
@inject(MonacoEditorService)
|
||||
protected readonly codeEditorService: MonacoEditorService;
|
||||
private readonly codeEditorService: MonacoEditorService;
|
||||
|
||||
@inject(ClipboardService)
|
||||
protected readonly clipboardService: ClipboardService;
|
||||
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferences: PreferenceService;
|
||||
private readonly clipboardService: ClipboardService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(EditContributions.Commands.GO_TO_LINE, {
|
||||
|
154
arduino-ide-extension/src/browser/contributions/ino-language.ts
Normal file
154
arduino-ide-extension/src/browser/contributions/ino-language.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
ArduinoDaemon,
|
||||
BoardsService,
|
||||
ExecutableService,
|
||||
} from '../../common/protocol';
|
||||
import { HostedPluginEvents } from '../hosted-plugin-events';
|
||||
import { SketchContribution, URI } from './contribution';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
|
||||
@injectable()
|
||||
export class InoLanguage extends SketchContribution {
|
||||
@inject(HostedPluginEvents)
|
||||
private readonly hostedPluginEvents: HostedPluginEvents;
|
||||
|
||||
@inject(ExecutableService)
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(ArduinoDaemon)
|
||||
private readonly daemon: ArduinoDaemon;
|
||||
|
||||
@inject(BoardsService)
|
||||
private readonly boardsService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
private languageServerFqbn?: string;
|
||||
private languageServerStartMutex = new Mutex();
|
||||
|
||||
override onReady(): void {
|
||||
const start = (
|
||||
{ selectedBoard }: BoardsConfig.Config,
|
||||
forceStart = false
|
||||
) => {
|
||||
if (selectedBoard) {
|
||||
const { name, fqbn } = selectedBoard;
|
||||
if (fqbn) {
|
||||
this.startLanguageServer(fqbn, name, forceStart);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(start);
|
||||
this.hostedPluginEvents.onPluginsDidStart(() =>
|
||||
start(this.boardsServiceProvider.boardsConfig)
|
||||
);
|
||||
this.hostedPluginEvents.onPluginsWillUnload(
|
||||
() => (this.languageServerFqbn = undefined)
|
||||
);
|
||||
this.preferences.onPreferenceChanged(
|
||||
({ preferenceName, oldValue, newValue }) => {
|
||||
if (oldValue !== newValue) {
|
||||
switch (preferenceName) {
|
||||
case 'arduino.language.log':
|
||||
case 'arduino.language.realTimeDiagnostics':
|
||||
start(this.boardsServiceProvider.boardsConfig, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
start(this.boardsServiceProvider.boardsConfig);
|
||||
}
|
||||
|
||||
private async startLanguageServer(
|
||||
fqbn: string,
|
||||
name: string | undefined,
|
||||
forceStart = false
|
||||
): Promise<void> {
|
||||
const port = await this.daemon.tryGetPort();
|
||||
if (!port) {
|
||||
return;
|
||||
}
|
||||
const release = await this.languageServerStartMutex.acquire();
|
||||
try {
|
||||
await this.hostedPluginEvents.didStart;
|
||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
// Core is not installed for the selected board.
|
||||
console.info(
|
||||
`Could not start language server for ${fqbn}. The core is not installed for the board.`
|
||||
);
|
||||
if (this.languageServerFqbn) {
|
||||
try {
|
||||
await this.commandService.executeCommand(
|
||||
'arduino.languageserver.stop'
|
||||
);
|
||||
console.info(
|
||||
`Stopped language server process for ${this.languageServerFqbn}.`
|
||||
);
|
||||
this.languageServerFqbn = undefined;
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Failed to start language server process for ${this.languageServerFqbn}`,
|
||||
e
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!forceStart && fqbn === this.languageServerFqbn) {
|
||||
// NOOP
|
||||
return;
|
||||
}
|
||||
this.logger.info(`Starting language server: ${fqbn}`);
|
||||
const log = this.preferences.get('arduino.language.log');
|
||||
let currentSketchPath: string | undefined = undefined;
|
||||
if (log) {
|
||||
const currentSketch = await this.sketchServiceClient.currentSketch();
|
||||
if (CurrentSketch.isValid(currentSketch)) {
|
||||
currentSketchPath = await this.fileService.fsPath(
|
||||
new URI(currentSketch.uri)
|
||||
);
|
||||
}
|
||||
}
|
||||
const { clangdUri, lsUri } = await this.executableService.list();
|
||||
const [clangdPath, lsPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(clangdUri)),
|
||||
this.fileService.fsPath(new URI(lsUri)),
|
||||
]);
|
||||
|
||||
this.languageServerFqbn = await Promise.race([
|
||||
new Promise<undefined>((_, reject) =>
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timeout after ${20_000} ms.`)),
|
||||
20_000
|
||||
)
|
||||
),
|
||||
this.commandService.executeCommand<string>(
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
lsPath,
|
||||
cliDaemonAddr: `localhost:${port}`,
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
cliDaemonInstance: '1',
|
||||
board: {
|
||||
fqbn,
|
||||
name: name ? `"${name}"` : undefined,
|
||||
},
|
||||
}
|
||||
),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Failed to start language server for ${fqbn}`, e);
|
||||
this.languageServerFqbn = undefined;
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import { CommandRegistry } from '@theia/core';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { BoardsConfigDialog } from '../boards/boards-config-dialog';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { Contribution, Command } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class OpenBoardsConfig extends Contribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(BoardsConfigDialog)
|
||||
private readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(OpenBoardsConfig.Commands.OPEN_DIALOG, {
|
||||
execute: async (query?: string | undefined) => {
|
||||
const boardsConfig = await this.boardsConfigDialog.open(query);
|
||||
if (boardsConfig) {
|
||||
this.boardsServiceProvider.boardsConfig = boardsConfig;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
export namespace OpenBoardsConfig {
|
||||
export namespace Commands {
|
||||
export const OPEN_DIALOG: Command = {
|
||||
id: 'arduino-open-boards-dialog',
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import type { EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { SketchesError } from '../../common/protocol';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
SketchContribution,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { SaveAsSketch } from './save-as-sketch';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketchFiles extends SketchContribution {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(OpenSketchFiles.Commands.OPEN_SKETCH_FILES, {
|
||||
execute: (uri: URI) => this.openSketchFiles(uri),
|
||||
});
|
||||
registry.registerCommand(OpenSketchFiles.Commands.ENSURE_OPENED, {
|
||||
execute: (
|
||||
uri: string,
|
||||
forceOpen?: boolean,
|
||||
options?: EditorOpenerOptions
|
||||
) => {
|
||||
this.ensureOpened(uri, forceOpen, options);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async openSketchFiles(uri: URI): Promise<void> {
|
||||
try {
|
||||
const sketch = await this.sketchService.loadSketch(uri.toString());
|
||||
const { mainFileUri, rootFolderFileUris } = sketch;
|
||||
for (const uri of [mainFileUri, ...rootFolderFileUris]) {
|
||||
await this.ensureOpened(uri);
|
||||
}
|
||||
if (mainFileUri.endsWith('.pde')) {
|
||||
const message = nls.localize(
|
||||
'arduino/common/oldFormat',
|
||||
"The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
|
||||
sketch.name
|
||||
);
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
this.messageService
|
||||
.info(message, nls.localize('arduino/common/later', 'Later'), yes)
|
||||
.then(async (answer) => {
|
||||
if (answer === yes) {
|
||||
this.commandService.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{
|
||||
execOnlyIfTemp: false,
|
||||
openAfterMove: true,
|
||||
wipeOriginal: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
if (SketchesError.NotFound.is(err)) {
|
||||
this.openFallbackSketch();
|
||||
} else {
|
||||
console.error(err);
|
||||
const message =
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: typeof err === 'string'
|
||||
? err
|
||||
: String(err);
|
||||
this.messageService.error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async openFallbackSketch(): Promise<void> {
|
||||
const sketch = await this.sketchService.createNewSketch();
|
||||
this.workspaceService.open(new URI(sketch.uri), { preserveWindow: true });
|
||||
}
|
||||
|
||||
private async ensureOpened(
|
||||
uri: string,
|
||||
forceOpen = false,
|
||||
options?: EditorOpenerOptions
|
||||
): Promise<unknown> {
|
||||
const widget = this.editorManager.all.find(
|
||||
(widget) => widget.editor.uri.toString() === uri
|
||||
);
|
||||
if (!widget || forceOpen) {
|
||||
return this.editorManager.open(
|
||||
new URI(uri),
|
||||
options ?? {
|
||||
mode: 'reveal',
|
||||
preview: false,
|
||||
counter: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export namespace OpenSketchFiles {
|
||||
export namespace Commands {
|
||||
export const OPEN_SKETCH_FILES: Command = {
|
||||
id: 'arduino-open-sketch-files',
|
||||
};
|
||||
export const ENSURE_OPENED: Command = {
|
||||
id: 'arduino-ensure-opened',
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import {
|
||||
StatusBar,
|
||||
StatusBarAlignment,
|
||||
} from '@theia/core/lib/browser/status-bar/status-bar';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { Contribution } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class SelectedBoard extends Contribution {
|
||||
@inject(StatusBar)
|
||||
private readonly statusBar: StatusBar;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
override onStart(): void {
|
||||
this.boardsServiceProvider.onBoardsConfigChanged((config) =>
|
||||
this.update(config)
|
||||
);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.update(this.boardsServiceProvider.boardsConfig);
|
||||
}
|
||||
|
||||
private update({ selectedBoard, selectedPort }: BoardsConfig.Config): void {
|
||||
this.statusBar.setElement('arduino-selected-board', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedBoard
|
||||
? `$(microchip) ${selectedBoard.name}`
|
||||
: `$(close) ${nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
)}`,
|
||||
className: 'arduino-selected-board',
|
||||
});
|
||||
if (selectedBoard) {
|
||||
this.statusBar.setElement('arduino-selected-port', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedPort
|
||||
? nls.localize(
|
||||
'arduino/common/selectedOn',
|
||||
'on {0}',
|
||||
selectedPort.address
|
||||
)
|
||||
: nls.localize('arduino/common/notConnected', '[not connected]'),
|
||||
className: 'arduino-selected-port',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
import { SaveableWidget } from '@theia/core/lib/browser/saveable';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/common/files';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
import { Sketch, SketchContribution, URI } from './contribution';
|
||||
import { OpenSketchFiles } from './open-sketch-files';
|
||||
|
||||
@injectable()
|
||||
export class SketchFilesTracker extends SketchContribution {
|
||||
@inject(FileSystemFrontendContribution)
|
||||
private readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
|
||||
private readonly toDisposeOnStop = new DisposableCollection();
|
||||
|
||||
override onStart(): void {
|
||||
this.fileSystemFrontendContribution.onDidChangeEditorFile(
|
||||
({ type, editor }) => {
|
||||
if (type === FileChangeType.DELETED) {
|
||||
const editorWidget = editor;
|
||||
if (SaveableWidget.is(editorWidget)) {
|
||||
editorWidget.closeWithoutSaving();
|
||||
} else {
|
||||
editorWidget.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.sketchServiceClient.currentSketch().then(async (sketch) => {
|
||||
if (
|
||||
CurrentSketch.isValid(sketch) &&
|
||||
!(await this.sketchService.isTemp(sketch))
|
||||
) {
|
||||
this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri)));
|
||||
this.toDisposeOnStop.push(
|
||||
this.fileService.onDidFilesChange(async (event) => {
|
||||
for (const { type, resource } of event.changes) {
|
||||
if (
|
||||
type === FileChangeType.ADDED &&
|
||||
resource.parent.toString() === sketch.uri
|
||||
) {
|
||||
const reloadedSketch = await this.sketchService.loadSketch(
|
||||
sketch.uri
|
||||
);
|
||||
if (Sketch.isInSketch(resource, reloadedSketch)) {
|
||||
this.commandService.executeCommand(
|
||||
OpenSketchFiles.Commands.ENSURE_OPENED.id,
|
||||
resource.toString(),
|
||||
true,
|
||||
{
|
||||
mode: 'open',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDisposeOnStop.dispose();
|
||||
}
|
||||
}
|
@ -5,7 +5,11 @@ import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { Examples } from './examples';
|
||||
import { SketchContainer } from '../../common/protocol';
|
||||
import {
|
||||
SketchContainer,
|
||||
SketchesError,
|
||||
SketchRef,
|
||||
} from '../../common/protocol';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@ -24,15 +28,14 @@ export class Sketchbook extends Examples {
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
override onStart(): void {
|
||||
this.sketchServiceClient.onSketchbookDidChange(() => {
|
||||
this.sketchService.getSketches({}).then((container) => {
|
||||
this.register(container);
|
||||
this.mainMenuManager.update();
|
||||
});
|
||||
});
|
||||
this.sketchServiceClient.onSketchbookDidChange(() => this.update());
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update() {
|
||||
this.sketchService.getSketches({}).then((container) => {
|
||||
this.register(container);
|
||||
this.mainMenuManager.update();
|
||||
@ -59,11 +62,24 @@ export class Sketchbook extends Examples {
|
||||
protected override createHandler(uri: string): CommandHandler {
|
||||
return {
|
||||
execute: async () => {
|
||||
const sketch = await this.sketchService.loadSketch(uri);
|
||||
return this.commandService.executeCommand(
|
||||
let sketch: SketchRef | undefined = undefined;
|
||||
try {
|
||||
sketch = await this.sketchService.loadSketch(uri);
|
||||
} catch (err) {
|
||||
if (SketchesError.NotFound.is(err)) {
|
||||
// To handle the following:
|
||||
// Open IDE2, delete a sketch from sketchbook, click on File > Sketchbook > the deleted sketch.
|
||||
// Filesystem watcher misses out delete events on macOS; hence IDE2 has no chance to update the menu items.
|
||||
this.messageService.error(err.message);
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
if (sketch) {
|
||||
await this.commandService.executeCommand(
|
||||
OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
sketch
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -227,7 +227,9 @@ export class UploadSketch extends CoreServiceContribution {
|
||||
fqbn,
|
||||
};
|
||||
let options: CoreService.Upload.Options | undefined = undefined;
|
||||
const optimizeForDebug = this.editorMode.compileForDebug;
|
||||
const optimizeForDebug = this.preferences.get(
|
||||
'arduino.compile.optimizeForDebug'
|
||||
);
|
||||
const { selectedPort } = boardsConfig;
|
||||
const port = selectedPort;
|
||||
const userFields =
|
||||
|
@ -114,11 +114,14 @@ export class VerifySketch extends CoreServiceContribution {
|
||||
};
|
||||
const verbose = this.preferences.get('arduino.compile.verbose');
|
||||
const compilerWarnings = this.preferences.get('arduino.compile.warnings');
|
||||
const optimizeForDebug = this.preferences.get(
|
||||
'arduino.compile.optimizeForDebug'
|
||||
);
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
await this.coreService.compile({
|
||||
sketch,
|
||||
board,
|
||||
optimizeForDebug: this.editorMode.compileForDebug,
|
||||
optimizeForDebug,
|
||||
verbose,
|
||||
exportBinaries,
|
||||
sourceOverride,
|
||||
|
@ -507,7 +507,8 @@ export class CreateApi {
|
||||
}
|
||||
|
||||
private domain(apiVersion = 'v2'): string {
|
||||
const endpoint = this.arduinoPreferences['arduino.cloud.sketchSyncEnpoint'];
|
||||
const endpoint =
|
||||
this.arduinoPreferences['arduino.cloud.sketchSyncEndpoint'];
|
||||
return `${endpoint}/${apiVersion}`;
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,10 @@ import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
ProgressInfo,
|
||||
SKIP_IDE_VERSION,
|
||||
UpdateInfo,
|
||||
} from '../../../common/protocol/ide-updater';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser';
|
||||
import { SKIP_IDE_VERSION } from '../../arduino-frontend-contribution';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
|
||||
@injectable()
|
||||
|
@ -1,37 +0,0 @@
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
FrontendApplication,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { MainMenuManager } from '../common/main-menu-manager';
|
||||
|
||||
@injectable()
|
||||
export class EditorMode implements FrontendApplicationContribution {
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
protected app: FrontendApplication;
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
get compileForDebug(): boolean {
|
||||
const value = window.localStorage.getItem(EditorMode.COMPILE_FOR_DEBUG_KEY);
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
async toggleCompileForDebug(): Promise<void> {
|
||||
const oldState = this.compileForDebug;
|
||||
const newState = !oldState;
|
||||
window.localStorage.setItem(
|
||||
EditorMode.COMPILE_FOR_DEBUG_KEY,
|
||||
String(newState)
|
||||
);
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
}
|
||||
|
||||
export namespace EditorMode {
|
||||
export const COMPILE_FOR_DEBUG_KEY = 'arduino-compile-for-debug';
|
||||
}
|
@ -134,21 +134,3 @@ button.secondary[disabled], .theia-button.secondary[disabled] {
|
||||
.fa-reload {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* restore the old Theia spinner */
|
||||
/* https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318 */
|
||||
.old-theia-preload {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50000;
|
||||
background: var(--theia-editor-background);
|
||||
background-image: var(--theia-preloader);
|
||||
background-size: 60px 60px;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
transition: opacity 0.8s;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { SketchesService } from '../../../common/protocol';
|
||||
import { ArduinoCommands } from '../../arduino-commands';
|
||||
import { OpenSketchFiles } from '../../contributions/open-sketch-files';
|
||||
|
||||
@injectable()
|
||||
export class FrontendApplication extends TheiaFrontendApplication {
|
||||
@ -25,33 +25,11 @@ export class FrontendApplication extends TheiaFrontendApplication {
|
||||
this.workspaceService.roots.then(async (roots) => {
|
||||
for (const root of roots) {
|
||||
await this.commandService.executeCommand(
|
||||
ArduinoCommands.OPEN_SKETCH_FILES.id,
|
||||
OpenSketchFiles.Commands.OPEN_SKETCH_FILES.id,
|
||||
root.resource
|
||||
);
|
||||
this.sketchesService.markAsRecentlyOpened(root.resource.toString()); // no await, will get the notification later and rebuild the menu
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override getStartupIndicator(
|
||||
host: HTMLElement
|
||||
): HTMLElement | undefined {
|
||||
let startupElement = this.doGetStartupIndicator(host, 'old-theia-preload'); // https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318
|
||||
if (!startupElement) {
|
||||
startupElement = this.doGetStartupIndicator(host, 'theia-preload'); // We show the new Theia spinner in dev mode.
|
||||
}
|
||||
return startupElement;
|
||||
}
|
||||
|
||||
private doGetStartupIndicator(
|
||||
host: HTMLElement,
|
||||
classNames: string
|
||||
): HTMLElement | undefined {
|
||||
const elements = host.getElementsByClassName(classNames);
|
||||
const first = elements[0];
|
||||
if (first instanceof HTMLElement) {
|
||||
return first;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,11 @@ import {
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { BoardsConfig } from '../../boards/boards-config';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import { StartupTask } from '../../widgets/sketchbook/startup-task';
|
||||
import {
|
||||
StartupTask,
|
||||
StartupTasks,
|
||||
} from '../../widgets/sketchbook/startup-task';
|
||||
import { setURL } from '../../utils/window';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceService extends TheiaWorkspaceService {
|
||||
@ -60,6 +64,17 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
this.onCurrentWidgetChange({ newValue, oldValue: null });
|
||||
}
|
||||
|
||||
protected override async toFileStat(
|
||||
uri: string | URI | undefined
|
||||
): Promise<FileStat | undefined> {
|
||||
const stat = await super.toFileStat(uri);
|
||||
if (!stat) {
|
||||
const newSketchUri = await this.sketchService.createNewSketch();
|
||||
return this.toFileStat(newSketchUri.uri);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
// Was copied from the Theia implementation.
|
||||
// Unlike the default behavior, IDE2 does not check the existence of the workspace before open.
|
||||
protected override async doGetDefaultWorkspaceUri(): Promise<
|
||||
@ -78,6 +93,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
const wpPath = decodeURI(window.location.hash.substring(1));
|
||||
const workspaceUri = new URI().withPath(wpPath).withScheme('file');
|
||||
// ### Customization! Here, we do no check if the workspace exists.
|
||||
// ### The error or missing sketch handling is done in the customized `toFileStat`.
|
||||
return workspaceUri.toString();
|
||||
} else {
|
||||
// Else, ask the server for its suggested workspace (usually the one
|
||||
@ -127,7 +143,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
protected override openWindow(uri: FileStat, options?: WorkspaceInput): void {
|
||||
const workspacePath = uri.resource.path.toString();
|
||||
if (this.shouldPreserveWindow(options)) {
|
||||
this.reloadWindow();
|
||||
this.reloadWindow(options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
} else {
|
||||
try {
|
||||
this.openNewWindow(workspacePath, options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
@ -139,21 +155,25 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}
|
||||
}
|
||||
|
||||
protected override reloadWindow(options?: WorkspaceInput): void {
|
||||
if (StartupTasks.WorkspaceInput.is(options)) {
|
||||
setURL(StartupTask.append(options.tasks, new URL(window.location.href)));
|
||||
}
|
||||
super.reloadWindow();
|
||||
}
|
||||
|
||||
protected override openNewWindow(
|
||||
workspacePath: string,
|
||||
options?: WorkspaceInput
|
||||
): void {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
const url = BoardsConfig.Config.setConfig(
|
||||
let url = BoardsConfig.Config.setConfig(
|
||||
boardsConfig,
|
||||
new URL(window.location.href)
|
||||
); // Set the current boards config for the new browser window.
|
||||
url.hash = workspacePath;
|
||||
if (StartupTask.WorkspaceInput.is(options)) {
|
||||
url.searchParams.set(
|
||||
StartupTask.QUERY_STRING,
|
||||
encodeURIComponent(JSON.stringify(options.tasks))
|
||||
);
|
||||
if (StartupTasks.WorkspaceInput.is(options)) {
|
||||
url = StartupTask.append(options.tasks, url);
|
||||
}
|
||||
|
||||
this.windowService.openNewWindow(url.toString());
|
||||
|
@ -10,21 +10,31 @@ import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../../common/protocol/sketches-service-client-impl';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceVariableContribution extends TheiaWorkspaceVariableContribution {
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
private readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
protected currentSketch?: Sketch;
|
||||
private currentSketch?: Sketch;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
this.sketchesServiceClient.currentSketch().then((sketch) => {
|
||||
const sketch = this.sketchesServiceClient.tryGetCurrentSketch();
|
||||
if (CurrentSketch.isValid(sketch)) {
|
||||
this.currentSketch = sketch;
|
||||
} else {
|
||||
const toDispose = new DisposableCollection();
|
||||
toDispose.push(
|
||||
this.sketchesServiceClient.onCurrentSketchDidChange((sketch) => {
|
||||
if (CurrentSketch.isValid(sketch)) {
|
||||
this.currentSketch = sketch;
|
||||
}
|
||||
});
|
||||
toDispose.dispose();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
override getResourceUri(): URI | undefined {
|
||||
|
7
arduino-ide-extension/src/browser/utils/window.ts
Normal file
7
arduino-ide-extension/src/browser/utils/window.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Changes the `window.location` without navigating away.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function setURL(url: URL, data: any = {}): void {
|
||||
history.pushState(data, '', url);
|
||||
}
|
@ -1,36 +1,86 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { WorkspaceInput as TheiaWorkspaceInput } from '@theia/workspace/lib/browser';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { setURL } from '../../utils/window';
|
||||
|
||||
export interface Task {
|
||||
@injectable()
|
||||
export class StartupTasks extends Contribution {
|
||||
override onReady(): void {
|
||||
const tasks = StartupTask.get(new URL(window.location.href));
|
||||
console.log(`Executing startup tasks: ${JSON.stringify(tasks)}`);
|
||||
tasks.forEach(({ command, args = [] }) =>
|
||||
this.commandService
|
||||
.executeCommand(command, ...args)
|
||||
.catch((err) =>
|
||||
console.error(
|
||||
`Error occurred when executing the startup task '${command}'${
|
||||
args?.length ? ` with args: '${JSON.stringify(args)}` : ''
|
||||
}.`,
|
||||
err
|
||||
)
|
||||
)
|
||||
);
|
||||
if (tasks.length) {
|
||||
// Remove the startup tasks after the execution.
|
||||
// Otherwise, IDE2 executes them again on a window reload event.
|
||||
setURL(StartupTask.set([], new URL(window.location.href)));
|
||||
console.info(`Removed startup tasks from URL.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface StartupTask {
|
||||
command: string;
|
||||
/**
|
||||
* This must be JSON serializable.
|
||||
* Must be JSON serializable.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
args?: any[];
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class StartupTask extends Contribution {
|
||||
override onReady(): void {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const encoded = params.get(StartupTask.QUERY_STRING);
|
||||
if (!encoded) return;
|
||||
|
||||
const commands = JSON.parse(decodeURIComponent(encoded));
|
||||
|
||||
if (Array.isArray(commands)) {
|
||||
commands.forEach(({ command, args }) => {
|
||||
this.commandService.executeCommand(command, ...args);
|
||||
});
|
||||
export namespace StartupTask {
|
||||
const QUERY = 'startupTasks';
|
||||
export function is(arg: unknown): arg is StartupTasks {
|
||||
if (typeof arg === 'object') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const object = arg as any;
|
||||
return 'command' in object && typeof object['command'] === 'string';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function get(url: URL): StartupTask[] {
|
||||
const { searchParams } = url;
|
||||
const encodedTasks = searchParams.get(QUERY);
|
||||
if (encodedTasks) {
|
||||
const rawTasks = decodeURIComponent(encodedTasks);
|
||||
const tasks = JSON.parse(rawTasks);
|
||||
if (Array.isArray(tasks)) {
|
||||
return tasks.filter((task) => {
|
||||
if (StartupTask.is(task)) {
|
||||
return true;
|
||||
}
|
||||
console.warn(`Was not a task: ${JSON.stringify(task)}. Ignoring.`);
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
debugger;
|
||||
console.warn(`Startup tasks was not an array: ${rawTasks}. Ignoring.`);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
export function set(tasks: StartupTask[], url: URL): URL {
|
||||
const copy = new URL(url);
|
||||
copy.searchParams.set(QUERY, encodeURIComponent(JSON.stringify(tasks)));
|
||||
return copy;
|
||||
}
|
||||
export function append(tasks: StartupTask[], url: URL): URL {
|
||||
return set([...get(url), ...tasks], url);
|
||||
}
|
||||
}
|
||||
export namespace StartupTask {
|
||||
export const QUERY_STRING = 'startupTasks';
|
||||
|
||||
export namespace StartupTasks {
|
||||
export interface WorkspaceInput extends TheiaWorkspaceInput {
|
||||
tasks: Task[];
|
||||
tasks: StartupTask[];
|
||||
}
|
||||
export namespace WorkspaceInput {
|
||||
export function is(
|
||||
|
@ -69,3 +69,5 @@ export interface IDEUpdaterClient {
|
||||
notifyDownloadProgressChanged(message: ProgressInfo): void;
|
||||
notifyDownloadFinished(message: UpdateInfo): void;
|
||||
}
|
||||
|
||||
export const SKIP_IDE_VERSION = 'skipIDEVersion';
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
} from '../../browser/utils/constants';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
const READ_ONLY_FILES = ['sketch.json'];
|
||||
const READ_ONLY_FILES_REMOTE = ['thingProperties.h', 'thingsProperties.h'];
|
||||
@ -47,7 +48,9 @@ export class SketchesServiceClientImpl
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
protected toDispose = new DisposableCollection();
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected sketches = new Map<string, SketchRef>();
|
||||
// TODO: rename this + event to the `onBlabla` pattern
|
||||
protected sketchbookDidChangeEmitter = new Emitter<{
|
||||
@ -55,8 +58,16 @@ export class SketchesServiceClientImpl
|
||||
removed: SketchRef[];
|
||||
}>();
|
||||
readonly onSketchbookDidChange = this.sketchbookDidChangeEmitter.event;
|
||||
protected currentSketchDidChangeEmitter = new Emitter<CurrentSketch>();
|
||||
readonly onCurrentSketchDidChange = this.currentSketchDidChangeEmitter.event;
|
||||
|
||||
private _currentSketch = new Deferred<CurrentSketch>();
|
||||
protected toDispose = new DisposableCollection(
|
||||
this.sketchbookDidChangeEmitter,
|
||||
this.currentSketchDidChangeEmitter
|
||||
);
|
||||
|
||||
private _currentSketch: CurrentSketch | undefined;
|
||||
private currentSketchLoaded = new Deferred<CurrentSketch>();
|
||||
|
||||
onStart(): void {
|
||||
this.configService.getConfiguration().then(({ sketchDirUri }) => {
|
||||
@ -110,9 +121,14 @@ export class SketchesServiceClientImpl
|
||||
);
|
||||
});
|
||||
});
|
||||
this.loadCurrentSketch().then((currentSketch) =>
|
||||
this._currentSketch.resolve(currentSketch)
|
||||
);
|
||||
this.appStateService
|
||||
.reachedState('started_contributions')
|
||||
.then(async () => {
|
||||
const currentSketch = await this.loadCurrentSketch();
|
||||
this._currentSketch = currentSketch;
|
||||
this.currentSketchDidChangeEmitter.fire(this._currentSketch);
|
||||
this.currentSketchLoaded.resolve(this._currentSketch);
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@ -143,7 +159,11 @@ export class SketchesServiceClientImpl
|
||||
}
|
||||
|
||||
async currentSketch(): Promise<CurrentSketch> {
|
||||
return this._currentSketch.promise;
|
||||
return this.currentSketchLoaded.promise;
|
||||
}
|
||||
|
||||
tryGetCurrentSketch(): CurrentSketch | undefined {
|
||||
return this._currentSketch;
|
||||
}
|
||||
|
||||
async currentSketchFile(): Promise<string | undefined> {
|
||||
|
@ -1,5 +1,21 @@
|
||||
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
|
||||
export namespace SketchesError {
|
||||
export const Codes = {
|
||||
NotFound: 5001,
|
||||
};
|
||||
export const NotFound = ApplicationError.declare(
|
||||
Codes.NotFound,
|
||||
(message: string, uri: string) => {
|
||||
return {
|
||||
message,
|
||||
data: { uri },
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const SketchesServicePath = '/services/sketches-service';
|
||||
export const SketchesService = Symbol('SketchesService');
|
||||
export interface SketchesService {
|
||||
|
@ -10,12 +10,13 @@ import { promisify } from 'util';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { isWindows, isOSX } from '@theia/core/lib/common/os';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
import {
|
||||
SketchesService,
|
||||
Sketch,
|
||||
SketchRef,
|
||||
SketchContainer,
|
||||
SketchesError,
|
||||
} from '../common/protocol/sketches-service';
|
||||
import { firstToLowerCase } from '../common/utils';
|
||||
import { NotificationServiceServerImpl } from './notification-service-server';
|
||||
@ -28,6 +29,7 @@ import {
|
||||
import { duration } from '../common/decorators';
|
||||
import * as glob from 'glob';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { ServiceError } from './service-error';
|
||||
|
||||
const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
|
||||
|
||||
@ -48,8 +50,8 @@ export class SketchesServiceImpl
|
||||
? tempDir
|
||||
: maybeNormalizeDrive(fs.realpathSync.native(tempDir));
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
|
||||
@inject(NotificationServiceServerImpl)
|
||||
protected readonly notificationService: NotificationServiceServerImpl;
|
||||
@ -201,7 +203,18 @@ export class SketchesServiceImpl
|
||||
const sketch = await new Promise<SketchWithDetails>((resolve, reject) => {
|
||||
client.loadSketch(req, async (err, resp) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
reject(
|
||||
isNotFoundError(err)
|
||||
? SketchesError.NotFound(
|
||||
fixErrorMessage(
|
||||
err,
|
||||
requestSketchPath,
|
||||
this.configService.cliConfiguration?.directories.user
|
||||
),
|
||||
uri
|
||||
)
|
||||
: err
|
||||
);
|
||||
return;
|
||||
}
|
||||
const responseSketchPath = maybeNormalizeDrive(resp.getLocationPath());
|
||||
@ -448,27 +461,16 @@ void loop() {
|
||||
private async _isSketchFolder(
|
||||
uri: string
|
||||
): Promise<SketchWithDetails | undefined> {
|
||||
const fsPath = FileUri.fsPath(uri);
|
||||
let stat: fs.Stats | undefined;
|
||||
try {
|
||||
stat = await promisify(fs.lstat)(fsPath);
|
||||
} catch {}
|
||||
if (stat && stat.isDirectory()) {
|
||||
const basename = path.basename(fsPath);
|
||||
const files = await promisify(fs.readdir)(fsPath);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (files[i] === basename + '.ino' || files[i] === basename + '.pde') {
|
||||
try {
|
||||
const sketch = await this.loadSketch(
|
||||
FileUri.create(fsPath).toString()
|
||||
);
|
||||
const sketch = await this.loadSketch(uri);
|
||||
return sketch;
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (SketchesError.NotFound.is(err)) {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async isTemp(sketch: SketchRef): Promise<boolean> {
|
||||
// Consider the following paths:
|
||||
@ -588,6 +590,40 @@ interface SketchWithDetails extends Sketch {
|
||||
readonly mtimeMs: number;
|
||||
}
|
||||
|
||||
// https://github.com/arduino/arduino-cli/issues/1797
|
||||
function fixErrorMessage(
|
||||
err: ServiceError,
|
||||
sketchPath: string,
|
||||
sketchbookPath: string | undefined
|
||||
): string {
|
||||
if (!sketchbookPath) {
|
||||
return err.details; // No way to repair the error message. The current sketchbook path is not available.
|
||||
}
|
||||
// Original: `Can't open sketch: no valid sketch found in /Users/a.kitta/Documents/Arduino: missing /Users/a.kitta/Documents/Arduino/Arduino.ino`
|
||||
// Fixed: `Can't open sketch: no valid sketch found in /Users/a.kitta/Documents/Arduino: missing $sketchPath`
|
||||
const message = err.details;
|
||||
const incorrectMessageSuffix = path.join(sketchbookPath, 'Arduino.ino');
|
||||
if (
|
||||
message.startsWith("Can't open sketch: no valid sketch found in") &&
|
||||
message.endsWith(`${incorrectMessageSuffix}`)
|
||||
) {
|
||||
const sketchName = path.basename(sketchPath);
|
||||
const correctMessagePrefix = message.substring(
|
||||
0,
|
||||
message.length - incorrectMessageSuffix.length
|
||||
);
|
||||
return `${correctMessagePrefix}${path.join(
|
||||
sketchPath,
|
||||
`${sketchName}.ino`
|
||||
)}`;
|
||||
}
|
||||
return err.details;
|
||||
}
|
||||
|
||||
function isNotFoundError(err: unknown): err is ServiceError {
|
||||
return ServiceError.is(err) && err.code === 5; // `NOT_FOUND` https://grpc.github.io/grpc/core/md_doc_statuscodes.html
|
||||
}
|
||||
|
||||
/**
|
||||
* If on Windows, will change the input `C:\\path\\to\\somewhere` to `c:\\path\\to\\somewhere`.
|
||||
*/
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Patch the Theia spinner: https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318
|
||||
// Replaces the `theia-preload` selector with `old-theia-preload` in the generated `index.html`.
|
||||
let arg = process.argv.splice(2)[0]
|
||||
if (!arg) {
|
||||
console.error("The path to the index.html to patch is missing. Use 'node patch-theia-preload.js ./path/to/index.html'")
|
||||
process.exit(1)
|
||||
}
|
||||
(async () => {
|
||||
const { promises: fs } = require('fs')
|
||||
const path = require('path')
|
||||
const index = path.isAbsolute(arg) ? arg : path.join(process.cwd(), arg)
|
||||
console.log(`>>> Patching 'theia-preload' with 'old-theia-preload' in ${index}.`)
|
||||
const content = await fs.readFile(index, { encoding: 'utf-8' })
|
||||
await fs.writeFile(index, content.replace(/theia-preload/g, 'old-theia-preload'), { encoding: 'utf-8' })
|
||||
console.log(`<<< Successfully patched index.html.`)
|
||||
})()
|
@ -23,7 +23,7 @@
|
||||
"package": "cross-env DEBUG=* && electron-builder --publish=never",
|
||||
"package:publish": "cross-env DEBUG=* && electron-builder --publish=always",
|
||||
"download:plugins": "theia download:plugins",
|
||||
"patch": "ncp ./patch/backend/main.js ./src-gen/backend/main.js && node ./scripts/patch-theia-preload.js ./lib/index.html"
|
||||
"patch": "ncp ./patch/backend/main.js ./src-gen/backend/main.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0 <15"
|
||||
|
@ -254,9 +254,10 @@
|
||||
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
|
||||
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
|
||||
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
|
||||
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
|
||||
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
|
||||
"compile": "compile",
|
||||
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
|
||||
"compile.optimizeForDebug": "Optimize compile output for debug, not for release. It's 'false' by default.",
|
||||
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
|
||||
"compile.verbose": "True for verbose compile output. False by default",
|
||||
"compile.warnings": "Tells gcc which warning level to use. It's 'None' by default",
|
||||
|
Loading…
x
Reference in New Issue
Block a user