Skip to content

Commit 1c75d78

Browse files
committed
fix: restore toolbar default items
- properly restore the toolbar items in the storage provider from the default factory and ensure to write to the config file - ensure the defaults are restored on startup in case there is no config file yet - in case the config file was corrupted, allow the user to restore the default toolbar items via an action in the error message popup - chore: remove unused injections in ToolbarCommandContribution Fixes #15231
1 parent 05dbec2 commit 1c75d78

File tree

3 files changed

+56
-60
lines changed

3 files changed

+56
-60
lines changed

packages/toolbar/src/browser/toolbar-command-contribution.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
bindContributionProvider,
1919
CommandContribution,
2020
CommandRegistry,
21-
CommandService,
2221
MenuContribution,
2322
MenuModelRegistry,
2423
} from '@theia/core';
@@ -30,11 +29,9 @@ import {
3029
PreferenceContribution,
3130
PreferenceScope,
3231
PreferenceService,
33-
QuickInputService,
3432
Widget,
3533
} from '@theia/core/lib/browser';
3634
import { injectable, inject, interfaces, Container } from '@theia/core/shared/inversify';
37-
import { EditorManager } from '@theia/editor/lib/browser';
3835
import { ToolbarImpl } from './toolbar';
3936
import { bindToolbarIconDialog } from './toolbar-icon-selector-dialog';
4037
import {
@@ -57,14 +54,11 @@ import URI from '@theia/core/lib/common/uri';
5754

5855
@injectable()
5956
export class ToolbarCommandContribution implements CommandContribution, KeybindingContribution, MenuContribution, JsonSchemaContribution {
60-
@inject(ToolbarController) protected readonly model: ToolbarController;
61-
@inject(QuickInputService) protected readonly quickInputService: QuickInputService;
57+
@inject(ToolbarController) protected readonly controller: ToolbarController;
6258
@inject(ToolbarCommandQuickInputService) protected toolbarCommandPickService: ToolbarCommandQuickInputService;
63-
@inject(CommandService) protected readonly commandService: CommandService;
64-
@inject(EditorManager) protected readonly editorManager: EditorManager;
6559
@inject(PreferenceService) protected readonly preferenceService: PreferenceService;
66-
@inject(ToolbarController) protected readonly toolbarModel: ToolbarController;
6760
@inject(JsonSchemaDataStore) protected readonly schemaStore: JsonSchemaDataStore;
61+
6862
protected readonly schemaURI = new URI(toolbarSchemaId);
6963

7064
registerSchemas(context: JsonSchemaRegisterContext): void {
@@ -77,10 +71,10 @@ export class ToolbarCommandContribution implements CommandContribution, Keybindi
7771

7872
registerCommands(registry: CommandRegistry): void {
7973
registry.registerCommand(ToolbarCommands.CUSTOMIZE_TOOLBAR, {
80-
execute: () => this.model.openOrCreateJSONFile(true),
74+
execute: () => this.controller.openOrCreateJSONFile(true),
8175
});
8276
registry.registerCommand(ToolbarCommands.RESET_TOOLBAR, {
83-
execute: () => this.model.clearAll(),
77+
execute: () => this.controller.restoreToolbarDefaults(),
8478
});
8579
registry.registerCommand(ToolbarCommands.TOGGLE_TOOLBAR, {
8680
execute: () => {
@@ -90,26 +84,26 @@ export class ToolbarCommandContribution implements CommandContribution, Keybindi
9084
});
9185

9286
registry.registerCommand(ToolbarCommands.REMOVE_COMMAND_FROM_TOOLBAR, {
93-
execute: async (_widget, position: ToolbarItemPosition | undefined, id?: string) => position && this.model.removeItem(position, id),
87+
execute: async (_widget, position: ToolbarItemPosition | undefined, id?: string) => position && this.controller.removeItem(position, id),
9488
isVisible: (...args) => this.isToolbarWidget(args[0]),
9589
});
9690
registry.registerCommand(ToolbarCommands.INSERT_GROUP_LEFT, {
97-
execute: async (_widget: Widget, position: ToolbarItemPosition | undefined) => position && this.model.insertGroup(position, 'left'),
91+
execute: async (_widget: Widget, position: ToolbarItemPosition | undefined) => position && this.controller.insertGroup(position, 'left'),
9892
isVisible: (widget: Widget, position: ToolbarItemPosition | undefined) => {
9993
if (position) {
10094
const { alignment, groupIndex, itemIndex } = position;
101-
const owningGroupLength = this.toolbarModel.toolbarItems.items[alignment][groupIndex].length;
95+
const owningGroupLength = this.controller.toolbarItems.items[alignment][groupIndex].length;
10296
return this.isToolbarWidget(widget) && (owningGroupLength > 1) && (itemIndex > 0);
10397
}
10498
return false;
10599
},
106100
});
107101
registry.registerCommand(ToolbarCommands.INSERT_GROUP_RIGHT, {
108-
execute: async (_widget: Widget, position: ToolbarItemPosition | undefined) => position && this.model.insertGroup(position, 'right'),
102+
execute: async (_widget: Widget, position: ToolbarItemPosition | undefined) => position && this.controller.insertGroup(position, 'right'),
109103
isVisible: (widget: Widget, position: ToolbarItemPosition | undefined) => {
110104
if (position) {
111105
const { alignment, groupIndex, itemIndex } = position;
112-
const owningGroupLength = this.toolbarModel.toolbarItems.items[alignment][groupIndex].length;
106+
const owningGroupLength = this.controller.toolbarItems.items[alignment][groupIndex].length;
113107
const isNotLastItem = itemIndex < (owningGroupLength - 1);
114108
return this.isToolbarWidget(widget) && owningGroupLength > 1 && isNotLastItem;
115109
}

packages/toolbar/src/browser/toolbar-controller.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
1515
// *****************************************************************************
1616

17-
import { Command, CommandRegistry, ContributionProvider, Emitter, MaybePromise, MessageService } from '@theia/core';
17+
import { Command, CommandRegistry, ContributionProvider, Emitter, MaybePromise, MessageService, nls } from '@theia/core';
1818
import { KeybindingRegistry, Widget } from '@theia/core/lib/browser';
1919
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
2020
import { Deferred } from '@theia/core/lib/common/promise-util';
2121
import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify';
22-
import { ToolbarDefaultsFactory } from './toolbar-defaults';
2322
import {
2423
DeflatedToolbarTree,
2524
ToolbarContribution,
@@ -31,13 +30,13 @@ import { ToolbarStorageProvider, TOOLBAR_BAD_JSON_ERROR_MESSAGE } from './toolba
3130
import { ReactToolbarItemImpl, RenderedToolbarItemImpl, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item';
3231
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
3332
import { LabelParser } from '@theia/core/lib/browser/label-parser';
33+
import { ToolbarCommands } from './toolbar-constants';
3434

3535
@injectable()
3636
export class ToolbarController {
3737
@inject(ToolbarStorageProvider) protected readonly storageProvider: ToolbarStorageProvider;
3838
@inject(FrontendApplicationStateService) protected readonly appState: FrontendApplicationStateService;
3939
@inject(MessageService) protected readonly messageService: MessageService;
40-
@inject(ToolbarDefaultsFactory) protected readonly defaultsFactory: () => DeflatedToolbarTree;
4140
@inject(CommandRegistry) commandRegistry: CommandRegistry;
4241
@inject(ContextKeyService) contextKeyService: ContextKeyService;
4342
@inject(KeybindingRegistry) keybindingRegistry: KeybindingRegistry;
@@ -64,30 +63,32 @@ export class ToolbarController {
6463
this.toolbarModelDidUpdateEmitter.fire();
6564
}
6665

67-
protected inflateItems(schema: DeflatedToolbarTree): ToolbarTreeSchema {
66+
protected inflateItems(schema?: DeflatedToolbarTree): ToolbarTreeSchema {
6867
const newTree: ToolbarTreeSchema = {
6968
items: {
7069
[ToolbarAlignment.LEFT]: [],
7170
[ToolbarAlignment.CENTER]: [],
7271
[ToolbarAlignment.RIGHT]: [],
7372
},
7473
};
75-
for (const column of Object.keys(schema.items)) {
76-
const currentColumn = schema.items[column as ToolbarAlignment];
77-
for (const group of currentColumn) {
78-
const newGroup: TabBarToolbarItem[] = [];
79-
for (const item of group) {
80-
if (item.group === 'contributed') {
81-
const contribution = this.getContributionByID(item.id);
82-
if (contribution) {
83-
newGroup.push(new ReactToolbarItemImpl(this.commandRegistry, this.contextKeyService, contribution));
74+
if (schema) {
75+
for (const column of Object.keys(schema.items)) {
76+
const currentColumn = schema.items[column as ToolbarAlignment];
77+
for (const group of currentColumn) {
78+
const newGroup: TabBarToolbarItem[] = [];
79+
for (const item of group) {
80+
if (item.group === 'contributed') {
81+
const contribution = this.getContributionByID(item.id);
82+
if (contribution) {
83+
newGroup.push(new ReactToolbarItemImpl(this.commandRegistry, this.contextKeyService, contribution));
84+
}
85+
} else {
86+
newGroup.push(new RenderedToolbarItemImpl(this.commandRegistry, this.contextKeyService, this.keybindingRegistry, this.labelParser, item));
8487
}
85-
} else {
86-
newGroup.push(new RenderedToolbarItemImpl(this.commandRegistry, this.contextKeyService, this.keybindingRegistry, this.labelParser, item));
8788
}
88-
}
89-
if (newGroup.length) {
90-
newTree.items[column as ToolbarAlignment].push(newGroup);
89+
if (newGroup.length) {
90+
newTree.items[column as ToolbarAlignment].push(newGroup);
91+
}
9192
}
9293
}
9394
}
@@ -108,7 +109,7 @@ export class ToolbarController {
108109
await this.storageProvider.ready;
109110
this.toolbarItems = await this.resolveToolbarItems();
110111
this.storageProvider.onToolbarItemsChanged(async () => {
111-
this.toolbarItems = await this.resolveToolbarItems();
112+
this.toolbarItems = await this.resolveToolbarItems(true);
112113
});
113114
this.ready.resolve();
114115
this.widgetContributions.getContributions().forEach(contribution => {
@@ -118,17 +119,21 @@ export class ToolbarController {
118119
});
119120
}
120121

121-
protected async resolveToolbarItems(): Promise<ToolbarTreeSchema> {
122+
protected async resolveToolbarItems(promptUserOnInvalidConfig = false): Promise<ToolbarTreeSchema> {
122123
await this.storageProvider.ready;
123124

124-
if (this.storageProvider.toolbarItems) {
125-
try {
126-
return this.inflateItems(this.storageProvider.toolbarItems);
127-
} catch (e) {
128-
this.messageService.error(TOOLBAR_BAD_JSON_ERROR_MESSAGE);
125+
if (!this.storageProvider.toolbarItems) {
126+
let restoreDefaults = true;
127+
if (promptUserOnInvalidConfig) {
128+
const resetLabel = ToolbarCommands.RESET_TOOLBAR.label!;
129+
const answer = await this.messageService.error(nls.localize('theia/toolbar/jsonError', TOOLBAR_BAD_JSON_ERROR_MESSAGE), resetLabel);
130+
restoreDefaults = answer === resetLabel;
131+
}
132+
if (restoreDefaults) {
133+
await this.restoreToolbarDefaults();
129134
}
130135
}
131-
return this.inflateItems(this.defaultsFactory());
136+
return this.inflateItems(this.storageProvider.toolbarItems);
132137
}
133138

134139
async swapValues(
@@ -142,8 +147,8 @@ export class ToolbarController {
142147
});
143148
}
144149

145-
async clearAll(): Promise<boolean> {
146-
return this.withBusy<boolean>(() => this.storageProvider.clearAll());
150+
async restoreToolbarDefaults(): Promise<boolean> {
151+
return this.withBusy<boolean>(() => this.storageProvider.restoreToolbarDefaults());
147152
}
148153

149154
async openOrCreateJSONFile(doOpen = false): Promise<Widget | undefined> {

packages/toolbar/src/browser/toolbar-storage-provider.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ import {
3838
} from './toolbar-interfaces';
3939
import { UserToolbarURI } from './toolbar-constants';
4040
import { isToolbarPreferences } from './toolbar-preference-schema';
41+
import { ToolbarDefaultsFactory } from './toolbar-defaults';
4142

4243
export const TOOLBAR_BAD_JSON_ERROR_MESSAGE = 'There was an error reading your toolbar.json file. Please check if it is corrupt'
4344
+ ' by right-clicking the toolbar and selecting "Customize Toolbar". You can also reset it to its defaults by selecting'
4445
+ ' "Restore Toolbar Defaults"';
46+
4547
@injectable()
4648
export class ToolbarStorageProvider implements Disposable {
4749
@inject(FrontendApplicationStateService) protected readonly appState: FrontendApplicationStateService;
@@ -50,6 +52,7 @@ export class ToolbarStorageProvider implements Disposable {
5052
@inject(MessageService) protected readonly messageService: MessageService;
5153
@inject(LateInjector) protected lateInjector: <T>(id: interfaces.ServiceIdentifier<T>) => T;
5254
@inject(UserToolbarURI) protected readonly USER_TOOLBAR_URI: URI;
55+
@inject(ToolbarDefaultsFactory) protected readonly defaultsFactory: () => DeflatedToolbarTree;
5356

5457
get ready(): Promise<void> {
5558
return this._ready.promise;
@@ -65,7 +68,12 @@ export class ToolbarStorageProvider implements Disposable {
6568
protected toDispose = new DisposableCollection();
6669
protected toolbarItemsUpdatedEmitter = new Emitter<void>();
6770
readonly onToolbarItemsChanged = this.toolbarItemsUpdatedEmitter.event;
68-
toolbarItems: DeflatedToolbarTree | undefined;
71+
72+
protected _toolbarItems: DeflatedToolbarTree | undefined;
73+
74+
get toolbarItems(): DeflatedToolbarTree | undefined {
75+
return this._toolbarItems;
76+
}
6977

7078
@postConstruct()
7179
protected init(): void {
@@ -97,9 +105,9 @@ export class ToolbarStorageProvider implements Disposable {
97105
try {
98106
if (this.model.valid) {
99107
const content = this.model.getText();
100-
this.toolbarItems = this.parseContent(content);
108+
this._toolbarItems = this.parseContent(content);
101109
} else {
102-
this.toolbarItems = undefined;
110+
this._toolbarItems = undefined;
103111
}
104112
this.toolbarItemsUpdatedEmitter.fire();
105113
} catch (e) {
@@ -253,20 +261,9 @@ export class ToolbarStorageProvider implements Disposable {
253261
return undefined;
254262
}
255263

256-
async clearAll(): Promise<boolean> {
257-
if (this.model) {
258-
const textModel = this.model.textEditorModel;
259-
await this.monacoWorkspace.applyBackgroundEdit(this.model, [
260-
{
261-
range: textModel.getFullModelRange(),
262-
// eslint-disable-next-line no-null/no-null
263-
text: null,
264-
forceMoveMarkers: false,
265-
},
266-
]);
267-
}
268-
this.toolbarItemsUpdatedEmitter.fire();
269-
return true;
264+
async restoreToolbarDefaults(): Promise<boolean> {
265+
this._toolbarItems = this.defaultsFactory();
266+
return this.writeToFile([], this._toolbarItems);
270267
}
271268

272269
protected async writeToFile(path: jsoncParser.JSONPath, value: unknown, insertion = false): Promise<boolean> {

0 commit comments

Comments
 (0)