diff --git a/examples/browser/package.json b/examples/browser/package.json index 0345f5ec90772..ce790c5efc18f 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -26,6 +26,7 @@ "@theia/filesystem": "1.34.0", "@theia/getting-started": "1.34.0", "@theia/git": "1.34.0", + "@theia/hang-backend": "1.34.0", "@theia/keymaps": "1.34.0", "@theia/markers": "1.34.0", "@theia/memory-inspector": "1.34.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index d4bcfc14426b9..0465391e60921 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -41,6 +41,9 @@ { "path": "../../packages/git" }, + { + "path": "../../packages/hang-backend" + }, { "path": "../../packages/keymaps" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index a839418200dd9..8985f1d0b15f3 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -28,6 +28,7 @@ "@theia/filesystem": "1.34.0", "@theia/getting-started": "1.34.0", "@theia/git": "1.34.0", + "@theia/hang-backend": "1.34.0", "@theia/keymaps": "1.34.0", "@theia/markers": "1.34.0", "@theia/memory-inspector": "1.34.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index a9ea549221e47..f47aa7b7461fa 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -47,6 +47,9 @@ { "path": "../../packages/git" }, + { + "path": "../../packages/hang-backend" + }, { "path": "../../packages/keymaps" }, diff --git a/packages/core/src/browser/connection-status-service.ts b/packages/core/src/browser/connection-status-service.ts index 084bc66ee1a3d..c9d0a8222225b 100644 --- a/packages/core/src/browser/connection-status-service.ts +++ b/packages/core/src/browser/connection-status-service.ts @@ -149,7 +149,9 @@ export class FrontendConnectionStatusService extends AbstractConnectionStatusSer protected async performPingRequest(): Promise { try { - await this.pingService.ping(); + const success = this.pingService.ping(); + const failure = new Promise((_, reject) => setTimeout(reject, this.options.offlineTimeout * 2)); + await Promise.race([success, failure]); this.updateStatus(true); } catch (e) { this.updateStatus(false); diff --git a/packages/hang-backend/.eslintrc.js b/packages/hang-backend/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/hang-backend/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/hang-backend/README.md b/packages/hang-backend/README.md new file mode 100644 index 0000000000000..e9f0478def703 --- /dev/null +++ b/packages/hang-backend/README.md @@ -0,0 +1,71 @@ +
+ +
+ +theia-ext-logo + +

ECLIPSE THEIA - MEMORY-INSPECTOR EXTENSION

+ +
+ +
+ +## Description + +This extension contributes a set of widgets for viewing memory in different ways. + +## Requirements + +This extension must be used in conjunction with a Debug Adapter that implements a `ReadMemoryRequest` handler or alternative custom request that returns memory data. +It has been tested against the [CDT-GDB Adapter](https://github.com/eclipse-cdt/cdt-gdb-adapter) used as the backend for the +[CDT-GDB VSCode](https://github.com/eclipse-cdt/cdt-gdb-vscode) plugin. This repository is configured to download that plugin as part of its build routine. +If you intend to use this extension with a different debug adapter, you may need to implement a custom +[`MemoryProvider`](./src/browser/memory-provider/memory-provider-service.ts) to handle any peculiarities of the requests and responses used by your adapter. + +## Widgets + +### Memory Widget + +The basic [`MemoryWidget` class](./src/browser/memory-widget/memory-widget.ts) is a wrapper around two functional widgets, a `MemoryOptionsWidget` and +a`MemoryTableWidget`. The [`MemoryOptionsWidget`](./src/browser/memory-widget/memory-options-widget.tsx) is responsible for configuring the display +and fetching memory, and the [`MemoryTableWidget`](./src/browser/memory-widget/memory-table-widget.tsx) renders the memory according to the options +specified by the user in the `MemoryOptionsWidget`. The basic combination of these three classes offers variable highlighting, ascii display, and +dynamic updating in response to events from the debug session, as well as the option to lock the view to ignore changes from the session. + +### Diff Widget + +The [`MemoryDiffWidget`](./src/browser/diff-widget/memory-diff-widget-types.ts) is an elaboration of the `MemoryWidget` type that allows side-by-side +comparison of the contents of two `MemoryWidgets`. + +### Register Widget + +The [`RegisterWidget`](./src/browser/register-widget/register-widget-types.ts) offers functionality to view and +manipulate those values when using a debug adapter that reports register contents. + +### Editable Widget + +The [`MemoryEditableTableWidget`](./src/browser/editable-widget/memory-editable-table-widget.tsx) adds UI functionality to allow users to modify values in +the table display and send them to a backend that supports that operation. + +## Using the Widgets + +The widgets are created by the [`MemoryWidgetManager`](./src/browser/utils/memory-widget-manager.ts), and modifying the `createNewMemoryWidget()` +method of that service allows you to change what kind of widget is instantiated and under what circumstances. The widgets get memory through the +[`MemoryProviderService`](./src/browser/memory-provider/memory-provider-service.ts), which delegates to implementations `MemoryProvider` interface +that are bound as `MemoryProvider` contributions. + + +## Additional Information + +- [API documentation for `@theia/getting-started`](https://eclipse-theia.github.io/theia/docs/next/modules/getting_started.html) +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [δΈ€ (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/hang-backend/package.json b/packages/hang-backend/package.json new file mode 100644 index 0000000000000..d605a493a82ae --- /dev/null +++ b/packages/hang-backend/package.json @@ -0,0 +1,44 @@ +{ + "name": "@theia/hang-backend", + "version": "1.34.0", + "description": "Theia - Hang Backend", + "keywords": [ + "theia-extension" + ], + "homepage": "https://github.com/eclipse-theia/theia", + "license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "dependencies": { + "@theia/core": "1.34.0" + }, + "devDependencies": { + "@types/long": "^4.0.0" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/hang-backend-frontend-module", + "backend": "lib/node/hang-backend-backend-module" + } + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/hang-backend/src/browser/hang-backend-command-contribution.ts b/packages/hang-backend/src/browser/hang-backend-command-contribution.ts new file mode 100644 index 0000000000000..d88043e727727 --- /dev/null +++ b/packages/hang-backend/src/browser/hang-backend-command-contribution.ts @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (C) 2023 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { HangBackendService } from '../common/types'; + +@injectable() +export class HangBackendCommandContribution implements CommandContribution { + @inject(HangBackendService) protected readonly hangBackendService: HangBackendService; + + registerCommands(commands: CommandRegistry): void { + commands.registerCommand({ id: 'hangBackend', label: 'Hang Backend' }, { + execute: () => this.hangBackendService.hangBackend(30_000), + }); + } +} diff --git a/packages/hang-backend/src/browser/hang-backend-frontend-module.ts b/packages/hang-backend/src/browser/hang-backend-frontend-module.ts new file mode 100644 index 0000000000000..724be4a580f91 --- /dev/null +++ b/packages/hang-backend/src/browser/hang-backend-frontend-module.ts @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (C) 2023 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { CommandContribution } from '@theia/core/lib/common/command'; +import { WebSocketConnectionProvider } from '@theia/core/lib/browser'; +import { HangBackendCommandContribution } from './hang-backend-command-contribution'; +import { HangBackendService, HANG_BACKEND_BACKEND_SERVICE_PATH } from '../common/types'; + +export default new ContainerModule(bind => { + bind(HangBackendCommandContribution).toSelf().inSingletonScope(); + bind(CommandContribution).toService(HangBackendCommandContribution); + + bind(HangBackendService) + .toDynamicValue( + ({ container }) => WebSocketConnectionProvider + .createProxy(container, HANG_BACKEND_BACKEND_SERVICE_PATH), + ).inSingletonScope(); +}); diff --git a/packages/hang-backend/src/common/types.ts b/packages/hang-backend/src/common/types.ts new file mode 100644 index 0000000000000..e6990cd6b8431 --- /dev/null +++ b/packages/hang-backend/src/common/types.ts @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (C) 2023 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +export const HANG_BACKEND_BACKEND_SERVICE_PATH = '/services/hang-backend-backend-service'; + +export const HangBackendService = Symbol('HangBackendService'); + +export interface HangBackendService { + hangBackend(timeout: number): void; +} + diff --git a/packages/hang-backend/src/node/hang-backend-backend-module.ts b/packages/hang-backend/src/node/hang-backend-backend-module.ts new file mode 100644 index 0000000000000..7d7bbe93f8e32 --- /dev/null +++ b/packages/hang-backend/src/node/hang-backend-backend-module.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2023 Ericsson and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging'; +import { HANG_BACKEND_BACKEND_SERVICE_PATH } from '../common/types'; +import { HangBackendBackendService } from './hang-backend-backend-service'; + +export default new ContainerModule(bind => { + bind(HangBackendBackendService).toSelf().inSingletonScope(); + bind(ConnectionHandler).toDynamicValue(({ container }) => new JsonRpcConnectionHandler( + HANG_BACKEND_BACKEND_SERVICE_PATH, + () => container.get(HangBackendBackendService), + )); +}); diff --git a/packages/hang-backend/src/node/hang-backend-backend-service.ts b/packages/hang-backend/src/node/hang-backend-backend-service.ts new file mode 100644 index 0000000000000..3849449bbd531 --- /dev/null +++ b/packages/hang-backend/src/node/hang-backend-backend-service.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2023 Ericsson and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { HangBackendService } from '../common/types'; + +@injectable() +export class HangBackendBackendService implements HangBackendService { + hangBackend(timeout: number): void { + const startingTime = Date.now(); + while (true) { + if (Date.now() - startingTime >= timeout) { + break; + } + } + console.log('breaking out of loop'); + } +} diff --git a/packages/hang-backend/tsconfig.json b/packages/hang-backend/tsconfig.json new file mode 100644 index 0000000000000..b623c1e105ac7 --- /dev/null +++ b/packages/hang-backend/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../core" + } + ] +} diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index cd0f4b36e0e48..1adfe5058aff7 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -16,6 +16,7 @@ import { Position, Range, TextDocumentSaveReason, TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol'; import { TextEditorDocument, EncodingMode, FindMatchesOptions, FindMatch, EditorPreferences } from '@theia/editor/lib/browser'; +import { ConnectionStatusService } from '@theia/core/lib/browser/connection-status-service'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { CancellationTokenSource, CancellationToken } from '@theia/core/lib/common/cancellation'; @@ -90,6 +91,7 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo protected readonly resource: Resource, protected readonly m2p: MonacoToProtocolConverter, protected readonly p2m: ProtocolToMonacoConverter, + protected readonly connectionStatusService: ConnectionStatusService, protected readonly logger?: ILogger, protected readonly editorPreferences?: EditorPreferences ) { @@ -105,6 +107,10 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo this.resolveModel = this.readContents().then( content => this.initialize(content || '') ); + this.toDispose.push(this.connectionStatusService.onStatusChange(() => { + this.cancelSave(); + this.cancelSync(); + })); } dispose(): void { diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 7262ae64ca7f9..7870a4f5c5a5e 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -56,6 +56,7 @@ import { IBulkEditService } from '@theia/monaco-editor-core/esm/vs/editor/browse import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; import { IQuickInputService } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput'; import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; +import { ConnectionStatusService } from '@theia/core/lib/browser/connection-status-service'; export const MonacoEditorFactory = Symbol('MonacoEditorFactory'); export interface MonacoEditorFactory { @@ -88,6 +89,9 @@ export class MonacoEditorProvider { @inject(MonacoQuickInputImplementation) protected readonly quickInputService: MonacoQuickInputImplementation; + @inject(ConnectionStatusService) + protected readonly connectionStatusService: ConnectionStatusService; + protected _current: MonacoEditor | undefined; /** * Returns the last focused MonacoEditor. @@ -406,7 +410,7 @@ export class MonacoEditorProvider { uri, readContents: async () => '', dispose: () => { } - }, this.m2p, this.p2m); + }, this.m2p, this.p2m, this.connectionStatusService); toDispose.push(document); const model = (await document.load()).textEditorModel; return new MonacoEditor( diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index 9386a8643d5b4..77d37079967dc 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -29,6 +29,7 @@ import { ITextModelUpdateOptions } from '@theia/monaco-editor-core/esm/vs/editor import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ITextResourcePropertiesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/textResourceConfiguration'; +import { ConnectionStatusService } from '@theia/core/lib/browser/connection-status-service'; export const MonacoEditorModelFactory = Symbol('MonacoEditorModelFactory'); export interface MonacoEditorModelFactory { @@ -78,6 +79,9 @@ export class MonacoTextModelService implements ITextModelService { @inject(FileService) protected readonly fileService: FileService; + @inject(ConnectionStatusService) + protected readonly connectionStatusService: ConnectionStatusService; + @postConstruct() public init(): void { const resourcePropertiesService = StandaloneServices.get(ITextResourcePropertiesService); @@ -122,7 +126,7 @@ export class MonacoTextModelService implements ITextModelService { protected createModel(resource: Resource): MaybePromise { const factory = this.factories.getContributions().find(({ scheme }) => resource.uri.scheme === scheme); - return factory ? factory.createModel(resource) : new MonacoEditorModel(resource, this.m2p, this.p2m, this.logger, this.editorPreferences); + return factory ? factory.createModel(resource) : new MonacoEditorModel(resource, this.m2p, this.p2m, this.connectionStatusService, this.logger, this.editorPreferences); } protected readonly modelOptions: { [name: string]: (keyof ITextModelUpdateOptions | undefined) } = { diff --git a/packages/output/src/browser/output-editor-model-factory.ts b/packages/output/src/browser/output-editor-model-factory.ts index 4c33396b45f8c..ffafda0d6e8d5 100644 --- a/packages/output/src/browser/output-editor-model-factory.ts +++ b/packages/output/src/browser/output-editor-model-factory.ts @@ -21,6 +21,7 @@ import { OutputUri } from '../common/output-uri'; import { MonacoEditorModelFactory } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter'; +import { ConnectionStatusService } from '@theia/core/lib/browser/connection-status-service'; @injectable() export class OutputEditorModelFactory implements MonacoEditorModelFactory { @@ -31,12 +32,15 @@ export class OutputEditorModelFactory implements MonacoEditorModelFactory { @inject(ProtocolToMonacoConverter) protected readonly p2m: ProtocolToMonacoConverter; + @inject(ConnectionStatusService) + protected readonly connectionStatusService: ConnectionStatusService; + readonly scheme: string = OutputUri.SCHEME; createModel( resource: Resource ): MonacoEditorModel { - return new OutputEditorModel(resource, this.m2p, this.p2m); + return new OutputEditorModel(resource, this.m2p, this.p2m, this.connectionStatusService); } } diff --git a/tsconfig.json b/tsconfig.json index bcdceef63de0c..73e248bae3277 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -81,6 +81,9 @@ { "path": "packages/git" }, + { + "path": "packages/hang-backend" + }, { "path": "packages/keymaps" },