From 59b36ffff9b768e4dfd0c85bd639e923fd496097 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 9 Aug 2023 17:15:49 +0200 Subject: [PATCH 1/6] Added the file/newFile menu including menu entries Signed-off-by: Jonah Iden --- .../browser/common-frontend-contribution.ts | 67 +++++++++++++++++-- .../core/src/browser/window-contribution.ts | 2 +- .../src/browser/workspace-commands.ts | 2 +- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index c9c7cf350db39..f2064f644fb95 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -18,7 +18,7 @@ import debounce = require('lodash.debounce'); import { injectable, inject, optional } from 'inversify'; -import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU } from '../common/menu'; +import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU, CompoundMenuNodeRole } from '../common/menu'; import { KeybindingContribution, KeybindingRegistry } from './keybinding'; import { FrontendApplication, FrontendApplicationContribution, OnWillStopAction } from './frontend-application'; import { CommandContribution, CommandRegistry, Command } from '../common/command'; @@ -52,7 +52,7 @@ import { UTF8 } from '../common/encodings'; import { EnvVariablesServer } from '../common/env-variables'; import { AuthenticationService } from './authentication-service'; import { FormatType, Saveable, SaveOptions } from './saveable'; -import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator } from './quick-input'; +import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSeparator } from './quick-input'; import { AsyncLocalizationProvider } from '../common/i18n/localization'; import { nls } from '../common/nls'; import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter'; @@ -69,6 +69,7 @@ import { LanguageQuickPickService } from './i18n/language-quick-pick-service'; export namespace CommonMenus { export const FILE = [...MAIN_MENU_BAR, '1_file']; + export const FILE_NEW_TEXT = [...FILE, '1_new_text']; export const FILE_NEW = [...FILE, '1_new']; export const FILE_OPEN = [...FILE, '2_open']; export const FILE_SAVE = [...FILE, '3_save']; @@ -79,6 +80,8 @@ export namespace CommonMenus { export const FILE_SETTINGS_SUBMENU_THEME = [...FILE_SETTINGS_SUBMENU, '2_settings_submenu_theme']; export const FILE_CLOSE = [...FILE, '6_close']; + export const FILE_NEW_CONTRIBUTIONS = 'file/newFile'; + export const EDIT = [...MAIN_MENU_BAR, '2_edit']; export const EDIT_UNDO = [...EDIT, '1_undo']; export const EDIT_CLIPBOARD = [...EDIT, '2_clipboard']; @@ -272,10 +275,15 @@ export namespace CommonCommands { category: VIEW_CATEGORY, label: 'Toggle Menu Bar' }); + export const NEW_UNTITLED_TEXT_FILE = Command.toDefaultLocalizedCommand({ + id: 'workbench.action.files.newUntitledTextFile', + category: FILE_CATEGORY, + label: 'New Untitled Text File' + }); export const NEW_UNTITLED_FILE = Command.toDefaultLocalizedCommand({ id: 'workbench.action.files.newUntitledFile', category: FILE_CATEGORY, - label: 'New Untitled Text File' + label: 'New Untitled File' }); export const SAVE = Command.toDefaultLocalizedCommand({ id: 'core.save', @@ -371,6 +379,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; + @inject(MenuModelRegistry) + protected readonly menuRegistry: MenuModelRegistry; + @inject(StorageService) protected readonly storageService: StorageService; @@ -545,6 +556,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi registry.registerSubmenu(CommonMenus.VIEW, nls.localizeByDefault('View')); registry.registerSubmenu(CommonMenus.HELP, nls.localizeByDefault('Help')); + // For plugins contributing create new file commands/menu-actions + registry.registerIndependentSubmenu(CommonMenus.FILE_NEW_CONTRIBUTIONS, nls.localizeByDefault('New File...')); + registry.registerMenuAction(CommonMenus.FILE_SAVE, { commandId: CommonCommands.SAVE.id }); @@ -693,10 +707,16 @@ export class CommonFrontendContribution implements FrontendApplicationContributi registry.registerSubmenu(CommonMenus.VIEW_APPEARANCE_SUBMENU, nls.localizeByDefault('Appearance')); - registry.registerMenuAction(CommonMenus.FILE_NEW, { + registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, { + commandId: CommonCommands.NEW_UNTITLED_TEXT_FILE.id, + label: nls.localizeByDefault('New Text File'), + order: 'a' + }); + + registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, { commandId: CommonCommands.NEW_UNTITLED_FILE.id, label: nls.localizeByDefault('New File...'), - order: 'a' + order: 'a1' }); } @@ -941,13 +961,16 @@ export class CommonFrontendContribution implements FrontendApplicationContributi execute: () => this.toggleBreadcrumbs(), isToggled: () => this.isBreadcrumbsEnabled(), }); - commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, { + commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE, { execute: async () => { const untitledUri = this.untitledResourceResolver.createUntitledURI('', await this.workingDirProvider.getUserWorkingDir()); this.untitledResourceResolver.resolve(untitledUri); return open(this.openerService, untitledUri); } }); + commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, { + execute: async () => this.showNewFilePicker() + }); for (const [index, ordinal] of this.getOrdinalNumbers().entries()) { commandRegistry.registerCommand({ id: `workbench.action.focus${ordinal}EditorGroup`, label: index === 0 ? nls.localizeByDefault('Focus First Editor Group') : '', category: nls.localize(CommonCommands.VIEW_CATEGORY_KEY, CommonCommands.VIEW_CATEGORY) }, { isEnabled: () => this.shell.mainAreaTabBars.length > index, @@ -1097,7 +1120,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi when: 'activeEditorIsPinned' }, { - command: CommonCommands.NEW_UNTITLED_FILE.id, + command: CommonCommands.NEW_UNTITLED_TEXT_FILE.id, keybinding: this.isElectron() ? 'ctrlcmd+n' : 'alt+n', } ); @@ -1294,6 +1317,36 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }); } + protected async showNewFilePicker(): Promise { + const newFileContributions = this.menuRegistry.getMenuNode(CommonMenus.FILE_NEW_CONTRIBUTIONS); // Add menus + const items: QuickPickItemOrSeparator[] = [ + { + label: nls.localizeByDefault('New Text File'), + execute: async () => this.commandRegistry.executeCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE.id) + }, + ...newFileContributions.children + .flatMap(node => { + if (node.children && node.children.length > 0) { + return node.role === CompoundMenuNodeRole.Group || node.role === CompoundMenuNodeRole.Submenu ? [node, ...node.children] : node.children; + } + return node; + }) + .filter(node => node.role || node.command) + .map(node => { + if (node.role) { + return { type: 'separator' } as QuickPickSeparator; + } + const command = this.commandRegistry.getCommand(node.command!); + return { + label: command!.label!, + execute: async () => this.commandRegistry.executeCommand(command!.id!) + }; + + }) + ]; + this.quickInputService.showQuickPick(items, { canSelectMany: false }); + } + registerColors(colors: ColorRegistry): void { colors.register( // Base Colors should be aligned with https://code.visualstudio.com/api/references/theme-color#base-colors diff --git a/packages/core/src/browser/window-contribution.ts b/packages/core/src/browser/window-contribution.ts index 9dcfd1be0d091..14fd338fe8ab4 100644 --- a/packages/core/src/browser/window-contribution.ts +++ b/packages/core/src/browser/window-contribution.ts @@ -51,7 +51,7 @@ export class WindowContribution implements CommandContribution, KeybindingContri } registerMenus(registry: MenuModelRegistry): void { - registry.registerMenuAction(CommonMenus.FILE_NEW, { + registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, { commandId: WindowCommands.NEW_WINDOW.id, order: 'c' }); diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index 6f7264b584be8..2e7625f434485 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -153,7 +153,7 @@ export namespace WorkspaceCommands { export class FileMenuContribution implements MenuContribution { registerMenus(registry: MenuModelRegistry): void { - registry.registerMenuAction(CommonMenus.FILE_NEW, { + registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, { commandId: WorkspaceCommands.NEW_FOLDER.id, order: 'b' }); From 2f4e8e34f759cbdb015d7b9fd2fabb2c16555960 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 10 Aug 2023 08:46:29 +0200 Subject: [PATCH 2/6] added default keybinding for New File... Signed-off-by: Jonah Iden --- packages/core/src/browser/common-frontend-contribution.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index f2064f644fb95..2fffb7a57f829 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -1122,6 +1122,10 @@ export class CommonFrontendContribution implements FrontendApplicationContributi { command: CommonCommands.NEW_UNTITLED_TEXT_FILE.id, keybinding: this.isElectron() ? 'ctrlcmd+n' : 'alt+n', + }, + { + command: CommonCommands.NEW_UNTITLED_FILE.id, + keybinding: 'ctrlcmd+alt+n' } ); for (const [index, ordinal] of this.getOrdinalNumbers().entries()) { From 8beeb18ea88ae44475b8fa36037be958c4eb10a9 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 11 Aug 2023 08:46:51 +0200 Subject: [PATCH 3/6] fixed playwright tests Signed-off-by: Jonah Iden --- examples/playwright/src/tests/theia-main-menu.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/playwright/src/tests/theia-main-menu.test.ts b/examples/playwright/src/tests/theia-main-menu.test.ts index aad8595efcd83..3ecdff8000105 100644 --- a/examples/playwright/src/tests/theia-main-menu.test.ts +++ b/examples/playwright/src/tests/theia-main-menu.test.ts @@ -44,17 +44,17 @@ test.describe('Theia Main Menu', () => { test("should show the menu items 'New File' and 'New Folder'", async () => { const mainMenu = await menuBar.openMenu('File'); const menuItems = await mainMenu.visibleMenuItems(); - expect(menuItems).toContain('New File...'); + expect(menuItems).toContain('New Text File'); expect(menuItems).toContain('New Folder...'); }); test("should return menu item by name 'New File'", async () => { const mainMenu = await menuBar.openMenu('File'); - const menuItem = await mainMenu.menuItemByName('New File...'); + const menuItem = await mainMenu.menuItemByName('New Text File'); expect(menuItem).toBeDefined(); const label = await menuItem?.label(); - expect(label).toBe('New File...'); + expect(label).toBe('New Text File'); const shortCut = await menuItem?.shortCut(); expect(shortCut).toBe(OSUtil.isMacOS ? '⌥ N' : 'Alt+N'); @@ -65,7 +65,7 @@ test.describe('Theia Main Menu', () => { test('should detect whether menu item has submenu', async () => { const mainMenu = await menuBar.openMenu('File'); - const newFileItem = await mainMenu.menuItemByName('New File...'); + const newFileItem = await mainMenu.menuItemByName('New Text File'); const settingsItem = await mainMenu.menuItemByName('Preferences'); expect(await newFileItem?.hasSubmenu()).toBe(false); From f8e18d29420843ad87fe84a1ae0b69e147487680 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 11 Aug 2023 09:33:38 +0200 Subject: [PATCH 4/6] removed seperators for now Signed-off-by: Jonah Iden --- packages/core/src/browser/common-frontend-contribution.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 2fffb7a57f829..90c7c8ced9807 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -18,7 +18,7 @@ import debounce = require('lodash.debounce'); import { injectable, inject, optional } from 'inversify'; -import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU, CompoundMenuNodeRole } from '../common/menu'; +import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU } from '../common/menu'; import { KeybindingContribution, KeybindingRegistry } from './keybinding'; import { FrontendApplication, FrontendApplicationContribution, OnWillStopAction } from './frontend-application'; import { CommandContribution, CommandRegistry, Command } from '../common/command'; @@ -971,6 +971,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, { execute: async () => this.showNewFilePicker() }); + for (const [index, ordinal] of this.getOrdinalNumbers().entries()) { commandRegistry.registerCommand({ id: `workbench.action.focus${ordinal}EditorGroup`, label: index === 0 ? nls.localizeByDefault('Focus First Editor Group') : '', category: nls.localize(CommonCommands.VIEW_CATEGORY_KEY, CommonCommands.VIEW_CATEGORY) }, { isEnabled: () => this.shell.mainAreaTabBars.length > index, @@ -1331,7 +1332,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi ...newFileContributions.children .flatMap(node => { if (node.children && node.children.length > 0) { - return node.role === CompoundMenuNodeRole.Group || node.role === CompoundMenuNodeRole.Submenu ? [node, ...node.children] : node.children; + return node.children; } return node; }) From 951318bf696b6dbc1aaebae5d13d8f48469db923 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 11 Aug 2023 11:40:25 +0200 Subject: [PATCH 5/6] added todo with link to issue Signed-off-by: Jonah Iden --- packages/core/src/browser/common-frontend-contribution.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 90c7c8ced9807..598a28f426641 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -1322,6 +1322,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }); } + /** + * @todo https://github.com/eclipse-theia/theia/issues/12824 + */ protected async showNewFilePicker(): Promise { const newFileContributions = this.menuRegistry.getMenuNode(CommonMenus.FILE_NEW_CONTRIBUTIONS); // Add menus const items: QuickPickItemOrSeparator[] = [ From 5795ccb084a9a9f20eedb60b6ba4c5fdffa1cf52 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 21 Aug 2023 09:47:40 +0200 Subject: [PATCH 6/6] review changes Signed-off-by: Jonah Iden --- examples/playwright/src/tests/theia-main-menu.test.ts | 4 ++-- .../core/src/browser/common-frontend-contribution.ts | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/playwright/src/tests/theia-main-menu.test.ts b/examples/playwright/src/tests/theia-main-menu.test.ts index 3ecdff8000105..cc305bed2696d 100644 --- a/examples/playwright/src/tests/theia-main-menu.test.ts +++ b/examples/playwright/src/tests/theia-main-menu.test.ts @@ -41,14 +41,14 @@ test.describe('Theia Main Menu', () => { expect(await mainMenu.isOpen()).toBe(true); }); - test("should show the menu items 'New File' and 'New Folder'", async () => { + test("should show the menu items 'New Text File' and 'New Folder'", async () => { const mainMenu = await menuBar.openMenu('File'); const menuItems = await mainMenu.visibleMenuItems(); expect(menuItems).toContain('New Text File'); expect(menuItems).toContain('New Folder...'); }); - test("should return menu item by name 'New File'", async () => { + test("should return menu item by name 'New Text File'", async () => { const mainMenu = await menuBar.openMenu('File'); const menuItem = await mainMenu.menuItemByName('New Text File'); expect(menuItem).toBeDefined(); diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 598a28f426641..2e95ea0a8eaa9 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -111,6 +111,7 @@ export namespace CommonCommands { export const FILE_CATEGORY = 'File'; export const VIEW_CATEGORY = 'View'; + export const CREATE_CATEGORY = 'Create'; export const PREFERENCES_CATEGORY = 'Preferences'; export const FILE_CATEGORY_KEY = nls.getDefaultKey(FILE_CATEGORY); export const VIEW_CATEGORY_KEY = nls.getDefaultKey(VIEW_CATEGORY); @@ -282,8 +283,8 @@ export namespace CommonCommands { }); export const NEW_UNTITLED_FILE = Command.toDefaultLocalizedCommand({ id: 'workbench.action.files.newUntitledFile', - category: FILE_CATEGORY, - label: 'New Untitled File' + category: CREATE_CATEGORY, + label: 'New File...' }); export const SAVE = Command.toDefaultLocalizedCommand({ id: 'core.save', @@ -1352,7 +1353,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }) ]; - this.quickInputService.showQuickPick(items, { canSelectMany: false }); + this.quickInputService.showQuickPick(items, { + title: nls.localizeByDefault('New File...'), + placeholder: nls.localizeByDefault('Select File Type or Enter File Name...'), + canSelectMany: false + }); } registerColors(colors: ColorRegistry): void {