mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-10 10:58:33 +00:00
chore: Updated to Theia 1.31.1 (#1662)
Closes #1655 Closes #1656 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
@@ -24,7 +24,7 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
CommonCommands.TOGGLE_MAXIMIZED,
|
||||
CommonCommands.PIN_TAB,
|
||||
CommonCommands.UNPIN_TAB,
|
||||
CommonCommands.NEW_FILE,
|
||||
CommonCommands.NEW_UNTITLED_FILE,
|
||||
]) {
|
||||
commandRegistry.unregisterCommand(command);
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class StatusBarImpl extends TheiaStatusBarImpl {
|
||||
override async removeElement(id: string): Promise<void> {
|
||||
await this.ready;
|
||||
if (this.entries.delete(id)) {
|
||||
// Unlike Theia, IDE2 updates the status bar only if the element to remove was among the entries. Otherwise, it's a NOOP.
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { LabelIcon } from '@theia/core/lib/browser/label-parser';
|
||||
import {
|
||||
TabBarToolbar as TheiaTabBarToolbar,
|
||||
TabBarToolbarItem,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
|
||||
@injectable()
|
||||
export class TabBarToolbar extends TheiaTabBarToolbar {
|
||||
/**
|
||||
* Copied over from Theia. Added an ID to the parent of the toolbar item (`--container`).
|
||||
* CSS3 does not support parent selectors but we want to style the parent of the toolbar item.
|
||||
*/
|
||||
protected override renderItem(item: TabBarToolbarItem): React.ReactNode {
|
||||
let innerText = '';
|
||||
const classNames = [];
|
||||
if (item.text) {
|
||||
for (const labelPart of this.labelParser.parse(item.text)) {
|
||||
if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) {
|
||||
const className = `fa fa-${labelPart.name}${
|
||||
labelPart.animation ? ' fa-' + labelPart.animation : ''
|
||||
}`;
|
||||
classNames.push(...className.split(' '));
|
||||
} else {
|
||||
innerText = labelPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
const command = this.commands.getCommand(item.command);
|
||||
const iconClass =
|
||||
(typeof item.icon === 'function' && item.icon()) ||
|
||||
item.icon ||
|
||||
(command && command.iconClass);
|
||||
if (iconClass) {
|
||||
classNames.push(iconClass);
|
||||
}
|
||||
const tooltip = item.tooltip || (command && command.label);
|
||||
return (
|
||||
<div
|
||||
id={`${item.id}--container`}
|
||||
key={item.id}
|
||||
className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM}${
|
||||
command && this.commandIsEnabled(command.id) ? ' enabled' : ''
|
||||
}`}
|
||||
onMouseDown={this.onMouseDownEvent}
|
||||
onMouseUp={this.onMouseUpEvent}
|
||||
onMouseOut={this.onMouseUpEvent}
|
||||
>
|
||||
<div
|
||||
id={item.id}
|
||||
className={classNames.join(' ')}
|
||||
onClick={this.executeCommand}
|
||||
title={tooltip}
|
||||
>
|
||||
{innerText}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
26
arduino-ide-extension/src/browser/theia/core/theming.ts
Normal file
26
arduino-ide-extension/src/browser/theia/core/theming.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ThemeService as TheiaThemeService } from '@theia/core/lib/browser/theming';
|
||||
import type { Theme } from '@theia/core/lib/common/theme';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
|
||||
export namespace ArduinoThemes {
|
||||
export const Light: Theme = {
|
||||
id: 'arduino-theme',
|
||||
type: 'light',
|
||||
label: 'Light (Arduino)',
|
||||
editorTheme: 'arduino-theme',
|
||||
};
|
||||
export const Dark: Theme = {
|
||||
id: 'arduino-theme-dark',
|
||||
type: 'dark',
|
||||
label: 'Dark (Arduino)',
|
||||
editorTheme: 'arduino-theme-dark',
|
||||
};
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ThemeService extends TheiaThemeService {
|
||||
protected override init(): void {
|
||||
this.register(ArduinoThemes.Light, ArduinoThemes.Dark);
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { MaybePromise } from '@theia/core';
|
||||
import type { Widget } from '@theia/core/lib/browser';
|
||||
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
|
||||
import {
|
||||
@@ -8,7 +7,6 @@ import {
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
|
||||
import deepEqual = require('deep-equal');
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
@@ -72,44 +70,4 @@ export class WidgetManager extends TheiaWidgetManager {
|
||||
title.className += title.className + ` ${uncloseableClass}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customized to find any existing widget based on `options` deepEquals instead of string equals.
|
||||
* See https://github.com/eclipse-theia/theia/issues/11309.
|
||||
*/
|
||||
protected override doGetWidget<T extends Widget>(
|
||||
key: string
|
||||
): MaybePromise<T> | undefined {
|
||||
const pendingWidget = this.findExistingWidget<T>(key);
|
||||
if (pendingWidget) {
|
||||
return pendingWidget as MaybePromise<T>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private findExistingWidget<T extends Widget>(
|
||||
key: string
|
||||
): MaybePromise<T> | undefined {
|
||||
const parsed = this.parseJson(key);
|
||||
for (const [candidateKey, widget] of [
|
||||
...this.widgetPromises.entries(),
|
||||
...this.pendingWidgetPromises.entries(),
|
||||
]) {
|
||||
const candidate = this.parseJson(candidateKey);
|
||||
if (deepEqual(candidate, parsed)) {
|
||||
return widget as MaybePromise<T>;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private parseJson(json: string): any {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (err) {
|
||||
console.log(`Failed to parse JSON: <${json}>.`, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
|
||||
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
|
||||
@injectable()
|
||||
export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
@inject(ApplicationServer)
|
||||
private readonly applicationServer: ApplicationServer;
|
||||
@inject(ApplicationShell)
|
||||
private readonly applicationShell: ApplicationShell;
|
||||
@inject(WorkspaceService)
|
||||
private readonly workspaceService: WorkspaceService;
|
||||
|
||||
private _previousRepresentedFilename: string | undefined;
|
||||
|
||||
private readonly applicationName =
|
||||
FrontendApplicationConfigProvider.get().applicationName;
|
||||
private applicationVersion: string | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
setTimeout(
|
||||
() =>
|
||||
this.applicationServer.getApplicationInfo().then((info) => {
|
||||
this.applicationVersion = info?.version;
|
||||
if (this.applicationVersion) {
|
||||
this.handleWidgetChange(this.applicationShell.currentWidget);
|
||||
}
|
||||
}),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
protected override handleWidgetChange(widget?: Widget | undefined): void {
|
||||
if (isOSX) {
|
||||
this.maybeUpdateRepresentedFilename(widget);
|
||||
}
|
||||
// Unlike Theia, IDE2 does not want to show in the window title if the current widget is dirty or not.
|
||||
// Hence, IDE2 does not track widgets but updates the window title on current widget change.
|
||||
this.updateTitleWidget(widget);
|
||||
}
|
||||
|
||||
protected override updateTitleWidget(widget?: Widget | undefined): void {
|
||||
let activeEditorShort = '';
|
||||
const rootName = this.workspaceService.workspace?.name ?? '';
|
||||
let appName = `${this.applicationName}${
|
||||
this.applicationVersion ? ` ${this.applicationVersion}` : ''
|
||||
}`;
|
||||
if (rootName) {
|
||||
appName = ` | ${appName}`;
|
||||
}
|
||||
const uri = NavigatableWidget.getUri(widget);
|
||||
if (uri) {
|
||||
const base = uri.path.base;
|
||||
// Do not show the basename of the main sketch file. Only other sketch file names are visible in the title.
|
||||
if (`${rootName}.ino` !== base) {
|
||||
activeEditorShort = ` - ${base} `;
|
||||
}
|
||||
}
|
||||
this.windowTitleService.update({ rootName, appName, activeEditorShort });
|
||||
}
|
||||
|
||||
private maybeUpdateRepresentedFilename(widget?: Widget | undefined): void {
|
||||
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());
|
||||
this._previousRepresentedFilename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
import debounce = require('p-debounce');
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
@@ -126,7 +130,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
const uri = tempFolderUri.resolve('launch.json');
|
||||
const { value } = await this.fileService.read(uri);
|
||||
const configurations = DebugConfigurationModel.parse(JSON.parse(value));
|
||||
return { uri, configurations };
|
||||
return { uri, configurations, compounds: [] };
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof FileOperationError &&
|
||||
|
||||
@@ -29,6 +29,7 @@ export class DebugConfigurationModel extends TheiaDebugConfigurationModel {
|
||||
return {
|
||||
uri: this.configUri,
|
||||
configurations: this.config,
|
||||
compounds: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { DebugError } from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
override async start(options: DebugSessionOptions): Promise<DebugSession | undefined> {
|
||||
return this.progressService.withProgress(
|
||||
nls.localize('theia/debug/start', 'Start...'),
|
||||
'debug',
|
||||
async () => {
|
||||
try {
|
||||
// Only save when dirty. To avoid saving temporary sketches.
|
||||
// This is a quick fix for not saving the editor when there are no dirty editors.
|
||||
// // https://github.com/bcmi-labs/arduino-editor/pull/172#issuecomment-741831888
|
||||
if (this.shell.canSaveAll()) {
|
||||
await this.shell.saveAll();
|
||||
}
|
||||
await this.fireWillStartDebugSession();
|
||||
const resolved = await this.resolveConfiguration(options);
|
||||
|
||||
//#region "cherry-picked" from here: https://github.com/eclipse-theia/theia/commit/e6b57ba4edabf797f3b4e67bc2968cdb8cc25b1e#diff-08e04edb57cd2af199382337aaf1dbdb31171b37ae4ab38a38d36cd77bc656c7R196-R207
|
||||
if (!resolved) {
|
||||
// As per vscode API: https://code.visualstudio.com/api/references/vscode-api#DebugConfigurationProvider
|
||||
// "Returning the value 'undefined' prevents the debug session from starting.
|
||||
// Returning the value 'null' prevents the debug session from starting and opens the
|
||||
// underlying debug configuration instead."
|
||||
|
||||
if (resolved === null) {
|
||||
this.debugConfigurationManager.openConfiguration();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
//#endregion end of cherry-pick
|
||||
|
||||
// preLaunchTask isn't run in case of auto restart as well as postDebugTask
|
||||
if (!options.configuration.__restart) {
|
||||
const taskRun = await this.runTask(
|
||||
options.workspaceFolderUri,
|
||||
resolved.configuration.preLaunchTask,
|
||||
true
|
||||
);
|
||||
if (!taskRun) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const sessionId = await this.debug.createDebugSession(
|
||||
resolved.configuration
|
||||
);
|
||||
return this.doStart(sessionId, resolved);
|
||||
} catch (e) {
|
||||
if (DebugError.NotFound.is(e)) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'theia/debug/typeNotSupported',
|
||||
'The debug session type "{0}" is not supported.',
|
||||
e.data.type
|
||||
)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'theia/debug/startError',
|
||||
'There was an error starting the debug session, check the logs for more details.'
|
||||
)
|
||||
);
|
||||
console.error('Error starting the debug session', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
override async terminateSession(session?: DebugSession): Promise<void> {
|
||||
if (!session) {
|
||||
this.updateCurrentSession(this._currentSession);
|
||||
session = this._currentSession;
|
||||
}
|
||||
// The cortex-debug extension does not respond to close requests
|
||||
// So we simply terminate the debug session immediately
|
||||
// Alternatively the `super.terminateSession` call will terminate it after 5 seconds without a response
|
||||
await this.debug.terminateDebugSession(session!.id);
|
||||
await super.terminateSession(session);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
|
||||
import {
|
||||
AbstractDialog as TheiaAbstractDialog,
|
||||
codiconArray,
|
||||
DialogProps,
|
||||
} from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
|
||||
constructor(@inject(DialogProps) protected override readonly props: DialogProps) {
|
||||
super(props);
|
||||
|
||||
this.closeCrossNode.classList.remove(...codiconArray('close'));
|
||||
this.closeCrossNode.classList.add('fa', 'fa-close');
|
||||
}
|
||||
}
|
||||
63
arduino-ide-extension/src/browser/theia/dialogs/dialogs.tsx
Normal file
63
arduino-ide-extension/src/browser/theia/dialogs/dialogs.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
AbstractDialog as TheiaAbstractDialog,
|
||||
DialogProps,
|
||||
} from '@theia/core/lib/browser/dialogs';
|
||||
import { ReactDialog as TheiaReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
|
||||
import { codiconArray, Message } from '@theia/core/lib/browser/widgets/widget';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { createRoot } from '@theia/core/shared/react-dom/client';
|
||||
|
||||
@injectable()
|
||||
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
|
||||
constructor(
|
||||
@inject(DialogProps) protected override readonly props: DialogProps
|
||||
) {
|
||||
super(props);
|
||||
|
||||
this.closeCrossNode.classList.remove(...codiconArray('close'));
|
||||
this.closeCrossNode.classList.add('fa', 'fa-close');
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export abstract class ReactDialog<T> extends TheiaReactDialog<T> {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
// This is tricky to bypass the default Theia code.
|
||||
// Otherwise, there is a warning when opening the dialog for the second time.
|
||||
// You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
|
||||
const disposables = new DisposableCollection();
|
||||
if (!this.isMounted) {
|
||||
// toggle the `isMounted` logic for the time being of the super call so that the `createRoot` does not run
|
||||
this.isMounted = true;
|
||||
disposables.push(Disposable.create(() => (this.isMounted = false)));
|
||||
}
|
||||
|
||||
// Always unset the `contentNodeRoot` so there is no double update when calling super.
|
||||
const restoreContentNodeRoot = this.contentNodeRoot;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.contentNodeRoot as any) = undefined;
|
||||
disposables.push(
|
||||
Disposable.create(() => (this.contentNodeRoot = restoreContentNodeRoot))
|
||||
);
|
||||
|
||||
try {
|
||||
super.onUpdateRequest(msg);
|
||||
} finally {
|
||||
disposables.dispose();
|
||||
}
|
||||
|
||||
// Use the patched rendering.
|
||||
if (!this.isMounted) {
|
||||
this.contentNodeRoot = createRoot(this.contentNode);
|
||||
// Resetting the prop is missing from the Theia code.
|
||||
// https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/browser/dialogs/react-dialog.tsx#L41-L47
|
||||
this.isMounted = true;
|
||||
}
|
||||
this.contentNodeRoot?.render(<>{this.render()}</>);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export class EditorWidgetFactory extends TheiaEditorWidgetFactory {
|
||||
|
||||
protected override async createEditor(
|
||||
uri: URI,
|
||||
options: NavigatableWidgetOptions
|
||||
options?: NavigatableWidgetOptions
|
||||
): Promise<EditorWidget> {
|
||||
const widget = await super.createEditor(uri, options);
|
||||
return this.maybeUpdateCaption(widget);
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Diagnostic } from 'vscode-languageserver-types';
|
||||
import { Diagnostic } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
@@ -25,15 +24,14 @@ export class NotificationsRenderer extends TheiaNotificationsRenderer {
|
||||
}
|
||||
|
||||
protected override render(): void {
|
||||
ReactDOM.render(
|
||||
this.containerRoot.render(
|
||||
<div>
|
||||
<NotificationToastsComponent
|
||||
manager={this.manager}
|
||||
corePreferences={this.corePreferences}
|
||||
/>
|
||||
<NotificationCenterComponent manager={this.manager} />
|
||||
</div>,
|
||||
this.container
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
import { ArduinoThemes } from '../core/theming';
|
||||
|
||||
@injectable()
|
||||
export class MonacoThemingService extends TheiaMonacoThemingService {
|
||||
override initialize(): void {
|
||||
super.initialize();
|
||||
const { Light, Dark } = ArduinoThemes;
|
||||
this.registerParsedTheme({
|
||||
id: Light.id,
|
||||
label: Light.label,
|
||||
uiTheme: 'vs',
|
||||
json: require('../../../../src/browser/data/default.color-theme.json'),
|
||||
});
|
||||
this.registerParsedTheme({
|
||||
id: Dark.id,
|
||||
label: Dark.label,
|
||||
uiTheme: 'vs-dark',
|
||||
json: require('../../../../src/browser/data/dark.color-theme.json'),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
TypeHierarchyCommands,
|
||||
TypeHierarchyContribution as TheiaTypeHierarchyContribution,
|
||||
} from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
||||
|
||||
@injectable()
|
||||
export class TypeHierarchyContribution extends TheiaTypeHierarchyContribution {
|
||||
protected override init(): void {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
super.registerCommands(registry);
|
||||
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
super.registerMenus(registry);
|
||||
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
|
||||
}
|
||||
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
super.registerKeybindings(registry);
|
||||
registry.unregisterKeybinding(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
|
||||
|
||||
@injectable()
|
||||
export class TypeHierarchyServiceProvider extends TheiaTypeHierarchyServiceProvider {
|
||||
override init(): void {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,37 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FocusTracker, Widget } from '@theia/core/lib/browser';
|
||||
import {
|
||||
DEFAULT_WINDOW_HASH,
|
||||
NewWindowOptions,
|
||||
} from '@theia/core/lib/common/window';
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import {
|
||||
WorkspaceInput,
|
||||
WorkspaceService as TheiaWorkspaceService,
|
||||
} from '@theia/workspace/lib/browser/workspace-service';
|
||||
import {
|
||||
SketchesService,
|
||||
Sketch,
|
||||
SketchesError,
|
||||
SketchesService,
|
||||
} from '../../../common/protocol/sketches-service';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import {
|
||||
StartupTask,
|
||||
StartupTaskProvider,
|
||||
} from '../../../electron-common/startup-task';
|
||||
import { WindowServiceExt } from '../core/window-service-ext';
|
||||
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceService extends TheiaWorkspaceService {
|
||||
@inject(SketchesService)
|
||||
private readonly sketchService: SketchesService;
|
||||
@inject(ApplicationServer)
|
||||
private readonly applicationServer: ApplicationServer;
|
||||
@inject(WindowServiceExt)
|
||||
private readonly windowServiceExt: WindowServiceExt;
|
||||
@inject(ContributionProvider)
|
||||
@named(StartupTaskProvider)
|
||||
private readonly providers: ContributionProvider<StartupTaskProvider>;
|
||||
|
||||
private version?: string;
|
||||
private _workspaceError: Error | undefined;
|
||||
|
||||
async onStart(application: FrontendApplication): Promise<void> {
|
||||
const info = await this.applicationServer.getApplicationInfo();
|
||||
this.version = info?.version;
|
||||
application.shell.onDidChangeCurrentWidget(
|
||||
this.onCurrentWidgetChange.bind(this)
|
||||
);
|
||||
const newValue = application.shell.currentWidget
|
||||
? application.shell.currentWidget
|
||||
: null;
|
||||
this.onCurrentWidgetChange({ newValue, oldValue: null });
|
||||
}
|
||||
|
||||
get workspaceError(): Error | undefined {
|
||||
return this._workspaceError;
|
||||
}
|
||||
@@ -121,58 +100,6 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from Theia as-is to be able to pass the original `options` down.
|
||||
*/
|
||||
protected override async doOpen(
|
||||
uri: URI,
|
||||
options?: WorkspaceInput
|
||||
): Promise<URI | undefined> {
|
||||
const stat = await this.toFileStat(uri);
|
||||
if (stat) {
|
||||
if (!stat.isDirectory && !this.isWorkspaceFile(stat)) {
|
||||
const message = `Not a valid workspace: ${uri.path.toString()}`;
|
||||
this.messageService.error(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
// The same window has to be preserved too (instead of opening a new one), if the workspace root is not yet available and we are setting it for the first time.
|
||||
// Option passed as parameter has the highest priority (for api developers), then the preference, then the default.
|
||||
await this.roots;
|
||||
const { preserveWindow } = {
|
||||
preserveWindow:
|
||||
this.preferences['workspace.preserveWindow'] || !this.opened,
|
||||
...options,
|
||||
};
|
||||
await this.server.setMostRecentlyUsedWorkspace(uri.toString());
|
||||
if (preserveWindow) {
|
||||
this._workspace = stat;
|
||||
}
|
||||
this.openWindow(stat, Object.assign(options ?? {}, { preserveWindow })); // Unlike Theia, IDE2 passes the whole `input` downstream and not only { preserveWindow }
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
'Invalid workspace root URI. Expected an existing directory or workspace file.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from Theia. Can pass the `options` further down the chain.
|
||||
*/
|
||||
protected override openWindow(uri: FileStat, options?: WorkspaceInput): void {
|
||||
const workspacePath = uri.resource.path.toString();
|
||||
if (this.shouldPreserveWindow(options)) {
|
||||
this.reloadWindow(options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
} else {
|
||||
try {
|
||||
this.openNewWindow(workspacePath, options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
} catch (error) {
|
||||
// Fall back to reloading the current window in case the browser has blocked the new window
|
||||
this._workspace = uri;
|
||||
this.logger.error(error.toString()).then(() => this.reloadWindow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override reloadWindow(options?: WorkspaceInput): void {
|
||||
const tasks = this.tasks(options);
|
||||
this.setURLFragment(this._workspace?.resource.path.toString() || '');
|
||||
@@ -192,6 +119,10 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
protected override updateTitle(): void {
|
||||
// NOOP. IDE2 handles the `window.title` updates solely via the customized `WindowTitleUpdater`.
|
||||
}
|
||||
|
||||
private tasks(options?: WorkspaceInput): StartupTask[] {
|
||||
const tasks = this.providers
|
||||
.getContributions()
|
||||
@@ -202,37 +133,4 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
protected onCurrentWidgetChange({
|
||||
newValue,
|
||||
}: FocusTracker.IChangedArgs<Widget>): void {
|
||||
if (newValue instanceof EditorWidget) {
|
||||
const { uri } = newValue.editor;
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
if (Sketch.isSketchFile(uri.toString())) {
|
||||
this.updateTitle();
|
||||
} else {
|
||||
const title = this.workspaceTitle;
|
||||
const fileName = this.labelProvider.getName(uri);
|
||||
document.title = this.formatTitle(
|
||||
title ? `${title} - ${fileName}` : fileName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.updateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
protected override formatTitle(title?: string): string {
|
||||
const version = this.version ? ` ${this.version}` : '';
|
||||
const name = `${this.applicationName} ${version}`;
|
||||
return title ? `${title} | ${name}` : name;
|
||||
}
|
||||
|
||||
protected get workspaceTitle(): string | undefined {
|
||||
if (this.workspace) {
|
||||
return this.labelProvider.getName(this.workspace.resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user