mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-11 11:19:26 +00:00
chore: updated to Theia 1.37.0
- Updated `@theia/*` to `1.37.0`. - Fixed all `yarn audit` security vulnerabilities. - Updated to `electron@23.2.4`: - `contextIsolation` is `true`, - `nodeIntegration` is `false`, and the - `webpack` target is moved from `electron-renderer` to `web`. - Updated to `typescript@4.9.3`. - Updated the `eslint` plugins. - Added the new `Light High Contrast` theme to the IDE2. - High contrast themes use Theia APIs for style adjustments. - Support for ESM modules: `"moduleResolution": "node16"`. - Node.js >= 16.14 is required. - VISX langage packs were bumped to `1.70.0`. - Removed undesired editor context menu items. (Closes #1394) Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
13
arduino-ide-extension/src/browser/app-service.ts
Normal file
13
arduino-ide-extension/src/browser/app-service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import type { StartupTasks } from '../electron-common/startup-task';
|
||||
import type { Sketch } from './contributions/contribution';
|
||||
|
||||
export const AppService = Symbol('AppService');
|
||||
export interface AppService {
|
||||
quit(): void;
|
||||
version(): Promise<string>;
|
||||
registerStartupTasksHandler(
|
||||
handler: (tasks: StartupTasks) => void
|
||||
): Disposable;
|
||||
scheduleDeletion(sketch: Sketch): void; // TODO: find a better place
|
||||
}
|
||||
@@ -1,41 +1,47 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
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';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import {
|
||||
ColorTheme,
|
||||
CssStyleCollector,
|
||||
StylingParticipant,
|
||||
} from '@theia/core/lib/browser/styling-service';
|
||||
import {
|
||||
CommandContribution,
|
||||
CommandRegistry,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import {
|
||||
MAIN_MENU_BAR,
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { isHighContrast } from '@theia/core/lib/common/theme';
|
||||
import { ElectronWindowPreferences } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import {
|
||||
MAIN_MENU_BAR,
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
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';
|
||||
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 { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser';
|
||||
import { EditorCommands } from '@theia/editor/lib/browser/editor-command';
|
||||
import { EditorMainMenu } from '@theia/editor/lib/browser/editor-menu';
|
||||
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 { ElectronWindowPreferences } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
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';
|
||||
import { SerialPlotterContribution } from './serial/plotter/plotter-frontend-contribution';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution
|
||||
@@ -44,7 +50,8 @@ export class ArduinoFrontendContribution
|
||||
TabBarToolbarContribution,
|
||||
CommandContribution,
|
||||
MenuContribution,
|
||||
ColorContribution
|
||||
ColorContribution,
|
||||
StylingParticipant
|
||||
{
|
||||
@inject(MessageService)
|
||||
private readonly messageService: MessageService;
|
||||
@@ -80,8 +87,7 @@ export class ArduinoFrontendContribution
|
||||
switch (event.preferenceName) {
|
||||
case 'window.zoomLevel':
|
||||
if (typeof event.newValue === 'number') {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
window.electronTheiaCore.setZoomLevel(event.newValue || 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -89,10 +95,9 @@ export class ArduinoFrontendContribution
|
||||
});
|
||||
this.appStateService.reachedState('ready').then(() =>
|
||||
this.electronWindowPreferences.ready.then(() => {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
const zoomLevel =
|
||||
this.electronWindowPreferences.get('window.zoomLevel');
|
||||
webContents.setZoomLevel(zoomLevel);
|
||||
window.electronTheiaCore.setZoomLevel(zoomLevel);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -168,7 +173,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'button.background',
|
||||
light: 'button.background',
|
||||
hc: 'activityBar.inactiveForeground',
|
||||
hcDark: 'activityBar.inactiveForeground',
|
||||
hcLight: 'activityBar.inactiveForeground',
|
||||
},
|
||||
description:
|
||||
'Background color of the toolbar items. Such as Upload, Verify, etc.',
|
||||
@@ -178,7 +184,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'button.hoverBackground',
|
||||
light: 'button.hoverBackground',
|
||||
hc: 'button.background',
|
||||
hcDark: 'button.background',
|
||||
hcLight: 'button.background',
|
||||
},
|
||||
description:
|
||||
'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.',
|
||||
@@ -188,7 +195,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'secondaryButton.foreground',
|
||||
light: 'button.foreground',
|
||||
hc: 'activityBar.inactiveForeground',
|
||||
hcDark: 'activityBar.inactiveForeground',
|
||||
hcLight: 'activityBar.inactiveForeground',
|
||||
},
|
||||
description:
|
||||
'Foreground color of the toolbar items. Such as Serial Monitor and Serial Plotter',
|
||||
@@ -198,7 +206,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'secondaryButton.hoverBackground',
|
||||
light: 'button.hoverBackground',
|
||||
hc: 'textLink.foreground',
|
||||
hcDark: 'textLink.foreground',
|
||||
hcLight: 'textLink.foreground',
|
||||
},
|
||||
description:
|
||||
'Background color of the toolbar items when hovering over them, such as "Serial Monitor" and "Serial Plotter"',
|
||||
@@ -208,7 +217,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'editor.selectionBackground',
|
||||
light: 'editor.selectionBackground',
|
||||
hc: 'textPreformat.foreground',
|
||||
hcDark: 'textPreformat.foreground',
|
||||
hcLight: 'textPreformat.foreground',
|
||||
},
|
||||
description:
|
||||
'Toggle color of the toolbar items when they are currently toggled (the command is in progress)',
|
||||
@@ -218,37 +228,38 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'dropdown.border',
|
||||
light: 'dropdown.border',
|
||||
hc: 'dropdown.border',
|
||||
hcDark: 'dropdown.border',
|
||||
hcLight: 'dropdown.border',
|
||||
},
|
||||
description: 'Border color of the Board Selector.',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'arduino.toolbar.dropdown.borderActive',
|
||||
defaults: {
|
||||
dark: 'focusBorder',
|
||||
light: 'focusBorder',
|
||||
hc: 'focusBorder',
|
||||
hcDark: 'focusBorder',
|
||||
hcLight: 'focusBorder',
|
||||
},
|
||||
description: "Border color of the Board Selector when it's active",
|
||||
},
|
||||
|
||||
{
|
||||
id: 'arduino.toolbar.dropdown.background',
|
||||
defaults: {
|
||||
dark: 'tab.unfocusedActiveBackground',
|
||||
light: 'dropdown.background',
|
||||
hc: 'dropdown.background',
|
||||
hcDark: 'dropdown.background',
|
||||
hcLight: 'dropdown.background',
|
||||
},
|
||||
description: 'Background color of the Board Selector.',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'arduino.toolbar.dropdown.label',
|
||||
defaults: {
|
||||
dark: 'dropdown.foreground',
|
||||
light: 'dropdown.foreground',
|
||||
hc: 'dropdown.foreground',
|
||||
hcDark: 'dropdown.foreground',
|
||||
hcLight: 'dropdown.foreground',
|
||||
},
|
||||
description: 'Font color of the Board Selector.',
|
||||
},
|
||||
@@ -257,7 +268,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'list.activeSelectionIconForeground',
|
||||
light: 'list.activeSelectionIconForeground',
|
||||
hc: 'list.activeSelectionIconForeground',
|
||||
hcDark: 'list.activeSelectionIconForeground',
|
||||
hcLight: 'list.activeSelectionIconForeground',
|
||||
},
|
||||
description:
|
||||
'Color of the selected protocol icon in the Board Selector.',
|
||||
@@ -267,7 +279,8 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'list.hoverBackground',
|
||||
light: 'list.hoverBackground',
|
||||
hc: 'list.hoverBackground',
|
||||
hcDark: 'list.hoverBackground',
|
||||
hcLight: 'list.hoverBackground',
|
||||
},
|
||||
description: 'Background color on hover of the Board Selector options.',
|
||||
},
|
||||
@@ -276,11 +289,191 @@ export class ArduinoFrontendContribution
|
||||
defaults: {
|
||||
dark: 'list.activeSelectionBackground',
|
||||
light: 'list.activeSelectionBackground',
|
||||
hc: 'list.activeSelectionBackground',
|
||||
hcDark: 'list.activeSelectionBackground',
|
||||
hcLight: 'list.activeSelectionBackground',
|
||||
},
|
||||
description:
|
||||
'Background color of the selected board in the Board Selector.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
registerThemeStyle(theme: ColorTheme, collector: CssStyleCollector): void {
|
||||
const warningForeground = theme.getColor('warningForeground');
|
||||
const warningBackground = theme.getColor('warningBackground');
|
||||
const focusBorder = theme.getColor('focusBorder');
|
||||
const contrastBorder = theme.getColor('contrastBorder');
|
||||
const notificationsBackground = theme.getColor('notifications.background');
|
||||
const buttonBorder = theme.getColor('button.border');
|
||||
const buttonBackground = theme.getColor('button.background') || 'none';
|
||||
const dropdownBackground = theme.getColor('dropdown.background');
|
||||
const arduinoToolbarButtonBackground = theme.getColor(
|
||||
'arduino.toolbar.button.background'
|
||||
);
|
||||
if (isHighContrast(theme.type)) {
|
||||
// toolbar items
|
||||
collector.addRule(`
|
||||
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-monitor,
|
||||
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-plotter {
|
||||
background: transparent;
|
||||
}
|
||||
`);
|
||||
if (contrastBorder) {
|
||||
collector.addRule(`
|
||||
.quick-input-widget {
|
||||
outline: 1px solid ${contrastBorder};
|
||||
outline-offset: -1px;
|
||||
}
|
||||
`);
|
||||
}
|
||||
if (focusBorder) {
|
||||
// customized react-select widget
|
||||
collector.addRule(`
|
||||
.arduino-select__option--is-selected {
|
||||
outline: 1px solid ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.arduino-select__option--is-focused {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
// boards selector dropdown
|
||||
collector.addRule(`
|
||||
#select-board-dialog .selectBoardContainer .list .item:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
// button hover
|
||||
collector.addRule(`
|
||||
.theia-button:hover,
|
||||
button.theia-button:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.theia-button {
|
||||
border: 1px solid ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.component-list-item .header .installed-version:hover:before {
|
||||
background-color: transparent;
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
// tree node
|
||||
collector.addRule(`
|
||||
.theia-TreeNode:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.quick-input-list .monaco-list-row.focused,
|
||||
.theia-Tree .theia-TreeNode.theia-mod-selected {
|
||||
outline: 1px dotted ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
div#select-board-dialog .selectBoardContainer .list .item.selected,
|
||||
.theia-Tree:focus .theia-TreeNode.theia-mod-selected,
|
||||
.theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
|
||||
outline: 1px solid ${focusBorder};
|
||||
}
|
||||
`);
|
||||
// quick input
|
||||
collector.addRule(`
|
||||
.quick-input-list .monaco-list-row:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
// editor tab-bar
|
||||
collector.addRule(`
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
#theia-main-content-panel .p-TabBar .p-TabBar-tab:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
outline-offset: -4px;
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
#theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current {
|
||||
outline: 1px solid ${focusBorder};
|
||||
outline-offset: -4px;
|
||||
}
|
||||
`);
|
||||
// boards selector dropdown
|
||||
collector.addRule(`
|
||||
.arduino-boards-dropdown-item:hover {
|
||||
outline: 1px dashed ${focusBorder};
|
||||
outline-offset: -2px;
|
||||
}
|
||||
`);
|
||||
if (notificationsBackground) {
|
||||
// notification
|
||||
collector.addRule(`
|
||||
.theia-notification-list-item:hover:not(:focus) {
|
||||
background-color: ${notificationsBackground};
|
||||
outline: 1px dashed ${focusBorder};
|
||||
outline-offset: -2px;
|
||||
}
|
||||
`);
|
||||
}
|
||||
if (arduinoToolbarButtonBackground) {
|
||||
// toolbar item
|
||||
collector.addRule(`
|
||||
.item.arduino-tool-item.toggled .arduino-upload-sketch--toolbar,
|
||||
.item.arduino-tool-item.toggled .arduino-verify-sketch--toolbar {
|
||||
background-color: ${arduinoToolbarButtonBackground} !important;
|
||||
outline: 1px solid ${focusBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div {
|
||||
background: ${arduinoToolbarButtonBackground};
|
||||
outline: 1px dashed ${focusBorder};
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
if (dropdownBackground) {
|
||||
// boards selector dropdown
|
||||
collector.addRule(`
|
||||
.arduino-boards-dropdown-item:hover {
|
||||
background: ${dropdownBackground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
if (warningForeground && warningBackground) {
|
||||
// <input> widget with inverted foreground and background colors
|
||||
collector.addRule(`
|
||||
.theia-input.warning:focus,
|
||||
.theia-input.warning::placeholder,
|
||||
.theia-input.warning {
|
||||
color: ${warningBackground};
|
||||
background-color: ${warningForeground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
if (buttonBorder) {
|
||||
collector.addRule(`
|
||||
button.theia-button,
|
||||
button.theia-button.secondary,
|
||||
.component-list-item .theia-button.secondary.no-border,
|
||||
.component-list-item .theia-button.secondary.no-border:hover {
|
||||
border: 1px solid ${buttonBorder};
|
||||
}
|
||||
`);
|
||||
collector.addRule(`
|
||||
.component-list-item .header .installed-version:before {
|
||||
color: ${buttonBackground};
|
||||
border: 1px solid ${buttonBorder};
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import '../../src/browser/style/index.css';
|
||||
import { Container, ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
|
||||
import { CommandContribution } from '@theia/core/lib/common/command';
|
||||
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
@@ -295,7 +295,7 @@ 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 { StartupTasks } from './contributions/startup-task';
|
||||
import { StartupTasksExecutor } from './contributions/startup-tasks-executor';
|
||||
import { IndexesUpdateProgress } from './contributions/indexes-update-progress';
|
||||
import { Daemon } from './contributions/daemon';
|
||||
import { FirstStartupInstaller } from './contributions/first-startup-installer';
|
||||
@@ -341,16 +341,6 @@ import { TypeHierarchyContribution } from './theia/typehierarchy/type-hierarchy-
|
||||
import { TypeHierarchyContribution as TheiaTypeHierarchyContribution } from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
||||
import { DefaultDebugSessionFactory } from './theia/debug/debug-session-contribution';
|
||||
import { DebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
|
||||
import { DebugToolbar } from './theia/debug/debug-toolbar-widget';
|
||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
import { PluginMenuCommandAdapter } from './theia/plugin-ext/plugin-menu-command-adapter';
|
||||
import { PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter } from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
||||
import { DebugSessionManager } from './theia/debug/debug-session-manager';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
|
||||
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
|
||||
import { DebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
||||
import { ConfigServiceClient } from './config/config-service-client';
|
||||
import { ValidateSketch } from './contributions/validate-sketch';
|
||||
import { RenameCloudSketch } from './contributions/rename-cloud-sketch';
|
||||
@@ -361,15 +351,29 @@ import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/
|
||||
import { CreateCloudCopy } from './contributions/create-cloud-copy';
|
||||
import { FileResourceResolver } from './theia/filesystem/file-resource';
|
||||
import { FileResourceResolver as TheiaFileResourceResolver } from '@theia/filesystem/lib/browser/file-resource';
|
||||
import { StylingParticipant } from '@theia/core/lib/browser/styling-service';
|
||||
import { MonacoEditorMenuContribution } from './theia/monaco/monaco-menu';
|
||||
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
|
||||
// Hack to fix copy/cut/paste issue after electron version update in Theia.
|
||||
// https://github.com/eclipse-theia/theia/issues/12487
|
||||
import('@theia/core/lib/browser/common-frontend-contribution.js').then(
|
||||
(theiaCommonContribution) => {
|
||||
theiaCommonContribution['supportCopy'] = true;
|
||||
theiaCommonContribution['supportCut'] = true;
|
||||
theiaCommonContribution['supportPaste'] = true;
|
||||
}
|
||||
);
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// Commands and toolbar items
|
||||
// Commands, colors, theme adjustments, and toolbar items
|
||||
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
|
||||
bind(CommandContribution).toService(ArduinoFrontendContribution);
|
||||
bind(MenuContribution).toService(ArduinoFrontendContribution);
|
||||
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
|
||||
bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution);
|
||||
bind(ColorContribution).toService(ArduinoFrontendContribution);
|
||||
bind(StylingParticipant).toService(ArduinoFrontendContribution);
|
||||
|
||||
bind(ArduinoToolbarContribution).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(ArduinoToolbarContribution);
|
||||
@@ -722,7 +726,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, PlotterFrontendContribution);
|
||||
Contribution.configure(bind, Format);
|
||||
Contribution.configure(bind, CompilerErrors);
|
||||
Contribution.configure(bind, StartupTasks);
|
||||
Contribution.configure(bind, StartupTasksExecutor);
|
||||
Contribution.configure(bind, IndexesUpdateProgress);
|
||||
Contribution.configure(bind, Daemon);
|
||||
Contribution.configure(bind, FirstStartupInstaller);
|
||||
@@ -982,9 +986,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// workaround for themes cannot be removed after registration
|
||||
// https://github.com/eclipse-theia/theia/issues/11151
|
||||
bind(CleanupObsoleteThemes).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(
|
||||
CleanupObsoleteThemes
|
||||
);
|
||||
bind(FrontendApplicationContribution).toService(CleanupObsoleteThemes);
|
||||
bind(ThemesRegistrationSummary).toSelf().inSingletonScope();
|
||||
bind(MonacoThemeRegistry).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoThemeRegistry).toService(MonacoThemeRegistry);
|
||||
@@ -998,37 +1000,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(TypeHierarchyContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaTypeHierarchyContribution).toService(TypeHierarchyContribution);
|
||||
|
||||
// patched the debugger for `cortex-debug@1.5.1`
|
||||
// https://github.com/eclipse-theia/theia/issues/11871
|
||||
// https://github.com/eclipse-theia/theia/issues/11879
|
||||
// https://github.com/eclipse-theia/theia/issues/11880
|
||||
// https://github.com/eclipse-theia/theia/issues/11885
|
||||
// https://github.com/eclipse-theia/theia/issues/11886
|
||||
// https://github.com/eclipse-theia/theia/issues/11916
|
||||
// based on: https://github.com/eclipse-theia/theia/compare/master...kittaakos:theia:%2311871
|
||||
bind(DefaultDebugSessionFactory).toSelf().inSingletonScope();
|
||||
rebind(DebugSessionFactory).toService(DefaultDebugSessionFactory);
|
||||
bind(DebugSessionManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
|
||||
bind(DebugToolbar).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugToolbar).toService(DebugToolbar);
|
||||
bind(PluginMenuCommandAdapter).toSelf().inSingletonScope();
|
||||
rebind(TheiaPluginMenuCommandAdapter).toService(PluginMenuCommandAdapter);
|
||||
bind(WidgetFactory)
|
||||
.toDynamicValue(({ container }) => ({
|
||||
id: DebugWidget.ID,
|
||||
createWidget: () => {
|
||||
const child = new Container({ defaultScope: 'Singleton' });
|
||||
child.parent = container;
|
||||
child.bind(DebugViewModel).toSelf();
|
||||
child.bind(DebugToolbar).toSelf(); // patched toolbar
|
||||
child.bind(DebugSessionWidget).toSelf();
|
||||
child.bind(DebugConfigurationWidget).toSelf();
|
||||
child.bind(DebugWidget).toSelf();
|
||||
return child.get(DebugWidget);
|
||||
},
|
||||
}))
|
||||
.inSingletonScope();
|
||||
|
||||
bind(SidebarBottomMenuWidget).toSelf();
|
||||
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
|
||||
@@ -1043,4 +1016,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// https://github.com/arduino/arduino-ide/issues/437
|
||||
bind(FileResourceResolver).toSelf().inSingletonScope();
|
||||
rebind(TheiaFileResourceResolver).toService(FileResourceResolver);
|
||||
|
||||
// Full control over the editor context menu to filter undesired menu items contributed by Theia.
|
||||
// https://github.com/arduino/arduino-ide/issues/1394
|
||||
// https://github.com/arduino/arduino-ide/pull/2027#pullrequestreview-1414246614
|
||||
bind(MonacoEditorMenuContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoEditorMenuContribution).toService(
|
||||
MonacoEditorMenuContribution
|
||||
);
|
||||
});
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
CommandContribution,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import {
|
||||
AuthOptions,
|
||||
AuthenticationService,
|
||||
AuthenticationServiceClient,
|
||||
AuthenticationSession,
|
||||
authServerPort,
|
||||
} from '../../common/protocol/authentication-service';
|
||||
import { CloudUserCommands } from './cloud-user-commands';
|
||||
import { serverPort } from '../../node/auth/authentication-server';
|
||||
import { AuthOptions } from '../../node/auth/types';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
|
||||
@injectable()
|
||||
@@ -61,7 +61,7 @@ export class AuthenticationClientService
|
||||
|
||||
setOptions(): Promise<void> {
|
||||
return this.service.setOptions({
|
||||
redirectUri: `http://localhost:${serverPort}/callback`,
|
||||
redirectUri: `http://localhost:${authServerPort}/callback`,
|
||||
responseType: 'code',
|
||||
clientID: this.arduinoPreferences['arduino.auth.clientID'],
|
||||
domain: this.arduinoPreferences['arduino.auth.domain'],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import PQueue from 'p-queue';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as moment from 'moment';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import {
|
||||
Contribution,
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
CommandRegistry,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import moment from 'moment';
|
||||
import { ConfigService } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { AppService } from '../app-service';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
Contribution,
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class About extends Contribution {
|
||||
@inject(ClipboardService)
|
||||
protected readonly clipboardService: ClipboardService;
|
||||
|
||||
private readonly clipboardService: ClipboardService;
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
private readonly configService: ConfigService;
|
||||
@inject(AppService)
|
||||
private readonly appService: AppService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(About.Commands.ABOUT_APP, {
|
||||
@@ -40,17 +41,20 @@ export class About extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
async showAbout(): Promise<void> {
|
||||
const version = await this.configService.getVersion();
|
||||
private async showAbout(): Promise<void> {
|
||||
const [appVersion, cliVersion] = await Promise.all([
|
||||
this.appService.version(),
|
||||
this.configService.getVersion(),
|
||||
]);
|
||||
const buildDate = this.buildDate;
|
||||
const detail = (showAll: boolean) =>
|
||||
nls.localize(
|
||||
'arduino/about/detail',
|
||||
'Version: {0}\nDate: {1}{2}\nCLI Version: {3}\n\n{4}',
|
||||
remote.app.getVersion(),
|
||||
appVersion,
|
||||
buildDate ? buildDate : nls.localize('', 'dev build'),
|
||||
buildDate && showAll ? ` (${this.ago(buildDate)})` : '',
|
||||
version,
|
||||
cliVersion,
|
||||
nls.localize(
|
||||
'arduino/about/copyright',
|
||||
'Copyright © {0} Arduino SA',
|
||||
@@ -60,34 +64,31 @@ export class About extends Contribution {
|
||||
const ok = nls.localize('vscode/issueMainService/ok', 'OK');
|
||||
const copy = nls.localize('vscode/textInputActions/copy', 'Copy');
|
||||
const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy];
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message: `${this.applicationName}`,
|
||||
title: `${this.applicationName}`,
|
||||
type: 'info',
|
||||
detail: detail(true),
|
||||
buttons,
|
||||
noLink: true,
|
||||
defaultId: buttons.indexOf(ok),
|
||||
cancelId: buttons.indexOf(ok),
|
||||
}
|
||||
);
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
message: `${this.applicationName}`,
|
||||
title: `${this.applicationName}`,
|
||||
type: 'info',
|
||||
detail: detail(true),
|
||||
buttons,
|
||||
noLink: true,
|
||||
defaultId: buttons.indexOf(ok),
|
||||
cancelId: buttons.indexOf(ok),
|
||||
});
|
||||
|
||||
if (buttons[response] === copy) {
|
||||
await this.clipboardService.writeText(detail(false).trim());
|
||||
}
|
||||
}
|
||||
|
||||
protected get applicationName(): string {
|
||||
private get applicationName(): string {
|
||||
return FrontendApplicationConfigProvider.get().applicationName;
|
||||
}
|
||||
|
||||
protected get buildDate(): string | undefined {
|
||||
private get buildDate(): string | undefined {
|
||||
return FrontendApplicationConfigProvider.get().buildDate;
|
||||
}
|
||||
|
||||
protected ago(isoTime: string): string {
|
||||
private ago(isoTime: string): string {
|
||||
const now = moment(Date.now());
|
||||
const other = moment(isoTime);
|
||||
let result = now.diff(other, 'minute');
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import {
|
||||
SketchContribution,
|
||||
Command,
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
URI,
|
||||
Sketch,
|
||||
SketchContribution,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class AddFile extends SketchContribution {
|
||||
@inject(FileDialogService)
|
||||
private readonly fileDialogService: FileDialogService;
|
||||
private readonly fileDialogService: FileDialogService; // TODO: use dialogService
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(AddFile.Commands.ADD_FILE, {
|
||||
@@ -50,7 +49,7 @@ export class AddFile extends SketchContribution {
|
||||
const { uri: targetUri, filename } = this.resolveTarget(sketch, toAddUri);
|
||||
const exists = await this.fileService.exists(targetUri);
|
||||
if (exists) {
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
type: 'question',
|
||||
title: nls.localize('arduino/contributions/replaceTitle', 'Replace'),
|
||||
buttons: [
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -42,23 +41,20 @@ export class AddZipLibrary extends SketchContribution {
|
||||
private async addZipLibrary(): Promise<void> {
|
||||
const homeUri = await this.envVariableServer.getHomeDirUri();
|
||||
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
|
||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: nls.localize(
|
||||
'arduino/selectZip',
|
||||
"Select a zip file containing the library you'd like to add"
|
||||
),
|
||||
defaultPath,
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: nls.localize('arduino/library/zipLibrary', 'Library'),
|
||||
extensions: ['zip'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
const { canceled, filePaths } = await this.dialogService.showOpenDialog({
|
||||
title: nls.localize(
|
||||
'arduino/selectZip',
|
||||
"Select a zip file containing the library you'd like to add"
|
||||
),
|
||||
defaultPath,
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: nls.localize('arduino/library/zipLibrary', 'Library'),
|
||||
extensions: ['zip'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (!canceled && filePaths.length) {
|
||||
const zipUri = await this.fileSystemExt.getUri(filePaths[0]);
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import dateFormat from 'dateformat';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -39,16 +38,13 @@ export class ArchiveSketch extends SketchContribution {
|
||||
const defaultContainerUri = await this.defaultUri();
|
||||
const defaultUri = defaultContainerUri.resolve(archiveBasename);
|
||||
const defaultPath = await this.fileService.fsPath(defaultUri);
|
||||
const { filePath, canceled } = await remote.dialog.showSaveDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveSketchAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
}
|
||||
);
|
||||
const { filePath, canceled } = await this.dialogService.showSaveDialog({
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveSketchAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
});
|
||||
if (!filePath || canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
DisposableCollection,
|
||||
@@ -65,7 +64,7 @@ VID: ${VID}
|
||||
PID: ${PID}
|
||||
SN: ${SN}
|
||||
`.trim();
|
||||
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
await this.dialogService.showMessageBox({
|
||||
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||
type: 'info',
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import type { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||
import type {
|
||||
FrontendApplication,
|
||||
OnWillStopAction,
|
||||
} from '@theia/core/lib/browser/frontend-application';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import type { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { WindowServiceExt } from '../theia/core/window-service-ext';
|
||||
import {
|
||||
SketchContribution,
|
||||
Command,
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
KeybindingRegistry,
|
||||
MenuModelRegistry,
|
||||
Sketch,
|
||||
SketchContribution,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { SaveAsSketch } from './save-as-sketch';
|
||||
|
||||
/**
|
||||
@@ -28,6 +28,9 @@ import { SaveAsSketch } from './save-as-sketch';
|
||||
*/
|
||||
@injectable()
|
||||
export class Close extends SketchContribution {
|
||||
@inject(WindowServiceExt)
|
||||
private readonly windowServiceExt: WindowServiceExt;
|
||||
|
||||
private shell: ApplicationShell | undefined;
|
||||
|
||||
override onStart(app: FrontendApplication): MaybePromise<void> {
|
||||
@@ -56,7 +59,7 @@ export class Close extends SketchContribution {
|
||||
}
|
||||
}
|
||||
}
|
||||
return remote.getCurrentWindow().close();
|
||||
return this.windowServiceExt.close();
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -150,26 +153,23 @@ export class Close extends SketchContribution {
|
||||
}
|
||||
|
||||
private async prompt(isTemp: boolean): Promise<Prompt> {
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message: nls.localize(
|
||||
'arduino/sketch/saveSketch',
|
||||
'Save your sketch to open it again later.'
|
||||
),
|
||||
title: nls.localize(
|
||||
'theia/core/quitTitle',
|
||||
'Are you sure you want to quit?'
|
||||
),
|
||||
type: 'question',
|
||||
buttons: [
|
||||
nls.localizeByDefault("Don't Save"),
|
||||
Dialog.CANCEL,
|
||||
nls.localizeByDefault(isTemp ? 'Save As...' : 'Save'),
|
||||
],
|
||||
defaultId: 2, // `Save`/`Save As...` button index is the default.
|
||||
}
|
||||
);
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
message: nls.localize(
|
||||
'arduino/sketch/saveSketch',
|
||||
'Save your sketch to open it again later.'
|
||||
),
|
||||
title: nls.localize(
|
||||
'theia/core/quitTitle',
|
||||
'Are you sure you want to quit?'
|
||||
),
|
||||
type: 'question',
|
||||
buttons: [
|
||||
nls.localizeByDefault("Don't Save"),
|
||||
Dialog.CANCEL,
|
||||
nls.localizeByDefault(isTemp ? 'Save As...' : 'Save'),
|
||||
],
|
||||
defaultId: 2, // `Save`/`Save As...` button index is the default.
|
||||
});
|
||||
switch (response) {
|
||||
case 0:
|
||||
return Prompt.DoNotSave;
|
||||
|
||||
@@ -67,6 +67,7 @@ import { WorkspaceService } from '../theia/workspace/workspace-service';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { DialogService } from '../dialog-service';
|
||||
|
||||
export {
|
||||
Command,
|
||||
@@ -115,6 +116,9 @@ export abstract class Contribution
|
||||
@inject(MainMenuManager)
|
||||
protected readonly menuManager: MainMenuManager;
|
||||
|
||||
@inject(DialogService)
|
||||
protected readonly dialogService: DialogService;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.appStateService.reachedState('ready').then(() => this.onReady());
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { ipcRenderer } from '@theia/core/electron-shared/electron';
|
||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
@@ -10,11 +8,11 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import type { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { SketchesError } from '../../common/protocol';
|
||||
import { SCHEDULE_DELETION_SIGNAL } from '../../electron-common/electron-messages';
|
||||
import { Sketch } from '../contributions/contribution';
|
||||
import { isNotFound } from '../create/typings';
|
||||
import { Command, CommandRegistry } from './contribution';
|
||||
import { CloudSketchContribution } from './cloud-contribution';
|
||||
import { AppService } from '../app-service';
|
||||
|
||||
export interface DeleteSketchParams {
|
||||
/**
|
||||
@@ -38,6 +36,8 @@ export class DeleteSketch extends CloudSketchContribution {
|
||||
private readonly shell: ApplicationShell;
|
||||
@inject(WindowService)
|
||||
private readonly windowService: WindowService;
|
||||
@inject(AppService)
|
||||
private readonly appService: AppService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(DeleteSketch.Commands.DELETE_SKETCH, {
|
||||
@@ -66,7 +66,7 @@ export class DeleteSketch extends CloudSketchContribution {
|
||||
}
|
||||
const cloudUri = this.createFeatures.cloudUri(sketch);
|
||||
if (willNavigateAway !== 'force') {
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
title: nls.localizeByDefault('Delete'),
|
||||
type: 'question',
|
||||
buttons: [Dialog.CANCEL, Dialog.OK],
|
||||
@@ -120,7 +120,7 @@ export class DeleteSketch extends CloudSketchContribution {
|
||||
}
|
||||
|
||||
private scheduleDeletion(sketch: Sketch): void {
|
||||
ipcRenderer.send(SCHEDULE_DELETION_SIGNAL, sketch);
|
||||
this.appService.scheduleDeletion(sketch);
|
||||
}
|
||||
|
||||
private async loadSketch(uri: string): Promise<Sketch | undefined> {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import PQueue from 'p-queue';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandHandler, CommandService } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
MenuPath,
|
||||
CompositeMenuNode,
|
||||
SubMenuOptions,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { MenuPath, SubMenuOptions } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
@@ -143,19 +139,6 @@ export abstract class Examples extends SketchContribution {
|
||||
}): void;
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
try {
|
||||
// This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222.
|
||||
const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1;
|
||||
const menuId = ArduinoMenus.FILE__EXAMPLES_SUBMENU[index];
|
||||
const groupPath =
|
||||
index === 0 ? [] : ArduinoMenus.FILE__EXAMPLES_SUBMENU.slice(0, index);
|
||||
const parent: CompositeMenuNode = (registry as any).findGroup(groupPath);
|
||||
const examples = new CompositeMenuNode(menuId, '', { order: '4' });
|
||||
parent.addNode(examples);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.warn('Could not patch menu ordering.');
|
||||
}
|
||||
// Registering the same submenu multiple times has no side-effect.
|
||||
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
||||
registry.registerSubmenu(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import PQueue from 'p-queue';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import {
|
||||
@@ -120,6 +123,7 @@ export class InoLanguage extends SketchContribution {
|
||||
return;
|
||||
}
|
||||
const release = await this.languageServerStartMutex.acquire();
|
||||
const toDisposeOnRelease = new DisposableCollection();
|
||||
try {
|
||||
await this.hostedPluginEvents.didStart;
|
||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||
@@ -179,12 +183,13 @@ export class InoLanguage extends SketchContribution {
|
||||
]);
|
||||
|
||||
this.languageServerFqbn = await Promise.race([
|
||||
new Promise<undefined>((_, reject) =>
|
||||
setTimeout(
|
||||
new Promise<undefined>((_, reject) => {
|
||||
const timer = setTimeout(
|
||||
() => reject(new Error(`Timeout after ${20_000} ms.`)),
|
||||
20_000
|
||||
)
|
||||
),
|
||||
);
|
||||
toDisposeOnRelease.push(Disposable.create(() => clearTimeout(timer)));
|
||||
}),
|
||||
this.commandService.executeCommand<string>(
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
@@ -206,6 +211,7 @@ export class InoLanguage extends SketchContribution {
|
||||
console.log(`Failed to start language server. Original FQBN: ${fqbn}`, e);
|
||||
this.languageServerFqbn = undefined;
|
||||
} finally {
|
||||
toDisposeOnRelease.dispose();
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CommandRegistry, MaybePromise, nls } from '@theia/core/lib/common';
|
||||
import { Settings } from '../dialogs/settings/settings';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
@injectable()
|
||||
export class InterfaceScale extends Contribution {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
@@ -9,7 +8,7 @@ import {
|
||||
MenuModelRegistry,
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketchExternal extends SketchContribution {
|
||||
@@ -41,7 +40,7 @@ export class OpenSketchExternal extends SketchContribution {
|
||||
if (exists) {
|
||||
const fsPath = await this.fileService.fsPath(new URI(uri));
|
||||
if (fsPath) {
|
||||
remote.shell.showItemInFolder(fsPath);
|
||||
window.electronTheiaCore.showItemInFolder(fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ export class OpenSketchFiles extends SketchContribution {
|
||||
fileService: this.fileService,
|
||||
sketchesService: this.sketchesService,
|
||||
labelProvider: this.labelProvider,
|
||||
dialogService: this.dialogService,
|
||||
});
|
||||
if (movedSketch) {
|
||||
this.workspaceService.open(new URI(movedSketch.uri), {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
SketchContribution,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { DialogService } from '../dialog-service';
|
||||
|
||||
export type SketchLocation = string | URI | SketchRef;
|
||||
export namespace SketchLocation {
|
||||
@@ -83,19 +83,16 @@ export class OpenSketch extends SketchContribution {
|
||||
|
||||
private async selectSketch(): Promise<Sketch | undefined> {
|
||||
const defaultPath = await this.defaultPath();
|
||||
const { filePaths } = await remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
defaultPath,
|
||||
properties: ['createDirectory', 'openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: nls.localize('arduino/sketch/sketch', 'Sketch'),
|
||||
extensions: ['ino', 'pde'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
const { filePaths } = await this.dialogService.showOpenDialog({
|
||||
defaultPath,
|
||||
properties: ['createDirectory', 'openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: nls.localize('arduino/sketch/sketch', 'Sketch'),
|
||||
extensions: ['ino', 'pde'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (!filePaths.length) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -115,6 +112,7 @@ export class OpenSketch extends SketchContribution {
|
||||
fileService: this.fileService,
|
||||
sketchesService: this.sketchesService,
|
||||
labelProvider: this.labelProvider,
|
||||
dialogService: this.dialogService,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -134,14 +132,16 @@ export async function promptMoveSketch(
|
||||
fileService: FileService;
|
||||
sketchesService: SketchesService;
|
||||
labelProvider: LabelProvider;
|
||||
dialogService: DialogService;
|
||||
}
|
||||
): Promise<Sketch | undefined> {
|
||||
const { fileService, sketchesService, labelProvider } = options;
|
||||
const { fileService, sketchesService, labelProvider, dialogService } =
|
||||
options;
|
||||
const uri =
|
||||
sketchFileUri instanceof URI ? sketchFileUri : new URI(sketchFileUri);
|
||||
const name = uri.path.name;
|
||||
const nameWithExt = labelProvider.getName(uri);
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
const { response } = await dialogService.showMessageBox({
|
||||
title: nls.localize('arduino/sketch/moving', 'Moving'),
|
||||
type: 'question',
|
||||
buttons: [
|
||||
@@ -160,7 +160,7 @@ export async function promptMoveSketch(
|
||||
const newSketchUri = uri.parent.resolve(name);
|
||||
const exists = await fileService.exists(newSketchUri);
|
||||
if (exists) {
|
||||
await remote.dialog.showMessageBox({
|
||||
await dialogService.showMessageBox({
|
||||
type: 'error',
|
||||
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
|
||||
message: nls.localize(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
Contribution,
|
||||
@@ -9,14 +8,18 @@ import {
|
||||
CommandRegistry,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { AppService } from '../app-service';
|
||||
|
||||
@injectable()
|
||||
export class QuitApp extends Contribution {
|
||||
@inject(AppService)
|
||||
private readonly appService: AppService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
if (!isOSX) {
|
||||
registry.registerCommand(QuitApp.Commands.QUIT_APP, {
|
||||
execute: () => remote.app.quit(),
|
||||
execute: () => this.appService.quit(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable';
|
||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||
@@ -8,7 +7,7 @@ import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { StartupTask } from '../../electron-common/startup-task';
|
||||
import { StartupTasks } from '../../electron-common/startup-task';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { CloudSketchContribution } from './cloud-contribution';
|
||||
@@ -95,7 +94,7 @@ export class SaveAsSketch extends CloudSketchContribution {
|
||||
if (markAsRecentlyOpened) {
|
||||
this.sketchesService.markAsRecentlyOpened(newWorkspaceUri);
|
||||
}
|
||||
const options: WorkspaceInput & StartupTask.Owner = {
|
||||
const options: WorkspaceInput & StartupTasks = {
|
||||
preserveWindow: true,
|
||||
tasks: [],
|
||||
};
|
||||
@@ -165,16 +164,13 @@ export class SaveAsSketch extends CloudSketchContribution {
|
||||
): Promise<string | undefined> {
|
||||
let sketchFolderDestinationUri: string | undefined;
|
||||
while (!sketchFolderDestinationUri) {
|
||||
const { filePath } = await remote.dialog.showSaveDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveFolderAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
}
|
||||
);
|
||||
const { filePath } = await this.dialogService.showSaveDialog({
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveFolderAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
});
|
||||
if (!filePath) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -225,13 +221,10 @@ ${dialogContent.details}
|
||||
|
||||
${dialogContent.question}`.trim();
|
||||
defaultPath = filePath;
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message,
|
||||
buttons: [Dialog.CANCEL, Dialog.YES],
|
||||
}
|
||||
);
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
message,
|
||||
buttons: [Dialog.CANCEL, Dialog.YES],
|
||||
});
|
||||
// cancel
|
||||
if (response === 0) {
|
||||
return undefined;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import type { IpcRendererEvent } from '@theia/core/electron-shared/electron';
|
||||
import { ipcRenderer } from '@theia/core/electron-shared/electron';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { StartupTask } from '../../electron-common/startup-task';
|
||||
import { Contribution } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class StartupTasks extends Contribution {
|
||||
override onReady(): void {
|
||||
ipcRenderer.once(
|
||||
StartupTask.Messaging.STARTUP_TASKS_SIGNAL,
|
||||
(_: IpcRendererEvent, args: unknown) => {
|
||||
console.debug(
|
||||
`Received the startup tasks from the electron main process. Args: ${JSON.stringify(
|
||||
args
|
||||
)}`
|
||||
);
|
||||
if (!StartupTask.has(args)) {
|
||||
console.warn(`Could not detect 'tasks' from the signal. Skipping.`);
|
||||
return;
|
||||
}
|
||||
const tasks = args.tasks;
|
||||
if (tasks.length) {
|
||||
console.log(`Executing startup tasks:`);
|
||||
tasks.forEach(({ command, args = [] }) => {
|
||||
console.log(
|
||||
` - '${command}' ${
|
||||
args.length ? `, args: ${JSON.stringify(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
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
const { id } = remote.getCurrentWindow();
|
||||
console.debug(
|
||||
`Signalling app ready event to the electron main process. Sender ID: ${id}.`
|
||||
);
|
||||
ipcRenderer.send(StartupTask.Messaging.APP_READY_SIGNAL(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import {
|
||||
hasStartupTasks,
|
||||
StartupTasks,
|
||||
} from '../../electron-common/startup-task';
|
||||
import { AppService } from '../app-service';
|
||||
import { Contribution } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class StartupTasksExecutor extends Contribution {
|
||||
@inject(AppService)
|
||||
private readonly appService: AppService;
|
||||
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.toDispose.push(
|
||||
this.appService.registerStartupTasksHandler((tasks) =>
|
||||
this.handleStartupTasks(tasks)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
private async handleStartupTasks(tasks: StartupTasks): Promise<void> {
|
||||
console.debug(
|
||||
`Received the startup tasks from the electron main process. Args: ${JSON.stringify(
|
||||
tasks
|
||||
)}`
|
||||
);
|
||||
if (!hasStartupTasks(tasks)) {
|
||||
console.warn(`Could not detect 'tasks' from the signal. Skipping.`);
|
||||
return;
|
||||
}
|
||||
await this.appStateService.reachedState('ready');
|
||||
console.log(`Executing startup tasks:`);
|
||||
tasks.tasks.forEach(({ command, args = [] }) => {
|
||||
console.log(
|
||||
` - '${command}' ${
|
||||
args.length ? `, args: ${JSON.stringify(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
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { Deferred, waitForEvent } from '@theia/core/lib/common/promise-util';
|
||||
@@ -180,15 +179,12 @@ export class ValidateSketch extends CloudSketchContribution {
|
||||
message: string,
|
||||
buttons: string[] = [Dialog.CANCEL, Dialog.OK]
|
||||
): Promise<boolean> {
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title,
|
||||
message,
|
||||
type: 'warning',
|
||||
buttons,
|
||||
}
|
||||
);
|
||||
const { response } = await this.dialogService.showMessageBox({
|
||||
title,
|
||||
message,
|
||||
type: 'warning',
|
||||
buttons,
|
||||
});
|
||||
// cancel
|
||||
if (response === 0) {
|
||||
return false;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Sketch } from '../../common/protocol';
|
||||
import { AuthenticationSession } from '../../node/auth/types';
|
||||
import { AuthenticationSession } from '../../common/protocol/authentication-service';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
||||
|
||||
15
arduino-ide-extension/src/browser/dialog-service.ts
Normal file
15
arduino-ide-extension/src/browser/dialog-service.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type {
|
||||
MessageBoxOptions,
|
||||
MessageBoxReturnValue,
|
||||
OpenDialogOptions,
|
||||
OpenDialogReturnValue,
|
||||
SaveDialogOptions,
|
||||
SaveDialogReturnValue,
|
||||
} from '../electron-common/electron-arduino';
|
||||
|
||||
export const DialogService = Symbol('DialogService');
|
||||
export interface DialogService {
|
||||
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;
|
||||
showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
|
||||
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree/tree';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { clipboard } from '@theia/core/electron-shared/@electron/remote';
|
||||
import { ReactWidget, DialogProps } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { CreateApi } from '../create/create-api';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
|
||||
const RadioButton = (props: {
|
||||
id: string;
|
||||
@@ -35,15 +37,18 @@ export const ShareSketchComponent = ({
|
||||
treeNode,
|
||||
createApi,
|
||||
domain = 'https://create.arduino.cc',
|
||||
writeClipboard,
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
treeNode: any;
|
||||
createApi: CreateApi;
|
||||
domain?: string;
|
||||
writeClipboard: (text: string) => MaybePromise<void>;
|
||||
}): React.ReactElement => {
|
||||
const [loading, setloading] = React.useState<boolean>(false);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
|
||||
const radioChangeHandler = async (event: React.BaseSyntheticEvent) => {
|
||||
setloading(true);
|
||||
setLoading(true);
|
||||
const sketch = await createApi.editSketch({
|
||||
id: treeNode.sketchId,
|
||||
params: {
|
||||
@@ -52,7 +57,7 @@ export const ShareSketchComponent = ({
|
||||
});
|
||||
// setPublicVisibility(sketch.is_public);
|
||||
treeNode.isPublic = sketch.is_public;
|
||||
setloading(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const sketchLink = `${domain}/editor/_/${treeNode.sketchId}/preview`;
|
||||
@@ -100,7 +105,7 @@ export const ShareSketchComponent = ({
|
||||
className="theia-input"
|
||||
/>
|
||||
<button
|
||||
onClick={() => clipboard.writeText(sketchLink)}
|
||||
onClick={() => writeClipboard(sketchLink)}
|
||||
value="copy"
|
||||
className="theia-button secondary"
|
||||
>
|
||||
@@ -121,44 +126,52 @@ export const ShareSketchComponent = ({
|
||||
);
|
||||
};
|
||||
|
||||
@injectable()
|
||||
export class ShareSketchWidget extends ReactWidget {
|
||||
constructor(private treeNode: any, private createApi: CreateApi) {
|
||||
private readonly writeClipboard = (text: string) =>
|
||||
this.clipboardService.writeText(text);
|
||||
|
||||
constructor(
|
||||
private treeNode: TreeNode,
|
||||
private createApi: CreateApi,
|
||||
private clipboardService: ClipboardService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
protected override render(): React.ReactNode {
|
||||
return (
|
||||
<ShareSketchComponent
|
||||
treeNode={this.treeNode}
|
||||
createApi={this.createApi}
|
||||
writeClipboard={this.writeClipboard}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ShareSketchDialogProps extends DialogProps {
|
||||
readonly node: any;
|
||||
readonly node: TreeNode;
|
||||
readonly createApi: CreateApi;
|
||||
readonly clipboardService: ClipboardService;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ShareSketchDialog extends AbstractDialog<void> {
|
||||
protected widget: ShareSketchWidget;
|
||||
|
||||
constructor(
|
||||
@inject(ShareSketchDialogProps)
|
||||
protected override readonly props: ShareSketchDialogProps
|
||||
) {
|
||||
constructor(protected override readonly props: ShareSketchDialogProps) {
|
||||
super({ title: props.title });
|
||||
this.contentNode.classList.add('arduino-share-sketch-dialog');
|
||||
this.widget = new ShareSketchWidget(props.node, props.createApi);
|
||||
this.widget = new ShareSketchWidget(
|
||||
props.node,
|
||||
props.createApi,
|
||||
props.clipboardService
|
||||
);
|
||||
}
|
||||
|
||||
get value(): void {
|
||||
override get value(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
|
||||
@@ -157,7 +157,7 @@ export const FirmwareUploaderComponent = ({
|
||||
options={firmwareOptions}
|
||||
value={selectedFirmware}
|
||||
tabSelectsValue={false}
|
||||
onChange={(value) => {
|
||||
onChange={(value: FirmwareOption | null) => {
|
||||
if (value) {
|
||||
setInstallFeedback(null);
|
||||
setSelectedFirmware(value);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
// @ts-expect-error see https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
|
||||
import type { Options } from 'react-markdown';
|
||||
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
|
||||
import ProgressBar from '../../components/ProgressBar';
|
||||
|
||||
const ReactMarkdown = React.lazy<React.ComponentType<Options>>(
|
||||
// @ts-expect-error see above
|
||||
() => import('react-markdown')
|
||||
);
|
||||
|
||||
export interface UpdateProgress {
|
||||
progressInfo?: ProgressInfo | undefined;
|
||||
downloadFinished?: boolean;
|
||||
@@ -15,6 +20,7 @@ export interface UpdateProgress {
|
||||
export interface IDEUpdaterComponentProps {
|
||||
updateInfo: UpdateInfo;
|
||||
updateProgress: UpdateProgress;
|
||||
openExternal: (url: string) => undefined;
|
||||
}
|
||||
|
||||
export const IDEUpdaterComponent = ({
|
||||
@@ -25,6 +31,7 @@ export const IDEUpdaterComponent = ({
|
||||
progressInfo,
|
||||
error,
|
||||
},
|
||||
openExternal,
|
||||
}: IDEUpdaterComponentProps): React.ReactElement => {
|
||||
const { version, releaseNotes } = updateInfo;
|
||||
const [changelog, setChangelog] = React.useState<string>('');
|
||||
@@ -95,20 +102,26 @@ export const IDEUpdaterComponent = ({
|
||||
{changelog && (
|
||||
<div className="dialogRow changelog-container">
|
||||
<div className="changelog">
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a
|
||||
onClick={() => href && shell.openExternal(href)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="fallback">
|
||||
<div className="spinner" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{changelog}
|
||||
</ReactMarkdown>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
// @ts-expect-error see imports. There is no ESM type-only import in CommonJS modules.
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a onClick={() => href && openExternal(href)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{changelog}
|
||||
</ReactMarkdown>
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
import { LocalStorageService } from '@theia/core/lib/browser';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
|
||||
const DOWNLOAD_PAGE_URL = 'https://www.arduino.cc/en/software';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialogProps extends DialogProps {}
|
||||
|
||||
@@ -76,11 +74,15 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
|
||||
<IDEUpdaterComponent
|
||||
updateInfo={this.updateInfo}
|
||||
updateProgress={this.updateProgress}
|
||||
openExternal={this.openExternal}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private readonly openExternal = (url: string) =>
|
||||
this.windowService.openNewWindow(url, { external: true });
|
||||
|
||||
get value(): UpdateInfo | undefined {
|
||||
return this.updateInfo;
|
||||
}
|
||||
@@ -164,7 +166,7 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
|
||||
}
|
||||
|
||||
private openDownloadPage(): void {
|
||||
this.windowService.openNewWindow(DOWNLOAD_PAGE_URL, { external: true });
|
||||
this.openExternal('https://www.arduino.cc/en/software');
|
||||
this.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@ import {
|
||||
Monitor,
|
||||
MonitorManagerProxyClient,
|
||||
MonitorManagerProxyFactory,
|
||||
} from '../common/protocol/monitor-service';
|
||||
import {
|
||||
PluggableMonitorSettings,
|
||||
MonitorSettings,
|
||||
} from '../node/monitor-settings/monitor-settings-provider';
|
||||
PluggableMonitorSettings,
|
||||
} from '../common/protocol/monitor-service';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
monitorConnectionStatusEquals,
|
||||
MonitorEOL,
|
||||
MonitorManagerProxyClient,
|
||||
MonitorSettings,
|
||||
MonitorState,
|
||||
} from '../common/protocol';
|
||||
import { isNullOrUndefined } from '../common/utils';
|
||||
import { MonitorSettings } from '../node/monitor-settings/monitor-settings-provider';
|
||||
|
||||
@injectable()
|
||||
export class MonitorModel implements FrontendApplicationContribution {
|
||||
|
||||
@@ -23,9 +23,9 @@ import { nls } from '@theia/core/lib/common';
|
||||
import {
|
||||
MonitorEOL,
|
||||
MonitorManagerProxyClient,
|
||||
MonitorSettings,
|
||||
} from '../../../common/protocol';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/react';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { areEqual, FixedSizeList as List } from 'react-window';
|
||||
import dateFormat = require('dateformat');
|
||||
import dateFormat from 'dateformat';
|
||||
import { messagesToLines, truncateLines } from './monitor-utils';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
import { Endpoint } from '@theia/core/lib/browser/endpoint';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
MaybePromise,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
|
||||
import { ipcRenderer } from '@theia/electron/shared/electron';
|
||||
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import type { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import queryString from 'query-string';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
|
||||
import {
|
||||
CLOSE_PLOTTER_WINDOW,
|
||||
SHOW_PLOTTER_WINDOW,
|
||||
} from '../../../common/ipc-communication';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
const queryString = require('query-string');
|
||||
|
||||
export namespace SerialPlotterContribution {
|
||||
export namespace Commands {
|
||||
@@ -44,38 +34,31 @@ export namespace SerialPlotterContribution {
|
||||
|
||||
@injectable()
|
||||
export class PlotterFrontendContribution extends Contribution {
|
||||
protected window: Window | null;
|
||||
protected url: string;
|
||||
protected wsPort: number;
|
||||
private readonly endpointUrl = new Endpoint({ path: '/plotter' })
|
||||
.getRestUrl()
|
||||
.toString();
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
private _plotterUrl: string | undefined;
|
||||
|
||||
@inject(MonitorModel)
|
||||
protected readonly model: MonitorModel;
|
||||
|
||||
private readonly model: MonitorModel;
|
||||
@inject(ThemeService)
|
||||
protected readonly themeService: ThemeService;
|
||||
|
||||
private readonly themeService: ThemeService;
|
||||
@inject(MonitorManagerProxyClient)
|
||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
private readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
override onStart(app: FrontendApplication): MaybePromise<void> {
|
||||
this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString();
|
||||
|
||||
ipcRenderer.on(CLOSE_PLOTTER_WINDOW, async () => {
|
||||
if (!!this.window) {
|
||||
this.window = null;
|
||||
}
|
||||
});
|
||||
override onStart(): void {
|
||||
this.toDispose.push(
|
||||
window.electronArduino.registerPlotterWindowCloseHandler(() => {
|
||||
this._plotterUrl = undefined;
|
||||
})
|
||||
);
|
||||
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
|
||||
|
||||
return super.onStart(app);
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
|
||||
execute: this.startPlotter.bind(this),
|
||||
execute: () => this.startPlotter(),
|
||||
});
|
||||
registry.registerCommand(SerialPlotterContribution.Commands.RESET, {
|
||||
execute: () => this.reset(),
|
||||
@@ -85,7 +68,7 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
{
|
||||
isVisible: (widget) =>
|
||||
ArduinoToolbar.is(widget) && widget.side === 'right',
|
||||
execute: this.startPlotter.bind(this),
|
||||
execute: () => this.startPlotter(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -98,10 +81,13 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
async startPlotter(): Promise<void> {
|
||||
private async startPlotter(forceReload = false): Promise<void> {
|
||||
await this.monitorManagerProxy.startMonitor();
|
||||
if (!!this.window) {
|
||||
ipcRenderer.send(SHOW_PLOTTER_WINDOW);
|
||||
if (this._plotterUrl) {
|
||||
window.electronArduino.showPlotterWindow({
|
||||
url: this._plotterUrl,
|
||||
forceReload,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const wsPort = this.monitorManagerProxy.getWebSocketPort();
|
||||
@@ -117,26 +103,30 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
}
|
||||
}
|
||||
|
||||
protected async open(wsPort: number): Promise<void> {
|
||||
private open(wsPort: number): void {
|
||||
const initConfig = {
|
||||
darkTheme: this.themeService.getCurrentTheme().type === 'dark',
|
||||
darkTheme: this.isDarkTheme,
|
||||
wsPort,
|
||||
serialPort: this.model.serialPort,
|
||||
};
|
||||
const urlWithParams = queryString.stringifyUrl(
|
||||
this._plotterUrl = queryString.stringifyUrl(
|
||||
{
|
||||
url: this.url,
|
||||
url: this.endpointUrl,
|
||||
query: initConfig,
|
||||
},
|
||||
{ arrayFormat: 'comma' }
|
||||
);
|
||||
this.window = window.open(urlWithParams, 'serialPlotter');
|
||||
window.electronArduino.showPlotterWindow({ url: this._plotterUrl });
|
||||
}
|
||||
|
||||
protected async reset(): Promise<void> {
|
||||
if (!!this.window) {
|
||||
this.window.close();
|
||||
await this.startPlotter();
|
||||
private get isDarkTheme(): boolean {
|
||||
const themeType = this.themeService.getCurrentTheme().type;
|
||||
return themeType === 'dark' || themeType === 'hc';
|
||||
}
|
||||
|
||||
private async reset(): Promise<void> {
|
||||
if (this._plotterUrl) {
|
||||
await this.startPlotter(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,3 @@
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc .arduino-select__option--is-selected {
|
||||
outline: 1px solid var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .arduino-select__option--is-focused {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
@@ -276,16 +276,6 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
|
||||
border-top: 1px solid var(--theia-dropdown-border);
|
||||
}
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc #select-board-dialog .selectBoardContainer .list .item:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc div#select-board-dialog .selectBoardContainer .list .item.selected {
|
||||
outline: 1px solid var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 400px) {
|
||||
div.dialogContent.select-board-dialog > div.head {
|
||||
display: none;
|
||||
|
||||
@@ -41,6 +41,17 @@
|
||||
overflow: auto;
|
||||
padding: 0 12px;
|
||||
cursor: text;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog .fallback {
|
||||
min-height: 180px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog .fallback .spinner {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.dialogContent.ide-updater-dialog
|
||||
@@ -104,3 +115,10 @@
|
||||
margin-left: 79px;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* https://github.com/arduino/arduino-ide/pull/2027#issuecomment-1533174638 */
|
||||
/* https://github.com/eclipse-theia/theia/commit/1b5ff9ee459df14cedc0e8266dd02dae93fcd1bf#diff-d8d45a890507a01141c010ad4a6891edf2ae727cfa6dfe604cebbd667812cccbR68 */
|
||||
/* Use normal whitespace handling for the changelog content in the update dialog. */
|
||||
.p-Widget.dialogOverlay .dialogContent.ide-updater-dialog {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ body.theia-dark {
|
||||
background-color: var(--theia-warningBackground);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .theia-input.warning,
|
||||
.hc-black.hc-theia.theia-hc .theia-input.warning::placeholder {
|
||||
color: var(--theia-warningBackground);
|
||||
background-color: var(--theia-warningForeground);
|
||||
}
|
||||
|
||||
.theia-input.error:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
@@ -170,25 +164,6 @@ button.theia-button.message-box-dialog-button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc button.theia-button:hover,
|
||||
.hc-black.hc-theia.theia-hc .theia-button:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc button.theia-button,
|
||||
.hc-black.hc-theia.theia-hc .theia-button,
|
||||
.hc-black.hc-theia.theia-hc button.theia-button.secondary {
|
||||
border: 1px solid var(--theia-button-border);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .theia-notification-list-item:hover:not(:focus) {
|
||||
background-color: var(--theia-notifications-background);
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.debug-toolbar .debug-action>div {
|
||||
font-family: var(--theia-ui-font-family);
|
||||
font-size: var(--theia-ui-font-size0);
|
||||
|
||||
@@ -219,15 +219,3 @@ div.filterable-list-container > div > div > div > div:nth-child(1) > div.separat
|
||||
width: 65px;
|
||||
min-width: 65px;
|
||||
}
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:hover:before {
|
||||
background-color: transparent;
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:before {
|
||||
color: var(--theia-button-background);
|
||||
border: 1px solid var(--theia-button-border);
|
||||
}
|
||||
|
||||
@@ -137,24 +137,6 @@
|
||||
color: var(--theia-titleBar-activeForeground);
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item > div.arduino-toggle-advanced-mode {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.arduino-toggle-advanced-mode-icon {
|
||||
mask: none;
|
||||
-webkit-mask: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--theia-titleBar-activeBackground);
|
||||
}
|
||||
|
||||
.arduino-open-boards-control-icon {
|
||||
mask: none;
|
||||
-webkit-mask: none;
|
||||
@@ -202,61 +184,6 @@
|
||||
background-color: var(--theia-terminal-background);
|
||||
}
|
||||
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div {
|
||||
background: var(--theia-arduino-toolbar-button-background);
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-plotter,
|
||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-monitor {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .item.arduino-tool-item.toggled .arduino-verify-sketch--toolbar,
|
||||
.hc-black.hc-theia.theia-hc .item.arduino-tool-item.toggled .arduino-upload-sketch--toolbar {
|
||||
background-color: var(--theia-arduino-toolbar-button-background) !important;
|
||||
outline: 1px solid var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .arduino-boards-dropdown-item:hover {
|
||||
background: var(--theia-dropdown-background);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .arduino-boards-dropdown-item:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current {
|
||||
outline: 1px solid var(--theia-focusBorder);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc #theia-main-content-panel .p-TabBar .p-TabBar-tab:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .quick-input-list .monaco-list-row.focused {
|
||||
outline: 1px dotted var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .quick-input-list .monaco-list-row:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .quick-input-widget {
|
||||
outline: 1px solid var(--theia-contrastBorder);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-row.markdown-hover:first-child p {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@@ -80,19 +80,3 @@ widget width.
|
||||
background: var(--theia-list-inactiveSelectionBackground);
|
||||
color: var(--theia-list-inactiveSelectionForeground) !important;
|
||||
}
|
||||
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc .theia-TreeNode:hover {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .theia-Tree .theia-TreeNode.theia-mod-selected {
|
||||
outline: 1px dotted var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .theia-Tree:focus .theia-TreeNode.theia-mod-selected,
|
||||
.hc-black.hc-theia.theia-hc .theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
|
||||
outline: 1px solid var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ import { ArduinoDaemon } from '../../../common/protocol';
|
||||
import { assertUnreachable } from '../../../common/utils';
|
||||
import { CreateFeatures } from '../../create/create-features';
|
||||
import { NotificationCenter } from '../../notification-center';
|
||||
import debounce = require('lodash.debounce');
|
||||
import isOnline = require('is-online');
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
@injectable()
|
||||
export class IsOnline implements FrontendApplicationContribution {
|
||||
@@ -30,17 +29,19 @@ export class IsOnline implements FrontendApplicationContribution {
|
||||
private stopped = false;
|
||||
|
||||
onStart(): void {
|
||||
const checkOnline = async () => {
|
||||
if (!this.stopped) {
|
||||
try {
|
||||
const online = await isOnline();
|
||||
this.setOnline(online);
|
||||
} finally {
|
||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||
import('is-online').then((module) => {
|
||||
const checkOnline = async () => {
|
||||
if (!this.stopped) {
|
||||
try {
|
||||
const online = await module.default();
|
||||
this.setOnline(online);
|
||||
} finally {
|
||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
checkOnline();
|
||||
};
|
||||
checkOnline();
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -56,7 +57,7 @@ export class IsOnline implements FrontendApplicationContribution {
|
||||
return this.onDidChangeOnlineEmitter.event;
|
||||
}
|
||||
|
||||
private setOnline(online: boolean) {
|
||||
private setOnline(online: boolean): void {
|
||||
const oldOnline = this._online;
|
||||
this._online = online;
|
||||
if (!this.stopped && this._online !== oldOnline) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TabBarRenderer as TheiaTabBarRenderer,
|
||||
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
|
||||
} from '@theia/core/lib/browser/shell/tab-bars';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
export class TabBarRenderer extends TheiaTabBarRenderer {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@@ -25,8 +25,8 @@ const builtInThemeIds = new Set(
|
||||
[
|
||||
ArduinoThemes.light,
|
||||
ArduinoThemes.dark,
|
||||
BuiltinThemeProvider.hcLightTheme,
|
||||
BuiltinThemeProvider.hcTheme,
|
||||
// TODO: add the HC light theme after Theia 1.36
|
||||
].map(({ id }) => id)
|
||||
);
|
||||
const deprecatedThemeIds = new Set(
|
||||
@@ -37,7 +37,14 @@ const deprecatedThemeIds = new Set(
|
||||
|
||||
export const lightThemeLabel = nls.localize('arduino/theme/light', 'Light');
|
||||
export const darkThemeLabel = nls.localize('arduino/theme/dark', 'Dark');
|
||||
export const hcThemeLabel = nls.localize('arduino/theme/hc', 'High Contrast');
|
||||
export const hcLightThemeLabel = nls.localize(
|
||||
'arduino/theme/hcLight',
|
||||
'Light High Contrast'
|
||||
);
|
||||
export const hcThemeLabel = nls.localize(
|
||||
'arduino/theme/hc',
|
||||
'Dark High Contrast'
|
||||
);
|
||||
export function userThemeLabel(theme: Theme): string {
|
||||
return nls.localize('arduino/theme/user', '{0} (user)', theme.label);
|
||||
}
|
||||
@@ -57,6 +64,8 @@ export function themeLabelForSettings(theme: Theme): string {
|
||||
return darkThemeLabel;
|
||||
case BuiltinThemeProvider.hcTheme.id:
|
||||
return hcThemeLabel;
|
||||
case BuiltinThemeProvider.hcLightTheme.id:
|
||||
return hcLightThemeLabel;
|
||||
case BuiltinThemeProvider.lightTheme.id: // fall-through
|
||||
case BuiltinThemeProvider.darkTheme.id:
|
||||
return deprecatedThemeLabel(theme);
|
||||
@@ -73,6 +82,8 @@ export function compatibleBuiltInTheme(theme: Theme): Theme {
|
||||
return ArduinoThemes.dark;
|
||||
case 'hc':
|
||||
return BuiltinThemeProvider.hcTheme;
|
||||
case 'hcLight':
|
||||
return BuiltinThemeProvider.hcLightTheme;
|
||||
default: {
|
||||
console.warn(
|
||||
`Unhandled theme type: ${theme.type}. Theme ID: ${theme.id}, label: ${theme.label}`
|
||||
@@ -91,7 +102,7 @@ interface ThemeProvider {
|
||||
/**
|
||||
* Returns with a list of built-in themes officially supported by IDE2 (https://github.com/arduino/arduino-ide/issues/1283).
|
||||
* The themes in the array follow the following order:
|
||||
* - built-in themes first (in `Light`, `Dark`, `High Contrast`), // TODO -> High Contrast will be split up to HC Dark and HC Light after the Theia version uplift
|
||||
* - built-in themes first (in `Light`, `Dark`, `Light High Contrast`, and `Dark High Contrast`),
|
||||
* - followed by user installed (VSIX) themes grouped by theme type, then alphabetical order,
|
||||
* - if the `currentTheme` is either Light (Theia) or Dark (Theia), the last item of the array will be the selected theme with `(deprecated)` suffix.
|
||||
*/
|
||||
@@ -171,7 +182,8 @@ const arduinoThemeTypeOrder: Record<ArduinoThemeType, number> = {
|
||||
const themeTypeOrder: Record<ThemeType, number> = {
|
||||
light: 0,
|
||||
dark: 1,
|
||||
hc: 2,
|
||||
hcLight: 2,
|
||||
hc: 3,
|
||||
};
|
||||
|
||||
export function arduinoThemeTypeOf(theme: Theme | string): ArduinoThemeType {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StartupTask } from '../../../electron-common/startup-task';
|
||||
import type { StartupTasks } from '../../../electron-common/startup-task';
|
||||
|
||||
export const WindowServiceExt = Symbol('WindowServiceExt');
|
||||
export interface WindowServiceExt {
|
||||
@@ -6,5 +6,6 @@ export interface WindowServiceExt {
|
||||
* Returns with a promise that resolves to `true` if the current window is the first window.
|
||||
*/
|
||||
isFirstWindow(): Promise<boolean>;
|
||||
reload(options?: StartupTask.Owner): void;
|
||||
reload(tasks?: StartupTasks): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
@@ -118,10 +117,8 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
if (widget instanceof EditorWidget) {
|
||||
const { uri } = widget.editor;
|
||||
const filename = uri.path.toString();
|
||||
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
|
||||
if (this.previousRepresentedFilename !== filename) {
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
window.electronArduino.setRepresentedFilename(uri.path.fsPath());
|
||||
this.previousRepresentedFilename = filename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugAction as TheiaDebugAction } from '@theia/debug/lib/browser/view/debug-action';
|
||||
import {
|
||||
codiconArray,
|
||||
DISABLED_CLASS,
|
||||
} from '@theia/core/lib/browser/widgets/widget';
|
||||
|
||||
// customized debug action to show the contributed command's label when there is no icon
|
||||
export class DebugAction extends TheiaDebugAction {
|
||||
override render(): React.ReactNode {
|
||||
const { enabled, label, iconClass } = this.props;
|
||||
const classNames = ['debug-action', ...codiconArray(iconClass, true)];
|
||||
if (enabled === false) {
|
||||
classNames.push(DISABLED_CLASS);
|
||||
}
|
||||
return (
|
||||
<span
|
||||
tabIndex={0}
|
||||
className={classNames.join(' ')}
|
||||
title={label}
|
||||
onClick={this.props.run}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{!iconClass ||
|
||||
(iconClass.match(/plugin-icon-\d+/) && <div>{label}</div>)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import debounce = require('p-debounce');
|
||||
import debounce from 'p-debounce';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
|
||||
@@ -43,7 +43,8 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.debugContributionProvider,
|
||||
this.workspaceService
|
||||
this.workspaceService,
|
||||
2_000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import type { ContextKey } from '@theia/core/lib/browser/context-key-service';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
DebugSession,
|
||||
DebugState,
|
||||
} from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import type { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
|
||||
function debugStateLabel(state: DebugState): string {
|
||||
switch (state) {
|
||||
case DebugState.Initializing:
|
||||
return 'initializing';
|
||||
case DebugState.Stopped:
|
||||
return 'stopped';
|
||||
case DebugState.Running:
|
||||
return 'running';
|
||||
default:
|
||||
return 'inactive';
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
protected debugStateKey: ContextKey<string>;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
this.debugStateKey = this.contextKeyService.createKey<string>(
|
||||
'debugState',
|
||||
debugStateLabel(this.state)
|
||||
);
|
||||
super.init();
|
||||
}
|
||||
|
||||
protected override fireDidChange(current: DebugSession | undefined): void {
|
||||
this.debugTypeKey.set(current?.configuration.type);
|
||||
this.inDebugModeKey.set(this.inDebugMode);
|
||||
this.debugStateKey.set(debugStateLabel(this.state));
|
||||
this.onDidChangeEmitter.fire(current);
|
||||
}
|
||||
|
||||
protected override async doStart(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions
|
||||
): Promise<DebugSession> {
|
||||
const parentSession =
|
||||
options.configuration.parentSession &&
|
||||
this._sessions.get(options.configuration.parentSession.id);
|
||||
const contrib = this.sessionContributionRegistry.get(
|
||||
options.configuration.type
|
||||
);
|
||||
const sessionFactory = contrib
|
||||
? contrib.debugSessionFactory()
|
||||
: this.debugSessionFactory;
|
||||
const session = sessionFactory.get(sessionId, options, parentSession);
|
||||
this._sessions.set(sessionId, session);
|
||||
|
||||
this.debugTypeKey.set(session.configuration.type);
|
||||
// this.onDidCreateDebugSessionEmitter.fire(session); // defer the didCreate event after start https://github.com/eclipse-theia/theia/issues/11916
|
||||
|
||||
let state = DebugState.Inactive;
|
||||
session.onDidChange(() => {
|
||||
if (state !== session.state) {
|
||||
state = session.state;
|
||||
if (state === DebugState.Stopped) {
|
||||
this.onDidStopDebugSessionEmitter.fire(session);
|
||||
}
|
||||
}
|
||||
this.updateCurrentSession(session);
|
||||
});
|
||||
session.onDidChangeBreakpoints((uri) =>
|
||||
this.fireDidChangeBreakpoints({ session, uri })
|
||||
);
|
||||
session.on('terminated', async (event) => {
|
||||
const restart = event.body && event.body.restart;
|
||||
if (restart) {
|
||||
// postDebugTask isn't run in case of auto restart as well as preLaunchTask
|
||||
this.doRestart(session, !!restart);
|
||||
} else {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
await this.runTask(
|
||||
session.options.workspaceFolderUri,
|
||||
session.configuration.postDebugTask
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
session.on('exited', async (event) => {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
});
|
||||
|
||||
session.onDispose(() => this.cleanup(session));
|
||||
session
|
||||
.start()
|
||||
.then(() => {
|
||||
this.onDidCreateDebugSessionEmitter.fire(session); // now fire the didCreate event
|
||||
this.onDidStartDebugSessionEmitter.fire(session);
|
||||
})
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
.catch((e) => {
|
||||
session.stop(false, () => {
|
||||
this.debug.terminateDebugSession(session.id);
|
||||
});
|
||||
});
|
||||
session.onDidCustomEvent(({ event, body }) =>
|
||||
this.onDidReceiveDebugSessionCustomEventEmitter.fire({
|
||||
event,
|
||||
body,
|
||||
session,
|
||||
})
|
||||
);
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@@ -1,231 +1,12 @@
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Mutable } from '@theia/core/lib/common/types';
|
||||
import { URI } from '@theia/core/lib/common/uri';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugFunctionBreakpoint } from '@theia/debug/lib/browser/model/debug-function-breakpoint';
|
||||
import { DebugSourceBreakpoint } from '@theia/debug/lib/browser/model/debug-source-breakpoint';
|
||||
import {
|
||||
DebugThreadData,
|
||||
StoppedDetails,
|
||||
} from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugThread } from './debug-thread';
|
||||
|
||||
export class DebugSession extends TheiaDebugSession {
|
||||
/**
|
||||
* The `send('initialize')` request resolves later than `on('initialized')` emits the event.
|
||||
* Hence, the `configure` would use the empty object `capabilities`.
|
||||
* Using the empty `capabilities` could result in missing exception breakpoint filters, as
|
||||
* always `capabilities.exceptionBreakpointFilters` is falsy. This deferred promise works
|
||||
* around this timing issue.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/11886.
|
||||
*/
|
||||
protected didReceiveCapabilities = new Deferred();
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
const clientName = FrontendApplicationConfigProvider.get().applicationName;
|
||||
try {
|
||||
const response = await this.connection.sendRequest('initialize', {
|
||||
clientID: clientName.toLocaleLowerCase().replace(/ /g, '_'),
|
||||
clientName,
|
||||
adapterID: this.configuration.type,
|
||||
locale: 'en-US',
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
pathFormat: 'path',
|
||||
supportsVariableType: false,
|
||||
supportsVariablePaging: false,
|
||||
supportsRunInTerminalRequest: true,
|
||||
});
|
||||
this.updateCapabilities(response?.body || {});
|
||||
this.didReceiveCapabilities.resolve();
|
||||
} catch (err) {
|
||||
this.didReceiveCapabilities.reject(err);
|
||||
throw err;
|
||||
}
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override handleDisconnectError(err: unknown): void {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
protected override async configure(): Promise<void> {
|
||||
await this.didReceiveCapabilities.promise;
|
||||
return super.configure();
|
||||
}
|
||||
|
||||
override async stop(isRestart: boolean, callback: () => void): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = this as any;
|
||||
if (!_this.isStopping) {
|
||||
_this.isStopping = true;
|
||||
if (this.configuration.lifecycleManagedByParent && this.parentSession) {
|
||||
await this.parentSession.stop(isRestart, callback);
|
||||
} else {
|
||||
if (this.canTerminate()) {
|
||||
const terminated = this.waitFor('terminated', 5000);
|
||||
try {
|
||||
await this.connection.sendRequest(
|
||||
'terminate',
|
||||
{ restart: isRestart },
|
||||
5000
|
||||
);
|
||||
await terminated;
|
||||
} catch (e) {
|
||||
console.error('Did not receive terminated event in time', e);
|
||||
}
|
||||
} else {
|
||||
const terminateDebuggee =
|
||||
this.initialized && this.capabilities.supportTerminateDebuggee;
|
||||
// Related https://github.com/microsoft/vscode/issues/165138
|
||||
try {
|
||||
await this.sendRequest(
|
||||
'disconnect',
|
||||
{ restart: isRestart, terminateDebuggee },
|
||||
2000
|
||||
);
|
||||
} catch (err) {
|
||||
if (
|
||||
'message' in err &&
|
||||
typeof err.message === 'string' &&
|
||||
err.message.test(err.message)
|
||||
) {
|
||||
// VS Code ignores errors when sending the `disconnect` request.
|
||||
// Debug adapter might not send the `disconnected` event as a response.
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async sendFunctionBreakpoints(
|
||||
affectedUri: URI
|
||||
): Promise<void> {
|
||||
const all = this.breakpoints
|
||||
.getFunctionBreakpoints()
|
||||
.map(
|
||||
(origin) =>
|
||||
new DebugFunctionBreakpoint(origin, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
if (this.capabilities.supportsFunctionBreakpoints) {
|
||||
try {
|
||||
const response = await this.sendRequest('setFunctionBreakpoints', {
|
||||
breakpoints: enabled.map((b) => b.origin.raw),
|
||||
});
|
||||
// Apparently, `body` and `breakpoints` can be missing.
|
||||
// https://github.com/eclipse-theia/theia/issues/11885
|
||||
// https://github.com/microsoft/vscode/blob/80004351ccf0884b58359f7c8c801c91bb827d83/src/vs/workbench/contrib/debug/browser/debugSession.ts#L448-L449
|
||||
if (response && response.body) {
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetFunctionBreakpoints
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetFunctionBreakpoints for invalid breakpoints
|
||||
const genericMessage =
|
||||
'Function breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle function breakpoints: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override async sendSourceBreakpoints(
|
||||
affectedUri: URI,
|
||||
sourceModified?: boolean
|
||||
): Promise<void> {
|
||||
const source = await this.toSource(affectedUri);
|
||||
const all = this.breakpoints
|
||||
.findMarkers({ uri: affectedUri })
|
||||
.map(
|
||||
({ data }) =>
|
||||
new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
try {
|
||||
const breakpoints = enabled.map(({ origin }) => origin.raw);
|
||||
const response = await this.sendRequest('setBreakpoints', {
|
||||
source: source.raw,
|
||||
sourceModified,
|
||||
breakpoints,
|
||||
lines: breakpoints.map(({ line }) => line),
|
||||
});
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetBreakpointsResponse
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetBreakpointsResponse for invalid breakpoints
|
||||
const genericMessage = 'Breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle breakpoints for ${affectedUri}: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
this.setSourceBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override doUpdateThreads(
|
||||
threads: DebugProtocol.Thread[],
|
||||
stoppedDetails?: StoppedDetails
|
||||
): void {
|
||||
const existing = this._threads;
|
||||
this._threads = new Map();
|
||||
for (const raw of threads) {
|
||||
const id = raw.id;
|
||||
const thread = existing.get(id) || new DebugThread(this); // patched debug thread
|
||||
this._threads.set(id, thread);
|
||||
const data: Partial<Mutable<DebugThreadData>> = { raw };
|
||||
if (stoppedDetails) {
|
||||
if (stoppedDetails.threadId === id) {
|
||||
data.stoppedDetails = stoppedDetails;
|
||||
} else if (stoppedDetails.allThreadsStopped) {
|
||||
data.stoppedDetails = {
|
||||
// When a debug adapter notifies us that all threads are stopped,
|
||||
// we do not know why the others are stopped, so we should default
|
||||
// to something generic.
|
||||
reason: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
thread.update(data);
|
||||
}
|
||||
this.updateCurrentThread(stoppedDetails);
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override handleTerminateError(err: unknown): void {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
|
||||
import { Range } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
|
||||
export class DebugStackFrame extends TheiaDebugStackFrame {
|
||||
override async open(
|
||||
options: WidgetOpenerOptions = {
|
||||
mode: 'reveal',
|
||||
}
|
||||
): Promise<EditorWidget | undefined> {
|
||||
if (!this.source) {
|
||||
return undefined;
|
||||
}
|
||||
const { line, column, endLine, endColumn, source } = this.raw;
|
||||
if (!source) {
|
||||
return undefined;
|
||||
}
|
||||
// create selection based on VS Code
|
||||
// https://github.com/eclipse-theia/theia/issues/11880
|
||||
const selection = Range.create(
|
||||
line,
|
||||
column,
|
||||
endLine || line,
|
||||
endColumn || column
|
||||
);
|
||||
this.source.open({
|
||||
...options,
|
||||
selection,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { DebugThread as TheiaDebugThread } from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugStackFrame } from './debug-stack-frame';
|
||||
|
||||
export class DebugThread extends TheiaDebugThread {
|
||||
protected override doUpdateFrames(
|
||||
frames: DebugProtocol.StackFrame[]
|
||||
): TheiaDebugStackFrame[] {
|
||||
const result = new Set<TheiaDebugStackFrame>();
|
||||
for (const raw of frames) {
|
||||
const id = raw.id;
|
||||
const frame =
|
||||
this._frames.get(id) || new DebugStackFrame(this, this.session); // patched debug stack frame
|
||||
this._frames.set(id, frame);
|
||||
frame.update({ raw });
|
||||
result.add(frame);
|
||||
}
|
||||
this.updateCurrentFrame();
|
||||
return [...result.values()];
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
ActionMenuNode,
|
||||
CompositeMenuNode,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugState } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugAction } from './debug-action';
|
||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
|
||||
@injectable()
|
||||
export class DebugToolbar extends TheiaDebugToolbar {
|
||||
@inject(CommandRegistry) private readonly commandRegistry: CommandRegistry;
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuModelRegistry: MenuModelRegistry;
|
||||
@inject(ContextKeyService)
|
||||
private readonly contextKeyService: ContextKeyService;
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
const { state } = this.model;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderContributedCommands()}
|
||||
{this.renderContinue()}
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOver}
|
||||
label={nls.localizeByDefault('Step Over')}
|
||||
iconClass="debug-step-over"
|
||||
ref={this.setStepRef}
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepIn}
|
||||
label={nls.localizeByDefault('Step Into')}
|
||||
iconClass="debug-step-into"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOut}
|
||||
label={nls.localizeByDefault('Step Out')}
|
||||
iconClass="debug-step-out"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state !== DebugState.Inactive}
|
||||
run={this.restart}
|
||||
label={nls.localizeByDefault('Restart')}
|
||||
iconClass="debug-restart"
|
||||
/>
|
||||
{this.renderStart()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContributedCommands(): React.ReactNode {
|
||||
return this.menuModelRegistry
|
||||
.getMenu(TheiaDebugToolbar.MENU)
|
||||
.children.filter((node) => node instanceof CompositeMenuNode)
|
||||
.map((node) => (node as CompositeMenuNode).children)
|
||||
.reduce((acc, curr) => acc.concat(curr), [])
|
||||
.filter((node) => node instanceof ActionMenuNode)
|
||||
.map((node) => this.debugAction(node as ActionMenuNode));
|
||||
}
|
||||
|
||||
private debugAction(node: ActionMenuNode): React.ReactNode {
|
||||
const { label, command, when, icon: iconClass = '' } = node;
|
||||
const run = () => this.commandRegistry.executeCommand(command);
|
||||
const enabled = when ? this.contextKeyService.match(when) : true;
|
||||
return (
|
||||
enabled && (
|
||||
<DebugAction
|
||||
key={command}
|
||||
enabled={enabled}
|
||||
label={label}
|
||||
iconClass={iconClass}
|
||||
run={run}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { ResourceSaveOptions } from '@theia/core/lib/common/resource';
|
||||
import { Readable } from '@theia/core/lib/common/stream';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
@@ -11,14 +9,13 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import {
|
||||
FileOperationError,
|
||||
FileOperationResult,
|
||||
FileStat,
|
||||
} from '@theia/filesystem/lib/common/files';
|
||||
import * as PQueue from 'p-queue';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
@injectable()
|
||||
export class FileResourceResolver extends TheiaFileResourceResolver {
|
||||
override async resolve(uri: URI): Promise<WriteQueuedFileResource> {
|
||||
let stat: FileStat | undefined;
|
||||
let stat;
|
||||
try {
|
||||
stat = await this.fileService.resolve(uri);
|
||||
} catch (e) {
|
||||
@@ -37,6 +34,7 @@ export class FileResourceResolver extends TheiaFileResourceResolver {
|
||||
);
|
||||
}
|
||||
return new WriteQueuedFileResource(uri, this.fileService, {
|
||||
isReadonly: stat?.isReadonly ?? false,
|
||||
shouldOverwrite: () => this.shouldOverwrite(uri),
|
||||
shouldOpenAsText: (error) => this.shouldOpenAsText(uri, error),
|
||||
});
|
||||
@@ -52,23 +50,32 @@ class WriteQueuedFileResource extends FileResource {
|
||||
options: FileResourceOptions
|
||||
) {
|
||||
super(uri, fileService, options);
|
||||
const originalDoWrite = this['doWrite'];
|
||||
this['doWrite'] = (content, options) =>
|
||||
this.writeQueue.add(() => originalDoWrite.bind(this)(content, options));
|
||||
const originalSaveStream = this['saveStream'];
|
||||
if (originalSaveStream) {
|
||||
this['saveStream'] = (content, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveStream.bind(this)(content, options)
|
||||
);
|
||||
}
|
||||
const originalSaveContents = this['saveContents'];
|
||||
if (originalSaveContents) {
|
||||
this['saveContents'] = (content, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveContents.bind(this)(content, options)
|
||||
);
|
||||
}
|
||||
const originalSaveContentChanges = this['saveContentChanges'];
|
||||
if (originalSaveContentChanges) {
|
||||
this['saveContentChanges'] = (changes, options) => {
|
||||
return this.writeQueue.add(() =>
|
||||
this['saveContentChanges'] = (changes, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveContentChanges.bind(this)(changes, options)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override async doWrite(
|
||||
content: string | Readable<string>,
|
||||
options?: ResourceSaveOptions
|
||||
): Promise<void> {
|
||||
return this.writeQueue.add(() => super.doWrite(content, options));
|
||||
}
|
||||
|
||||
protected override async isInSync(): Promise<boolean> {
|
||||
// Let all the write operations finish to update the version (mtime) before checking whether the resource is in sync.
|
||||
// https://github.com/eclipse-theia/theia/issues/12327
|
||||
|
||||
@@ -8,7 +8,7 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
import {
|
||||
ARDUINO_CLOUD_FOLDER,
|
||||
REMOTE_SKETCHBOOK_FOLDER,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
|
||||
@injectable()
|
||||
export class MonacoEditorMenuContribution extends TheiaMonacoEditorMenuContribution {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
super.registerMenus(registry);
|
||||
// https://github.com/arduino/arduino-ide/issues/1394
|
||||
registry.unregisterMenuAction('editor.action.refactor'); // Refactor...
|
||||
registry.unregisterMenuAction('editor.action.sourceAction'); // Source Action...
|
||||
// https://github.com/arduino/arduino-ide/pull/2027#pullrequestreview-1414246614
|
||||
// Root editor context menu
|
||||
registry.unregisterMenuAction('editor.action.revealDeclaration'); // Go to Declaration
|
||||
registry.unregisterMenuAction('editor.action.goToTypeDefinition'); // Go to Type Definition
|
||||
registry.unregisterMenuAction('editor.action.goToImplementation'); // Go to Implementations
|
||||
registry.unregisterMenuAction('editor.action.goToReferences'); // Go to References
|
||||
// Peek submenu
|
||||
registry.unregisterMenuAction('editor.action.peekDeclaration'); // Peek Declaration
|
||||
registry.unregisterMenuAction('editor.action.peekTypeDefinition'); // Peek Type Definition
|
||||
registry.unregisterMenuAction('editor.action.peekImplementation'); // Peek Implementation
|
||||
registry.unregisterMenuAction('editor.action.referenceSearch.trigger'); // Peek References
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { DebuggerDescription } from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugMainImpl as TheiaDebugMainImpl } from '@theia/plugin-ext/lib/main/browser/debug/debug-main';
|
||||
import { PluginDebugAdapterContribution } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-adapter-contribution';
|
||||
import { PluginDebugSessionFactory } from './plugin-debug-session-factory';
|
||||
|
||||
export class DebugMainImpl extends TheiaDebugMainImpl {
|
||||
override async $registerDebuggerContribution(
|
||||
description: DebuggerDescription
|
||||
): Promise<void> {
|
||||
const debugType = description.type;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = <any>this;
|
||||
const terminalOptionsExt = await _this.debugExt.$getTerminalCreationOptions(
|
||||
debugType
|
||||
);
|
||||
|
||||
if (_this.toDispose.disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const debugSessionFactory = new PluginDebugSessionFactory(
|
||||
_this.terminalService,
|
||||
_this.editorManager,
|
||||
_this.breakpointsManager,
|
||||
_this.labelProvider,
|
||||
_this.messages,
|
||||
_this.outputChannelManager,
|
||||
_this.debugPreferences,
|
||||
async (sessionId: string) => {
|
||||
const connection = await _this.connectionMain.ensureConnection(
|
||||
sessionId
|
||||
);
|
||||
return connection;
|
||||
},
|
||||
_this.fileService,
|
||||
terminalOptionsExt,
|
||||
_this.debugContributionProvider,
|
||||
_this.workspaceService
|
||||
);
|
||||
|
||||
const toDispose = new DisposableCollection(
|
||||
Disposable.create(() => _this.debuggerContributions.delete(debugType))
|
||||
);
|
||||
_this.debuggerContributions.set(debugType, toDispose);
|
||||
toDispose.pushAll([
|
||||
_this.pluginDebugService.registerDebugAdapterContribution(
|
||||
new PluginDebugAdapterContribution(
|
||||
description,
|
||||
_this.debugExt,
|
||||
_this.pluginService
|
||||
)
|
||||
),
|
||||
_this.sessionContributionRegistrator.registerDebugSessionContribution({
|
||||
debugType: description.type,
|
||||
debugSessionFactory: () => debugSessionFactory,
|
||||
}),
|
||||
]);
|
||||
_this.toDispose.push(
|
||||
Disposable.create(() => this.$unregisterDebuggerConfiguration(debugType))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
|
||||
import { injectable, interfaces } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
||||
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
|
||||
import {
|
||||
HostedPluginSupport as TheiaHostedPluginSupport,
|
||||
PluginHost,
|
||||
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { PluginWorker } from '@theia/plugin-ext/lib/hosted/browser/plugin-worker';
|
||||
import { setUpPluginApi } from '@theia/plugin-ext/lib/main/browser/main-context';
|
||||
import { PLUGIN_RPC_CONTEXT } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
|
||||
import { DebugMainImpl } from './debug-main';
|
||||
import { ConnectionImpl } from '@theia/plugin-ext/lib/common/connection';
|
||||
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
|
||||
@injectable()
|
||||
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
@@ -41,26 +32,4 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this as any).server;
|
||||
}
|
||||
|
||||
// to patch the VS Code extension based debugger
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override initRpc(host: PluginHost, pluginId: string): RPCProtocol {
|
||||
const rpc =
|
||||
host === 'frontend' ? new PluginWorker().rpc : this.createServerRpc(host);
|
||||
setUpPluginApi(rpc, this.container);
|
||||
this.patchDebugMain(rpc);
|
||||
this.mainPluginApiProviders
|
||||
.getContributions()
|
||||
.forEach((p) => p.initialize(rpc, this.container));
|
||||
return rpc;
|
||||
}
|
||||
|
||||
private patchDebugMain(rpc: RPCProtocol): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const connectionMain = (rpc as any).locals.get(
|
||||
PLUGIN_RPC_CONTEXT.CONNECTION_MAIN.id
|
||||
) as ConnectionImpl;
|
||||
const debugMain = new DebugMainImpl(rpc, connectionMain, this.container);
|
||||
rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { PluginDebugSessionFactory as TheiaPluginDebugSessionFactory } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-session-factory';
|
||||
import { PluginDebugSession } from './plugin-debug-session';
|
||||
|
||||
export class PluginDebugSessionFactory extends TheiaPluginDebugSessionFactory {
|
||||
override get(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions,
|
||||
parentSession?: DebugSession
|
||||
): DebugSession {
|
||||
const connection = new DebugSessionConnection(
|
||||
sessionId,
|
||||
this.connectionFactory,
|
||||
this.getTraceOutputChannel()
|
||||
);
|
||||
|
||||
return new PluginDebugSession(
|
||||
sessionId,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
this.terminalService,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.editorManager as any,
|
||||
this.breakpoints,
|
||||
this.labelProvider,
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.terminalOptionsExt,
|
||||
this.debugContributionProvider,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.workspaceService as any
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { ContributionProvider, MessageClient } from '@theia/core';
|
||||
import { LabelProvider } from '@theia/core/lib/browser';
|
||||
import { BreakpointManager } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
|
||||
import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { TerminalOptionsExt } from '@theia/plugin-ext';
|
||||
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
||||
import {
|
||||
TerminalWidget,
|
||||
TerminalWidgetOptions,
|
||||
} from '@theia/terminal/lib/browser/base/terminal-widget';
|
||||
import { DebugSession } from '../debug/debug-session';
|
||||
import { EditorManager } from '../editor/editor-manager';
|
||||
import { WorkspaceService } from '../workspace/workspace-service';
|
||||
|
||||
// This class extends the patched debug session, and not the default debug session from Theia
|
||||
export class PluginDebugSession extends DebugSession {
|
||||
constructor(
|
||||
override readonly id: string,
|
||||
override readonly options: DebugConfigurationSessionOptions,
|
||||
override readonly parentSession: TheiaDebugSession | undefined,
|
||||
protected override readonly connection: DebugSessionConnection,
|
||||
protected override readonly terminalServer: TerminalService,
|
||||
protected override readonly editorManager: EditorManager,
|
||||
protected override readonly breakpoints: BreakpointManager,
|
||||
protected override readonly labelProvider: LabelProvider,
|
||||
protected override readonly messages: MessageClient,
|
||||
protected override readonly fileService: FileService,
|
||||
protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
|
||||
protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
|
||||
protected override readonly workspaceService: WorkspaceService
|
||||
) {
|
||||
super(
|
||||
id,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
terminalServer,
|
||||
editorManager,
|
||||
breakpoints,
|
||||
labelProvider,
|
||||
messages,
|
||||
fileService,
|
||||
debugContributionProvider,
|
||||
workspaceService
|
||||
);
|
||||
}
|
||||
|
||||
protected override async doCreateTerminal(
|
||||
terminalWidgetOptions: TerminalWidgetOptions
|
||||
): Promise<TerminalWidget> {
|
||||
terminalWidgetOptions = Object.assign(
|
||||
{},
|
||||
terminalWidgetOptions,
|
||||
this.terminalOptionsExt
|
||||
);
|
||||
return super.doCreateTerminal(terminalWidgetOptions);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import { MenuPath } from '@theia/core';
|
||||
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
import { DebugVariablesWidget } from '@theia/debug/lib/browser/view/debug-variables-widget';
|
||||
import {
|
||||
ArgumentAdapter,
|
||||
PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
||||
import {
|
||||
codeToTheiaMappings,
|
||||
ContributionPoint,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
|
||||
|
||||
function patch(
|
||||
toPatch: typeof codeToTheiaMappings,
|
||||
key: string,
|
||||
value: MenuPath[]
|
||||
): void {
|
||||
const loose = toPatch as Map<string, MenuPath[]>;
|
||||
if (!loose.has(key)) {
|
||||
loose.set(key, value);
|
||||
}
|
||||
}
|
||||
// mappings is a const and cannot be customized with DI
|
||||
patch(codeToTheiaMappings, 'debug/variables/context', [
|
||||
DebugVariablesWidget.CONTEXT_MENU,
|
||||
]);
|
||||
patch(codeToTheiaMappings, 'debug/toolBar', [DebugToolBar.MENU]);
|
||||
|
||||
@injectable()
|
||||
export class PluginMenuCommandAdapter extends TheiaPluginMenuCommandAdapter {
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
const toCommentArgs: ArgumentAdapter = (...args) =>
|
||||
this.toCommentArgs(...args);
|
||||
const firstArgOnly: ArgumentAdapter = (...args) => [args[0]];
|
||||
const noArgs: ArgumentAdapter = () => [];
|
||||
const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args);
|
||||
const selectedResource = () => this.getSelectedResources();
|
||||
const widgetURI: ArgumentAdapter = (widget) =>
|
||||
this.codeEditorUtil.is(widget)
|
||||
? [this.codeEditorUtil.getResourceUri(widget)]
|
||||
: [];
|
||||
(<Array<[ContributionPoint, ArgumentAdapter | undefined]>>[
|
||||
['comments/comment/context', toCommentArgs],
|
||||
['comments/comment/title', toCommentArgs],
|
||||
['comments/commentThread/context', toCommentArgs],
|
||||
['debug/callstack/context', firstArgOnly],
|
||||
['debug/variables/context', firstArgOnly],
|
||||
['debug/toolBar', noArgs],
|
||||
['editor/context', selectedResource],
|
||||
['editor/title', widgetURI],
|
||||
['editor/title/context', selectedResource],
|
||||
['explorer/context', selectedResource],
|
||||
['scm/resourceFolder/context', toScmArgs],
|
||||
['scm/resourceGroup/context', toScmArgs],
|
||||
['scm/resourceState/context', toScmArgs],
|
||||
['scm/title', () => this.toScmArg(this.scmService.selectedRepository)],
|
||||
['timeline/item/context', (...args) => this.toTimelineArgs(...args)],
|
||||
['view/item/context', (...args) => this.toTreeArgs(...args)],
|
||||
['view/title', noArgs],
|
||||
]).forEach(([contributionPoint, adapter]) => {
|
||||
if (adapter) {
|
||||
const paths = codeToTheiaMappings.get(contributionPoint);
|
||||
if (paths) {
|
||||
paths.forEach((path) => this.addArgumentAdapter(path, adapter));
|
||||
}
|
||||
}
|
||||
});
|
||||
this.addArgumentAdapter(TAB_BAR_TOOLBAR_CONTEXT_MENU, widgetURI);
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,9 @@ import {
|
||||
SketchesService,
|
||||
} from '../../../common/protocol/sketches-service';
|
||||
import {
|
||||
StartupTask,
|
||||
StartupTaskProvider,
|
||||
hasStartupTasks,
|
||||
StartupTask,
|
||||
} from '../../../electron-common/startup-task';
|
||||
import { WindowServiceExt } from '../core/window-service-ext';
|
||||
|
||||
@@ -128,7 +129,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
.getContributions()
|
||||
.map((contribution) => contribution.tasks())
|
||||
.reduce((prev, curr) => prev.concat(curr), []);
|
||||
if (StartupTask.has(options)) {
|
||||
if (hasStartupTasks(options)) {
|
||||
tasks.push(...options.tasks);
|
||||
}
|
||||
return tasks;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { notEmpty } from '@theia/core';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
|
||||
/**
|
||||
* Finds the closest child HTMLButtonElement representing a Theia button.
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import type { Props, StylesConfig, ThemeConfig } from 'react-select';
|
||||
import Select from 'react-select';
|
||||
import type { StylesConfig } from 'react-select/dist/declarations/src/styles';
|
||||
import type { ThemeConfig } from 'react-select/dist/declarations/src/theme';
|
||||
import type { GroupBase } from 'react-select/dist/declarations/src/types';
|
||||
import type { StateManagerProps } from 'react-select/dist/declarations/src/useStateManager';
|
||||
|
||||
export class ArduinoSelect<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
> extends React.Component<StateManagerProps<Option, IsMulti, Group>> {
|
||||
constructor(props: StateManagerProps<Option, IsMulti, Group>) {
|
||||
export class ArduinoSelect extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import { AuthenticationClientService } from '../../auth/authentication-client-se
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
||||
import { CreateNew } from '../sketchbook/create-new';
|
||||
import { AuthenticationSession } from '../../../node/auth/types';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
import { AuthenticationSession } from '../../../common/protocol/authentication-service';
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidget<CloudSketchbookTreeWidget> {
|
||||
|
||||
@@ -34,6 +34,7 @@ import { SketchbookCommands } from '../sketchbook/sketchbook-commands';
|
||||
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
|
||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||
import { CreateUri } from '../../create/create-uri';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
|
||||
const SKETCHBOOKSYNC__CONTEXT = ['arduino-sketchbook-sync--context'];
|
||||
|
||||
@@ -61,6 +62,8 @@ export class CloudSketchbookContribution extends CloudSketchContribution {
|
||||
private readonly configServiceClient: ConfigServiceClient;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
@inject(ClipboardService)
|
||||
private readonly clipboardService: ClipboardService;
|
||||
|
||||
private readonly onDidChangeToolbarEmitter = new Emitter<void>();
|
||||
private readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
|
||||
@@ -176,6 +179,7 @@ export class CloudSketchbookContribution extends CloudSketchContribution {
|
||||
node: arg.node,
|
||||
title: nls.localize('arduino/cloud/shareSketch', 'Share Sketch'),
|
||||
createApi: this.createApi,
|
||||
clipboardService: this.clipboardService,
|
||||
}).open();
|
||||
},
|
||||
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
|
||||
@@ -35,6 +35,7 @@ function sketchBaseDir(sketch: Create.Sketch): FileStat {
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
isSymbolicLink: false,
|
||||
isReadonly: false,
|
||||
resource: createPath,
|
||||
mtime,
|
||||
ctime,
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import { NodeProps } from '@theia/core/lib/browser/tree/tree-widget';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { CompositeTreeNode } from '@theia/core/lib/browser';
|
||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
||||
import { SketchbookTreeWidget } from '../sketchbook/sketchbook-tree-widget';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
@@ -60,7 +59,12 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
</div>
|
||||
<button
|
||||
className="theia-button uppercase"
|
||||
onClick={() => shell.openExternal('https://create.arduino.cc/editor')}
|
||||
onClick={() =>
|
||||
this.windowService.openNewWindow(
|
||||
'https://create.arduino.cc/editor',
|
||||
{ external: true }
|
||||
)
|
||||
}
|
||||
>
|
||||
{nls.localize('arduino/cloud/goToCloud', 'Go to Cloud')}
|
||||
</button>
|
||||
@@ -81,7 +85,10 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
return CompositeTreeNode.is(node) && node.children.length === 0;
|
||||
}
|
||||
|
||||
protected override createNodeClassNames(node: any, props: NodeProps): string[] {
|
||||
protected override createNodeClassNames(
|
||||
node: any,
|
||||
props: NodeProps
|
||||
): string[] {
|
||||
const classNames = super.createNodeClassNames(node, props);
|
||||
|
||||
if (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
@@ -120,7 +119,7 @@ export class SketchbookWidgetContribution
|
||||
if (exists) {
|
||||
const fsPath = await this.fileService.fsPath(new URI(arg.node.uri));
|
||||
if (fsPath) {
|
||||
remote.shell.openPath(fsPath);
|
||||
window.electronArduino.openPath(fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user