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:
Akos Kitta
2022-11-29 09:39:54 +01:00
committed by GitHub
parent 3ad660927f
commit ac9cce16f7
66 changed files with 3278 additions and 4168 deletions

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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>
);
}
}

View 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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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 &&

View File

@@ -29,6 +29,7 @@ export class DebugConfigurationModel extends TheiaDebugConfigurationModel {
return {
uri: this.configUri,
configurations: this.config,
compounds: [],
};
}
}

View File

@@ -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);
}
}

View File

@@ -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');
}
}

View 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()}</>);
}
}

View File

@@ -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);

View File

@@ -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';

View File

@@ -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>
);
}
}

View File

@@ -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'),
});
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}
}