diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d26c26def..7aa9c8fb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,8 +42,8 @@ jobs: - os: windows-2019 certificate-secret: WINDOWS_SIGNING_CERTIFICATE_PFX # Name of the secret that contains the certificate. certificate-password-secret: WINDOWS_SIGNING_CERTIFICATE_PASSWORD # Name of the secret that contains the certificate password. - certificate-extension: pfx # File extension for the certificate. - - os: ubuntu-18.04 # https://github.com/arduino/arduino-ide/issues/259 + certificate-extension: pfx # File extension for the certificate. + - os: ubuntu-latest - os: macos-latest # APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from: # https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 5bd714377..bf884fcbe 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -21,28 +21,28 @@ "test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\"" }, "dependencies": { - "@grpc/grpc-js": "^1.6.7", - "@theia/application-package": "1.31.1", - "@theia/core": "1.31.1", - "@theia/debug": "1.31.1", - "@theia/editor": "1.31.1", - "@theia/electron": "1.31.1", - "@theia/filesystem": "1.31.1", - "@theia/keymaps": "1.31.1", - "@theia/markers": "1.31.1", - "@theia/messages": "1.31.1", - "@theia/monaco": "1.31.1", - "@theia/monaco-editor-core": "1.67.2", - "@theia/navigator": "1.31.1", - "@theia/outline-view": "1.31.1", - "@theia/output": "1.31.1", - "@theia/plugin-ext": "1.31.1", - "@theia/preferences": "1.31.1", - "@theia/scm": "1.31.1", - "@theia/search-in-workspace": "1.31.1", - "@theia/terminal": "1.31.1", - "@theia/typehierarchy": "1.31.1", - "@theia/workspace": "1.31.1", + "@grpc/grpc-js": "^1.8.12", + "@theia/application-package": "1.36.0", + "@theia/core": "1.36.0", + "@theia/debug": "1.36.0", + "@theia/editor": "1.36.0", + "@theia/electron": "1.36.0", + "@theia/filesystem": "1.36.0", + "@theia/keymaps": "1.36.0", + "@theia/markers": "1.36.0", + "@theia/messages": "1.36.0", + "@theia/monaco": "1.36.0", + "@theia/monaco-editor-core": "1.72.3", + "@theia/navigator": "1.36.0", + "@theia/outline-view": "1.36.0", + "@theia/output": "1.36.0", + "@theia/plugin-ext": "1.36.0", + "@theia/preferences": "1.36.0", + "@theia/scm": "1.36.0", + "@theia/search-in-workspace": "1.36.0", + "@theia/terminal": "1.36.0", + "@theia/typehierarchy": "1.36.0", + "@theia/workspace": "1.36.0", "@tippyjs/react": "^4.2.5", "@types/auth0-js": "^9.14.0", "@types/btoa": "^1.2.3", @@ -123,6 +123,7 @@ "ncp": "^2.0.0", "protoc": "^1.0.4", "shelljs": "^0.8.3", + "stat-mode": "^1.0.0", "uuid": "^3.2.1", "yargs": "^11.1.0" }, diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index d2161ebf2..281957e50 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -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'; @@ -338,16 +338,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'; @@ -995,37 +985,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); diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx index 93a15a3c5..4d46ef232 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -9,7 +9,7 @@ import { AvailableBoard, } from './boards-service-provider'; import { nls } from '@theia/core/lib/common'; -import classNames from 'classnames'; +import * as classNames from 'classnames'; import { BoardsConfig } from './boards-config'; export interface BoardsDropDownListCoords { diff --git a/arduino-ide-extension/src/browser/contributions/examples.ts b/arduino-ide-extension/src/browser/contributions/examples.ts index 02c19694d..e21040ae4 100644 --- a/arduino-ide-extension/src/browser/contributions/examples.ts +++ b/arduino-ide-extension/src/browser/contributions/examples.ts @@ -1,11 +1,7 @@ import * as 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( diff --git a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx index 8dd12e681..a8db65627 100644 --- a/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-component.tsx @@ -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); diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx index 1470ec192..e2bb38d76 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx @@ -1,5 +1,5 @@ import * as React from '@theia/core/shared/react'; -import classnames from 'classnames'; +import * as classnames from 'classnames'; interface SettingsStepInputProps { initialValue: number; diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-action.tsx b/arduino-ide-extension/src/browser/theia/debug/debug-action.tsx deleted file mode 100644 index c0f691b49..000000000 --- a/arduino-ide-extension/src/browser/theia/debug/debug-action.tsx +++ /dev/null @@ -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 ( - - {!iconClass || - (iconClass.match(/plugin-icon-\d+/) &&
{label}
)} -
- ); - } -} diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-session-contribution.ts b/arduino-ide-extension/src/browser/theia/debug/debug-session-contribution.ts index d0ba503ef..769c3c930 100644 --- a/arduino-ide-extension/src/browser/theia/debug/debug-session-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/debug/debug-session-contribution.ts @@ -43,7 +43,8 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory this.messages, this.fileService, this.debugContributionProvider, - this.workspaceService + this.workspaceService, + 2_000 ); } } diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-session-manager.ts b/arduino-ide-extension/src/browser/theia/debug/debug-session-manager.ts deleted file mode 100644 index f641a6535..000000000 --- a/arduino-ide-extension/src/browser/theia/debug/debug-session-manager.ts +++ /dev/null @@ -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; - - @postConstruct() - protected override init(): void { - this.debugStateKey = this.contextKeyService.createKey( - '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 { - 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; - } -} diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-session.ts b/arduino-ide-extension/src/browser/theia/debug/debug-session.ts index 7db51c2ac..5b3ad61bb 100644 --- a/arduino-ide-extension/src/browser/theia/debug/debug-session.ts +++ b/arduino-ide-extension/src/browser/theia/debug/debug-session.ts @@ -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 { - 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; - } - } - - protected override async configure(): Promise { - await this.didReceiveCapabilities.promise; - return super.configure(); - } - - override async stop(isRestart: boolean, callback: () => void): Promise { - // 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(); - } - } + // eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars + protected override handleDisconnectError(err: unknown): void { + // NOOP } - - protected override async sendFunctionBreakpoints( - affectedUri: URI - ): Promise { - 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 { - 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> = { 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 } } diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-stack-frame.ts b/arduino-ide-extension/src/browser/theia/debug/debug-stack-frame.ts deleted file mode 100644 index c52e238ae..000000000 --- a/arduino-ide-extension/src/browser/theia/debug/debug-stack-frame.ts +++ /dev/null @@ -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 { - 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, - }); - } -} diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-thread.ts b/arduino-ide-extension/src/browser/theia/debug/debug-thread.ts deleted file mode 100644 index bb0d3313c..000000000 --- a/arduino-ide-extension/src/browser/theia/debug/debug-thread.ts +++ /dev/null @@ -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(); - 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()]; - } -} diff --git a/arduino-ide-extension/src/browser/theia/debug/debug-toolbar-widget.tsx b/arduino-ide-extension/src/browser/theia/debug/debug-toolbar-widget.tsx deleted file mode 100644 index bc6e135e8..000000000 --- a/arduino-ide-extension/src/browser/theia/debug/debug-toolbar-widget.tsx +++ /dev/null @@ -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 ( - - {this.renderContributedCommands()} - {this.renderContinue()} - - - - - {this.renderStart()} - - ); - } - - 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 && ( - - ) - ); - } -} diff --git a/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts b/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts index 168883910..a7a7c273f 100644 --- a/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts +++ b/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts @@ -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'; @injectable() export class FileResourceResolver extends TheiaFileResourceResolver { override async resolve(uri: URI): Promise { - 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, - options?: ResourceSaveOptions - ): Promise { - return this.writeQueue.add(() => super.doWrite(content, options)); - } - protected override async isInSync(): Promise { // 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 diff --git a/arduino-ide-extension/src/browser/theia/monaco/monaco-text-model-service.ts b/arduino-ide-extension/src/browser/theia/monaco/monaco-text-model-service.ts index ec775685a..b75ce4003 100644 --- a/arduino-ide-extension/src/browser/theia/monaco/monaco-text-model-service.ts +++ b/arduino-ide-extension/src/browser/theia/monaco/monaco-text-model-service.ts @@ -19,7 +19,9 @@ export class MonacoTextModelService extends TheiaMonacoTextModelService { const factory = this.factories .getContributions() .find(({ scheme }) => resource.uri.scheme === scheme); - const readOnly = this.sketchesServiceClient.isReadOnly(resource.uri); + const readOnly = + resource.isReadonly ?? + this.sketchesServiceClient.isReadOnly(resource.uri); return factory ? factory.createModel(resource) : new MaybeReadonlyMonacoEditorModel( diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/debug-main.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/debug-main.ts deleted file mode 100644 index 0845d6196..000000000 --- a/arduino-ide-extension/src/browser/theia/plugin-ext/debug-main.ts +++ /dev/null @@ -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 { - const debugType = description.type; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const _this = 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)) - ); - } -} diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts index 666a6eedc..326e02ee4 100644 --- a/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts +++ b/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts @@ -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); - } } diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session-factory.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session-factory.ts deleted file mode 100644 index a84275e1a..000000000 --- a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session-factory.ts +++ /dev/null @@ -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 - ); - } -} diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session.ts deleted file mode 100644 index 28b1de7ef..000000000 --- a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-debug-session.ts +++ /dev/null @@ -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, - 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 { - terminalWidgetOptions = Object.assign( - {}, - terminalWidgetOptions, - this.terminalOptionsExt - ); - return super.doCreateTerminal(terminalWidgetOptions); - } -} diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-menu-command-adapter.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-menu-command-adapter.ts deleted file mode 100644 index 156bbb2cb..000000000 --- a/arduino-ide-extension/src/browser/theia/plugin-ext/plugin-menu-command-adapter.ts +++ /dev/null @@ -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; - 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)] - : []; - (>[ - ['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); - } -} diff --git a/arduino-ide-extension/src/browser/widgets/arduino-select.tsx b/arduino-ide-extension/src/browser/widgets/arduino-select.tsx index da3ff5f53..1bc1f3479 100644 --- a/arduino-ide-extension/src/browser/widgets/arduino-select.tsx +++ b/arduino-ide-extension/src/browser/widgets/arduino-select.tsx @@ -5,12 +5,10 @@ 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