From adee42af22fa8950a28a8680f19921710dcab418 Mon Sep 17 00:00:00 2001 From: Dan Arad Date: Tue, 16 Feb 2021 13:08:16 +0200 Subject: [PATCH 1/2] 8969-upgrade-monaco-editor-core-to-standalone-0.23.x In preparation for upgrading monaco-editor-core to standalone/0.23.x these changes were implemented: 1) signatures alignment to standalone/0.23.x 2) editor preferences synced with standalone/0.23.x 3) Changes to theme services based on changes in 0.23.x i4) Removal of QuickOpen modules and implementing QuickInput instead --- .../src/browser/bulk-edit-contribution.ts | 8 +- .../bulk-edit-tree/bulk-edit-tree-widget.tsx | 11 +- .../browser/color-application-contribution.ts | 4 +- .../browser/common-frontend-contribution.ts | 124 +- .../browser/frontend-application-module.ts | 42 +- packages/core/src/browser/index.ts | 2 +- .../browser-keyboard-frontend-contribution.ts | 21 +- .../{quick-open => quick-input}/index.ts | 20 +- .../quick-input/quick-access-contribution.ts | 23 + .../quick-command-frontend-contribution.ts} | 36 +- .../quick-input/quick-command-service.ts | 146 ++ .../quick-input/quick-editor-service.ts | 33 + .../quick-help-frontend-contribution.ts | 29 + .../browser/quick-input/quick-help-service.ts | 24 + .../quick-input-frontend-contribution.ts} | 19 +- .../quick-input/quick-input-service.ts | 213 +++ .../quick-input/quick-pick-service-impl.ts | 69 + .../browser/quick-input/quick-view-service.ts | 37 + .../quick-open/prefix-quick-open-service.ts | 284 --- .../quick-open/quick-command-service.ts | 271 --- .../browser/quick-open/quick-input-service.ts | 200 --- .../quick-open/quick-open-action-provider.ts | 88 - .../browser/quick-open/quick-open-model.ts | 42 - .../browser/quick-open/quick-open-service.ts | 52 - .../quick-open/quick-pick-service-impl.ts | 159 -- .../browser/quick-open/quick-pick-service.ts | 41 - .../src/browser/quick-open/quick-title-bar.ts | 234 --- .../core/src/browser/quick-view-service.ts | 93 - .../src/browser/shell/view-contribution.ts | 8 +- packages/core/src/browser/theming.ts | 2 +- packages/core/src/common/quick-open-model.ts | 140 -- .../core/src/common/quick-open-service.ts | 90 - .../core/src/common/quick-pick-service.ts | 89 +- .../browser/debug-configuration-manager.ts | 11 +- ...debug-frontend-application-contribution.ts | 2 +- .../src/browser/debug-frontend-module.ts | 5 +- .../src/browser/debug-prefix-configuration.ts | 81 +- packages/editor/src/browser/editor-command.ts | 55 +- .../editor/src/browser/editor-contribution.ts | 19 +- .../src/browser/editor-frontend-module.ts | 7 +- .../editor/src/browser/editor-preferences.ts | 256 ++- .../src/browser/editor-quick-open-service.ts | 129 -- .../external-terminal-contribution.ts | 5 +- packages/file-search/compile.tsconfig.json | 3 + packages/file-search/package.json | 1 + .../browser/file-search-frontend-module.ts | 5 +- .../browser/quick-file-open-contribution.ts | 8 +- .../src/browser/quick-file-open.ts | 225 ++- packages/file-search/src/common/monaco.d.ts | 18 + .../git/src/browser/git-quick-open-service.ts | 397 ++--- packages/git/src/browser/git-sync-service.ts | 35 +- .../prompt/git-quick-open-prompt.ts | 45 +- .../src/browser/mini-browser-open-handler.ts | 6 +- packages/monaco/package.json | 2 +- .../src/browser/monaco-browser-module.ts | 4 + .../src/browser/monaco-color-registry.ts | 2 +- packages/monaco/src/browser/monaco-command.ts | 83 +- .../monaco/src/browser/monaco-comparers.ts | 36 - .../monaco/src/browser/monaco-editor-model.ts | 2 +- .../src/browser/monaco-editor-provider.ts | 67 +- .../src/browser/monaco-editor-service.ts | 2 +- .../src/browser/monaco-editor-zone-widget.ts | 8 +- .../browser/monaco-formatting-conflicts.ts | 78 +- .../src/browser/monaco-frontend-module.ts | 32 +- packages/monaco/src/browser/monaco-loader.ts | 19 +- packages/monaco/src/browser/monaco-menu.ts | 6 +- .../browser/monaco-quick-command-service.ts | 132 ++ .../browser/monaco-quick-editor-service.ts | 104 ++ .../src/browser/monaco-quick-help-service.ts | 106 ++ .../src/browser/monaco-quick-input-service.ts | 235 +++ .../src/browser/monaco-quick-open-service.ts | 573 ------ .../src/browser/monaco-quick-view-service.ts | 97 + .../src/browser/monaco-resolved-keybinding.ts | 4 + .../monaco-snippet-suggest-provider.ts | 2 +- .../src/browser/monaco-theming-service.ts | 2 +- packages/monaco/src/browser/style/index.css | 168 +- .../textmate/monaco-textmate-service.ts | 2 +- .../browser/textmate/monaco-theme-registry.ts | 2 +- .../src/browser/workspace-symbol-command.ts | 196 +- .../monaco-electron-module.ts | 4 + packages/monaco/src/typings/monaco/index.d.ts | 1582 +++++++++++++---- .../output/src/browser/output-contribution.ts | 10 +- .../plugin-vscode-commands-contribution.ts | 10 +- .../src/common/plugin-api-rpc-model.ts | 5 + .../plugin-ext/src/common/plugin-api-rpc.ts | 147 +- .../src/main/browser/authentication-main.ts | 9 +- .../src/main/browser/languages-main.ts | 6 +- .../menus/menus-contribution-handler.ts | 10 +- .../main/browser/plugin-ext-deploy-command.ts | 90 +- .../src/main/browser/plugin-shared-style.ts | 2 +- .../src/main/browser/quick-open-main.ts | 542 +++--- .../src/main/browser/theming-main.ts | 2 +- .../main/browser/view/plugin-view-registry.ts | 10 +- .../src/main/browser/workspace-main.ts | 42 +- packages/plugin-ext/src/plugin/languages.ts | 7 +- .../src/plugin/languages/completion.ts | 5 +- packages/plugin-ext/src/plugin/quick-open.ts | 393 ++-- .../plugin-ext/src/plugin/type-converters.ts | 16 +- packages/plugin-ext/src/plugin/types-impl.ts | 11 +- .../browser/plugin-metrics-languages-main.ts | 4 +- packages/plugin/src/theia.d.ts | 159 +- .../preview/src/browser/preview-widget.ts | 2 +- packages/scm/src/browser/scm-contribution.ts | 2 - .../scm/src/browser/scm-quick-open-service.ts | 71 +- packages/task/src/browser/quick-open-task.ts | 640 +++---- .../src/browser/task-configuration-manager.ts | 6 +- .../src/browser/task-frontend-contribution.ts | 11 +- .../task/src/browser/task-frontend-module.ts | 9 +- packages/task/src/browser/task-service.ts | 24 +- packages/task/src/browser/task-templates.ts | 4 +- packages/terminal/compile.tsconfig.json | 3 + packages/terminal/package.json | 1 + .../browser/terminal-frontend-contribution.ts | 37 +- .../src/browser/terminal-frontend-module.ts | 5 +- .../browser/terminal-quick-open-service.ts | 143 +- .../src/browser/terminal-theme-service.ts | 2 +- .../src/common/monaco.d.ts} | 5 +- .../browser/common-variable-contribution.ts | 17 +- .../browser/variable-quick-open-service.ts | 39 +- ...ble-resolver-frontend-contribution.spec.ts | 6 + .../src/browser/quick-open-workspace.ts | 47 +- yarn.lock | 8 +- 122 files changed, 4646 insertions(+), 5433 deletions(-) rename packages/core/src/browser/{quick-open => quick-input}/index.ts (66%) create mode 100644 packages/core/src/browser/quick-input/quick-access-contribution.ts rename packages/core/src/browser/{quick-open/quick-command-contribution.ts => quick-input/quick-command-frontend-contribution.ts} (66%) create mode 100644 packages/core/src/browser/quick-input/quick-command-service.ts create mode 100644 packages/core/src/browser/quick-input/quick-editor-service.ts create mode 100644 packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts create mode 100644 packages/core/src/browser/quick-input/quick-help-service.ts rename packages/core/src/browser/{quick-open/quick-open-frontend-contribution.ts => quick-input/quick-input-frontend-contribution.ts} (68%) create mode 100644 packages/core/src/browser/quick-input/quick-input-service.ts create mode 100644 packages/core/src/browser/quick-input/quick-pick-service-impl.ts create mode 100644 packages/core/src/browser/quick-input/quick-view-service.ts delete mode 100644 packages/core/src/browser/quick-open/prefix-quick-open-service.ts delete mode 100644 packages/core/src/browser/quick-open/quick-command-service.ts delete mode 100644 packages/core/src/browser/quick-open/quick-input-service.ts delete mode 100644 packages/core/src/browser/quick-open/quick-open-action-provider.ts delete mode 100644 packages/core/src/browser/quick-open/quick-open-model.ts delete mode 100644 packages/core/src/browser/quick-open/quick-open-service.ts delete mode 100644 packages/core/src/browser/quick-open/quick-pick-service-impl.ts delete mode 100644 packages/core/src/browser/quick-open/quick-pick-service.ts delete mode 100644 packages/core/src/browser/quick-open/quick-title-bar.ts delete mode 100644 packages/core/src/browser/quick-view-service.ts delete mode 100644 packages/core/src/common/quick-open-model.ts delete mode 100644 packages/core/src/common/quick-open-service.ts delete mode 100644 packages/editor/src/browser/editor-quick-open-service.ts create mode 100644 packages/file-search/src/common/monaco.d.ts create mode 100644 packages/monaco/src/browser/monaco-quick-command-service.ts create mode 100644 packages/monaco/src/browser/monaco-quick-editor-service.ts create mode 100644 packages/monaco/src/browser/monaco-quick-help-service.ts create mode 100644 packages/monaco/src/browser/monaco-quick-input-service.ts delete mode 100644 packages/monaco/src/browser/monaco-quick-open-service.ts create mode 100644 packages/monaco/src/browser/monaco-quick-view-service.ts rename packages/{task/src/browser/task-action-provider.ts => terminal/src/common/monaco.d.ts} (84%) diff --git a/packages/bulk-edit/src/browser/bulk-edit-contribution.ts b/packages/bulk-edit/src/browser/bulk-edit-contribution.ts index b73e128e46bb9..910dc8a10438c 100644 --- a/packages/bulk-edit/src/browser/bulk-edit-contribution.ts +++ b/packages/bulk-edit/src/browser/bulk-edit-contribution.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { Widget } from '@theia/core/lib/browser/widgets/widget'; import { CommandRegistry } from '@theia/core/lib/common'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; @@ -22,7 +22,7 @@ import { BulkEditCommands } from './bulk-edit-commands'; import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { BulkEditTreeWidget, BULK_EDIT_TREE_WIDGET_ID } from './bulk-edit-tree'; -import { QuickViewService } from '@theia/core/lib/browser/quick-view-service'; +import { QuickViewService } from '@theia/core/lib/browser'; export const BULK_EDIT_WIDGET_NAME = 'Refactor Preview'; @@ -30,7 +30,7 @@ export const BULK_EDIT_WIDGET_NAME = 'Refactor Preview'; export class BulkEditContribution extends AbstractViewContribution implements TabBarToolbarContribution { private workspaceEdit: monaco.languages.WorkspaceEdit; - @inject(QuickViewService) + @inject(QuickViewService) @optional() protected readonly quickView: QuickViewService; constructor(private readonly bulkEditService: MonacoBulkEditService) { @@ -46,7 +46,7 @@ export class BulkEditContribution extends AbstractViewContribution this.withWidget(widget, () => true), diff --git a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx index 7d0bf88ea4b44..9bd84dfa250c3 100644 --- a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx +++ b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx @@ -14,10 +14,10 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { TreeWidget, TreeProps, ContextMenuRenderer, TreeNode, TreeModel, - CompositeTreeNode, NodeProps + CompositeTreeNode, NodeProps, QuickViewService } from '@theia/core/lib/browser'; import * as React from '@theia/core/shared/react'; import { BulkEditInfoNode, BulkEditNode } from './bulk-edit-tree'; @@ -28,7 +28,6 @@ import { EditorWidget, EditorManager, EditorOpenerOptions } from '@theia/editor/ import { DiffUris } from '@theia/core/lib/browser'; import { MEMORY_TEXT } from './in-memory-text-resource'; import { Disposable } from '@theia/core/lib/common/disposable'; -import { QuickViewService } from '@theia/core/lib/browser/quick-view-service'; export const BULK_EDIT_TREE_WIDGET_ID = 'bulkedit'; export const BULK_EDIT_WIDGET_NAME = 'Refactor Preview'; @@ -43,7 +42,7 @@ export class BulkEditTreeWidget extends TreeWidget { @inject(EditorManager) protected readonly editorManager: EditorManager; - @inject(QuickViewService) + @inject(QuickViewService) @optional() protected readonly quickView: QuickViewService; constructor( @@ -66,7 +65,7 @@ export class BulkEditTreeWidget extends TreeWidget { async initModel(workspaceEdit: monaco.languages.WorkspaceEdit): Promise { await this.model.initModel(workspaceEdit, await this.getFileContentsMap(workspaceEdit)); - this.quickView.showItem(BULK_EDIT_WIDGET_NAME); + this.quickView?.showItem(BULK_EDIT_WIDGET_NAME); } protected handleClickEvent(node: TreeNode | undefined, event: React.MouseEvent): void { @@ -225,6 +224,6 @@ export class BulkEditTreeWidget extends TreeWidget { private disposeEditors(): void { this.editorWidgets.forEach(w => w.dispose()); - this.quickView.hideItem(BULK_EDIT_WIDGET_NAME); + this.quickView?.hideItem(BULK_EDIT_WIDGET_NAME); } } diff --git a/packages/core/src/browser/color-application-contribution.ts b/packages/core/src/browser/color-application-contribution.ts index 3dfee669d19d7..548708459bb47 100644 --- a/packages/core/src/browser/color-application-contribution.ts +++ b/packages/core/src/browser/color-application-contribution.ts @@ -47,10 +47,10 @@ export class ColorApplicationContribution implements FrontendApplicationContribu } this.updateThemeBackground(); - ThemeService.get().onThemeChange(() => this.updateThemeBackground()); + ThemeService.get().onDidColorThemeChange(() => this.updateThemeBackground()); this.update(); - ThemeService.get().onThemeChange(() => this.update()); + ThemeService.get().onDidColorThemeChange(() => this.update()); this.colors.onDidChange(() => this.update()); } diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 470e2c2265fbb..3abcb7012c0ab 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -17,7 +17,7 @@ /* eslint-disable max-len, @typescript-eslint/indent */ import debounce = require('lodash.debounce'); -import { injectable, inject } from 'inversify'; +import { injectable, inject, optional } from 'inversify'; import { TabBar, Widget } from '@phosphor/widgets'; import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU } from '../common/menu'; import { KeybindingContribution, KeybindingRegistry } from './keybinding'; @@ -38,8 +38,7 @@ import { ResourceContextKey } from './resource-context-key'; import { UriSelection } from '../common/selection'; import { StorageService } from './storage-service'; import { Navigatable } from './navigatable'; -import { QuickViewService } from './quick-view-service'; -import { PrefixQuickOpenService, QuickOpenItem, QuickOpenMode, QuickOpenService, QuickOpenGroupItem } from './quick-open'; +import { QuickViewService } from './quick-input/quick-view-service'; import { environment } from '@theia/application-package/lib/environment'; import { IconThemeService } from './icon-theme-service'; import { ColorContribution } from './color-application-contribution'; @@ -53,6 +52,7 @@ import { UTF8 } from '../common/encodings'; import { EnvVariablesServer } from '../common/env-variables'; import { AuthenticationService } from './authentication-service'; import { FormatType } from './saveable'; +import { QuickInputService, QuickPick, QuickPickItem } from './quick-input'; export namespace CommonMenus { @@ -304,11 +304,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(StorageService) protected readonly storageService: StorageService; - @inject(QuickViewService) + @inject(QuickViewService) @optional() protected readonly quickView: QuickViewService; - @inject(PrefixQuickOpenService) - protected readonly quickOpen: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(IconThemeService) protected readonly iconThemes: IconThemeService; @@ -316,9 +316,6 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(ThemeService) protected readonly themeService: ThemeService; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; - @inject(CorePreferences) protected readonly preferences: CorePreferences; @@ -363,7 +360,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi this.updateThemeFromPreference(e.preferenceName); } }); - this.themeService.onThemeChange(() => this.updateThemePreference('workbench.colorTheme')); + this.themeService.onDidColorThemeChange(() => this.updateThemePreference('workbench.colorTheme')); this.iconThemes.onDidChangeCurrent(() => this.updateThemePreference('workbench.iconTheme')); app.shell.leftPanelHandler.addMenu({ @@ -765,7 +762,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }); commandRegistry.registerCommand(CommonCommands.OPEN_VIEW, { - execute: () => this.quickOpen.open(this.quickView.prefix) + execute: () => this.quickInputService?.open(this.quickView?.prefix) }); commandRegistry.registerCommand(CommonCommands.SELECT_COLOR_THEME, { @@ -991,82 +988,79 @@ export class CommonFrontendContribution implements FrontendApplicationContributi let resetTo: string | undefined = this.iconThemes.current; const previewTheme = debounce((id: string) => this.iconThemes.current = id, 200); - let items: (QuickOpenItem & { id: string })[] = []; + let items: Array = []; for (const iconTheme of this.iconThemes.definitions) { - const item = Object.assign(new QuickOpenItem({ + items.push({ + id: iconTheme.id, label: iconTheme.label, description: iconTheme.description, - run: (mode: QuickOpenMode) => { - if (mode === QuickOpenMode.OPEN) { - resetTo = undefined; - } - previewTheme(iconTheme.id); - return true; - } - }), { id: iconTheme.id }); - items.push(item); + }); } items = items.sort((a, b) => { if (a.id === 'none') { return -1; } - return a.getLabel()!.localeCompare(b.getLabel()!); - }); - this.quickOpenService.open({ - onType: (_, accept) => accept(items) - }, { - placeholder: 'Select File Icon Theme', - fuzzyMatchLabel: true, - selectIndex: () => items.findIndex(item => item.id === this.iconThemes.current), - onClose: () => { - if (resetTo) { - previewTheme.cancel(); - this.iconThemes.current = resetTo; - } - } + return a.label!.localeCompare(b.label!); }); + + this.quickInputService?.showQuickPick(items, + { + placeholder: 'Select File Icon Theme', + activeItem: items.find(item => item.id === resetTo), + onDidChangeSelection: (quickPick: QuickPick, selectedItems: Array) => { + resetTo = undefined; + previewTheme(selectedItems[0].id!); + }, + onDidChangeActive: (quickPick: QuickPick, activeItems: Array) => { + previewTheme(activeItems[0].id!); + }, + onDidHide: () => { + if (resetTo) { + this.iconThemes.current = resetTo; + } + } + }); } protected selectColorTheme(): void { let resetTo: string | undefined = this.themeService.getCurrentTheme().id; const previewTheme = debounce((id: string) => this.themeService.setCurrentTheme(id), 200); - type ThemeQuickOpenItem = QuickOpenItem & { id: string }; - const itemsByTheme: { light: ThemeQuickOpenItem[], dark: ThemeQuickOpenItem[], hc: ThemeQuickOpenItem[] } = { light: [], dark: [], hc: [] }; + const itemsByTheme: { light: Array, dark: Array, hc: Array } = { light: [], dark: [], hc: [] }; for (const theme of this.themeService.getThemes().sort((a, b) => a.label.localeCompare(b.label))) { const themeItems = itemsByTheme[theme.type]; - const groupLabel = themeItems.length === 0 ? (theme.type === 'hc' ? 'high contrast' : theme.type) + ' themes' : undefined; - themeItems.push(Object.assign(new QuickOpenGroupItem({ + if (themeItems.length === 0) { + themeItems.push({ + type: 'separator', + label: (theme.type === 'hc' ? 'high contrast' : theme.type) + ' themes' + }); + } + themeItems.push({ + id: theme.id, label: theme.label, description: theme.description, - run: (mode: QuickOpenMode) => { - if (mode === QuickOpenMode.OPEN) { - resetTo = undefined; - } - previewTheme(theme.id); - return true; - }, - groupLabel, - showBorder: !!groupLabel && theme.type !== 'light' - }), { id: theme.id })); + }); } const items = [...itemsByTheme.light, ...itemsByTheme.dark, ...itemsByTheme.hc]; - this.quickOpenService.open({ - onType: (_, accept) => accept(items) - }, { - placeholder: 'Select Color Theme (Up/Down Keys to Preview)', - fuzzyMatchLabel: true, - selectIndex: () => { - const current = this.themeService.getCurrentTheme().id; - return items.findIndex(item => item.id === current); - }, - onClose: () => { - if (resetTo) { - previewTheme.cancel(); - this.themeService.setCurrentTheme(resetTo); + this.quickInputService?.showQuickPick(items, + { + placeholder: 'Select Color Theme (Up/Down Keys to Preview)', + activeItem: items.find((item: QuickPickItem) => item.id === resetTo), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onDidChangeSelection: (quickPick: any, selectedItems: Array) => { + resetTo = undefined; + previewTheme(selectedItems[0].id!); + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onDidChangeActive: (quickPick: any, activeItems: Array) => { + previewTheme(activeItems[0].id!); + }, + onDidHide: () => { + if (resetTo) { + this.themeService.setCurrentTheme(resetTo); + } } - } - }); + }); } registerColors(colors: ColorRegistry): void { diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 9f041675a91b0..9f0d24f1b6ce7 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -35,11 +35,6 @@ import { FrontendApplication, FrontendApplicationContribution, DefaultFrontendAp import { DefaultOpenerService, OpenerService, OpenHandler } from './opener-service'; import { HttpOpenHandler } from './http-open-handler'; import { CommonFrontendContribution } from './common-frontend-contribution'; -import { - QuickOpenService, QuickCommandService, QuickCommandFrontendContribution, QuickOpenContribution, - QuickOpenHandlerRegistry, CommandQuickOpenContribution, HelpQuickOpenHandler, - QuickOpenFrontendContribution, PrefixQuickOpenService, QuickInputService -} from './quick-open'; import { LocalStorageService, StorageService } from './storage-service'; import { WidgetFactory, WidgetManager } from './widget-manager'; import { @@ -65,16 +60,13 @@ import { FrontendApplicationStateService } from './frontend-application-state'; import { JsonSchemaStore, JsonSchemaContribution, DefaultJsonSchemaContribution } from './json-schema-store'; import { TabBarToolbarRegistry, TabBarToolbarContribution, TabBarToolbarFactory, TabBarToolbar } from './shell/tab-bar-toolbar'; import { bindCorePreferences } from './core-preferences'; -import { QuickPickServiceImpl } from './quick-open/quick-pick-service-impl'; -import { QuickPickService, quickPickServicePath } from '../common/quick-pick-service'; import { ContextKeyService } from './context-key-service'; import { ResourceContextKey } from './resource-context-key'; import { KeyboardLayoutService } from './keyboard/keyboard-layout-service'; import { MimeService } from './mime-service'; import { ApplicationShellMouseTracker } from './shell/application-shell-mouse-tracker'; import { ViewContainer, ViewContainerIdentifier } from './view-container'; -import { QuickViewService } from './quick-view-service'; -import { QuickTitleBar } from './quick-open/quick-title-bar'; +import { QuickViewService } from './quick-input/quick-view-service'; import { DialogOverlayService } from './dialogs'; import { ProgressLocationService } from './progress-location-service'; import { ProgressClient } from '../common/progress-service-protocol'; @@ -98,6 +90,14 @@ import { EncodingRegistry } from './encoding-registry'; import { EncodingService } from '../common/encoding-service'; import { AuthenticationService, AuthenticationServiceImpl } from '../browser/authentication-service'; import { DecorationsService, DecorationsServiceImpl } from './decorations-service'; +import { QuickCommandFrontendContribution } from './quick-input/quick-command-frontend-contribution'; +import { QuickHelpFrontendContribution } from './quick-input/quick-help-frontend-contribution'; +import { QuickPickService, quickPickServicePath } from '../common/quick-pick-service'; +import { + QuickPickServiceImpl, + QuickInputFrontendContribution +} from './quick-input'; +import { QuickAccessContribution } from './quick-input/quick-access-contribution'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -196,7 +196,6 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo }); bind(CommandService).toService(CommandRegistry); bindContributionProvider(bind, CommandContribution); - bind(QuickOpenContribution).to(CommandQuickOpenContribution); bind(ContextKeyService).toSelf().inSingletonScope(); @@ -225,28 +224,22 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo bind(serviceIdentifier).toService(CommonFrontendContribution) ); - bind(QuickOpenService).toSelf().inSingletonScope(); - bind(QuickInputService).toSelf().inSingletonScope(); - bind(QuickTitleBar).toSelf().inSingletonScope(); - bind(QuickCommandService).toSelf().inSingletonScope(); bind(QuickCommandFrontendContribution).toSelf().inSingletonScope(); - [CommandContribution, KeybindingContribution, MenuContribution].forEach(serviceIdentifier => + [CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution].forEach(serviceIdentifier => bind(serviceIdentifier).toService(QuickCommandFrontendContribution) ); + bind(QuickHelpFrontendContribution).toSelf().inSingletonScope(); + bind(QuickAccessContribution).toService(QuickHelpFrontendContribution); + bind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope().onActivation(({ container }, quickPickService: QuickPickService) => { WebSocketConnectionProvider.createProxy(container, quickPickServicePath, quickPickService); return quickPickService; }); - bind(PrefixQuickOpenService).toSelf().inSingletonScope(); - bindContributionProvider(bind, QuickOpenContribution); - bind(QuickOpenHandlerRegistry).toSelf().inSingletonScope(); - bind(QuickOpenFrontendContribution).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(QuickOpenFrontendContribution); - - bind(HelpQuickOpenHandler).toSelf().inSingletonScope(); - bind(QuickOpenContribution).toService(HelpQuickOpenHandler); + bindContributionProvider(bind, QuickAccessContribution); + bind(QuickInputFrontendContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(QuickInputFrontendContribution); bind(LocalStorageService).toSelf().inSingletonScope(); bind(StorageService).toService(LocalStorageService); @@ -318,8 +311,7 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo return container.get(ViewContainer); }); - bind(QuickViewService).toSelf().inSingletonScope(); - bind(QuickOpenContribution).toService(QuickViewService); + bind(QuickAccessContribution).toService(QuickViewService); bind(DialogOverlayService).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(DialogOverlayService); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index 584bb4cde2511..b6ef3247daea0 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -26,7 +26,7 @@ export * from './tree'; export * from './messaging'; export * from './endpoint'; export * from './common-frontend-contribution'; -export * from './quick-open'; +export * from './quick-input'; export * from './widget-manager'; export * from './saveable'; export * from './storage-service'; diff --git a/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts b/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts index 6bcfd2de72822..1dbfb394e7e02 100644 --- a/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +++ b/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts @@ -14,11 +14,11 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from 'inversify'; +import { inject, injectable, optional } from 'inversify'; import { isOSX } from '../../common/os'; import { CommandContribution, CommandRegistry, Command } from '../../common/command'; -import { QuickPickService, QuickPickItem } from '../../common/quick-pick-service'; import { BrowserKeyboardLayoutProvider, KeyboardLayoutData } from './browser-keyboard-layout-provider'; +import { QuickPickValue, QuickInputService, QuickPickItem } from '../quick-input'; export namespace KeyboardCommands { @@ -38,8 +38,8 @@ export class BrowserKeyboardFrontendContribution implements CommandContribution @inject(BrowserKeyboardLayoutProvider) protected readonly layoutProvider: BrowserKeyboardLayoutProvider; - @inject(QuickPickService) - protected readonly quickPickService: QuickPickService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; registerCommands(commandRegistry: CommandRegistry): void { commandRegistry.registerCommand(KeyboardCommands.CHOOSE_KEYBOARD_LAYOUT, { @@ -49,7 +49,7 @@ export class BrowserKeyboardFrontendContribution implements CommandContribution protected async chooseLayout(): Promise { const current = this.layoutProvider.currentLayoutData; - const autodetect: QuickPickItem<'autodetect'> = { + const autodetect: QuickPickValue<'autodetect'> = { label: 'Auto-detect', description: this.layoutProvider.currentLayoutSource !== 'user-choice' ? `(current: ${current.name})` : undefined, detail: 'Try to detect the keyboard layout from browser information and pressed keys.', @@ -63,7 +63,7 @@ export class BrowserKeyboardFrontendContribution implements CommandContribution .filter(layout => layout.hardware === 'mac') .sort((a, b) => compare(a.name, b.name)) .map(layout => this.toQuickPickValue(layout, current === layout)); - let layouts: QuickPickItem[]; + let layouts: Array | QuickPickItem>; if (isOSX) { layouts = [ autodetect, @@ -77,20 +77,19 @@ export class BrowserKeyboardFrontendContribution implements CommandContribution { type: 'separator', label: 'Mac Keyboards' }, ...macLayouts ]; } - const chosen = await this.quickPickService.show(layouts, { placeholder: 'Choose a keyboard layout' }); - if (chosen) { - return this.layoutProvider.setLayoutData(chosen); + const selectedItem = await this.quickInputService?.showQuickPick(layouts, { placeholder: 'Choose a keyboard layout' }); + if (selectedItem && ('value' in selectedItem)) { + return this.layoutProvider.setLayoutData(selectedItem.value); } } - protected toQuickPickValue(layout: KeyboardLayoutData, isCurrent: boolean): QuickPickItem { + protected toQuickPickValue(layout: KeyboardLayoutData, isCurrent: boolean): QuickPickValue { return { label: layout.name, description: `${layout.hardware === 'mac' ? 'Mac' : 'PC'} (${layout.language})${isCurrent ? ' - current layout' : ''}`, value: layout }; } - } function compare(a: string, b: string): number { diff --git a/packages/core/src/browser/quick-open/index.ts b/packages/core/src/browser/quick-input/index.ts similarity index 66% rename from packages/core/src/browser/quick-open/index.ts rename to packages/core/src/browser/quick-input/index.ts index cf5bd3fa0e1b7..e720b9e53447f 100644 --- a/packages/core/src/browser/quick-open/index.ts +++ b/packages/core/src/browser/quick-input/index.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2017 TypeFox and others. + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 @@ -13,13 +13,13 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - -export * from './quick-open-model'; -export * from './quick-open-action-provider'; -export * from './quick-open-service'; -export * from './quick-pick-service'; -export * from './quick-input-service'; +export * from './quick-command-frontend-contribution'; export * from './quick-command-service'; -export * from './quick-command-contribution'; -export * from './quick-open-frontend-contribution'; -export * from './prefix-quick-open-service'; +export * from './quick-editor-service'; +export * from './quick-help-frontend-contribution'; +export * from './quick-help-service'; +export * from './quick-access-contribution'; +export * from './quick-input-frontend-contribution'; +export * from './quick-input-service'; +export * from './quick-view-service'; +export * from './quick-pick-service-impl'; diff --git a/packages/core/src/browser/quick-input/quick-access-contribution.ts b/packages/core/src/browser/quick-input/quick-access-contribution.ts new file mode 100644 index 0000000000000..a810f22e6ea10 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-access-contribution.ts @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 QuickAccessContribution = Symbol('QuickAccessContribution'); +/** + * The quick access contribution should be implemented to register custom quick access provider. + */ +export interface QuickAccessContribution { + registerQuickAccessProvider(): void; +} diff --git a/packages/core/src/browser/quick-open/quick-command-contribution.ts b/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts similarity index 66% rename from packages/core/src/browser/quick-open/quick-command-contribution.ts rename to packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts index 2bf46b5a563d0..02131987fbfac 100644 --- a/packages/core/src/browser/quick-open/quick-command-contribution.ts +++ b/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts @@ -13,33 +13,29 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - -import { injectable, inject } from 'inversify'; -import { Command, CommandRegistry, CommandContribution, MenuContribution, MenuModelRegistry } from '../../common'; +import { injectable, inject, optional } from 'inversify'; +import { CommandRegistry, CommandContribution, MenuContribution, MenuModelRegistry } from '../../common'; import { KeybindingRegistry, KeybindingContribution } from '../keybinding'; -import { PrefixQuickOpenService, QuickOpenHandlerRegistry } from './prefix-quick-open-service'; import { CommonMenus } from '../common-frontend-contribution'; - -export const quickCommand: Command = { - id: 'workbench.action.showCommands' -}; - -export const CLEAR_COMMAND_HISTORY: Command = { - id: 'clear.command.history', - label: 'Clear Command History' -}; +import { QuickInputService } from './quick-input-service'; +import { CLEAR_COMMAND_HISTORY, quickCommand, QuickCommandService } from './quick-command-service'; +import { QuickAccessContribution } from './quick-access-contribution'; @injectable() -export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution { +export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution { - @inject(PrefixQuickOpenService) - protected readonly quickOpenService: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; - @inject(QuickOpenHandlerRegistry) protected readonly quickOpenHandlerRegistry: QuickOpenHandlerRegistry; + @inject(QuickCommandService) @optional() + protected readonly quickCommandService: QuickCommandService; registerCommands(commands: CommandRegistry): void { commands.registerCommand(quickCommand, { - execute: () => this.quickOpenService.open('>') + execute: () => { + this.quickCommandService?.reset(); + this.quickInputService?.open('>'); + } }); commands.registerCommand(CLEAR_COMMAND_HISTORY, { execute: () => commands.clearCommandHistory(), @@ -63,4 +59,8 @@ export class QuickCommandFrontendContribution implements CommandContribution, Ke keybinding: 'ctrlcmd+shift+p' }); } + + registerQuickAccessProvider(): void { + this.quickCommandService?.registerQuickAccessProvider(); + } } diff --git a/packages/core/src/browser/quick-input/quick-command-service.ts b/packages/core/src/browser/quick-input/quick-command-service.ts new file mode 100644 index 0000000000000..8b0d468fb04c1 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-command-service.ts @@ -0,0 +1,146 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; +import { Disposable, Command, CommandRegistry } from '../../common'; +import { ContextKeyService } from '../context-key-service'; +import { CorePreferences } from '../core-preferences'; +import { QuickAccessContribution } from './quick-access-contribution'; +import { QuickInputService } from './quick-input-service'; + +export const quickCommand: Command = { + id: 'workbench.action.showCommands' +}; + +export const CLEAR_COMMAND_HISTORY: Command = { + id: 'clear.command.history', + label: 'Clear Command History' +}; + +@injectable() +export class QuickCommandService extends QuickInputService implements QuickAccessContribution { + + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + + @inject(CommandRegistry) + protected readonly commandRegistry: CommandRegistry; + + @inject(CorePreferences) + protected readonly corePreferences: CorePreferences; + + // The list of exempted commands not to be displayed in the recently used list. + readonly exemptedCommands: Command[] = [ + CLEAR_COMMAND_HISTORY, + ]; + + registerQuickAccessProvider(): void { } + + protected readonly contexts = new Map(); + pushCommandContext(commandId: string, when: string): Disposable { + const contexts = this.contexts.get(commandId) || []; + contexts.push(when); + this.contexts.set(commandId, contexts); + return Disposable.create(() => { + const index = contexts.indexOf(when); + if (index !== -1) { + contexts.splice(index, 1); + } + }); + } + + /** + * Get the list of valid commands. + * + * @param commands the list of raw commands. + * @returns the list of valid commands. + */ + protected getValidCommands(raw: Command[]): Command[] { + const valid: Command[] = []; + raw.forEach(command => { + if (command.label) { + const contexts = this.contexts.get(command.id); + if (!contexts || contexts.some(when => this.contextKeyService.match(when))) { + valid.push(command); + } + } + }); + return valid; + } + + /** + * Get the list of recently used and other commands. + * + * @returns the list of recently used commands and other commands. + */ + protected getCommands(): { recent: Command[], other: Command[] } { + + // Get the list of recent commands. + const recentCommands = this.commandRegistry.recent; + + // Get the list of all valid commands. + const allCommands = this.getValidCommands(this.commandRegistry.commands); + + // Get the max history limit. + const limit = this.corePreferences['workbench.commandPalette.history']; + + // Build the list of recent commands. + let rCommands: Command[] = []; + if (limit > 0) { + rCommands.push(...recentCommands.filter(r => + !this.exemptedCommands.some(c => Command.equals(r, c)) && + allCommands.some(c => Command.equals(r, c))) + ); + if (rCommands.length > limit) { + rCommands = rCommands.slice(0, limit); + } + } + + // Build the list of other commands. + const oCommands = allCommands.filter(c => !rCommands.some(r => Command.equals(r, c))); + + // Normalize the list of recent commands. + const recent = this.normalize(rCommands); + + // Normalize, and sort the list of other commands. + const other = this.sort( + this.normalize(oCommands) + ); + + return { recent, other }; + } + + /** + * Normalizes a list of commands. + * Normalization includes obtaining commands that have labels, are visible, and are enabled. + * + * @param commands the list of commands. + * @returns the list of normalized commands. + */ + private normalize(commands: Command[]): Command[] { + return commands.filter((a: Command) => a.label && (this.commandRegistry.isVisible(a.id) && this.commandRegistry.isEnabled(a.id))); + } + + /** + * Sorts a list of commands alphabetically. + * + * @param commands the list of commands. + * @returns the list of sorted commands. + */ + private sort(commands: Command[]): Command[] { + return commands.sort((a: Command, b: Command) => Command.compareCommands(a, b)); + } +} diff --git a/packages/core/src/browser/quick-input/quick-editor-service.ts b/packages/core/src/browser/quick-input/quick-editor-service.ts new file mode 100644 index 0000000000000..4067f3a9cef34 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-editor-service.ts @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; +import { LabelProvider } from '../label-provider'; +import { OpenerService } from '../opener-service'; +import { QuickAccessContribution } from './quick-access-contribution'; +import { QuickInputService } from './quick-input-service'; + +@injectable() +export class QuickEditorService extends QuickInputService implements QuickAccessContribution { + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; + + registerQuickAccessProvider(): void { } +} diff --git a/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts b/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts new file mode 100644 index 0000000000000..c9b55cd58e63c --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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, inject, optional } from 'inversify'; +import { QuickHelpService } from './quick-help-service'; +import { QuickAccessContribution } from './quick-access-contribution'; + +@injectable() +export class QuickHelpFrontendContribution implements QuickAccessContribution { + + @inject(QuickHelpService) @optional() + protected readonly quickHelpService: QuickHelpService; + + registerQuickAccessProvider(): void { + this.quickHelpService?.registerQuickAccessProvider(); + } +} diff --git a/packages/core/src/browser/quick-input/quick-help-service.ts b/packages/core/src/browser/quick-input/quick-help-service.ts new file mode 100644 index 0000000000000..caaf22b77b9b5 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-help-service.ts @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; +import { QuickAccessContribution } from './quick-access-contribution'; +import { QuickInputService } from './quick-input-service'; + +@injectable() +export class QuickHelpService extends QuickInputService implements QuickAccessContribution { + registerQuickAccessProvider(): void { } +} diff --git a/packages/core/src/browser/quick-open/quick-open-frontend-contribution.ts b/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts similarity index 68% rename from packages/core/src/browser/quick-open/quick-open-frontend-contribution.ts rename to packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts index 4b27be3aacf3d..3d2e44feb55e4 100644 --- a/packages/core/src/browser/quick-open/quick-open-frontend-contribution.ts +++ b/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2018 Red Hat, Inc. and others. + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 @@ -17,20 +17,17 @@ import { injectable, inject, named } from 'inversify'; import { ContributionProvider } from '../../common'; import { FrontendApplicationContribution } from '../frontend-application'; -import { QuickOpenContribution, QuickOpenHandlerRegistry } from './prefix-quick-open-service'; +import { QuickAccessContribution } from './quick-access-contribution'; @injectable() -export class QuickOpenFrontendContribution implements FrontendApplicationContribution { +export class QuickInputFrontendContribution implements FrontendApplicationContribution { - @inject(QuickOpenHandlerRegistry) - protected readonly quickOpenHandlerRegistry: QuickOpenHandlerRegistry; - - @inject(ContributionProvider) @named(QuickOpenContribution) - protected readonly contributionProvider: ContributionProvider; + @inject(ContributionProvider) @named(QuickAccessContribution) + protected readonly contributionProvider: ContributionProvider; onStart(): void { - this.contributionProvider.getContributions().forEach(contrib => - contrib.registerQuickOpenHandlers(this.quickOpenHandlerRegistry) - ); + this.contributionProvider.getContributions().forEach(contrib => { + contrib.registerQuickAccessProvider(); + }); } } diff --git a/packages/core/src/browser/quick-input/quick-input-service.ts b/packages/core/src/browser/quick-input/quick-input-service.ts new file mode 100644 index 0000000000000..1869ed9fca043 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-input-service.ts @@ -0,0 +1,213 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; +import { CancellationToken, Event } from '../../common'; +import URI from '../../common/uri'; + +export interface QuickPickItem { + type?: 'item' | 'separator'; + id?: string; + label: string; + meta?: string; + ariaLabel?: string; + description?: string; + detail?: string; + resource?: URI; + iconClasses?: string[]; + execute?: (item: QuickPickItem, lookFor: string) => void; +} + +export interface QuickPickSeparator { + type: 'separator'; + label?: string; +} + +export interface QuickPickValue extends QuickPickItem { + value: V +} + +export interface QuickInputButton { + iconPath?: URI | { light: URI; dark: URI } | { id: string }; + iconClass?: string; + tooltip?: string; +} + +export interface QuickInputButtonHandle extends QuickInputButton { + index: number; // index of where they are in buttons array if QuickInputButton or -1 if QuickInputButtons.Back +} + +export interface QuickInput { + readonly onDidHide: Event; + readonly onDispose: Event; + title: string | undefined; + description: string | undefined; + step: number | undefined; + totalSteps: number | undefined; + enabled: boolean; + contextKey: string | undefined; + busy: boolean; + ignoreFocusOut: boolean; + show(): void; + hide(): void; + dispose(): void; +} + +export interface IInputBox extends QuickInput { + value: string | undefined; + valueSelection: Readonly<[number, number]> | undefined; + placeholder: string | undefined; + password: boolean; + readonly onDidChangeValue: Event; + readonly onDidAccept: Event; + buttons: ReadonlyArray; + readonly onDidTriggerButton: Event; + prompt: string | undefined; + validationMessage: string | undefined; +} + +export interface QuickPick extends QuickInput { + value: string; + placeholder: string | undefined; + readonly onDidChangeValue: Event; + readonly onDidAccept: Event; + buttons: ReadonlyArray; + readonly onDidTriggerButton: Event; + items: ReadonlyArray; + canSelectMany: boolean; + matchOnDescription: boolean; + matchOnDetail: boolean; + activeItems: ReadonlyArray; + readonly onDidChangeActive: Event; + selectedItems: ReadonlyArray; + readonly onDidChangeSelection: Event; +} + +export interface IPickOptions { + placeHolder?: string; + matchOnDescription?: boolean; + matchOnDetail?: boolean; + matchOnLabel?: boolean; + autoFocusOnList?: boolean; + ignoreFocusLost?: boolean; + canPickMany?: boolean; + contextKey?: string; + activeItem?: Promise | T; + onDidFocus?: (entry: T) => void; +} + +export interface IInputOptions { + value?: string; + valueSelection?: [number, number]; + prompt?: string; + placeHolder?: string; + password?: boolean; + ignoreFocusLost?: boolean; + validateInput?(input: string): Promise | undefined; +} + +export interface QuickPickItemButtonEvent { + button: QuickInputButton; + item: T; +} + +export interface QuickPickOptions { + busy?: boolean; + enabled?: boolean; + title?: string; + description?: string; + value?: string; + filterValue?: (value: string) => string; + ariaLabel?: string; + buttons?: Array; + placeholder?: string; + canAcceptInBackground?: boolean; + customButton?: boolean; + customLabel?: string; + customHover?: string; + canSelectMany?: boolean; + matchOnDescription?: boolean; + matchOnDetail?: boolean; + matchOnLabel?: boolean; + sortByLabel?: boolean; + autoFocusOnList?: boolean; + ignoreFocusOut?: boolean; + valueSelection?: Readonly<[number, number]>; + validationMessage?: string; + hideInput?: boolean; + hideCheckAll?: boolean; + runIfSingle?: boolean + contextKey?: string; + activeItem?: T, + step?: number; + totalSteps?: number; + + onDidAccept?: () => void, + onDidChangeActive?: (quickPick: QuickPick, activeItems: Array) => void, + onDidChangeSelection?: (quickPick: QuickPick, selectedItems: Array) => void, + onDidChangeValue?: (quickPick: QuickPick, filter: string) => void, + onDidCustom?: () => void, + onDidHide?: () => void; + onDidTriggerButton?: Event; + onDidTriggerItemButton?: Event>; +} + +export interface IQuickInputService { + readonly backButton: QuickInputButton; + open(filter: string): void; + reset(): void; + createInputBox(): IInputBox; + createQuickPick(): QuickPick; + input(options?: IInputOptions, token?: CancellationToken): Promise; + pick>(picks: Promise | T[], options?: O, token?: CancellationToken): + Promise<(O extends { canPickMany: true } ? T[] : T) | undefined>; + showQuickPick(items: Array, options?: QuickPickOptions): Promise; + hide(): void; +} + +export function filterItems(items: Array, filter: string): Array { + return filter.trim().length === 0 ? items : items + .filter(item => item.label.toLowerCase().indexOf(filter.toLowerCase()) > -1) + .map(item => Object.assign(item, { highlights: { label: findMatches(item.label.toLowerCase(), filter.toLowerCase()) } })); +} + +export function findMatches(label: string, lookFor: string): Array<{ start: number, end: number }> | undefined { + return label.indexOf(lookFor) > -1 ? [{ start: label.indexOf(lookFor), end: label.indexOf(lookFor) + lookFor.length }] : undefined; +} + +@injectable() +export class QuickInputService implements IQuickInputService { + createInputBox(): IInputBox { return {} as IInputBox; } + + createQuickPick(): QuickPick { return {} as QuickPick; } + + input(options?: IInputOptions, token?: CancellationToken): Promise { return Promise.resolve(undefined); } + + pick>(picks: Promise | T[], options: O = {}, token?: CancellationToken): + Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { + return Promise.resolve(undefined); + } + + showQuickPick(items: Array, options?: QuickPickOptions): Promise { return Promise.resolve({} as T); } + + get backButton(): QuickInputButton { return {} as QuickInputButton; } + + open(filter: string): void { } + + reset(): void { } + + hide(): void { } +} diff --git a/packages/core/src/browser/quick-input/quick-pick-service-impl.ts b/packages/core/src/browser/quick-input/quick-pick-service-impl.ts new file mode 100644 index 0000000000000..5065482fb1909 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-pick-service-impl.ts @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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, inject, optional } from 'inversify'; +import { Emitter } from '../../common/event'; +import { QuickPickService } from '../../common/quick-pick-service'; +import { QuickInputService, QuickPickItem, QuickInputButtonHandle, QuickPick, QuickPickOptions } from './quick-input-service'; + +@injectable() +export class QuickPickServiceImpl implements QuickPickService { + + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; + + private readonly onDidHideEmitter = new Emitter(); + readonly onDidHide = this.onDidHideEmitter.event; + + private readonly onDidChangeValueEmitter = new Emitter<{ quickPick: QuickPick, filter: string }>(); + readonly onDidChangeValue = this.onDidChangeValueEmitter.event; + + private readonly onDidAcceptEmitter = new Emitter(); + readonly onDidAccept = this.onDidAcceptEmitter.event; + + private readonly onDidChangeActiveEmitter = new Emitter<{ quickPick: QuickPick, activeItems: Array }>(); + readonly onDidChangeActive = this.onDidChangeActiveEmitter.event; + + private readonly onDidChangeSelectionEmitter = new Emitter<{ quickPick: QuickPick, selectedItems: Array }>(); + readonly onDidChangeSelection = this.onDidChangeSelectionEmitter.event; + + private readonly onDidTriggerButtonEmitter = new Emitter(); + readonly onDidTriggerButton = this.onDidTriggerButtonEmitter.event; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private items: Array = []; + + async show(items: Array, options?: QuickPickOptions): Promise { + this.items = items; + const opts = Object.assign({}, options, { + onDidAccept: () => this.onDidAcceptEmitter.fire(), + onDidChangeActive: (quickPick: QuickPick, activeItems: Array) => this.onDidChangeActiveEmitter.fire({ quickPick, activeItems }), + onDidChangeSelection: (quickPick: QuickPick, selectedItems: Array) => this.onDidChangeSelectionEmitter.fire({ quickPick, selectedItems }), + onDidChangeValue: (quickPick: QuickPick, filter: string) => this.onDidChangeValueEmitter.fire({ quickPick, filter }), + onDidHide: () => this.onDidHideEmitter.fire(), + onDidTriggerButton: (btn: QuickInputButtonHandle) => this.onDidTriggerButtonEmitter.fire(btn), + }); + return this.quickInputService?.showQuickPick(this.items, opts); + } + + hide(): void { + this.quickInputService?.hide(); + } + + setItems(items: Array): void { + this.items = items; + } +} diff --git a/packages/core/src/browser/quick-input/quick-view-service.ts b/packages/core/src/browser/quick-input/quick-view-service.ts new file mode 100644 index 0000000000000..5ffd727f27f41 --- /dev/null +++ b/packages/core/src/browser/quick-input/quick-view-service.ts @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; +import { Disposable } from '../../common'; +import { QuickInputService } from './quick-input-service'; + +export interface QuickViewItem { + readonly label: string; + readonly when?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readonly open: () => any; +} + +@injectable() +export class QuickViewService extends QuickInputService { + readonly prefix = 'view '; + + registerItem(item: QuickViewItem): Disposable { + return Disposable.create(() => { }); + } + hideItem(label: string): void { } + showItem(label: string): void { } +} diff --git a/packages/core/src/browser/quick-open/prefix-quick-open-service.ts b/packages/core/src/browser/quick-open/prefix-quick-open-service.ts deleted file mode 100644 index ce424f778031b..0000000000000 --- a/packages/core/src/browser/quick-open/prefix-quick-open-service.ts +++ /dev/null @@ -1,284 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2018 Red Hat, Inc. 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, inject } from 'inversify'; -import { QuickOpenModel, QuickOpenItem, QuickOpenMode } from './quick-open-model'; -import { QuickOpenService, QuickOpenOptions } from './quick-open-service'; -import { Disposable, DisposableCollection } from '../../common/disposable'; -import { ILogger } from '../../common/logger'; -import { MaybePromise } from '../../common/types'; -import { QuickOpenActionProvider } from './quick-open-action-provider'; -import { QuickTitleBar } from './quick-title-bar'; - -export const QuickOpenContribution = Symbol('QuickOpenContribution'); -/** - * The quick open contribution should be implemented to register custom quick open handler. - */ -export interface QuickOpenContribution { - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void; -} - -/** - * A handler allows to call it's quick open model when - * the handler's prefix is typed in the quick open widget. - */ -export interface QuickOpenHandler { - - /** - * Prefix to trigger this handler's model. - */ - readonly prefix: string; - - /** - * A human-readable description of this handler. - */ - readonly description: string; - - /** - * Called immediately when the user's input in - * the quick open widget matches this handler's prefix. - * Allows to initialize the model with some initial data. - */ - init?(): MaybePromise; - - /** - * A model that should be used by the quick open widget when this handler's prefix is used. - */ - getModel(): QuickOpenModel; - - /** - * Returns the options which should be used for the quick open widget. - * Note, that the `prefix` and `skipPrefix` options are ignored and will be overridden. - * The `placeholder` option makes sense for a default handler only since it's used without a prefix in quick open widget. - */ - getOptions(): QuickOpenOptions; -} - -@injectable() -export class QuickOpenHandlerRegistry implements Disposable { - - protected readonly handlers: Map = new Map(); - protected readonly toDispose = new DisposableCollection(); - protected defaultHandler: QuickOpenHandler | undefined; - - @inject(ILogger) - protected readonly logger: ILogger; - - /** - * Register the given handler. - * Do nothing if a handler is already registered. - * @param handler the handler to register - * @param defaultHandler default means that a handler is used when the user's - * input in the quick open widget doesn't match any of known prefixes - */ - registerHandler(handler: QuickOpenHandler, defaultHandler: boolean = false): Disposable { - if (this.handlers.has(handler.prefix)) { - this.logger.warn(`A handler with prefix ${handler.prefix} is already registered.`); - return Disposable.NULL; - } - this.handlers.set(handler.prefix, handler); - const disposable = { - dispose: () => this.handlers.delete(handler.prefix) - }; - this.toDispose.push(disposable); - if (defaultHandler) { - this.defaultHandler = handler; - } - return disposable; - } - - getDefaultHandler(): QuickOpenHandler | undefined { - return this.defaultHandler; - } - - isDefaultHandler(handler: QuickOpenHandler): boolean { - return handler === this.getDefaultHandler(); - } - - /** - * Return all registered handlers. - */ - getHandlers(): QuickOpenHandler[] { - return [...this.handlers.values()]; - } - - /** - * Return a handler that matches the given text or the default handler if none. - */ - getHandlerOrDefault(text: string): QuickOpenHandler | undefined { - for (const handler of this.handlers.values()) { - if (text.startsWith(handler.prefix)) { - return handler; - } - } - return this.getDefaultHandler(); - } - - dispose(): void { - this.toDispose.dispose(); - } -} - -/** Prefix-based quick open service. */ -@injectable() -export class PrefixQuickOpenService { - - @inject(QuickOpenHandlerRegistry) - protected readonly handlers: QuickOpenHandlerRegistry; - - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; - - @inject(QuickTitleBar) - protected readonly quickTitleBar: QuickTitleBar; - - /** - * Opens a quick open widget with the model that handles the known prefixes. - * @param prefix string that may contain a prefix of some of the known quick open handlers. - * A default quick open handler will be called if the provided string doesn't match any. - * An empty quick open will be opened if there's no default handler registered. - */ - open(prefix: string): void { - const handler = this.handlers.getHandlerOrDefault(prefix); - this.setCurrentHandler(prefix, handler); - } - - protected toDisposeCurrent = new DisposableCollection(); - protected currentHandler: QuickOpenHandler | undefined; - - protected async setCurrentHandler(prefix: string, handler: QuickOpenHandler | undefined): Promise { - if (handler !== this.currentHandler) { - this.toDisposeCurrent.dispose(); - this.currentHandler = handler; - this.toDisposeCurrent.push(Disposable.create(() => { - const closingHandler = handler && handler.getOptions().onClose; - if (closingHandler) { - closingHandler(true); - } - })); - } - if (!handler) { - this.doOpen(); - return; - } - if (handler.init) { - await handler.init(); - } - let optionsPrefix = prefix; - if (this.handlers.isDefaultHandler(handler) && prefix.startsWith(handler.prefix)) { - optionsPrefix = prefix.substr(handler.prefix.length); - } - const skipPrefix = this.handlers.isDefaultHandler(handler) ? 0 : handler.prefix.length; - const handlerOptions = handler.getOptions(); - this.doOpen({ - prefix: optionsPrefix, - placeholder: "Type '?' to get help on the actions you can take from here", - skipPrefix, - ...handlerOptions - }); - } - - protected doOpen(options?: QuickOpenOptions): void { - if (this.quickTitleBar.isAttached) { - this.quickTitleBar.hide(); - } - this.quickOpenService.open({ - onType: (lookFor, acceptor) => this.onType(lookFor, acceptor) - }, options); - } - - protected onType(lookFor: string, acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider | undefined) => void): void { - const handler = this.handlers.getHandlerOrDefault(lookFor); - if (handler === undefined) { - const items: QuickOpenItem[] = []; - items.push(new QuickOpenItem({ - label: lookFor.length === 0 ? 'No default handler is registered' : `No handlers matches the prefix ${lookFor} and no default handler is registered.` - })); - acceptor(items); - } else if (handler !== this.currentHandler) { - this.setCurrentHandler(lookFor, handler); - } else { - const handlerModel = handler.getModel(); - const searchValue = this.handlers.isDefaultHandler(handler) ? lookFor : lookFor.substr(handler.prefix.length); - handlerModel.onType(searchValue, (items, actionProvider) => acceptor(items, actionProvider)); - } - } - -} - -@injectable() -export class HelpQuickOpenHandler implements QuickOpenHandler, QuickOpenContribution { - - readonly prefix: string = '?'; - readonly description: string = ''; - protected items: QuickOpenItem[]; - - @inject(QuickOpenHandlerRegistry) - protected readonly handlers: QuickOpenHandlerRegistry; - - @inject(PrefixQuickOpenService) - protected readonly quickOpenService: PrefixQuickOpenService; - - init(): void { - this.items = this.handlers.getHandlers() - .filter(handler => handler.prefix !== this.prefix) - .sort((a, b) => this.comparePrefix(a.prefix, b.prefix)) - .map(handler => new QuickOpenItem({ - label: handler.prefix, - description: handler.description, - run: (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.quickOpenService.open(handler.prefix); - return false; - } - })); - - if (this.items.length === 0) { - this.items.push(new QuickOpenItem({ - label: 'No handlers registered', - run: () => false - })); - } - } - - getModel(): QuickOpenModel { - return { - onType: (lookFor: string, acceptor: (items: QuickOpenItem[]) => void) => { - acceptor(this.items); - } - }; - } - - getOptions(): QuickOpenOptions { - return {}; - } - - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this); - } - - /** - * Compare two prefixes. - * - * @param a {string} first prefix. - * @param b {string} second prefix. - */ - protected comparePrefix(a: string, b: string): number { - return a.toLowerCase().localeCompare(b.toLowerCase()); - } -} diff --git a/packages/core/src/browser/quick-open/quick-command-service.ts b/packages/core/src/browser/quick-open/quick-command-service.ts deleted file mode 100644 index d0fc5dc3decb1..0000000000000 --- a/packages/core/src/browser/quick-open/quick-command-service.ts +++ /dev/null @@ -1,271 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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 'inversify'; -import { Command, CommandRegistry, Disposable } from '../../common'; -import { Keybinding } from '../../common/keybinding'; -import { KeybindingRegistry } from '../keybinding'; -import { QuickOpenModel, QuickOpenItem, QuickOpenGroupItemOptions, QuickOpenMode, QuickOpenGroupItem } from '../../common/quick-open-model'; -import { QuickOpenOptions } from './quick-open-service'; -import { QuickOpenContribution, QuickOpenHandlerRegistry, QuickOpenHandler } from './prefix-quick-open-service'; -import { ContextKeyService } from '../context-key-service'; -import { CLEAR_COMMAND_HISTORY } from './quick-command-contribution'; -import { CorePreferences } from '../core-preferences'; - -@injectable() -export class QuickCommandService implements QuickOpenModel, QuickOpenHandler { - - private items: QuickOpenItem[]; - - readonly prefix: string = '>'; - - readonly description: string = 'Quick Command'; - - // The list of exempted commands not to be displayed in the recently used list. - readonly exemptedCommands: Command[] = [ - CLEAR_COMMAND_HISTORY, - ]; - - @inject(CommandRegistry) - protected readonly commands: CommandRegistry; - - @inject(KeybindingRegistry) - protected readonly keybindings: KeybindingRegistry; - - @inject(ContextKeyService) - protected readonly contextKeyService: ContextKeyService; - - @inject(CorePreferences) - protected readonly corePreferences: CorePreferences; - - protected readonly contexts = new Map(); - pushCommandContext(commandId: string, when: string): Disposable { - const contexts = this.contexts.get(commandId) || []; - contexts.push(when); - this.contexts.set(commandId, contexts); - return Disposable.create(() => { - const index = contexts.indexOf(when); - if (index !== -1) { - contexts.splice(index, 1); - } - }); - } - - /** Initialize this quick open model with the commands. */ - init(): void { - // let's compute the items here to do it in the context of the currently activeElement - this.items = []; - const { recent, other } = this.getCommands(); - this.items.push( - ...recent.map((command, index) => - this.createCommandQuickOpenItem( - command, - { - groupLabel: index === 0 ? 'recently used' : '', - showBorder: false, - } - ) - ), - ...other.map((command, index) => - this.createCommandQuickOpenItem( - command, - { - groupLabel: recent.length > 0 && index === 0 ? 'other commands' : '', - showBorder: recent.length > 0 && index === 0 ? true : false, - } - ) - ), - ); - } - - protected createCommandQuickOpenItem(command: Command, commandOptions?: QuickOpenGroupItemOptions | undefined): CommandQuickOpenItem { - return new CommandQuickOpenItem(command, this.commands, this.keybindings, commandOptions); - } - - public onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(this.items); - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { fuzzyMatchLabel: true }; - } - - /** - * Get the list of recently used and other commands. - * - * @returns the list of recently used commands and other commands. - */ - private getCommands(): { recent: Command[], other: Command[] } { - - // Get the list of recent commands. - const recentCommands: Command[] = this.commands.recent; - - // Get the list of all valid commands. - const allCommands: Command[] = this.getValidCommands(this.commands.commands); - - // Get the max history limit. - const limit: number = this.corePreferences['workbench.commandPalette.history']; - - // Build the list of recent commands. - const rCommands: Command[] = []; - recentCommands.forEach((r: Command) => { - // Opt out of displaying the recently used list. - if (limit === 0) { - return; - } - // Determine if the command is exempted from display. - const exempted: boolean = this.exemptedCommands.some((c: Command) => Command.equals(r, c)); - // Determine if the command currently exists in the list of all available commands. - const exists: boolean = allCommands.some((c: Command) => Command.equals(r, c)); - // Add the recently used item to the list. - if (exists && !exempted && rCommands.length < limit) { - rCommands.push(r); - } - }); - - // Build the list of other commands. - const oCommands: Command[] = []; - allCommands.forEach((a: Command) => { - const exists = rCommands.some((c: Command) => Command.equals(a, c)); - // If the command does not exist in the recently used list, add it to the other list. - if (!exists) { oCommands.push(a); } - }); - - // Normalize the list of recent commands. - const recent: Command[] = this.normalize(rCommands); - - // Normalize, and sort the list of other commands. - const other: Command[] = this.sort( - this.normalize(oCommands) - ); - - return { recent, other }; - } - - /** - * Normalizes a list of commands. - * Normalization includes obtaining commands that have labels, are visible, and are enabled. - * - * @param commands the list of commands. - * @returns the list of normalized commands. - */ - private normalize(commands: Command[]): Command[] { - return commands.filter((a: Command) => a.label && (this.commands.isVisible(a.id) && this.commands.isEnabled(a.id))); - } - - /** - * Sorts a list of commands alphabetically. - * - * @param commands the list of commands. - * @returns the list of sorted commands. - */ - private sort(commands: Command[]): Command[] { - return commands.sort((a: Command, b: Command) => Command.compareCommands(a, b)); - } - - /** - * Get the list of valid commands. - * - * @param commands the list of raw commands. - * @returns the list of valid commands. - */ - private getValidCommands(raw: Command[]): Command[] { - const valid: Command[] = []; - raw.forEach((command: Command) => { - if (command.label) { - const contexts = this.contexts.get(command.id); - if (!contexts || contexts.some(when => { - when = when.trim(); - return when === 'true' || this.contextKeyService.match(when); - })) { - valid.push(command); - } - } - }); - return valid; - } - -} - -export class CommandQuickOpenItem extends QuickOpenGroupItem { - - private activeElement: HTMLElement; - private hidden: boolean; - - constructor( - protected readonly command: Command, - protected readonly commands: CommandRegistry, - protected readonly keybindings: KeybindingRegistry, - protected readonly commandOptions?: QuickOpenGroupItemOptions, - ) { - super(commandOptions); - this.activeElement = window.document.activeElement as HTMLElement; - this.hidden = !this.commands.getActiveHandler(this.command.id); - } - - getLabel(): string { - return (this.command.category) - ? `${this.command.category}: ` + this.command.label! - : this.command.label!; - } - - isHidden(): boolean { - return this.hidden; - } - - getIconClass(): string | undefined { - const toggledHandler = this.commands.getToggledHandler(this.command.id); - if (toggledHandler) { - return 'fa fa-check'; - } - return super.getIconClass(); - } - - getKeybinding(): Keybinding | undefined { - const bindings = this.keybindings.getKeybindingsForCommand(this.command.id); - return bindings ? bindings[0] : undefined; - } - - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - // allow the quick open widget to close itself - setTimeout(() => { - // reset focus on the previously active element. - this.activeElement.focus({ preventScroll: true }); - this.commands.executeCommand(this.command.id); - - this.commands.addRecentCommand(this.command); - }, 50); - return true; - } -} - -@injectable() -export class CommandQuickOpenContribution implements QuickOpenContribution { - - @inject(QuickCommandService) - protected readonly commandQuickOpenHandler: QuickCommandService; - - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this.commandQuickOpenHandler); - } -} diff --git a/packages/core/src/browser/quick-open/quick-input-service.ts b/packages/core/src/browser/quick-open/quick-input-service.ts deleted file mode 100644 index ac76cfb067bff..0000000000000 --- a/packages/core/src/browser/quick-open/quick-input-service.ts +++ /dev/null @@ -1,200 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2018 TypeFox 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 'inversify'; -import { QuickOpenService } from './quick-open-service'; -import { QuickOpenItem, QuickOpenMode } from './quick-open-model'; -import { Deferred } from '../../common/promise-util'; -import { MaybePromise } from '../../common/types'; -import { MessageType } from '../../common/message-service-protocol'; -import { Emitter, Event } from '../../common/event'; -import { QuickTitleBar } from './quick-title-bar'; -import { QuickTitleButton } from '../../common/quick-open-model'; -import { CancellationToken } from '../../common/cancellation'; - -export interface QuickInputOptions { - - /** - * Show the progress indicator if true - */ - busy?: boolean - - /** - * Allow user input - */ - enabled?: boolean; - - /** - * Current step count - */ - step?: number | undefined - - /** - * The title of the input - */ - title?: string | undefined - - /** - * Total number of steps - */ - totalSteps?: number | undefined - - /** - * Buttons that are displayed on the title panel - */ - buttons?: ReadonlyArray - - /** - * Text for when there is a problem with the current input value - */ - validationMessage?: string | undefined; - - /** - * The prefill value. - */ - value?: string; - - /** - * The text to display under the input box. - */ - prompt?: string; - - /** - * The place holder in the input box to guide the user what to type. - */ - placeHolder?: string; - - /** - * Set to `true` to show a password prompt that will not show the typed value. - */ - password?: boolean; - - /** - * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. - */ - ignoreFocusOut?: boolean; - - /** - * Selection of the prefilled [`value`](#InputBoxOptions.value). Defined as tuple of two number where the - * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole - * word will be selected, when empty (start equals end) only the cursor will be set, - * otherwise the defined range will be selected. - */ - valueSelection?: [number, number] - - /** - * An optional function that will be called to validate input and to give a hint - * to the user. - * - * @param value The current value of the input box. - * @return Return `undefined`, or the empty string when 'value' is valid. - */ - validateInput?(value: string): MaybePromise; -} - -@injectable() -export class QuickInputService { - - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; - - @inject(QuickTitleBar) - protected readonly quickTitleBar: QuickTitleBar; - - open(options: QuickInputOptions, token: CancellationToken = CancellationToken.None): Promise { - const result = new Deferred(); - const prompt = this.createPrompt(options.prompt); - let label = prompt; - let currentText = ''; - const validateInput = options && options.validateInput; - let initial: boolean = true; - - const toDispose = token.onCancellationRequested(() => - this.quickOpenService.hide() - ); - const resolve = (value: string | undefined) => { - toDispose.dispose(); - result.resolve(value); - this.quickTitleBar.hide(); - }; - this.quickOpenService.open({ - onType: async (lookFor, acceptor) => { - let error: string | undefined; - if (initial) { - initial = false; - } else { - this.onDidChangeValueEmitter.fire(lookFor); - error = validateInput && lookFor !== undefined ? await validateInput(lookFor) : undefined; - } - label = error || prompt; - if (error) { - this.quickOpenService.showDecoration(MessageType.Error); - } else { - this.quickOpenService.hideDecoration(); - } - acceptor([new QuickOpenItem({ - label, - run: mode => { - if (!error && mode === QuickOpenMode.OPEN) { - this.onDidAcceptEmitter.fire(undefined); - resolve(currentText); - return true; - } - return false; - } - })]); - currentText = lookFor; - } - }, { - prefix: options.value, - placeholder: options.placeHolder, - password: options.password, - ignoreFocusOut: options.ignoreFocusOut, - enabled: options.enabled, - valueSelection: options.valueSelection, - onClose: () => { - result.resolve(undefined); - this.quickTitleBar.hide(); - } - }); - - if (options && this.quickTitleBar.shouldShowTitleBar(options.title, options.step)) { - this.quickTitleBar.attachTitleBar(this.quickOpenService.widgetNode, options.title, options.step, options.totalSteps, options.buttons); - } - - return result.promise; - } - - refresh(): void { - this.quickOpenService.refresh(); - } - - protected defaultPrompt = "Press 'Enter' to confirm your input or 'Escape' to cancel"; - protected createPrompt(prompt?: string): string { - return prompt ? `${prompt} (${this.defaultPrompt})` : this.defaultPrompt; - } - - readonly onDidAcceptEmitter: Emitter = new Emitter(); - get onDidAccept(): Event { - return this.onDidAcceptEmitter.event; - } - - readonly onDidChangeValueEmitter: Emitter = new Emitter(); - get onDidChangeValue(): Event { - return this.onDidChangeValueEmitter.event; - } - -} diff --git a/packages/core/src/browser/quick-open/quick-open-action-provider.ts b/packages/core/src/browser/quick-open/quick-open-action-provider.ts deleted file mode 100644 index 565f0c79ae485..0000000000000 --- a/packages/core/src/browser/quick-open/quick-open-action-provider.ts +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 Red Hat, Inc. 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 'inversify'; -import { QuickOpenItem } from '../../common/quick-open-model'; -import * as common from '../../common/quick-open-model'; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenActionProvider = common.QuickOpenActionProvider; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenActionOptions = common.QuickOpenActionOptions; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenAction = common.QuickOpenAction; - -@injectable() -export abstract class QuickOpenBaseAction implements QuickOpenAction { - constructor(protected options: QuickOpenActionOptions) { - } - - get id(): string { - return this.options.id; - } - - get label(): string { - return this.options.label || ''; - } - - set label(value: string) { - this.options.label = value; - } - - get tooltip(): string { - return this.options.tooltip || ''; - } - - set tooltip(value: string) { - this.options.tooltip = value; - } - - get class(): string | undefined { - return this.options.class || ''; - } - - set class(value: string | undefined) { - this.options.class = value; - } - - get enabled(): boolean { - return this.options.enabled || true; - } - - set enabled(value: boolean) { - this.options.enabled = value; - } - - get checked(): boolean { - return this.options.checked || false; - } - - set checked(value: boolean) { - this.options.checked = value; - } - - abstract run(item?: QuickOpenItem): Promise; - - dispose(): void { } -} diff --git a/packages/core/src/browser/quick-open/quick-open-model.ts b/packages/core/src/browser/quick-open/quick-open-model.ts deleted file mode 100644 index 0f04e4e61622b..0000000000000 --- a/packages/core/src/browser/quick-open/quick-open-model.ts +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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 * as common from '../../common/quick-open-model'; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type Highlight = common.Highlight; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenItemOptions = common.QuickOpenItemOptions; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenGroupItemOptions = common.QuickOpenGroupItemOptions; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export { QuickOpenItem, QuickOpenGroupItem, QuickOpenMode } from '../../common/quick-open-model'; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-model` instead - */ -export type QuickOpenModel = common.QuickOpenModel; diff --git a/packages/core/src/browser/quick-open/quick-open-service.ts b/packages/core/src/browser/quick-open/quick-open-service.ts deleted file mode 100644 index 5a7299d8b3dbe..0000000000000 --- a/packages/core/src/browser/quick-open/quick-open-service.ts +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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 'inversify'; -import { QuickOpenModel } from './quick-open-model'; -import { MessageType } from '../../common/message-service-protocol'; -import * as common from '../../common/quick-open-service'; -import { QuickOpenItem } from '../../common/quick-open-model'; -import { Emitter } from '../../common/event'; - -/** - * @deprecated import from `@theia/core/lib/common/quick-open-service` instead - */ -export { QuickOpenOptions } from '../../common/quick-open-service'; - -@injectable() -export class QuickOpenService { - - /** - * Dom node of the QuickOpenWidget - */ - widgetNode: HTMLElement; - - protected readonly onDidChangeActiveEmitter = new Emitter(); - readonly onDidChangeActive = this.onDidChangeActiveEmitter.event; - getActive(): QuickOpenItem[] { - return []; - } - - /** - * It should be implemented by an extension, e.g. by the monaco extension. - */ - open(model: QuickOpenModel, options?: common.QuickOpenOptions): void { } - hide(reason?: common.QuickOpenHideReason): void { } - showDecoration(type: MessageType): void { } - hideDecoration(): void { } - refresh(): void { } - -} diff --git a/packages/core/src/browser/quick-open/quick-pick-service-impl.ts b/packages/core/src/browser/quick-open/quick-pick-service-impl.ts deleted file mode 100644 index 39f1eb7692f04..0000000000000 --- a/packages/core/src/browser/quick-open/quick-pick-service-impl.ts +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2018 TypeFox 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, inject, postConstruct } from 'inversify'; -import { QuickOpenItem, QuickOpenMode, QuickOpenGroupItem, QuickOpenItemOptions } from './quick-open-model'; -import { QuickOpenService } from './quick-open-service'; -import { QuickPickService, QuickPickOptions, QuickPickItem, QuickPickSeparator, QuickPickValue } from '../../common/quick-pick-service'; -import { QuickOpenHideReason } from '../../common/quick-open-service'; -import { QuickTitleBar } from './quick-title-bar'; -import { Emitter } from '../../common/event'; - -@injectable() -export class QuickPickServiceImpl implements QuickPickService { - - @inject(QuickTitleBar) - protected readonly quickTitleBar: QuickTitleBar; - - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; - - private readonly onDidChangeValueEmitter = new Emitter(); - readonly onDidChangeValue = this.onDidChangeValueEmitter.event; - - private readonly onDidAcceptEmitter = new Emitter(); - readonly onDidAccept = this.onDidAcceptEmitter.event; - - private readonly onDidChangeActiveEmitter = new Emitter<(string | QuickPickValue)[]>(); - readonly onDidChangeActive = this.onDidChangeActiveEmitter.event; - - private readonly onDidChangeSelectionEmitter = new Emitter<(string | QuickPickValue)[]>(); - readonly onDidChangeSelection = this.onDidChangeSelectionEmitter.event; - - // TODO when to release last elements? - private elements: (string | QuickPickItem)[] = []; - - @postConstruct() - protected init(): void { - this.quickOpenService.onDidChangeActive(() => { - const active: (string | QuickPickValue)[] = []; - for (const item of this.quickOpenService.getActive()) { - if ('element' in item) { - active.push(item['element']); - } - } - this.onDidChangeActiveEmitter.fire(active); - }); - } - - show(elements: string[], options?: QuickPickOptions): Promise; - show(elements: QuickPickItem[], options?: QuickPickOptions): Promise; - async show(elements: (string | QuickPickItem)[], options?: QuickPickOptions): Promise { - // Set `runIfSingle` to the value passed through options, else defaults to true. - const runIfSingle: boolean = (options && options.runIfSingle !== undefined) ? options.runIfSingle : true; - return new Promise(resolve => { - this.elements = elements; - let items = this.toItems(elements, resolve); - if (runIfSingle && items.length === 0) { - resolve(undefined); - return; - } - if (runIfSingle && items.length === 1) { - items[0].run(QuickOpenMode.OPEN); - return; - } - const prefix = options && options.value ? options.value : ''; - let savedValue: string; - this.quickOpenService.open({ - onType: (value, acceptor) => { - if (this.elements !== elements) { - elements = this.elements; - items = this.toItems(elements, resolve); - } - acceptor(items); - if (savedValue !== value) { - this.onDidChangeValueEmitter.fire(value); - savedValue = value; - } - } - }, Object.assign({ - onClose: () => { - resolve(undefined); - this.quickTitleBar.hide(); - }, - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, - prefix - }, options)); - if (options && this.quickTitleBar.shouldShowTitleBar(options.title, options.step)) { - this.quickTitleBar.attachTitleBar(this.quickOpenService.widgetNode, options.title, options.step, options.totalSteps, options.buttons); - } - }); - } - protected toItems(elements: (string | QuickPickItem)[], resolve: (element: Object) => void): QuickOpenItem[] { - const items: QuickOpenItem[] = []; - let groupLabel: string | undefined; - for (const element of elements) { - if (QuickPickSeparator.is(element)) { - groupLabel = element.label; - } else { - const options = this.toItemOptions(element, resolve); - let item; - if (groupLabel) { - item = new QuickOpenGroupItem(Object.assign(options, { groupLabel, showBorder: true })); - groupLabel = undefined; - } else { - item = new QuickOpenItem(options); - } - items.push(Object.assign(item, { element })); - } - } - return items; - } - protected toItemOptions(element: string | QuickPickValue, resolve: (element: Object) => void): QuickOpenItemOptions { - const label = typeof element === 'string' ? element : element.label; - const value = typeof element === 'string' ? element : element.value; - const description = typeof element === 'string' ? undefined : element.description; - const detail = typeof element === 'string' ? undefined : element.detail; - const iconClass = typeof element === 'string' ? undefined : element.iconClass; - return { - label, - description, - detail, - iconClass, - run: mode => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.onDidChangeSelectionEmitter.fire([element]); - this.onDidChangeActiveEmitter.fire([element]); - this.onDidAcceptEmitter.fire(undefined); - resolve(value); - return true; - } - }; - } - - hide(reason?: QuickOpenHideReason): void { - this.quickOpenService.hide(reason); - } - - setItems(elements: QuickPickItem[]): void { - this.elements = elements; - this.quickOpenService.refresh(); - } - -} diff --git a/packages/core/src/browser/quick-open/quick-pick-service.ts b/packages/core/src/browser/quick-open/quick-pick-service.ts deleted file mode 100644 index d03cfc2a060d7..0000000000000 --- a/packages/core/src/browser/quick-open/quick-pick-service.ts +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2018 TypeFox 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 * as common from '../../common/quick-pick-service'; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export const QuickPickService = common.QuickPickService; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export type QuickPickService = common.QuickPickService; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export type QuickPickOptions = common.QuickPickOptions; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export type QuickPickItem = common.QuickPickItem; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export type QuickPickSeparator = common.QuickPickSeparator; -/** - * @deprecated import from `@theia/core/lib/common/quick-pick-service` instead - */ -export type QuickPickValue = common.QuickPickValue; diff --git a/packages/core/src/browser/quick-open/quick-title-bar.ts b/packages/core/src/browser/quick-open/quick-title-bar.ts deleted file mode 100644 index d7eab677812ed..0000000000000 --- a/packages/core/src/browser/quick-open/quick-title-bar.ts +++ /dev/null @@ -1,234 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 Red Hat, Inc. 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 { Emitter, Event } from '../../common/event'; -import { injectable } from 'inversify'; -import { QuickTitleButton, QuickTitleButtonSide } from '../../common/quick-open-model'; - -@injectable() -export class QuickTitleBar { - - private readonly onDidTriggerButtonEmitter: Emitter; - private _isAttached: boolean; - - private titleElement: HTMLElement; - private titleBarContainer: HTMLElement; - private attachedNode: HTMLElement | undefined; - - private _title: string | undefined; - private _step: number | undefined; - private _totalSteps: number | undefined; - private _buttons: ReadonlyArray; - - private tabIndex = 2; // Keep track of the tabIndex for the buttons - - constructor() { - this.titleElement = document.createElement('div'); - this.titleElement.className = QuickTitleBar.Styles.QUICK_TITLE_HEADER; - - this.onDidTriggerButtonEmitter = new Emitter(); - } - - get onDidTriggerButton(): Event { - return this.onDidTriggerButtonEmitter.event; - } - - get isAttached(): boolean { - return this._isAttached; - } - - set isAttached(isAttached: boolean) { - this._isAttached = isAttached; - } - - set title(title: string | undefined) { - this._title = title; - this.updateInnerTitleText(); - } - - get title(): string | undefined { - return this._title; - } - - set step(step: number | undefined) { - this._step = step; - this.updateInnerTitleText(); - } - - get step(): number | undefined { - return this._step; - } - - set totalSteps(totalSteps: number | undefined) { - this._totalSteps = totalSteps; - this.updateInnerTitleText(); - } - - get totalSteps(): number | undefined { - return this._totalSteps; - } - - set buttons(buttons: ReadonlyArray | undefined) { - if (buttons === undefined) { - this._buttons = []; - return; - } - - this._buttons = buttons; - } - - get buttons(): ReadonlyArray | undefined { - return this._buttons; - } - - private updateInnerTitleText(): void { - let innerTitle = ''; - - if (this.title) { - innerTitle = this.title + ' '; - } - - if (this.step && this.totalSteps) { - innerTitle += `(${this.step}/${this.totalSteps})`; - } else if (this.step) { - innerTitle += this.step; - } - - this.titleElement.innerText = innerTitle; - } - - // Left buttons are for the buttons derived from QuickInputButtons - private getLeftButtons(): ReadonlyArray { - if (this._buttons === undefined || this._buttons.length === 0) { - return []; - } - return this._buttons.filter(btn => btn.side === QuickTitleButtonSide.LEFT); - } - - private getRightButtons(): ReadonlyArray { - if (this._buttons === undefined || this._buttons.length === 0) { - return []; - } - return this._buttons.filter(btn => btn.side === QuickTitleButtonSide.RIGHT); - } - - private createButtonElements(buttons: ReadonlyArray): HTMLSpanElement[] { - return buttons.map(btn => { - const spanElement = document.createElement('span'); - spanElement.className = QuickTitleBar.Styles.QUICK_TITLE_BUTTON; - spanElement.tabIndex = 0; - if (btn.iconClass) { - spanElement.classList.add(...btn.iconClass.split(' ')); - } - - if (btn.icon !== '') { - spanElement.style.backgroundImage = `url(\'${btn.icon}\')`; - } - - spanElement.classList.add('icon'); - spanElement.tabIndex = this.tabIndex; - spanElement.title = btn.tooltip ? btn.tooltip : ''; - spanElement.onclick = () => { - this.onDidTriggerButtonEmitter.fire(btn); - }; - spanElement.onkeyup = event => { - if (event.code === 'Enter') { - spanElement.click(); - } - }; - this.tabIndex += 1; - return spanElement; - }); - } - - private createTitleBarDiv(): HTMLDivElement { - const div = document.createElement('div'); - div.className = QuickTitleBar.Styles.QUICK_TITLE_CONTAINER; - div.onclick = event => { - event.stopPropagation(); - event.preventDefault(); - }; - return div; - } - - private createLeftButtonDiv(): HTMLDivElement { - const leftButtonDiv = document.createElement('div'); // Holds all the buttons that get added to the left - leftButtonDiv.className = QuickTitleBar.Styles.QUICK_TITLE_LEFT_BAR; - - this.createButtonElements(this.getLeftButtons()).forEach(btn => leftButtonDiv.appendChild(btn)); - return leftButtonDiv; - } - - private createRightButtonDiv(): HTMLDivElement { - const rightButtonDiv = document.createElement('div'); - rightButtonDiv.className = QuickTitleBar.Styles.QUICK_TITLE_RIGHT_BAR; - - this.createButtonElements(this.getRightButtons()).forEach(btn => rightButtonDiv.appendChild(btn)); - return rightButtonDiv; - } - - // eslint-disable-next-line max-len - public attachTitleBar(widgetNode: HTMLElement, title: string | undefined, step: number | undefined, totalSteps: number | undefined, buttons: ReadonlyArray | undefined): void { - const div = this.createTitleBarDiv(); - - this.updateInnerTitleText(); - - this.title = title; - this.step = step; - this.totalSteps = totalSteps; - this.buttons = buttons; - - div.appendChild(this.createLeftButtonDiv()); - div.appendChild(this.titleElement); - div.appendChild(this.createRightButtonDiv()); - - if (widgetNode.contains(this.titleBarContainer)) { - widgetNode.removeChild(this.titleBarContainer); - } - widgetNode.prepend(div); - - this.titleBarContainer = div; - this.attachedNode = widgetNode; - this.isAttached = true; - } - - hide(): void { - this.title = undefined; - this.buttons = undefined; - this.step = undefined; - this.totalSteps = undefined; - this.isAttached = false; - if (this.attachedNode && this.attachedNode.contains(this.titleBarContainer)) { - this.attachedNode.removeChild(this.titleBarContainer); - } - this.attachedNode = undefined; - } - - shouldShowTitleBar(title: string | undefined, step: number | undefined): boolean { - return ((title !== undefined) || (step !== undefined)); - } - -} - -export namespace QuickTitleBar { - export namespace Styles { - export const QUICK_TITLE_CONTAINER = 'theia-quick-title-container'; - export const QUICK_TITLE_LEFT_BAR = 'theia-quick-title-left-bar'; - export const QUICK_TITLE_RIGHT_BAR = 'theia-quick-title-right-bar'; - export const QUICK_TITLE_HEADER = 'theia-quick-title-header'; - export const QUICK_TITLE_BUTTON = 'theia-quick-title-button'; - } -} diff --git a/packages/core/src/browser/quick-view-service.ts b/packages/core/src/browser/quick-view-service.ts deleted file mode 100644 index 76aa498e4bb87..0000000000000 --- a/packages/core/src/browser/quick-view-service.ts +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 TypeFox 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, inject } from 'inversify'; -import { QuickOpenHandler, QuickOpenOptions, QuickOpenItem, QuickOpenMode, QuickOpenContribution, QuickOpenHandlerRegistry } from './quick-open'; -import { Disposable } from '../common/disposable'; -import { ContextKeyService } from './context-key-service'; -import { QuickOpenModel } from '../common/quick-open-model'; - -export interface QuickViewItem { - readonly label: string; - readonly when?: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - readonly open: () => any; -} - -@injectable() -export class QuickViewService implements QuickOpenModel, QuickOpenHandler, QuickOpenContribution { - // hiddenItemLabels contains item labels hidden from Quick View - private hiddenItemLabels = new Set(); - readonly prefix: string = 'view '; - readonly description: string = 'Open View'; - protected readonly items: (QuickOpenItem & { when?: string })[] = []; - - @inject(ContextKeyService) - protected readonly contextKexService: ContextKeyService; - - registerItem(item: QuickViewItem): Disposable { - const quickOpenItem = Object.assign(new QuickOpenItem({ - label: item.label, - run: mode => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - item.open(); - return true; - } - }), { when: item.when }); - this.items.push(quickOpenItem); - this.items.sort((a, b) => a.getLabel()!.localeCompare(b.getLabel()!)); - - return Disposable.create(() => { - const index = this.items.indexOf(quickOpenItem); - if (index !== -1) { - this.items.splice(index, 1); - } - }); - } - - hideItem(label: string): void { - this.hiddenItemLabels.add(label); - } - - showItem(label: string): void { - this.hiddenItemLabels.delete(label); - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - skipPrefix: this.prefix.length, - fuzzyMatchLabel: true - }; - } - - onType(_: string, acceptor: (items: QuickOpenItem[]) => void): void { - const items = this.items.filter(item => - (item.when === undefined || this.contextKexService.match(item.when)) && - (!this.hiddenItemLabels.has(item.getLabel())) - ); - acceptor(items); - } - - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this); - } -} diff --git a/packages/core/src/browser/shell/view-contribution.ts b/packages/core/src/browser/shell/view-contribution.ts index 846f9858e88c1..3aa1ca58a0998 100644 --- a/packages/core/src/browser/shell/view-contribution.ts +++ b/packages/core/src/browser/shell/view-contribution.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject, interfaces } from 'inversify'; +import { injectable, inject, interfaces, optional } from 'inversify'; import { Widget } from '@phosphor/widgets'; import { MenuModelRegistry, Command, CommandContribution, @@ -24,7 +24,7 @@ import { KeybindingContribution, KeybindingRegistry } from '../keybinding'; import { WidgetManager } from '../widget-manager'; import { CommonMenus } from '../common-frontend-contribution'; import { ApplicationShell } from './application-shell'; -import { QuickViewService } from '../quick-view-service'; +import { QuickViewService } from '../quick-input'; export interface OpenViewArguments extends ApplicationShell.WidgetOptions { toggle?: boolean @@ -59,7 +59,7 @@ export abstract class AbstractViewContribution implements Comm @inject(WidgetManager) protected readonly widgetManager: WidgetManager; @inject(ApplicationShell) protected readonly shell: ApplicationShell; - @inject(QuickViewService) + @inject(QuickViewService) @optional() protected readonly quickView: QuickViewService; readonly toggleCommand?: Command; @@ -140,7 +140,7 @@ export abstract class AbstractViewContribution implements Comm execute: () => this.toggleView() }); } - this.quickView.registerItem({ + this.quickView?.registerItem({ label: this.viewLabel, open: () => this.openView({ activate: true }) }); diff --git a/packages/core/src/browser/theming.ts b/packages/core/src/browser/theming.ts index 107a6d59f4d94..36f4a648d0d35 100644 --- a/packages/core/src/browser/theming.ts +++ b/packages/core/src/browser/theming.ts @@ -44,7 +44,7 @@ export class ThemeService { protected activeTheme: Theme | undefined; protected readonly themeChange = new Emitter(); - readonly onThemeChange: Event = this.themeChange.event; + readonly onDidColorThemeChange: Event = this.themeChange.event; static get(): ThemeService { const global = window as any; // eslint-disable-line @typescript-eslint/no-explicit-any diff --git a/packages/core/src/common/quick-open-model.ts b/packages/core/src/common/quick-open-model.ts deleted file mode 100644 index c4aa973e38237..0000000000000 --- a/packages/core/src/common/quick-open-model.ts +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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 URI from './uri'; -import { Keybinding } from './keybinding'; -import { Disposable } from './disposable'; - -export interface Highlight { - start: number - end: number -} - -export enum QuickOpenMode { - PREVIEW, - OPEN, - OPEN_IN_BACKGROUND -} - -export interface QuickOpenItemOptions { - tooltip?: string; - label?: string; - labelHighlights?: Highlight[]; - description?: string; - descriptionHighlights?: Highlight[]; - detail?: string; - detailHighlights?: Highlight[]; - hidden?: boolean; - uri?: URI; - iconClass?: string; - keybinding?: Keybinding; - run?(mode: QuickOpenMode): boolean; -} -export interface QuickOpenGroupItemOptions extends QuickOpenItemOptions { - groupLabel?: string; - showBorder?: boolean; -} - -export class QuickOpenItem { - - constructor( - protected options: T = {} as T - ) { } - - getTooltip(): string | undefined { - return this.options.tooltip || this.getLabel(); - } - getLabel(): string | undefined { - return this.options.label; - } - getLabelHighlights(): Highlight[] { - return this.options.labelHighlights || []; - } - getDescription(): string | undefined { - return this.options.description; - } - getDescriptionHighlights(): Highlight[] | undefined { - return this.options.descriptionHighlights; - } - getDetail(): string | undefined { - return this.options.detail; - } - getDetailHighlights(): Highlight[] | undefined { - return this.options.detailHighlights; - } - isHidden(): boolean { - return this.options.hidden || false; - } - getUri(): URI | undefined { - return this.options.uri; - } - getIconClass(): string | undefined { - return this.options.iconClass; - } - getKeybinding(): Keybinding | undefined { - return this.options.keybinding; - } - run(mode: QuickOpenMode): boolean { - if (!this.options.run) { - return false; - } - return this.options.run(mode); - } -} - -export class QuickOpenGroupItem extends QuickOpenItem { - - getGroupLabel(): string | undefined { - return this.options.groupLabel; - } - showBorder(): boolean { - return this.options.showBorder || false; - } -} - -export interface QuickOpenModel { - onType(lookFor: string, acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider) => void): void; -} - -export interface QuickOpenActionProvider { - hasActions(item: QuickOpenItem): boolean; - getActions(item: QuickOpenItem): ReadonlyArray; -} - -export interface QuickOpenActionOptions { - id: string; - label?: string; - tooltip?: string; - class?: string | undefined; - enabled?: boolean; - checked?: boolean; -} - -export interface QuickOpenAction extends QuickOpenActionOptions, Disposable { - run(item?: QuickOpenItem): Promise; -} - -export enum QuickTitleButtonSide { - LEFT = 0, - RIGHT = 1 -} - -export interface QuickTitleButton { - icon: string; // a background image coming from a url - iconClass?: string; // a class such as one coming from font awesome - tooltip?: string | undefined; - side: QuickTitleButtonSide -} diff --git a/packages/core/src/common/quick-open-service.ts b/packages/core/src/common/quick-open-service.ts deleted file mode 100644 index 78ab39377caf2..0000000000000 --- a/packages/core/src/common/quick-open-service.ts +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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 enum QuickOpenHideReason { - ELEMENT_SELECTED, - FOCUS_LOST, - CANCELED, -} - -export type QuickOpenOptions = Partial; -export namespace QuickOpenOptions { - export interface FuzzyMatchOptions { - /** - * Default: `false` - */ - enableSeparateSubstringMatching?: boolean - } - export interface Resolved { - readonly enabled: boolean; - - /** `true` means that input of quick open widget will be trimmed by default. */ - readonly trimInput: boolean; - readonly prefix: string; - readonly placeholder: string; - readonly ignoreFocusOut: boolean; - readonly valueSelection: Readonly<[number, number]>; - - readonly fuzzyMatchLabel: boolean | FuzzyMatchOptions; - readonly fuzzyMatchDetail: boolean | FuzzyMatchOptions; - readonly fuzzyMatchDescription: boolean | FuzzyMatchOptions; - readonly fuzzySort: boolean; - - /** The amount of first symbols to be ignored by quick open widget (e.g. don't affect matching). */ - readonly skipPrefix: number; - - /** - * Whether to display the items that don't have any highlight. - */ - readonly showItemsWithoutHighlight: boolean; - - /** - * `true` if the quick open widget provides a way for the user to securely enter a password. - * Otherwise, `false`. - */ - readonly password: boolean; - - selectIndex(lookFor: string): number; - - onClose(canceled: boolean): void; - } - export const defaultOptions: Resolved = Object.freeze({ - enabled: true, - - trimInput: true, - prefix: '', - placeholder: '', - ignoreFocusOut: false, - valueSelection: [-1, -1] as Readonly<[number, number]>, - - fuzzyMatchLabel: false, - fuzzyMatchDetail: false, - fuzzyMatchDescription: false, - fuzzySort: false, - - skipPrefix: 0, - - showItemsWithoutHighlight: false, - password: false, - - onClose: () => { /* no-op*/ }, - - selectIndex: () => -1 - }); - export function resolve(options: QuickOpenOptions = {}, source: Resolved = defaultOptions): Resolved { - return Object.assign({}, source, options); - } -} diff --git a/packages/core/src/common/quick-pick-service.ts b/packages/core/src/common/quick-pick-service.ts index d7b70d2282abe..801e88d664cca 100644 --- a/packages/core/src/common/quick-pick-service.ts +++ b/packages/core/src/common/quick-pick-service.ts @@ -13,92 +13,21 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { QuickOpenHideReason } from './quick-open-service'; -import { QuickTitleButton } from './quick-open-model'; import { Event } from './event'; - -export type QuickPickItem = QuickPickValue | QuickPickSeparator; - -export interface QuickPickSeparator { - type: 'separator' - label: string -} -export namespace QuickPickSeparator { - export function is(item: string | QuickPickItem): item is QuickPickSeparator { - return typeof item === 'object' && 'type' in item && item['type'] === 'separator'; - } -} - -export interface QuickPickValue { - label: string - value: T - description?: string - detail?: string - iconClass?: string -} - -export interface QuickPickOptions { - placeholder?: string - /** - * default: true - */ - fuzzyMatchLabel?: boolean - /** - * default: true - */ - fuzzyMatchDescription?: boolean - - /** - * Current step count - */ - step?: number | undefined - - /** - * The title of the input - */ - title?: string | undefined - - /** - * Total number of steps - */ - totalSteps?: number | undefined - - /** - * Buttons that are displayed on the title panel - */ - buttons?: ReadonlyArray - - /** - * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. - */ - ignoreFocusOut?: boolean - - /** - * The prefill value. - */ - value?: string; - - /** - * Determines if the quick pick with a single item should - * execute the item instead of displaying. The default is `true`. - */ - runIfSingle?: boolean; -} +import { QuickInputButtonHandle, QuickPick, QuickPickItem, QuickPickOptions } from '../browser/quick-input/quick-input-service'; export const quickPickServicePath = '/services/quickPick'; export const QuickPickService = Symbol('QuickPickService'); export interface QuickPickService { - show(elements: string[], options?: QuickPickOptions): Promise; - - show(elements: QuickPickItem[], options?: QuickPickOptions): Promise; - - setItems(elements: QuickPickItem[]): void; - - hide(reason?: QuickOpenHideReason): void + show(items: Array, options?: QuickPickOptions): Promise; + setItems(items: Array): void; + hide(): void + readonly onDidHide: Event; readonly onDidAccept: Event; - readonly onDidChangeValue: Event; - readonly onDidChangeActive: Event<(string | QuickPickValue)[]>; - readonly onDidChangeSelection: Event<(string | QuickPickValue)[]>; + readonly onDidChangeValue: Event<{ quickPick: QuickPick, filter: string }>; + readonly onDidChangeActive: Event<{ quickPick: QuickPick, activeItems: Array }>; + readonly onDidChangeSelection: Event<{ quickPick: QuickPick, selectedItems: Array }>; + readonly onDidTriggerButton: Event; } diff --git a/packages/debug/src/browser/debug-configuration-manager.ts b/packages/debug/src/browser/debug-configuration-manager.ts index 5045fb8b8bbbb..daf957acf5cd1 100644 --- a/packages/debug/src/browser/debug-configuration-manager.ts +++ b/packages/debug/src/browser/debug-configuration-manager.ts @@ -26,7 +26,7 @@ import URI from '@theia/core/lib/common/uri'; import { Emitter, Event, WaitUntilEvent } from '@theia/core/lib/common/event'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; -import { PreferenceService, StorageService, PreferenceScope } from '@theia/core/lib/browser'; +import { PreferenceScope, PreferenceService, QuickPickValue, StorageService } from '@theia/core/lib/browser'; import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { DebugConfigurationModel } from './debug-configuration-model'; @@ -51,7 +51,7 @@ export class DebugConfigurationManager { @inject(DebugService) protected readonly debug: DebugService; @inject(QuickPickService) - protected readonly quickPick: QuickPickService; + protected readonly quickPickService: QuickPickService; @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; @@ -317,10 +317,9 @@ export class DebugConfigurationManager { } const { languageId } = widget.editor.document; const debuggers = await this.debug.getDebuggersForLanguage(languageId); - return this.quickPick.show(debuggers.map( - ({ label, type }) => ({ label, value: type }), - { placeholder: 'Select Environment' }) - ); + const items: Array> = debuggers.map(({ label, type }) => ({ label, value: type })); + const selectedItem = await this.quickPickService.show(items, { placeholder: 'Select Environment' }); + return selectedItem?.value; } @inject(StorageService) diff --git a/packages/debug/src/browser/debug-frontend-application-contribution.ts b/packages/debug/src/browser/debug-frontend-application-contribution.ts index d371bc6e7f8a1..b07743d94e235 100644 --- a/packages/debug/src/browser/debug-frontend-application-contribution.ts +++ b/packages/debug/src/browser/debug-frontend-application-contribution.ts @@ -376,7 +376,7 @@ function updateTheme(): void { } } updateTheme(); -ThemeService.get().onThemeChange(() => updateTheme()); +ThemeService.get().onDidColorThemeChange(() => updateTheme()); @injectable() export class DebugFrontendApplicationContribution extends AbstractViewContribution implements TabBarToolbarContribution, ColorContribution { diff --git a/packages/debug/src/browser/debug-frontend-module.ts b/packages/debug/src/browser/debug-frontend-module.ts index f4c96416dd32c..e94f9340d7e88 100644 --- a/packages/debug/src/browser/debug-frontend-module.ts +++ b/packages/debug/src/browser/debug-frontend-module.ts @@ -22,7 +22,7 @@ import { DebugWidget } from './view/debug-widget'; import { DebugPath, DebugService } from '../common/debug-service'; import { WidgetFactory, WebSocketConnectionProvider, FrontendApplicationContribution, - bindViewContribution, KeybindingContext, QuickOpenContribution + bindViewContribution, KeybindingContext } from '@theia/core/lib/browser'; import { DebugSessionManager } from './debug-session-manager'; import { DebugResourceResolver } from './debug-resource'; @@ -59,6 +59,7 @@ import { DebugInlineValueDecorator } from './editor/debug-inline-value-decorator import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; import { TabBarDecorator } from '@theia/core/lib/browser/shell/tab-bar-decorator'; import { DebugTabBarDecorator } from './debug-tab-bar-decorator'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; export default new ContainerModule((bind: interfaces.Bind) => { bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) => @@ -110,7 +111,7 @@ export default new ContainerModule((bind: interfaces.Bind) => { bind(DebugSessionContributionRegistry).toService(DebugSessionContributionRegistryImpl); bind(DebugPrefixConfiguration).toSelf().inSingletonScope(); - for (const identifier of [CommandContribution, QuickOpenContribution]) { + for (const identifier of [CommandContribution, QuickAccessContribution]) { bind(identifier).toService(DebugPrefixConfiguration); } diff --git a/packages/debug/src/browser/debug-prefix-configuration.ts b/packages/debug/src/browser/debug-prefix-configuration.ts index 0ae18e6e55509..fbb9aca3fc5ca 100644 --- a/packages/debug/src/browser/debug-prefix-configuration.ts +++ b/packages/debug/src/browser/debug-prefix-configuration.ts @@ -14,12 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; import { Command, CommandContribution, CommandHandler, CommandRegistry } from '@theia/core/lib/common/command'; -import { - QuickOpenContribution, QuickOpenHandler, QuickOpenModel, - PrefixQuickOpenService, QuickOpenOptions, QuickOpenHandlerRegistry, QuickOpenItem, QuickOpenMode -} from '@theia/core/lib/browser/quick-open'; import { DebugSessionManager } from './debug-session-manager'; import { DebugConfigurationManager } from './debug-configuration-manager'; import { DebugCommands } from './debug-frontend-application-contribution'; @@ -27,11 +23,12 @@ import { DebugSessionOptions } from './debug-session-options'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import URI from '@theia/core/lib/common/uri'; -import { StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; +import { QuickAccessContribution, QuickInputService, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; import { DebugPreferences } from './debug-preferences'; +import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; @injectable() -export class DebugPrefixConfiguration implements CommandContribution, CommandHandler, QuickOpenContribution, QuickOpenHandler, QuickOpenModel { +export class DebugPrefixConfiguration implements CommandContribution, CommandHandler, QuickAccessContribution, monaco.quickInput.IQuickAccessDataService { @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -45,8 +42,8 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan @inject(DebugConfigurationManager) protected readonly debugConfigurationManager: DebugConfigurationManager; - @inject(PrefixQuickOpenService) - protected readonly prefixQuickOpenService: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -57,8 +54,6 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan @inject(StatusBar) protected readonly statusBar: StatusBar; - readonly prefix = 'debug '; - readonly description = 'Debug Configuration'; readonly statusBarId = 'select-run-debug-statusbar-item'; private readonly command: Command = { @@ -83,7 +78,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan } execute(): void { - this.prefixQuickOpenService.open(this.prefix); + this.quickInputService?.open(DebugQuickAccessProvider.PREFIX); } isEnabled(): boolean { @@ -94,44 +89,33 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan return true; } - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - fuzzyMatchLabel: true, - fuzzySort: false, - }; - } - registerCommands(commands: CommandRegistry): void { commands.registerCommand(this.command, this); } - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this); + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: DebugQuickAccessProvider, + prefix: DebugQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Debug Configuration', needsEditor: false }] + }); + DebugQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } - async onType(_lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - const items: QuickOpenItem[] = []; + async getPicks(filter: string, token: monaco.CancellationToken): Promise> { + const items: Array = []; const configurations = this.debugConfigurationManager.all; Array.from(configurations).forEach(config => { - items.push(new QuickOpenItem({ + items.push({ label: config.configuration.name, description: this.workspaceService.isMultiRootWorkspaceOpened ? this.labelProvider.getName(new URI(config.workspaceFolderUri)) : '', - run: (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.runConfiguration(config); - return true; - } - })); + accept: () => this.runConfiguration(config) + }); }); - acceptor(items); + return filterItems(items, filter); } /** @@ -180,3 +164,26 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan this.statusBar.removeElement(this.statusBarId); } } +export class DebugQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = 'debug '; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching launch configurations' + }; + + constructor() { + super(DebugQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: DebugQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return DebugQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/editor/src/browser/editor-command.ts b/packages/editor/src/browser/editor-command.ts index cb86386848dad..dd34b5cdc2369 100644 --- a/packages/editor/src/browser/editor-command.ts +++ b/packages/editor/src/browser/editor-command.ts @@ -14,16 +14,16 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common'; import URI from '@theia/core/lib/common/uri'; -import { CommonCommands, PreferenceService, QuickPickItem, QuickPickService, LabelProvider, QuickPickValue, ApplicationShell } from '@theia/core/lib/browser'; +import { CommonCommands, PreferenceService, LabelProvider, ApplicationShell, QuickInputService, QuickPickItem, QuickPickValue } from '@theia/core/lib/browser'; import { EditorManager } from './editor-manager'; -import { EncodingMode } from './editor'; import { EditorPreferences } from './editor-preferences'; import { ResourceProvider, MessageService } from '@theia/core'; import { LanguageService, Language } from '@theia/core/lib/browser/language-service'; import { SUPPORTED_ENCODINGS } from '@theia/core/lib/browser/supported-encodings'; +import { EncodingMode } from './editor'; export namespace EditorCommands { @@ -197,8 +197,8 @@ export class EditorCommandContribution implements CommandContribution { @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences; - @inject(QuickPickService) - protected readonly quickPick: QuickPickService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(MessageService) protected readonly messageService: MessageService; @@ -267,20 +267,18 @@ export class EditorCommandContribution implements CommandContribution { return; } const current = editor.document.languageId; - const items: QuickPickItem<'autoDetect' | Language>[] = [ + const items: Array | QuickPickItem> = [ { label: 'Auto Detect', value: 'autoDetect' }, { type: 'separator', label: 'languages (identifier)' }, - ... (this.languages.languages.map( - language => this.toQuickPickLanguage(language, current) - )).sort((e, e2) => e.label.localeCompare(e2.label)) + ... (this.languages.languages.map(language => this.toQuickPickLanguage(language, current))).sort((e, e2) => e.label.localeCompare(e2.label)) ]; - const selected = await this.quickPick.show(items, { - placeholder: 'Select Language Mode' - }); - if (selected === 'autoDetect') { - editor.detectLanguage(); - } else if (selected) { - editor.setLanguage(selected.id); + const selectedMode = await this.quickInputService?.showQuickPick(items, { placeholder: 'Select Language Mode' }); + if (selectedMode && ('value' in selectedMode)) { + if (selectedMode.value === 'autoDetect') { + editor.detectLanguage(); + } else if (selectedMode.value) { + editor.setLanguage(selectedMode.value.id); + } } } @@ -297,17 +295,15 @@ export class EditorCommandContribution implements CommandContribution { } const reopenWithEncodingPick = { label: 'Reopen with Encoding', value: 'reopen' }; const saveWithEncodingPick = { label: 'Save with Encoding', value: 'save' }; - const actionItems: QuickPickItem[] = [ + const actionItems: QuickPickValue[] = [ reopenWithEncodingPick, saveWithEncodingPick ]; - const action = await this.quickPick.show(actionItems, { - placeholder: 'Select Action' - }); - if (!action) { + const selectedEncoding = await this.quickInputService?.showQuickPick(actionItems, { placeholder: 'Select Action' }); + if (!selectedEncoding) { return; } - const isReopenWithEncoding = (action === reopenWithEncodingPick.value); + const isReopenWithEncoding = (selectedEncoding.value === reopenWithEncodingPick.value); const configuredEncoding = this.preferencesService.get('files.encoding', 'utf8', editor.uri.toString()); @@ -315,7 +311,7 @@ export class EditorCommandContribution implements CommandContribution { const guessedEncoding = resource.guessEncoding ? await resource.guessEncoding() : undefined; resource.dispose(); - const encodingItems: QuickPickItem<{ id: string, description: string }>[] = Object.keys(SUPPORTED_ENCODINGS) + const encodingItems: QuickPickValue<{ id: string, description: string }>[] = Object.keys(SUPPORTED_ENCODINGS) .sort((k1, k2) => { if (k1 === configuredEncoding) { return -1; @@ -340,29 +336,30 @@ export class EditorCommandContribution implements CommandContribution { value: { id: guessedEncoding, description: guessedEncoding } }); } - const encoding = await this.quickPick.show(encodingItems, { + const selectedFileEncoding = await this.quickInputService?.showQuickPick>(encodingItems, { placeholder: isReopenWithEncoding ? 'Select File Encoding to Reopen File' : 'Select File Encoding to Save with' }); - if (!encoding) { + + if (!selectedFileEncoding) { return; } if (editor.document.dirty && isReopenWithEncoding) { this.messageService.info('The file is dirty. Please save it first before reopening it with another encoding.'); return; - } else { - editor.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); + } else if (selectedFileEncoding.value) { + editor.setEncoding(selectedFileEncoding.value.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); } } protected toQuickPickLanguage(value: Language, current: string): QuickPickValue { const languageUri = this.toLanguageUri(value); const icon = this.labelProvider.getIcon(languageUri); - const iconClass = icon !== '' ? icon + ' file-icon' : undefined; + const iconClasses = icon !== '' ? [icon + ' file-icon'] : undefined; return { value, label: value.name, description: `(${value.id})${current === value.id ? ' - Configured Language' : ''}`, - iconClass + iconClasses }; } protected toLanguageUri(language: Language): URI { diff --git a/packages/editor/src/browser/editor-contribution.ts b/packages/editor/src/browser/editor-contribution.ts index 3e8efa7ce56f9..2415c8bf5b996 100644 --- a/packages/editor/src/browser/editor-contribution.ts +++ b/packages/editor/src/browser/editor-contribution.ts @@ -16,20 +16,21 @@ import { EditorManager } from './editor-manager'; import { TextEditor } from './editor'; -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { StatusBarAlignment, StatusBar } from '@theia/core/lib/browser/status-bar/status-bar'; import { FrontendApplicationContribution, DiffUris, DockLayout } from '@theia/core/lib/browser'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { CommandHandler, DisposableCollection } from '@theia/core'; import { EditorCommands } from './editor-command'; -import { EditorQuickOpenService } from './editor-quick-open-service'; import { CommandRegistry, CommandContribution } from '@theia/core/lib/common'; -import { KeybindingRegistry, KeybindingContribution, QuickOpenContribution, QuickOpenHandlerRegistry } from '@theia/core/lib/browser'; +import { KeybindingRegistry, KeybindingContribution } from '@theia/core/lib/browser'; import { LanguageService } from '@theia/core/lib/browser/language-service'; import { SUPPORTED_ENCODINGS } from '@theia/core/lib/browser/supported-encodings'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickEditorService } from '@theia/core/lib/browser/quick-input/quick-editor-service'; @injectable() -export class EditorContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution, QuickOpenContribution { +export class EditorContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution, QuickAccessContribution { @inject(StatusBar) protected readonly statusBar: StatusBar; @inject(EditorManager) protected readonly editorManager: EditorManager; @@ -38,8 +39,8 @@ export class EditorContribution implements FrontendApplicationContribution, Comm @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; - @inject(EditorQuickOpenService) - protected readonly editorQuickOpenService: EditorQuickOpenService; + @inject(QuickEditorService) @optional() + protected readonly quickEditorService: QuickEditorService; onStart(): void { this.initEditorContextKeys(); @@ -131,7 +132,7 @@ export class EditorContribution implements FrontendApplicationContribution, Comm registerCommands(commands: CommandRegistry): void { commands.registerCommand(EditorCommands.SHOW_ALL_OPENED_EDITORS, { - execute: () => this.editorQuickOpenService.open() + execute: () => this.quickEditorService?.open('edt ') }); const splitHandlerFactory = (splitMode: DockLayout.InsertMode): CommandHandler => ({ isEnabled: () => !!this.editorManager.currentEditor, @@ -169,7 +170,7 @@ export class EditorContribution implements FrontendApplicationContribution, Comm }); } - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this.editorQuickOpenService); + registerQuickAccessProvider(): void { + this.quickEditorService?.registerQuickAccessProvider(); } } diff --git a/packages/editor/src/browser/editor-frontend-module.ts b/packages/editor/src/browser/editor-frontend-module.ts index 3feda7e790e55..e8b6f71be4df5 100644 --- a/packages/editor/src/browser/editor-frontend-module.ts +++ b/packages/editor/src/browser/editor-frontend-module.ts @@ -18,7 +18,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; -import { OpenHandler, WidgetFactory, FrontendApplicationContribution, KeybindingContext, KeybindingContribution, QuickOpenContribution } from '@theia/core/lib/browser'; +import { OpenHandler, WidgetFactory, FrontendApplicationContribution, KeybindingContext, KeybindingContribution } from '@theia/core/lib/browser'; import { VariableContribution } from '@theia/variable-resolver/lib/browser'; import { EditorManager, EditorAccess, ActiveEditorAccess, CurrentEditorAccess } from './editor-manager'; import { EditorContribution } from './editor-contribution'; @@ -33,7 +33,7 @@ import { NavigationLocationUpdater } from './navigation/navigation-location-upda import { NavigationLocationService } from './navigation/navigation-location-service'; import { NavigationLocationSimilarity } from './navigation/navigation-location-similarity'; import { EditorVariableContribution } from './editor-variable-contribution'; -import { EditorQuickOpenService } from './editor-quick-open-service'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; export default new ContainerModule(bind => { bindEditorPreferences(bind); @@ -68,10 +68,9 @@ export default new ContainerModule(bind => { bind(VariableContribution).to(EditorVariableContribution).inSingletonScope(); - [CommandContribution, KeybindingContribution, QuickOpenContribution].forEach(serviceIdentifier => { + [CommandContribution, KeybindingContribution, QuickAccessContribution].forEach(serviceIdentifier => { bind(serviceIdentifier).toService(EditorContribution); }); - bind(EditorQuickOpenService).toSelf().inSingletonScope(); bind(CurrentEditorAccess).toSelf().inSingletonScope(); bind(ActiveEditorAccess).toSelf().inSingletonScope(); diff --git a/packages/editor/src/browser/editor-preferences.ts b/packages/editor/src/browser/editor-preferences.ts index b15b44c2eb966..74b50cc77cabc 100644 --- a/packages/editor/src/browser/editor-preferences.ts +++ b/packages/editor/src/browser/editor-preferences.ts @@ -35,7 +35,7 @@ const platform = { isLinux: OS.type() === OS.Type.Linux }; -// should be in sync with https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/editorOptions.ts#L3042 +// should be in sync with https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/editorOptions.ts#L3687 export const EDITOR_FONT_DEFAULTS = { fontFamily: ( isOSX ? DEFAULT_MAC_FONT_FAMILY : (isWindows ? DEFAULT_WINDOWS_FONT_FAMILY : DEFAULT_LINUX_FONT_FAMILY) @@ -48,7 +48,7 @@ export const EDITOR_FONT_DEFAULTS = { letterSpacing: 0, }; -// should be in sync with https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/editorOptions.ts#L3057 +// should be in sync with https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/editorOptions.ts#L3702 export const EDITOR_MODEL_DEFAULTS = { tabSize: 4, indentSize: 4, @@ -64,11 +64,11 @@ export const DEFAULT_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; /* eslint-disable no-null/no-null */ // should be in sync with: -// 1. https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/commonEditorConfig.ts#L441 -// 2. https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/commonEditorConfig.ts#L530 +// 1. https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/commonEditorConfig.ts#L458 +// 2. https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/commonEditorConfig.ts#L577 -// 1. Copy from https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/commonEditorConfig.ts#L530 -// 2. Align first items with https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/config/commonEditorConfig.ts#L441 +// 1. Copy from https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/commonEditorConfig.ts#L577 +// 2. Align first items with https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/config/commonEditorConfig.ts#L458 // 3. Find -> Use Regular Expressions to clean up data and replace " by ', for example -> nls\.localize\(.*, "(.*)"\) -> "$1" // 4. Apply `quotemark` quick fixes // 5. Fix the rest manually @@ -109,9 +109,24 @@ const codeEditorPreferenceProperties = { 'default': true, 'description': 'Controls whether completions should be computed based on words in the document.' }, + 'editor.wordBasedSuggestionsMode': { + 'enum': ['currentDocument', 'matchingDocuments', 'allDocuments'], + 'default': 'matchingDocuments', + 'enumDescriptions': [ + 'Only suggest words from the active document.', + 'Suggest words from all open documents of the same language.', + 'Suggest words from all open documents.' + ], + 'description': 'Controls form what documents word based completions are computed.' + }, 'editor.semanticHighlighting.enabled': { - 'type': 'boolean', - 'default': true, + 'enum': [true, false, 'configuredByTheme'], + 'enumDescriptions': [ + 'Semantic highlighting enabled for all color themes.', + 'Semantic highlighting disabled for all color themes.', + 'Semantic highlighting is configured by the current color theme\'s `semanticHighlighting` setting.' + ], + 'default': 'configuredByTheme', 'description': 'Controls whether the semanticHighlighting is shown for the languages that support it.' }, 'editor.stablePeek': { @@ -137,13 +152,28 @@ const codeEditorPreferenceProperties = { 'diffEditor.ignoreTrimWhitespace': { 'type': 'boolean', 'default': true, - 'description': 'Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.' + 'description': 'When enabled, the diff editor ignores changes in leading or trailing whitespace.' }, 'diffEditor.renderIndicators': { 'type': 'boolean', 'default': true, 'description': 'Controls whether the diff editor shows +/- indicators for added/removed changes.' }, + 'diffEditor.codeLens': { + 'type': 'boolean', + 'default': false, + 'description': 'Controls whether the editor shows CodeLens.' + }, + 'diffEditor.wordWrap': { + 'type': 'string', + 'enum': ['off', 'on', 'inherit'], + 'default': 'inherit', + 'markdownEnumDescriptions': [ + 'Lines will never wrap.', + 'Lines will wrap at the viewport width.', + 'Lines will wrap according to the `#editor.wordWrap#` setting.' + ] + }, 'editor.acceptSuggestionOnCommitCharacter': { 'markdownDescription': 'Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.', 'type': 'boolean', @@ -271,11 +301,32 @@ const codeEditorPreferenceProperties = { ], 'default': 'languageDefined' }, + 'editor.ariaLabel': { + 'type': 'string', + 'description': 'Editor content', + 'default': 'ariaLabel' + }, + 'editor.automaticLayout': { + 'type': 'boolean', + 'default': false + }, 'editor.codeLens': { 'description': 'Controls whether the editor shows CodeLens.', 'type': 'boolean', 'default': true }, + 'editor.codeLensFontFamily': { + 'description': 'Controls the font family for CodeLens.', + 'type': 'string', + 'default': true + }, + 'editor.codeLensFontSize': { + 'type': 'integer', + 'default': 0, + 'minimum': 0, + 'maximum': 100, + 'description': 'Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.' + }, 'editor.colorDecorators': { 'description': 'Controls whether the editor should render the inline color decorators and color picker.', 'type': 'boolean', @@ -286,6 +337,15 @@ const codeEditorPreferenceProperties = { 'default': true, 'description': 'Controls whether a space character is inserted when commenting.' }, + 'editor.comments.ignoreEmptyLines': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls if empty lines should be ignored with toggle, add or remove actions for line comments.' + }, + 'editor.contextmenu': { + 'type': 'boolean', + 'default': true, + }, 'editor.copyWithSyntaxHighlighting': { 'description': 'Controls whether syntax highlighting should be copied into the clipboard.', 'type': 'boolean', @@ -348,6 +408,14 @@ const codeEditorPreferenceProperties = { 'minimum': 0, 'maximum': 1073741824 }, + 'editor.disableLayerHinting': { + 'type': 'boolean', + 'default': false + }, + 'editor.disableMonospaceOptimizations': { + 'type': 'boolean', + 'default': false + }, 'editor.dragAndDrop': { 'description': 'Controls whether the editor should allow moving selections via drag and drop.', 'type': 'boolean', @@ -358,11 +426,20 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.extraEditorClassName': { + 'type': 'string', + 'default': '' + }, 'editor.fastScrollSensitivity': { 'markdownDescription': 'Scrolling speed multiplier when pressing `Alt`.', 'type': 'number', 'default': 5 }, + 'editor.find.cursorMoveOnType': { + 'description': 'Controls whether the cursor should jump to find matches while typing.', + 'type': 'boolean', + 'default': true + }, 'editor.find.seedSearchStringFromSelection': { 'type': 'boolean', 'default': true, @@ -394,6 +471,15 @@ const codeEditorPreferenceProperties = { 'default': true, 'description': 'Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible.' }, + 'editor.find.loop': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found.' + }, + 'editor.fixedOverflowWidgets': { + 'type': 'boolean', + 'default': false, + }, 'editor.folding': { 'description': 'Controls whether the editor has code folding enabled.', 'type': 'boolean', @@ -413,6 +499,11 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.unfoldOnClickAfterEndOfLine': { + 'description': 'Controls whether clicking on the empty content after a folded line will unfold the line.', + 'type': 'boolean', + 'default': false + }, 'editor.fontFamily': { 'description': 'Controls the font family.', 'type': 'string', @@ -602,6 +693,10 @@ const codeEditorPreferenceProperties = { 'default': true, 'description': 'Controls whether the hover should remain visible when mouse is moved over it.' }, + 'editor.inDiffEditor': { + 'type': 'boolean', + 'default': true, + }, 'editor.letterSpacing': { 'description': 'Controls the letter spacing in pixels.', 'type': 'number', @@ -636,6 +731,18 @@ const codeEditorPreferenceProperties = { 'default': 'on', 'description': 'Controls the display of line numbers.' }, + 'editor.lineNumbersMinChars': { + 'description': 'Controls the line height. Use 0 to compute the line height from the font size.', + 'type': 'integer', + 'default': 5, + 'minimum': 1, + 'maximum': 300 + }, + 'editor.linkedEditing': { + 'description': 'Controls whether the editor has linked editing enabled. Depending on the language, related symbols, e.g. HTML tags, are updated while editing.', + 'type': 'boolean', + 'default': false + }, 'editor.links': { 'description': 'Controls whether the editor should detect links and make them clickable.', 'type': 'boolean', @@ -691,6 +798,11 @@ const codeEditorPreferenceProperties = { 'default': 120, 'description': 'Limit the width of the minimap to render at most a certain number of columns.' }, + 'editor.mouseStyle': { + 'type': 'string', + 'enum': ['text', 'default', 'copy'], + 'default': 'text' + }, 'editor.mouseWheelScrollSensitivity': { 'markdownDescription': 'A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.', 'type': 'number', @@ -742,6 +854,26 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.overviewRulerLanes': { + 'type': 'integer', + 'default': 3, + 'minimum': 0, + 'maximum': 3 + }, + 'editor.padding.top': { + 'type': 'number', + 'default': 0, + 'minimum': 0, + 'maximum': 1000, + 'description': 'Controls the amount of space between the top edge of the editor and the first line.' + }, + 'editor.padding.bottom': { + 'type': 'number', + 'default': 0, + 'minimum': 0, + 'maximum': 1000, + 'description': 'Controls the amount of space between the bottom edge of the editor and the last line.' + }, 'editor.parameterHints.enabled': { 'type': 'boolean', 'default': true, @@ -765,6 +897,11 @@ const codeEditorPreferenceProperties = { ], 'default': 'tree' }, + 'editor.definitionLinkOpensInPeek': { + 'type': 'boolean', + 'default': false, + 'description': 'Controls whether the Go to Definition mouse gesture always opens the peek widget.' + }, 'editor.quickSuggestions': { 'anyOf': [ { @@ -805,6 +942,10 @@ const codeEditorPreferenceProperties = { 'minimum': 0, 'maximum': 1073741824 }, + 'editor.readOnly': { + 'type': 'boolean', + 'default': false + }, 'editor.rename.enablePreview': { 'description': 'Controls whether the editor should display refactor preview pane for rename.', 'type': 'boolean', @@ -842,6 +983,16 @@ const codeEditorPreferenceProperties = { ], 'default': 'line' }, + 'editor.renderLineHighlightOnlyWhenFocus': { + 'description': 'Controls if the editor should render the current line highlight only when the editor is focused.', + 'type': 'boolean', + 'default': false + }, + 'editor.renderValidationDecorations': { + 'type': 'string', + 'enum': ['editable', 'on', 'off'], + 'default': 'editable' + }, 'editor.renderWhitespace': { 'enumDescriptions': [ '', @@ -859,6 +1010,12 @@ const codeEditorPreferenceProperties = { ], 'default': 'none' }, + 'editor.revealHorizontalRightPadding': { + 'type': 'integer', + 'default': 30, + 'minimum': 0, + 'maximum': 1000 + }, 'editor.roundedSelection': { 'description': 'Controls whether selections should have rounded corners.', 'type': 'boolean', @@ -884,6 +1041,11 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.scrollPredominantAxis': { + 'description': 'Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad.', + 'type': 'boolean', + 'default': true + }, 'editor.selectionClipboard': { 'description': 'Controls whether the Linux primary clipboard should be supported.', 'included': platform.isLinux, @@ -895,6 +1057,10 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.selectOnLineNumbers': { + 'type': 'boolean', + 'default': true + }, 'editor.showFoldingControls': { 'description': 'Controls whether the fold controls on the gutter are automatically hidden.', 'type': 'string', @@ -909,6 +1075,26 @@ const codeEditorPreferenceProperties = { 'type': 'boolean', 'default': true }, + 'editor.showDeprecated': { + 'description': 'Controls strikethrough deprecated variables.', + 'type': 'boolean', + 'default': true + }, + 'editor.inlineHints.enabled': { + 'type': 'boolean', + 'default': true, + 'description': 'Enables the inline hints in the editor.' + }, + 'editor.inlineHints.fontSize': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.fontSize, + description: 'Controls font size of inline hints in the editor. When set to `0`, the 90% of `#editor.fontSize#` is used.' + }, + 'editor.inlineHints.fontFamily': { + 'type': 'string', + 'default': EDITOR_FONT_DEFAULTS.fontFamily, + 'description': 'Controls font family of inline hints in the editor.' + }, 'editor.snippetSuggestions': { 'enumDescriptions': [ 'Show snippet suggestions on top of other suggestions.', @@ -926,11 +1112,27 @@ const codeEditorPreferenceProperties = { ], 'default': 'inline' }, + 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { + 'description': 'Whether leading and trailing whitespace should always be selected.', + 'default': true, + 'type': 'boolean' + }, 'editor.smoothScrolling': { 'description': 'Controls whether the editor will scroll using an animation.', 'type': 'boolean', 'default': false }, + 'editor.stickyTabStops': { + 'description': 'Emulate selection behaviour of tab characters when using spaces for indentation. Selection will stick to tab stops.', + 'type': 'boolean', + 'default': false + }, + 'editor.stopRenderingLineAfter': { + 'type': 'integer', + 'default': 10000, + 'minimum': -1, + 'maximum': 1073741824 + }, 'editor.suggest.insertMode': { 'type': 'string', 'enum': [ @@ -1170,6 +1372,24 @@ const codeEditorPreferenceProperties = { ], 'default': 'off' }, + 'editor.tabIndex': { + 'markdownDescription': 'Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.', + 'type': 'integer', + 'default': 0, + 'minimum': -1, + 'maximum': 1073741824 + }, + 'editor.unusualLineTerminators': { + 'markdownEnumDescriptions': [ + 'Unusual line terminators are automatically removed.', + 'Unusual line terminators are ignored.', + 'Unusual line terminators prompt to be removed.' + ], + 'description': 'Remove unusual line terminators that might cause problems.', + 'type': 'string', + 'enum': ['auto', 'off', 'prompt'], + 'default': 'prompt' + }, 'editor.useTabStops': { 'description': 'Inserting and deleting whitespace follows tab stops.', 'type': 'boolean', @@ -1197,6 +1417,14 @@ const codeEditorPreferenceProperties = { ], 'default': 'off' }, + 'editor.wordWrapBreakAfterCharacters': { + 'type': 'string', + 'default': ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', + }, + 'editor.wordWrapBreakBeforeCharacters': { + 'type': 'string', + 'default': '([{‘“〈《「『【〔([{「£¥$£¥++', + }, 'editor.wordWrapColumn': { 'markdownDescription': 'Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.', 'type': 'integer', @@ -1204,6 +1432,16 @@ const codeEditorPreferenceProperties = { 'minimum': 1, 'maximum': 1073741824 }, + 'editor.wordWrapOverride1': { + 'type': 'string', + 'enum': ['off', 'on', 'inherit'], + 'default': 'inherit' + }, + 'editor.wordWrapOverride2': { + 'type': 'string', + 'enum': ['off', 'on', 'inherit'], + 'default': 'inherit' + }, 'editor.wrappingIndent': { 'enumDescriptions': [ 'No indentation. Wrapped lines begin at column 1.', diff --git a/packages/editor/src/browser/editor-quick-open-service.ts b/packages/editor/src/browser/editor-quick-open-service.ts deleted file mode 100644 index 163c175ba2962..0000000000000 --- a/packages/editor/src/browser/editor-quick-open-service.ts +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 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 { - QuickOpenModel, QuickOpenItem, QuickOpenMode, PrefixQuickOpenService, - OpenerService, QuickOpenItemOptions, - QuickOpenHandler, QuickOpenOptions -} from '@theia/core/lib/browser'; -import URI from '@theia/core/lib/common/uri'; -import { LabelProvider } from '@theia/core/lib/browser/label-provider'; -import { EditorManager } from './editor-manager'; -import { EditorWidget } from './editor-widget'; - -@injectable() -export class EditorQuickOpenService implements QuickOpenModel, QuickOpenHandler { - - @inject(OpenerService) - protected readonly openerService: OpenerService; - - @inject(PrefixQuickOpenService) - protected readonly prefixQuickOpenService: PrefixQuickOpenService; - - @inject(LabelProvider) - protected readonly labelProvider: LabelProvider; - - @inject(EditorManager) - protected readonly editorManager: EditorManager; - - readonly prefix: string = 'edt '; - - get description(): string { - return 'Show All Opened Editors'; - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - fuzzyMatchLabel: { - enableSeparateSubstringMatching: true - }, - fuzzyMatchDescription: { - enableSeparateSubstringMatching: true - } - }; - } - - open(): void { - this.prefixQuickOpenService.open(this.prefix); - } - - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - const editorItems: QuickOpenItem[] = []; - - // Get the alphabetically sorted list of URIs of all currently opened editor widgets. - const widgets: URI[] = this.editorManager.all - .map((w: EditorWidget) => w.editor.uri) - .sort(); - - if (widgets.length === 0) { - editorItems.push(new QuickOpenItem({ - label: 'List of opened editors is currently empty', - run: () => false - })); - acceptor(editorItems); - return; - } - - for (const uri of widgets) { - const item = this.toItem(uri); - editorItems.push(item); - acceptor(editorItems); - } - return; - } - - protected toItem(uri: URI): QuickOpenItem { - const description = this.labelProvider.getLongName(uri.parent); - const icon = this.labelProvider.getIcon(uri); - const iconClass = icon === '' ? undefined : icon + ' file-icon'; - - const options: QuickOpenItemOptions = { - label: this.labelProvider.getName(uri), - iconClass, - description: description, - tooltip: uri.path.toString(), - uri: uri, - hidden: false, - run: this.getRunFunction(uri) - }; - return new QuickOpenItem(options); - } - - /** - * Gets the function that can open the editor file - * @param uri the file uri - * @returns the function that opens the file if mode === QuickOpenMode.OPEN - */ - protected getRunFunction(uri: URI): (mode: QuickOpenMode) => boolean { - return (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.openFile(uri); - return true; - }; - } - - protected openFile(uri: URI): void { - this.openerService.getOpener(uri) - .then(opener => opener.open(uri)); - } -} diff --git a/packages/external-terminal/src/electron-browser/external-terminal-contribution.ts b/packages/external-terminal/src/electron-browser/external-terminal-contribution.ts index ce197ac4cf432..9c119fa9d4d0f 100644 --- a/packages/external-terminal/src/electron-browser/external-terminal-contribution.ts +++ b/packages/external-terminal/src/electron-browser/external-terminal-contribution.ts @@ -17,12 +17,12 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; -import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { KeybindingContribution, KeybindingRegistry, LabelProvider } from '@theia/core/lib/browser'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { ExternalTerminalService } from '../common/external-terminal'; import { ExternalTerminalPreferenceService } from './external-terminal-preference'; +import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; export namespace ExternalTerminalCommands { export const OPEN_NATIVE_CONSOLE: Command = { @@ -103,12 +103,13 @@ export class ExternalTerminalFrontendContribution implements CommandContribution */ protected async selectCwd(): Promise { const roots = this.workspaceService.tryGetRoots(); - return this.quickPickService.show(roots.map( + const selectedItem = await this.quickPickService.show(roots.map( ({ resource }) => ({ label: this.labelProvider.getName(resource), description: this.labelProvider.getLongName(resource), value: resource.toString() }) ), { placeholder: 'Select current working directory for new external terminal' }); + return selectedItem?.value; } } diff --git a/packages/file-search/compile.tsconfig.json b/packages/file-search/compile.tsconfig.json index 208e509b8dc1d..36c328af97aff 100644 --- a/packages/file-search/compile.tsconfig.json +++ b/packages/file-search/compile.tsconfig.json @@ -12,6 +12,9 @@ { "path": "../core/compile.tsconfig.json" }, + { + "path": "../monaco/compile.tsconfig.json" + }, { "path": "../editor/compile.tsconfig.json" }, diff --git a/packages/file-search/package.json b/packages/file-search/package.json index a0e409c786dcb..f30a74b258576 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -5,6 +5,7 @@ "dependencies": { "@theia/core": "1.13.0", "@theia/editor": "1.13.0", + "@theia/monaco": "1.13.0", "@theia/filesystem": "1.13.0", "@theia/process": "1.13.0", "@theia/workspace": "1.13.0", diff --git a/packages/file-search/src/browser/file-search-frontend-module.ts b/packages/file-search/src/browser/file-search-frontend-module.ts index 40c64a3e3ac54..829fae7d3271d 100644 --- a/packages/file-search/src/browser/file-search-frontend-module.ts +++ b/packages/file-search/src/browser/file-search-frontend-module.ts @@ -16,10 +16,11 @@ import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { CommandContribution } from '@theia/core/lib/common'; -import { WebSocketConnectionProvider, KeybindingContribution, QuickOpenContribution } from '@theia/core/lib/browser'; +import { WebSocketConnectionProvider, KeybindingContribution } from '@theia/core/lib/browser'; import { QuickFileOpenFrontendContribution } from './quick-file-open-contribution'; import { QuickFileOpenService } from './quick-file-open'; import { fileSearchServicePath, FileSearchService } from '../common/file-search-service'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; export default new ContainerModule((bind: interfaces.Bind) => { bind(FileSearchService).toDynamicValue(ctx => { @@ -28,7 +29,7 @@ export default new ContainerModule((bind: interfaces.Bind) => { }).inSingletonScope(); bind(QuickFileOpenFrontendContribution).toSelf().inSingletonScope(); - [CommandContribution, KeybindingContribution, QuickOpenContribution].forEach(serviceIdentifier => + [CommandContribution, KeybindingContribution, QuickAccessContribution].forEach(serviceIdentifier => bind(serviceIdentifier).toService(QuickFileOpenFrontendContribution) ); diff --git a/packages/file-search/src/browser/quick-file-open-contribution.ts b/packages/file-search/src/browser/quick-file-open-contribution.ts index d79359f9517e7..40091a2b87a20 100644 --- a/packages/file-search/src/browser/quick-file-open-contribution.ts +++ b/packages/file-search/src/browser/quick-file-open-contribution.ts @@ -18,10 +18,10 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { QuickFileOpenService, quickFileOpen } from './quick-file-open'; import { CommandRegistry, CommandContribution } from '@theia/core/lib/common'; -import { KeybindingRegistry, KeybindingContribution, QuickOpenContribution, QuickOpenHandlerRegistry } from '@theia/core/lib/browser'; +import { KeybindingRegistry, KeybindingContribution, QuickAccessContribution } from '@theia/core/lib/browser'; @injectable() -export class QuickFileOpenFrontendContribution implements CommandContribution, KeybindingContribution, QuickOpenContribution { +export class QuickFileOpenFrontendContribution implements QuickAccessContribution, CommandContribution, KeybindingContribution { @inject(QuickFileOpenService) protected readonly quickFileOpenService: QuickFileOpenService; @@ -50,7 +50,7 @@ export class QuickFileOpenFrontendContribution implements CommandContribution, K }); } - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this.quickFileOpenService, true); + registerQuickAccessProvider(): void { + this.quickFileOpenService.registerQuickAccessProvider(); } } diff --git a/packages/file-search/src/browser/quick-file-open.ts b/packages/file-search/src/browser/quick-file-open.ts index 48a2d51dfa298..714b1bf010673 100644 --- a/packages/file-search/src/browser/quick-file-open.ts +++ b/packages/file-search/src/browser/quick-file-open.ts @@ -14,22 +14,19 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; -import { - QuickOpenModel, QuickOpenItem, QuickOpenMode, PrefixQuickOpenService, - OpenerService, KeybindingRegistry, QuickOpenGroupItem, QuickOpenGroupItemOptions, QuickOpenItemOptions, - QuickOpenHandler, QuickOpenOptions -} from '@theia/core/lib/browser'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; +import { OpenerService, KeybindingRegistry } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import URI from '@theia/core/lib/common/uri'; import { FileSearchService, WHITESPACE_QUERY_SEPARATOR } from '../common/file-search-service'; -import { CancellationTokenSource } from '@theia/core/lib/common'; +import { CancellationToken } from '@theia/core/lib/common'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import { Command } from '@theia/core/lib/common'; import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/navigation-location-service'; import * as fuzzy from '@theia/core/shared/fuzzy'; import { MessageService } from '@theia/core/lib/common/message-service'; import { FileSystemPreferences } from '@theia/filesystem/lib/browser'; +import { findMatches, QuickInputService } from '@theia/core/lib/browser/quick-input/quick-input-service'; import { EditorOpenerOptions, Position, Range } from '@theia/editor/lib/browser'; export const quickFileOpen: Command = { @@ -47,16 +44,15 @@ export interface FilterAndRange { const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?\s*$/; @injectable() -export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { - +export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataService { @inject(KeybindingRegistry) protected readonly keybindingRegistry: KeybindingRegistry; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(OpenerService) protected readonly openerService: OpenerService; - @inject(PrefixQuickOpenService) - protected readonly quickOpenService: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(FileSearchService) protected readonly fileSearchService: FileSearchService; @inject(LabelProvider) @@ -68,6 +64,16 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { @inject(FileSystemPreferences) protected readonly fsPreferences: FileSystemPreferences; + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: AnythingQuickAccessProvider, + prefix: AnythingQuickAccessProvider.PREFIX, + placeholder: this.getPlaceHolder(), + helpEntries: [{ description: 'Open File', needsEditor: false }] + }); + AnythingQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; + } + /** * Whether to hide .gitignored (and other ignored) files. */ @@ -94,38 +100,6 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { partial: 250 // represents the score assigned to partial matching. }; - readonly prefix: string = '...'; - - get description(): string { - return 'Open File'; - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - let placeholder = 'File name to search (append : to go to line).'; - const keybinding = this.getKeyCommand(); - if (keybinding) { - placeholder += ` (Press ${keybinding} to show/hide ignored files)`; - } - return { - placeholder, - fuzzyMatchLabel: { - enableSeparateSubstringMatching: true - }, - fuzzyMatchDescription: { - enableSeparateSubstringMatching: true - }, - showItemsWithoutHighlight: true, - onClose: () => { - this.isOpen = false; - this.cancelIndicator.cancel(); - } - }; - } - isEnabled(): boolean { return this.workspaceService.opened; } @@ -141,7 +115,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { this.isOpen = true; } - this.quickOpenService.open(this.filterAndRange.filter); + this.quickInputService?.open(this.filterAndRange.filter); } /** @@ -159,45 +133,39 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { return undefined; } - private cancelIndicator = new CancellationTokenSource(); - - public async onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - this.cancelIndicator.cancel(); - this.cancelIndicator = new CancellationTokenSource(); - const token = this.cancelIndicator.token; - + async getPicks(filter: string, token: CancellationToken): Promise> { const roots = this.workspaceService.tryGetRoots(); - this.filterAndRange = this.splitFilterAndRange(lookFor); + this.filterAndRange = this.splitFilterAndRange(filter); const fileFilter = this.filterAndRange.filter; const alreadyCollected = new Set(); - const recentlyUsedItems: QuickOpenItem[] = []; + const recentlyUsedItems: Array = []; const locations = [...this.navigationLocationService.locations()].reverse(); for (const location of locations) { const uriString = location.uri.toString(); + if (location.uri.scheme === 'file' && !alreadyCollected.has(uriString) && fuzzy.test(fileFilter, uriString)) { - const item = this.toItem(location.uri, { groupLabel: recentlyUsedItems.length === 0 ? 'recently opened' : undefined, showBorder: false }); + if (recentlyUsedItems.length === 0) { + recentlyUsedItems.push({ type: 'separator', label: 'recently opened' }); + } + const item = this.toItem(fileFilter, location.uri); recentlyUsedItems.push(item); alreadyCollected.add(uriString); } } + if (fileFilter.length > 0) { const handler = async (results: string[]) => { - if (token.isCancellationRequested) { - return; - } - const fileSearchResultItems: QuickOpenItem[] = []; - - if (results.length <= 0) { - acceptor([this.toNoResultsItem()]); - return; + if (token.isCancellationRequested || results.length <= 0) { + return []; } + const fileSearchResultItems: Array = []; for (const fileUri of results) { if (!alreadyCollected.has(fileUri)) { - const item = this.toItem(fileUri); + const item = this.toItem(fileFilter, fileUri); fileSearchResultItems.push(item); alreadyCollected.add(fileUri); } @@ -207,18 +175,15 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { const sortedResults = fileSearchResultItems.slice(); sortedResults.sort((a, b) => this.compareItems(a, b)); - // Extract the first element, and re-add it to the array with the group label. - const first = sortedResults[0]; - sortedResults.shift(); - if (first) { - const item = this.toItem(first.getUri()!, { groupLabel: 'file results', showBorder: !!recentlyUsedItems.length }); - sortedResults.unshift(item); + if (sortedResults.length > 0) { + sortedResults.unshift({ type: 'separator', label: 'file results' }); } + // Return the recently used items, followed by the search results. - acceptor([...recentlyUsedItems, ...sortedResults]); + return ([...recentlyUsedItems, ...sortedResults]); }; - this.fileSearchService.find(fileFilter, { + return this.fileSearchService.find(fileFilter, { rootUris: roots.map(r => r.resource.toString()), fuzzyMatch: true, limit: 200, @@ -228,33 +193,21 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { : undefined, }, token).then(handler); } else { - if (roots.length !== 0) { - acceptor(recentlyUsedItems); - } + return roots.length !== 0 ? recentlyUsedItems : []; } } - protected getRunFunction(uri: URI): (mode: QuickOpenMode) => boolean { - return (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.openFile(uri); - return true; - }; - } - /** - * Compare two `QuickOpenItem`. + * Compare two `IAnythingQuickPickItem`. * - * @param a `QuickOpenItem` for comparison. - * @param b `QuickOpenItem` for comparison. - * @param member the `QuickOpenItem` object member for comparison. + * @param a `IAnythingQuickPickItem` for comparison. + * @param b `IAnythingQuickPickItem` for comparison. + * @param member the `IAnythingQuickPickItem` object member for comparison. */ protected compareItems( - a: QuickOpenItem, - b: QuickOpenItem, - member: 'getLabel' | 'getUri' = 'getLabel'): number { + a: monaco.quickInput.IAnythingQuickPickItem, + b: monaco.quickInput.IAnythingQuickPickItem, + member: 'label' | 'resource' = 'label'): number { /** * Normalize a given string. @@ -309,8 +262,8 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { } // Get the item's member values for comparison. - let itemA = a[member]()!; - let itemB = b[member]()!; + let itemA = a[member]!; + let itemB = b[member]!; // If the `URI` is used as a comparison member, perform the necessary string conversions. if (typeof itemA !== 'string') { @@ -347,7 +300,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { // If the alphabetical comparison is equal, call `compareItems` recursively using the `URI` member instead. if (comparison === 0) { - return this.compareItems(a, b, 'getUri'); + return this.compareItems(a, b, 'resource'); } return itemB.localeCompare(itemA); @@ -371,39 +324,48 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { return { selection: this.filterAndRange.range }; } - private toItem(uriOrString: URI | string, group?: QuickOpenGroupItemOptions): QuickOpenItem { + private toItem(lookFor: string, uriOrString: URI | string): monaco.quickInput.IAnythingQuickPickItem { const uri = uriOrString instanceof URI ? uriOrString : new URI(uriOrString); + const label = this.labelProvider.getName(uri); + const description = this.getItemDescription(uri); + const iconClasses = this.getItemIconClasses(uri); + + return { + resource: uri, + label, + description, + highlights: { + label: findMatches(label, lookFor), + description: findMatches(description, lookFor) + }, + iconClasses, + accept: () => this.openFile(uri) + }; + } + + private getItemIconClasses(uri: URI): string[] | undefined { + const icon = this.labelProvider.getIcon(uri); + return icon !== '' ? [icon + ' file-icon'] : []; + } + + private getItemDescription(uri: URI): string { let description = this.labelProvider.getLongName(uri.parent); if (this.workspaceService.isMultiRootWorkspaceOpened) { const rootUri = this.workspaceService.getWorkspaceRootUri(uri); if (rootUri) { - description = `${rootUri.displayName} • ${description}`; + description = `${this.labelProvider.getLongName(rootUri)} • ${description}`; } } - const icon = this.labelProvider.getIcon(uri); - const iconClass = icon === '' ? undefined : icon + ' file-icon'; - const options: QuickOpenItemOptions = { - label: this.labelProvider.getName(uri), - iconClass, - description, - tooltip: this.labelProvider.getLongName(uri), - uri: uri, - hidden: false, - run: this.getRunFunction(uri) - }; - if (group) { - return new QuickOpenGroupItem({ ...options, ...group }); - } else { - return new QuickOpenItem(options); - } + return description; } - private toNoResultsItem(): QuickOpenItem { - const options: QuickOpenItemOptions = { - label: 'No matching results', - run: () => false - }; - return new QuickOpenItem(options); + private getPlaceHolder(): string { + let placeholder = 'File name to search (append : to go to line).'; + const keybinding = this.getKeyCommand(); + if (keybinding) { + placeholder += ` (Press ${keybinding} to show/hide ignored files)`; + } + return placeholder; } /** @@ -425,7 +387,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { lineNumber = line > 0 ? line - 1 : 0; const column = parseInt(patternMatch[2] ?? '', 10); - startColumn = Number.isFinite(column) && column > 0 ? column - 1 : 0; + startColumn = Number.isFinite(column) && column > 0 ? column - 1 : 0; } } @@ -438,3 +400,28 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler { }; } } + +export class AnythingQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = ''; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(AnythingQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: AnythingQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // TODO: disposabled + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return AnythingQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/file-search/src/common/monaco.d.ts b/packages/file-search/src/common/monaco.d.ts new file mode 100644 index 0000000000000..f11e85e573305 --- /dev/null +++ b/packages/file-search/src/common/monaco.d.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 + ********************************************************************************/ + +// eslint-disable-next-line spaced-comment +/// diff --git a/packages/git/src/browser/git-quick-open-service.ts b/packages/git/src/browser/git-quick-open-service.ts index 25bf174136bb7..a0817ce80aaf9 100644 --- a/packages/git/src/browser/git-quick-open-service.ts +++ b/packages/git/src/browser/git-quick-open-service.ts @@ -14,9 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { QuickOpenItem, QuickOpenMode, QuickOpenModel } from '@theia/core/lib/common/quick-open-model'; -import { QuickOpenService, QuickOpenOptions } from '@theia/core/lib/browser/quick-open/quick-open-service'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { Git, Repository, Branch, BranchType, Tag, Remote, StashEntry } from '../common'; import { GitRepositoryProvider } from './git-repository-provider'; import { MessageService } from '@theia/core/lib/common/message-service'; @@ -24,7 +22,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service import { GitErrorHandler } from './git-error-handler'; import { ProgressService } from '@theia/core/lib/common/progress-service'; import URI from '@theia/core/lib/common/uri'; -import { LabelProvider } from '@theia/core/lib/browser'; +import { LabelProvider, QuickInputService, QuickPick, QuickPickItem } from '@theia/core/lib/browser'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileStat } from '@theia/filesystem/lib/common/files'; @@ -34,7 +32,7 @@ export enum GitAction { } /** - * Service delegating into the `Quick Open Service`, so that the Git commands can be further refined. + * Service delegating into the `Quick Input Service`, so that the Git commands can be further refined. * For instance, the `remote` can be specified for `pull`, `push`, and `fetch`. And the branch can be * specified for `git merge`. */ @@ -47,7 +45,7 @@ export class GitQuickOpenService { @inject(Git) protected readonly git: Git; @inject(GitRepositoryProvider) protected readonly repositoryProvider: GitRepositoryProvider; - @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(MessageService) protected readonly messageService: MessageService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(FileService) protected readonly fileService: FileService; @@ -69,33 +67,42 @@ export class GitQuickOpenService { return repo.localUri; } - const gitCloneLocalTargetFolder = folder; - const { git, buildDefaultProjectPath, gitErrorHandler, wrapWithProgress } = this; - const cloneRepoModel: QuickOpenModel = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - const dynamicItems: QuickOpenItem[] = []; - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; - if (lookFor === undefined || lookFor.length === 0) { - dynamicItems.push(new SingleStringInputOpenItem(`Please provide a Git repository location. ${suffix}`, () => { }, () => false)); - } else { - dynamicItems.push(new SingleStringInputOpenItem( - `Clone the Git repository: ${lookFor}. ${suffix}`, - wrapWithProgress(async () => { - try { - await git.clone(lookFor, { localUri: await buildDefaultProjectPath(gitCloneLocalTargetFolder, lookFor) }); - } catch (error) { - gitErrorHandler.handleError(error); - } - }) - )); - } - acceptor(dynamicItems); - } - }; - this.quickOpenService.open(cloneRepoModel, this.getOptions('Git repository location:', false)); + this.quickInputService?.showQuickPick([new GitQuickPickItem('Please provide a Git repository location. Press \'Enter\' to confirm or \'Escape\' to cancel.')], + { + placeholder: 'Git repository location:', + onDidChangeValue: (quickPick: QuickPick, filter: string) => this.query(quickPick, filter, folder) + }); }); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private query(quickPick: any, filter: string, folder: any): void { + quickPick.busy = true; + const { git, buildDefaultProjectPath, gitErrorHandler, wrapWithProgress } = this; + + try { + const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; + + if (filter === undefined || filter.length === 0) { + quickPick.items = [new GitQuickPickItem(`Please provide a Git repository location. ${suffix}`)]; + } else { + quickPick.items = [new GitQuickPickItem(`Clone the Git repository: ${filter}. ${suffix}`, + wrapWithProgress(async () => { + try { + await git.clone(filter, { localUri: await buildDefaultProjectPath(folder, filter) }); + } catch (error) { + gitErrorHandler.handleError(error); + } + }))]; + } + } catch (err) { + quickPick.items = [new GitQuickPickItem(`$(error) Error: ${err.message}`)]; + console.error(err); + } finally { + quickPick.busy = false; + } + } + private buildDefaultProjectPath = this.doBuildDefaultProjectPath.bind(this); private async doBuildDefaultProjectPath(folderPath: string, gitURI: string): Promise { if (!(await this.fileService.exists(new URI(folderPath)))) { @@ -117,25 +124,21 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const remotes = await this.getRemotes(); - const execute = async (item: QuickOpenItem) => { + const execute = async (item: GitQuickPickItem, lookFor: string) => { try { - await this.git.fetch(repository, { remote: item.getLabel() }); + await this.git.fetch(repository, { remote: item.ref!.name }); } catch (error) { this.gitErrorHandler.handleError(error); } }; - const items = remotes.map(remote => { - const toLabel = () => remote.name; - const toDescription = () => remote.fetch; - return new GitQuickOpenItem(remote.name, this.wrapWithProgress(execute), toLabel, toDescription); - }); - this.open(items, 'Pick a remote to fetch from:'); + const items = remotes.map(remote => new GitQuickPickItem(remote.name, execute, remote, remote.fetch)); + this.quickInputService?.showQuickPick(items, { placeholder: 'Pick a remote to fetch from:' }); }); } async performDefaultGitAction(action: GitAction): Promise { const remote = await this.getRemotes(); - const defaultRemote = remote[0].name; + const defaultRemote = remote[0]?.name; const repository = this.getRepository(); if (!repository) { return; @@ -162,20 +165,16 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [remotes, currentBranch] = await Promise.all([this.getRemotes(), this.getCurrentBranch()]); - const execute = async (item: QuickOpenItem) => { + const execute = async (item: GitQuickPickItem, lookFor: string) => { try { - await this.git.push(repository, { remote: item.getLabel(), setUpstream: true }); + await this.git.push(repository, { remote: item.label, setUpstream: true }); } catch (error) { this.gitErrorHandler.handleError(error); } }; - const items = remotes.map(remote => { - const toLabel = () => remote.name; - const toDescription = () => remote.push; - return new GitQuickOpenItem(remote.name, this.wrapWithProgress(execute), toLabel, toDescription); - }); + const items = remotes.map(remote => new GitQuickPickItem(remote.name, execute, remote, remote.push)); const branchName = currentBranch ? `'${currentBranch.name}' ` : ''; - this.open(items, `Pick a remote to push the currently active branch ${branchName}to:`); + this.quickInputService?.showQuickPick(items, { placeholder: `Pick a remote to push the currently active branch ${branchName}to:` }); }); } @@ -187,38 +186,34 @@ export class GitQuickOpenService { return this.withProgress(async () => { const remotes = await this.getRemotes(); const defaultRemote = remotes[0].name; // I wish I could use assignment destructuring here. (GH-413) - const executeRemote = async (remoteItem: GitQuickOpenItem) => { + const executeRemote = async (remoteItem: GitQuickPickItem, lookFor: string) => { // The first remote is the default. - if (remoteItem.ref.name === defaultRemote) { + if (remoteItem.ref!.name === defaultRemote) { try { - await this.git.pull(repository, { remote: remoteItem.getLabel() }); + await this.git.pull(repository, { remote: remoteItem.label }); } catch (error) { this.gitErrorHandler.handleError(error); } } else { // Otherwise we need to propose the branches from const branches = await this.getBranches(); - const executeBranch = async (branchItem: GitQuickOpenItem) => { + const executeBranch = async (branchItem: GitQuickPickItem, lookForBranch: string) => { try { - await this.git.pull(repository, { remote: remoteItem.ref.name, branch: branchItem.ref.nameWithoutRemote }); + await this.git.pull(repository, { remote: remoteItem.ref!.name, branch: branchItem.ref!.nameWithoutRemote }); } catch (error) { this.gitErrorHandler.handleError(error); } }; - const toLabel = (branchItem: GitQuickOpenItem) => branchItem.ref.name; const branchItems = branches .filter(branch => branch.type === BranchType.Remote) - .filter(branch => (branch.name || '').startsWith(`${remoteItem.ref}/`)) - .map(branch => new GitQuickOpenItem(branch, this.wrapWithProgress(executeBranch), toLabel)); - this.open(branchItems, 'Select the branch to pull the changes from:'); + .filter(branch => (branch.name || '').startsWith(`${remoteItem.label}/`)) + .map(branch => new GitQuickPickItem(branch.name, executeBranch, branch)); + + this.quickInputService?.showQuickPick(branchItems, { placeholder: 'Select the branch to pull the changes from:' }); } }; - const remoteItems = remotes.map(remote => { - const toLabel = () => remote.name; - const toDescription = () => remote.fetch; - return new GitQuickOpenItem(remote, this.wrapWithProgress(executeRemote), toLabel, toDescription); - }); - this.open(remoteItems, 'Pick a remote to pull the branch from:'); + const remoteItems = remotes.map(remote => new GitQuickPickItem(remote.name, executeRemote, remote, remote.fetch)); + this.quickInputService?.showQuickPick(remoteItems, { placeholder: 'Pick a remote to pull the branch from:' }); }); } @@ -229,17 +224,16 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [branches, currentBranch] = await Promise.all([this.getBranches(), this.getCurrentBranch()]); - const execute = async (item: GitQuickOpenItem) => { + const execute = async (item: GitQuickPickItem, lookFor: string) => { try { - await this.git.merge(repository, { branch: item.getLabel()! }); + await this.git.merge(repository, { branch: item.label }); } catch (error) { this.gitErrorHandler.handleError(error); } }; - const toLabel = (item: GitQuickOpenItem) => item.ref.name; - const items = branches.map(branch => new GitQuickOpenItem(branch, this.wrapWithProgress(execute), toLabel)); + const items = branches.map(branch => new GitQuickPickItem(branch.name, execute, branch)); const branchName = currentBranch ? `'${currentBranch.name}' ` : ''; - this.open(items, `Pick a branch to merge into the currently active ${branchName}branch:`); + this.quickInputService?.showQuickPick(items, { placeholder: `Pick a branch to merge into the currently active ${branchName}branch:` }); }); } @@ -255,56 +249,51 @@ export class GitQuickOpenService { const index = branches.findIndex(branch => branch && branch.name === currentBranch.name); branches.splice(index, 1); } - const switchBranch = async (item: GitQuickOpenItem) => { + const switchBranch = async (item: GitQuickPickItem, lookFor: string) => { try { - await this.git.checkout(repository, { branch: item.ref.nameWithoutRemote }); + await this.git.checkout(repository, { branch: item.ref!.nameWithoutRemote }); } catch (error) { this.gitErrorHandler.handleError(error); } }; - const toLabel = (item: GitQuickOpenItem) => { - const branch = item.ref; - return branch.type === BranchType.Remote ? branch.name : branch.nameWithoutRemote; - }; - const toDescription = (item: GitQuickOpenItem) => { - const branch = item.ref; - // We have only the long SHA1, but getting the first seven characters is the same. - const tip = branch.tip.sha.length > 8 ? ` ${branch.tip.sha.slice(0, 7)}` : ''; - return branch.type === BranchType.Remote ? `Remote branch at${tip}` : `${tip}`; - }; - const items: QuickOpenItem[] = branches.map(branch => new GitQuickOpenItem(branch, this.wrapWithProgress(switchBranch), toLabel, toDescription)); - const createBranchItem = async (item: QuickOpenItem) => { + + const items = branches.map(branch => new GitQuickPickItem( + branch.type === BranchType.Remote ? branch.name : branch.nameWithoutRemote, switchBranch, + branch, + branch.type === BranchType.Remote ? 'Remote branch at' : '' + `${(branch.tip.sha.length > 8 ? ` ${branch.tip.sha.slice(0, 7)}` : '')}`)); + + const createBranchItem = async () => { const { git, gitErrorHandler, wrapWithProgress } = this; - const createBranchModel: QuickOpenModel = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - const dynamicItems: QuickOpenItem[] = []; - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; - if (lookFor === undefined || lookFor.length === 0) { - dynamicItems.push(new SingleStringInputOpenItem(`Please provide a branch name. ${suffix}`, () => { }, () => false)); - } else { - dynamicItems.push(new SingleStringInputOpenItem( - `Create a new local branch with name: ${lookFor}. ${suffix}`, - wrapWithProgress(async () => { - try { - await git.branch(repository, { toCreate: lookFor }); - await git.checkout(repository, { branch: lookFor }); - } catch (error) { - gitErrorHandler.handleError(error); - } - }) - )); - } - acceptor(dynamicItems); + const getItems = (lookFor?: string) => { + const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; + const dynamicItems: GitQuickPickItem[] = []; + if (lookFor === undefined || lookFor.length === 0) { + dynamicItems.push(new GitQuickPickItem(`Please provide a branch name. ${suffix}`, () => { })); + } else { + dynamicItems.push(new GitQuickPickItem( + `Create a new local branch with name: ${lookFor}. ${suffix}`, + wrapWithProgress(async () => { + try { + await git.branch(repository, { toCreate: lookFor }); + await git.checkout(repository, { branch: lookFor }); + } catch (error) { + gitErrorHandler.handleError(error); + } + }) + )); } + return dynamicItems; }; - this.quickOpenService.open(createBranchModel, this.getOptions('The name of the branch:', false)); + this.quickInputService?.showQuickPick(getItems(), { + placeholder: 'The name of the branch:', + onDidChangeValue: (quickPick: QuickPick, filter: string) => { + quickPick.items = getItems(filter); + } + }); }; - items.unshift(new SingleStringInputOpenItem( - 'Create new branch...', - this.wrapWithProgress(createBranchItem), - (mode: QuickOpenMode) => mode === QuickOpenMode.OPEN, () => false)); - this.open(items, 'Select a ref to checkout or create a new local branch:'); + items.unshift(new GitQuickPickItem('Create new branch...', createBranchItem)); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select a ref to checkout or create a new local branch:' }); }); } @@ -314,15 +303,15 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [branches, tags, currentBranch] = await Promise.all([this.getBranches(repository), this.getTags(repository), this.getCurrentBranch(repository)]); - const execute = async (item: GitQuickOpenItem) => { - execFunc(item.ref.name, currentBranch ? currentBranch.name : ''); + const execute = async (item: GitQuickPickItem, lookFor: string) => { + execFunc(item.ref!.name, currentBranch ? currentBranch.name : ''); }; - const toLabel = (item: GitQuickOpenItem) => item.ref.name; - const branchItems = branches.map(branch => new GitQuickOpenItem(branch, this.wrapWithProgress(execute), toLabel)); + const branchItems = branches.map(branch => new GitQuickPickItem(branch.name, execute, branch)); const branchName = currentBranch ? `'${currentBranch.name}' ` : ''; - const tagItems = tags.map(tag => new GitQuickOpenItem(tag, execute, toLabel)); + const tagItems = tags.map(tag => new GitQuickPickItem(tag.name, execute, tag)); - this.open([...branchItems, ...tagItems], `Pick a branch or tag to compare with the currently active ${branchName} branch:`); + this.quickInputService?.showQuickPick([...branchItems, ...tagItems], + { placeholder: `Pick a branch or tag to compare with the currently active ${branchName} branch:` }); }); } @@ -337,25 +326,21 @@ export class GitQuickOpenService { throw new Error(`Repository ${repository.localUri} is not yet initialized.`); } const message = lastMessage.replace(/[\r\n]+/g, ' '); - const result = await new Promise((resolve, reject) => { - const createEditCommitMessageModel: QuickOpenModel = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - const dynamicItems: QuickOpenItem[] = []; - if (!lookFor) { - const description = "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."; - dynamicItems.push(new GitQuickOpenItem(description, () => resolve(lastMessage), () => description)); - } else { - dynamicItems.push(new GitQuickOpenItem("Rewrite previous commit message. Press 'Enter' to confirm or 'Escape' to cancel.", item => resolve(lookFor))); - } - acceptor(dynamicItems); - }, - }; - const onClose = (canceled: boolean): void => { - if (canceled) { - reject(new Error('User abort.')); + const result = await new Promise(async (resolve, reject) => { + const getItems = (lookFor?: string) => { + const items = []; + if (!lookFor) { + const label = "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."; + items.push(new GitQuickPickItem(label, () => resolve(lastMessage), label)); + } else { + items.push(new GitQuickPickItem("Rewrite previous commit message. Press 'Enter' to confirm or 'Escape' to cancel.", () => resolve(lookFor))); } + return items; }; - this.quickOpenService.open(createEditCommitMessageModel, this.getOptions(message, false, onClose)); + const updateItems = (quickPick: QuickPick, filter: string) => { + quickPick.items = getItems(filter); + }; + this.quickInputService?.showQuickPick(getItems(), { placeholder: message, onDidChangeValue: updateItems }); }); return result; }); @@ -370,26 +355,20 @@ export class GitQuickOpenService { const doStash = this.wrapWithProgress(async (message: string) => { this.git.stash(repository, { message }); }); - const quickOpenModel: QuickOpenModel = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - const dynamicItems: QuickOpenItem[] = []; - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; - - if (lookFor === undefined || lookFor.length === 0) { - dynamicItems.push(new SingleStringInputOpenItem( - `Stash changes. ${suffix}`, - () => doStash(lookFor) - )); - } else { - dynamicItems.push(new SingleStringInputOpenItem( - `Stash changes with message: ${lookFor}. ${suffix}`, - () => doStash(lookFor) - )); - } - acceptor(dynamicItems); + const getItems = (lookFor?: string) => { + const items = []; + const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; + if (lookFor === undefined || lookFor.length === 0) { + items.push(new GitQuickPickItem(`Stash changes. ${suffix}`, () => doStash(''))); + } else { + items.push(new GitQuickPickItem(`Stash changes with message: ${lookFor}. ${suffix}`, () => doStash(lookFor))); } + return items; + }; + const updateItems = (quickPick: QuickPick, filter: string) => { + quickPick.items = getItems(filter); }; - this.quickOpenService.open(quickOpenModel, this.getOptions('Stash message', false)); + this.quickInputService?.showQuickPick(getItems(), { placeholder: 'Stash message', onDidChangeValue: updateItems }); }); } @@ -401,20 +380,18 @@ export class GitQuickOpenService { return this.withProgress(async () => { const list = await this.git.stash(repository, { action: 'list' }); if (list) { - const quickOpenItems = list.map(stash => new GitQuickOpenItem(stash, this.wrapWithProgress(async () => { - try { - await this.git.stash(repository, { - action, - id: stash.id - }); - if (getMessage) { - this.messageService.info(await getMessage()); + const items = list.map(stash => new GitQuickPickItem(stash.message, + this.wrapWithProgress(async () => { + try { + await this.git.stash(repository, { action, id: stash.id }); + if (getMessage) { + this.messageService.info(await getMessage()); + } + } catch (error) { + this.gitErrorHandler.handleError(error); } - } catch (error) { - this.gitErrorHandler.handleError(error); - } - }), () => stash.message)); - this.open(quickOpenItems, text); + }))); + this.quickInputService?.showQuickPick(items, { placeholder: text }); } }); } @@ -480,9 +457,8 @@ export class GitQuickOpenService { async initRepository(): Promise { const wsRoots = await this.workspaceService.roots; if (wsRoots && wsRoots.length > 1) { - const placeholder = 'Choose workspace root to initialize git repo in'; - const items = wsRoots.map>(root => this.toRepositoryPathQuickOpenItem(root)); - this.open(items, placeholder); + const items = wsRoots.map>(root => this.toRepositoryPathQuickOpenItem(root)); + this.quickInputService?.showQuickPick(items, { placeholder: 'Choose workspace root to initialize git repo in' }); } else { const rootUri = wsRoots[0].resource; this.doInitRepository(rootUri.toString()); @@ -493,36 +469,13 @@ export class GitQuickOpenService { this.withProgress(async () => this.git.exec({ localUri: uri }, ['init'])); } - private toRepositoryPathQuickOpenItem(root: FileStat): GitQuickOpenItem { + private toRepositoryPathQuickOpenItem(root: FileStat): GitQuickPickItem { const rootUri = root.resource; - const toLabel = (item: GitQuickOpenItem) => this.labelProvider.getName(item.ref); - const toDescription = (item: GitQuickOpenItem) => this.labelProvider.getLongName(item.ref.parent); - const execute = async (item: GitQuickOpenItem) => { - const wsRoot = item.ref.toString(); + const execute = async (item: GitQuickPickItem, lookFor: string) => { + const wsRoot = item.ref!.toString(); this.doInitRepository(wsRoot); }; - return new GitQuickOpenItem(rootUri, execute, toLabel, toDescription); - } - - private open(items: QuickOpenItem | QuickOpenItem[], placeholder: string): void { - this.quickOpenService.open(this.getModel(Array.isArray(items) ? items : [items]), this.getOptions(placeholder)); - } - - private getOptions(placeholder: string, fuzzyMatchLabel: boolean = true, onClose: (canceled: boolean) => void = () => { }): QuickOpenOptions { - return QuickOpenOptions.resolve({ - placeholder, - fuzzyMatchLabel, - fuzzySort: false, - onClose - }); - } - - private getModel(items: QuickOpenItem | QuickOpenItem[]): QuickOpenModel { - return { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(Array.isArray(items) ? items : [items]); - } - }; + return new GitQuickPickItem(this.labelProvider.getName(rootUri), execute, rootUri, this.labelProvider.getLongName(rootUri.parent)); } private getRepository(): Repository | undefined { @@ -594,62 +547,14 @@ export class GitQuickOpenService { protected doWrapWithProgress(fn: (...args: In[]) => Promise): (...args: In[]) => Promise { return (...args: In[]) => this.withProgress(() => fn(...args)); } - } -/** - * Git specific quick open item that wraps a branch a remote name or something else. - */ -class GitQuickOpenItem extends QuickOpenItem { - - constructor( - public readonly ref: T, - protected readonly execute: (item: GitQuickOpenItem) => void, - private readonly toLabel: (item: GitQuickOpenItem) => string = (item: QuickOpenItem) => `${ref}`, - private readonly toDescription: (item: GitQuickOpenItem) => string | undefined = (item: QuickOpenItem) => undefined) { - - super(); - } - - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.execute(this); - return true; - } - - getLabel(): string { - return this.toLabel(this); - } - - getDescription(): string | undefined { - return this.toDescription(this); - } - -} - -class SingleStringInputOpenItem extends QuickOpenItem { - +class GitQuickPickItem implements QuickPickItem { constructor( - private readonly label: string, - private readonly execute: (item: QuickOpenItem) => void = () => { }, - private readonly canRun: (mode: QuickOpenMode) => boolean = mode => mode === QuickOpenMode.OPEN, - private readonly canClose: (mode: QuickOpenMode) => boolean = mode => true) { - - super(); - } - - getLabel(): string { - return this.label; - } - - run(mode: QuickOpenMode): boolean { - if (!this.canRun(mode)) { - return false; - } - this.execute(this); - return this.canClose(mode); - } - + public label: string, + public readonly execute?: (item: QuickPickItem, lookFor: string) => void, + public readonly ref?: T, + public description?: string, + public alwaysShow = true, + public sortByLabel = false) { } } diff --git a/packages/git/src/browser/git-sync-service.ts b/packages/git/src/browser/git-sync-service.ts index d76133cdac914..e5cdc2f32af3d 100644 --- a/packages/git/src/browser/git-sync-service.ts +++ b/packages/git/src/browser/git-sync-service.ts @@ -14,9 +14,9 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { MessageService, Emitter, Event } from '@theia/core'; -import { QuickPickService, ConfirmDialog } from '@theia/core/lib/browser'; +import { ConfirmDialog, QuickInputService } from '@theia/core/lib/browser'; import { GitRepositoryTracker } from './git-repository-tracker'; import { Git, Repository, WorkingDirectoryStatus } from '../common'; import { GitErrorHandler } from './git-error-handler'; @@ -36,8 +36,8 @@ export class GitSyncService { @inject(GitErrorHandler) protected readonly gitErrorHandler: GitErrorHandler; - @inject(QuickPickService) - protected readonly quickPickService: QuickPickService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; protected readonly onDidChangeEmitter = new Emitter(); readonly onDidChange: Event = this.onDidChangeEmitter.event; @@ -107,27 +107,27 @@ export class GitSyncService { const methods: { label: string warning: string - value: GitSyncService.SyncMethod + detail: GitSyncService.SyncMethod }[] = [{ label: `Pull and push commits from and to '${upstreamBranch}'`, warning: `This action will pull and push commits from and to '${upstreamBranch}'.`, - value: 'pull-push' + detail: 'pull-push' }, { label: `Fetch, rebase and push commits from and to '${upstreamBranch}'`, warning: `This action will fetch, rebase and push commits from and to '${upstreamBranch}'.`, - value: 'rebase-push' + detail: 'rebase-push' }, { label: `Force push commits to '${upstreamBranch}'`, warning: `This action will override commits in '${upstreamBranch}'.`, - value: 'force-push' + detail: 'force-push' }]; - const method = await this.quickPickService.show(methods, { - placeholder: 'Pick how changes should be synchronized:' - }); - if (method && await this.confirm('Synchronize Changes', methods.find(({ value }) => value === method)!.warning)) { - return method; + + const selectedCWD = await this.quickInputService?.showQuickPick(methods, { placeholder: 'Select current working directory for new terminal' }); + if (await this.confirm('Synchronize Changes', methods.find(({ detail }) => detail === selectedCWD.detail)!.warning)) { + return (selectedCWD.detail as GitSyncService.SyncMethod); + } else { + return (undefined); } - return undefined; } canPublish(): boolean { @@ -162,9 +162,10 @@ export class GitSyncService { if (remotes.length === 0) { this.messageService.warn('Your repository has no remotes configured to publish to.'); } - return this.quickPickService.show(remotes, { - placeholder: `Pick a remote to publish the branch ${branch} to:` - }); + + const selectedRemote = await this.quickInputService?.showQuickPick(remotes.map(remote => ({ label: remote })), + { placeholder: `Pick a remote to publish the branch ${branch} to:` }); + return selectedRemote.label; } protected shouldPush(status: WorkingDirectoryStatus): boolean { diff --git a/packages/git/src/electron-browser/prompt/git-quick-open-prompt.ts b/packages/git/src/electron-browser/prompt/git-quick-open-prompt.ts index 219759467feea..3e0ddffa91c92 100644 --- a/packages/git/src/electron-browser/prompt/git-quick-open-prompt.ts +++ b/packages/git/src/electron-browser/prompt/git-quick-open-prompt.ts @@ -14,59 +14,36 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; +import { QuickInputService } from '@theia/core/lib/browser'; import * as PQueue from 'p-queue'; -import { QuickOpenItem, QuickOpenMode } from '@theia/core/lib/browser/quick-open/quick-open-model'; -import { QuickOpenService } from '@theia/core/lib/browser/quick-open/quick-open-service'; import { GitPrompt } from '../../common/git-prompt'; @injectable() export class GitQuickOpenPrompt extends GitPrompt { - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); async ask(question: GitPrompt.Question): Promise { return this.queue.add(() => { const { details, text, password } = question; - return new Promise(resolve => { - const model = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor([ - new QuickOpenItem({ - label: details, - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - resolve(GitPrompt.Success.create(lookFor)); - return true; - } - }) - ]); - } - }; - const options = { - onClose: (canceled: boolean): void => { - if (canceled) { - resolve(GitPrompt.Cancel.create()); - } - }, - placeholder: text, - password - }; - this.quickOpenService.open(model, options); + return new Promise(async resolve => { + const result = await this.quickInputService?.input({ + placeHolder: text, + prompt: details!, + password, + }); + resolve(GitPrompt.Success.create(result!)); }); }); } - dispose(): void { if (!this.queue.isPaused) { this.queue.pause(); } this.queue.clear(); } - } diff --git a/packages/mini-browser/src/browser/mini-browser-open-handler.ts b/packages/mini-browser/src/browser/mini-browser-open-handler.ts index 3cf5fc36b20df..2ad0fd612e792 100644 --- a/packages/mini-browser/src/browser/mini-browser-open-handler.ts +++ b/packages/mini-browser/src/browser/mini-browser-open-handler.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import { Widget } from '@theia/core/shared/@phosphor/widgets'; -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { MaybePromise } from '@theia/core/lib/common/types'; import { QuickInputService } from '@theia/core/lib/browser'; @@ -81,7 +81,7 @@ export class MiniBrowserOpenHandler extends NavigatableWidgetOpenHandler { - const url = arg ? arg : await this.quickInputService.open({ + const url = arg ? arg : await this.quickInputService?.input({ prompt: 'URL to open', placeHolder: 'Type a URL' }); diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 7013b557b7dec..9ef220ebec537 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -7,7 +7,7 @@ "@theia/editor": "1.13.0", "@theia/filesystem": "1.13.0", "@theia/markers": "1.13.0", - "@theia/monaco-editor-core": "^0.20.0", + "@theia/monaco-editor-core": "^0.23.0-next-82e8ea39fc101d63.2", "@theia/outline-view": "1.13.0", "@theia/workspace": "1.13.0", "deepmerge": "2.0.1", diff --git a/packages/monaco/src/browser/monaco-browser-module.ts b/packages/monaco/src/browser/monaco-browser-module.ts index ac02f67644c9a..10ffead0775d6 100644 --- a/packages/monaco/src/browser/monaco-browser-module.ts +++ b/packages/monaco/src/browser/monaco-browser-module.ts @@ -21,6 +21,10 @@ export { ContainerModule }; export default loadVsRequire(window) .then(vsRequire => loadMonaco(vsRequire)) + .then(() => + // Clear Monaco QuickAccessRegistry as it currently includes monaco internal providers and not Theia's providers + monaco.platform.Registry.as('workbench.contributions.quickaccess').clear() + ) .then(() => import('./monaco-frontend-module') ).then(module => diff --git a/packages/monaco/src/browser/monaco-color-registry.ts b/packages/monaco/src/browser/monaco-color-registry.ts index 0c95dd4057f2f..214406386bffa 100644 --- a/packages/monaco/src/browser/monaco-color-registry.ts +++ b/packages/monaco/src/browser/monaco-color-registry.ts @@ -31,7 +31,7 @@ export class MonacoColorRegistry extends ColorRegistry { } getCurrentColor(id: string): string | undefined { - const color = this.monacoThemeService.getTheme().getColor(id); + const color = this.monacoThemeService.getColorTheme().getColor(id); return color && color.toString(); } diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index b42ad6da80b76..52d1530cc56bd 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -14,12 +14,10 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { Position, Location } from '@theia/core/shared/vscode-languageserver-types'; import { CommandContribution, CommandRegistry, CommandHandler } from '@theia/core/lib/common/command'; -import { CommonCommands } from '@theia/core/lib/browser'; -import { QuickOpenService } from '@theia/core/lib/browser/quick-open/quick-open-service'; -import { QuickOpenItem, QuickOpenMode } from '@theia/core/lib/browser/quick-open/quick-open-model'; +import { CommonCommands, QuickInputService } from '@theia/core/lib/browser'; import { EditorCommands } from '@theia/editor/lib/browser'; import { MonacoEditor } from './monaco-editor'; import { MonacoCommandRegistry, MonacoEditorCommandHandler } from './monaco-command-registry'; @@ -59,8 +57,8 @@ export class MonacoEditorCommandHandlers implements CommandContribution { @inject(ProtocolToMonacoConverter) protected readonly p2m: ProtocolToMonacoConverter; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(MonacoEditorService) protected readonly codeEditorService: MonacoEditorService; @@ -208,21 +206,12 @@ export class MonacoEditorCommandHandlers implements CommandContribution { }; } protected configureIndentation(editor: MonacoEditor): void { - const options = [true, false].map(useSpaces => - new QuickOpenItem({ - label: `Indent Using ${useSpaces ? 'Spaces' : 'Tabs'}`, - run: (mode: QuickOpenMode) => { - if (mode === QuickOpenMode.OPEN) { - this.configureTabSize(editor, useSpaces); - } - return false; - } - }) + const items = [true, false].map(useSpaces => ({ + label: `Indent Using ${useSpaces ? 'Spaces' : 'Tabs'}`, + execute: () => this.configureTabSize(editor, useSpaces) + }) ); - this.quickOpenService.open({ onType: (_, acceptor) => acceptor(options) }, { - placeholder: 'Select Action', - fuzzyMatchLabel: true - }); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select Action' }); } protected newConfigEolHandler(): MonacoEditorCommandHandler { @@ -232,22 +221,12 @@ export class MonacoEditorCommandHandlers implements CommandContribution { } protected configureEol(editor: MonacoEditor): void { - const options = ['LF', 'CRLF'].map(lineEnding => - new QuickOpenItem({ - label: lineEnding, - run: (mode: QuickOpenMode) => { - if (mode === QuickOpenMode.OPEN) { - this.setEol(editor, lineEnding); - return true; - } - return false; - } - }) - ); - this.quickOpenService.open({ onType: (_, acceptor) => acceptor(options) }, { - placeholder: 'Select End of Line Sequence', - fuzzyMatchLabel: true - }); + const items = ['LF', 'CRLF'].map(lineEnding => + ({ + label: lineEnding, + execute: () => this.setEol(editor, lineEnding) + })); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select End of Line Sequence' }); } protected setEol(editor: MonacoEditor, lineEnding: string): void { @@ -272,31 +251,15 @@ export class MonacoEditorCommandHandlers implements CommandContribution { const { tabSize } = model.getOptions(); const sizes = Array.from(Array(8), (_, x) => x + 1); const tabSizeOptions = sizes.map(size => - new QuickOpenItem({ - label: size === tabSize ? `${size} Configured Tab Size` : size.toString(), - run: (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - model.updateOptions({ - tabSize: size || tabSize, - insertSpaces: useSpaces - }); - return true; - } + ({ + label: size === tabSize ? `${size} Configured Tab Size` : size.toString(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + execute: (quickPick: any, lookFor: string) => model.updateOptions({ + tabSize: size || tabSize, + insertSpaces: useSpaces }) - ); - this.quickOpenService.open({ onType: (_, acceptor) => acceptor(tabSizeOptions) }, { - placeholder: 'Select Tab Size for Current File', - fuzzyMatchLabel: true, - selectIndex: lookFor => { - if (!lookFor || lookFor === '') { - return tabSize - 1; - } - return 0; - } - }); + })); + this.quickInputService?.showQuickPick(tabSizeOptions, { placeholder: 'Select Tab Size for Current File' }); } } - } diff --git a/packages/monaco/src/browser/monaco-comparers.ts b/packages/monaco/src/browser/monaco-comparers.ts index 6e1c26a8fbaf1..201c5c31d5451 100644 --- a/packages/monaco/src/browser/monaco-comparers.ts +++ b/packages/monaco/src/browser/monaco-comparers.ts @@ -22,7 +22,6 @@ import strings = monaco.strings; import IdleValue = monaco.async.IdleValue; -import QuickOpenEntry = monaco.quickOpen.QuickOpenEntry; let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>; @@ -124,38 +123,3 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu return 0; } - -/** - * A good default sort implementation for quick open entries respecting highlight information - * as well as associated resources. - */ -// copied from vscode: https://github.com/microsoft/vscode/blob/standalone/0.17.x/src/vs/base/parts/quickopen/browser/quickOpenModel.ts#L584 -export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number { - - // Give matches with label highlights higher priority over - // those with only description highlights - const labelHighlightsA = elementA.getHighlights()[0] || []; - const labelHighlightsB = elementB.getHighlights()[0] || []; - if (labelHighlightsA.length && !labelHighlightsB.length) { - return -1; - } - - if (!labelHighlightsA.length && labelHighlightsB.length) { - return 1; - } - - // Fallback to the full path if labels are identical and we have associated resources - let nameA = elementA.getLabel()!; - let nameB = elementB.getLabel()!; - if (nameA === nameB) { - const resourceA = elementA.getResource(); - const resourceB = elementB.getResource(); - - if (resourceA && resourceB) { - nameA = resourceA.fsPath; - nameB = resourceB.fsPath; - } - } - - return compareAnything(nameA, nameB, lookFor); -} diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index 2ff794943e8dc..a0c94cb32aa77 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -471,7 +471,7 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument { protected applyEdits( operations: monaco.editor.IIdentifiedSingleEditOperation[], options?: Partial - ): monaco.editor.IIdentifiedSingleEditOperation[] { + ): void { return this.updateModel(() => this.model.applyEdits(operations), options); } diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 62c50b03d2566..52bd1b7976884 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -28,7 +28,6 @@ import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { MonacoEditor, MonacoEditorServices } from './monaco-editor'; import { MonacoEditorModel, WillSaveMonacoModelEvent } from './monaco-editor-model'; import { MonacoEditorService } from './monaco-editor-service'; -import { MonacoQuickOpenService } from './monaco-quick-open-service'; import { MonacoTextModelService } from './monaco-text-model-service'; import { MonacoWorkspace } from './monaco-workspace'; import { MonacoBulkEditService } from './monaco-bulk-edit-service'; @@ -90,7 +89,6 @@ export class MonacoEditorProvider { @inject(MonacoWorkspace) protected readonly workspace: MonacoWorkspace, @inject(MonacoCommandServiceFactory) protected readonly commandServiceFactory: MonacoCommandServiceFactory, @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences, - @inject(MonacoQuickOpenService) protected readonly quickOpenService: MonacoQuickOpenService, @inject(MonacoDiffNavigatorFactory) protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, /** @deprecated since 1.6.0 */ @inject(ApplicationServer) protected readonly applicationServer: ApplicationServer, @@ -99,32 +97,6 @@ export class MonacoEditorProvider { const staticServices = monaco.services.StaticServices; const init = staticServices.init.bind(monaco.services.StaticServices); - const themeService = staticServices.standaloneThemeService.get(); - const originalGetTheme: (typeof themeService)['getTheme'] = themeService.getTheme.bind(themeService); - const patchedGetTokenStyleMetadataFlag = '__patched_getTokenStyleMetadata'; - // based on https://github.com/microsoft/vscode/commit/4731a227e377da8cb14ed5697dd1ba8faea40538 - // TODO remove after migrating to monaco 0.21 - themeService.getTheme = () => { - const theme = originalGetTheme(); - if (!(patchedGetTokenStyleMetadataFlag in theme)) { - Object.defineProperty(theme, patchedGetTokenStyleMetadataFlag, { enumerable: false, configurable: false, writable: false, value: true }); - theme.getTokenStyleMetadata = (type, modifiers) => { - // use theme rules match - const style = theme.tokenTheme._match([type].concat(modifiers).join('.')); - const metadata = style.metadata; - const foreground = monaco.modes.TokenMetadata.getForeground(metadata); - const fontStyle = monaco.modes.TokenMetadata.getFontStyle(metadata); - return { - foreground: foreground, - italic: Boolean(fontStyle & monaco.modes.FontStyle.Italic), - bold: Boolean(fontStyle & monaco.modes.FontStyle.Bold), - underline: Boolean(fontStyle & monaco.modes.FontStyle.Underline) - }; - }; - } - return theme; - }; - monaco.services.StaticServices.init = o => { const result = init(o); result[0].set(monaco.services.ICodeEditorService, codeEditorService); @@ -153,7 +125,8 @@ export class MonacoEditorProvider { protected async doCreateEditor(uri: URI, factory: (override: IEditorOverrideServices, toDispose: DisposableCollection) => Promise): Promise { const commandService = this.commandServiceFactory(); - const contextKeyService = this.contextKeyService.createScoped(); + const domNode = document.createElement('div'); + const contextKeyService = this.contextKeyService.createScoped(domNode); const { codeEditorService, textModelService, contextMenuService } = this; const IWorkspaceEditService = this.bulkEditService; const toDispose = new DisposableCollection(commandService); @@ -177,7 +150,6 @@ export class MonacoEditorProvider { const standaloneCommandService = new monaco.services.StandaloneCommandService(editor.instantiationService); commandService.setDelegate(standaloneCommandService); - toDispose.push(this.installQuickOpenService(editor)); toDispose.push(this.installReferencesController(editor)); toDispose.push(editor.onFocusChanged(focused => { @@ -419,41 +391,6 @@ export class MonacoEditorProvider { } } - protected installQuickOpenService(editor: MonacoEditor): Disposable { - const control = editor.getControl(); - const quickOpenController = control._contributions['editor.controller.quickOpenController']; - const originalRun = quickOpenController.run; - const toDispose = new DisposableCollection(); - quickOpenController.dispose = () => toDispose.dispose(); - quickOpenController.run = options => { - const toDisposeOnClose = toDispose.push(Disposable.create(() => this.quickOpenService.hide())); - - const selection = control.getSelection(); - this.quickOpenService.internalOpen({ - ...options, - onClose: canceled => { - toDisposeOnClose.dispose(); - - quickOpenController.clearDecorations(); - - // Restore selection if canceled - if (canceled && selection) { - control.setSelection(selection); - control.revealRangeInCenterIfOutsideViewport(selection); - } - - // Return focus to the editor if - // - focus is back on the element because no other focusable element was clicked - // - a command was picked from the picker which indicates the editor should get focused - if (document.activeElement === document.body || !canceled) { - editor.focus(); - } - } - }); - }; - return Disposable.create(() => quickOpenController.run = originalRun); - } - protected installReferencesController(editor: MonacoEditor): Disposable { const control = editor.getControl(); const referencesController = control._contributions['editor.contrib.referencesController']; diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts index 0c1deb1254151..2c291528db0ac 100644 --- a/packages/monaco/src/browser/monaco-editor-service.ts +++ b/packages/monaco/src/browser/monaco-editor-service.ts @@ -49,7 +49,7 @@ export class MonacoEditorService extends monaco.services.CodeEditorServiceImpl { protected readonly preferencesService: PreferenceService; constructor() { - super(monaco.services.StaticServices.standaloneThemeService.get()); + super(undefined, monaco.services.StaticServices.standaloneThemeService.get()); } /** diff --git a/packages/monaco/src/browser/monaco-editor-zone-widget.ts b/packages/monaco/src/browser/monaco-editor-zone-widget.ts index 6e5768e5cb5e9..abfa792d7b71a 100644 --- a/packages/monaco/src/browser/monaco-editor-zone-widget.ts +++ b/packages/monaco/src/browser/monaco-editor-zone-widget.ts @@ -133,7 +133,7 @@ export class MonacoEditorZoneWidget implements Disposable { } protected updateTop(top: number): void { - this.zoneNode.style.top = top + (this.showArrow ? 6 : 0) + 'px'; + this.zoneNode.style.top = top + (this.showArrow ? 6 : 0) + 'px'; } protected updateHeight(zoneHeight: number): void { this.zoneNode.style.height = zoneHeight + 'px'; @@ -166,12 +166,12 @@ export class MonacoEditorZoneWidget implements Disposable { this.zoneNode.style.left = this.computeLeft(info) + 'px'; } protected computeWidth(info: monaco.editor.EditorLayoutInfo = this.editor.getLayoutInfo()): number { - return info.width - info.minimapWidth - info.verticalScrollbarWidth; + return info.width - info.minimap.minimapWidth - info.verticalScrollbarWidth; } protected computeLeft(info: monaco.editor.EditorLayoutInfo = this.editor.getLayoutInfo()): number { // If minimap is to the left, we move beyond it - if (info.minimapWidth > 0 && info.minimapLeft === 0) { - return info.minimapWidth; + if (info.minimap.minimapWidth > 0 && info.minimap.minimapLeft === 0) { + return info.minimap.minimapWidth; } return 0; } diff --git a/packages/monaco/src/browser/monaco-formatting-conflicts.ts b/packages/monaco/src/browser/monaco-formatting-conflicts.ts index 33cbb4eeff018..d2d36c38d468f 100644 --- a/packages/monaco/src/browser/monaco-formatting-conflicts.ts +++ b/packages/monaco/src/browser/monaco-formatting-conflicts.ts @@ -15,11 +15,9 @@ ********************************************************************************/ import { injectable, inject } from '@theia/core/shared/inversify'; -import { MonacoQuickOpenService } from './monaco-quick-open-service'; -import { QuickOpenModel, QuickOpenItem, QuickOpenMode } from '@theia/core/lib/common/quick-open-model'; -import { Deferred } from '@theia/core/lib/common/promise-util'; import { PreferenceService, FrontendApplicationContribution, PreferenceLanguageOverrideService } from '@theia/core/lib/browser'; import { EditorManager } from '@theia/editor/lib/browser'; +import { MonacoQuickInputService } from './monaco-quick-input-service'; type FormattingEditProvider = monaco.languages.DocumentFormattingEditProvider | monaco.languages.DocumentRangeFormattingEditProvider; @@ -28,8 +26,8 @@ const PREFERENCE_NAME = 'editor.defaultFormatter'; @injectable() export class MonacoFormattingConflictsContribution implements FrontendApplicationContribution { - @inject(MonacoQuickOpenService) - protected readonly quickOpenService: MonacoQuickOpenService; + @inject(MonacoQuickInputService) + protected readonly monacoQuickInputService: MonacoQuickInputService; @inject(PreferenceService) protected readonly preferenceService: PreferenceService; @@ -81,7 +79,7 @@ export class MonacoFormattingConflictsContribution implements FrontendApplicatio } const languageId = currentEditor.editor.document.languageId; - const defaultFormatterId = await this.getDefaultFormatter(languageId); + const defaultFormatterId = this.getDefaultFormatter(languageId); if (defaultFormatterId) { const formatter = formatters.find(f => f.extensionId && f.extensionId.value === defaultFormatterId); @@ -90,59 +88,19 @@ export class MonacoFormattingConflictsContribution implements FrontendApplicatio } } - let deferred: Deferred | undefined = new Deferred(); - - const items: QuickOpenItem[] = formatters - .filter(formatter => formatter.displayName) - .map(formatter => { - const displayName: string = formatter.displayName!; - const extensionId = formatter.extensionId ? formatter.extensionId.value : undefined; - - return new QuickOpenItem({ - label: displayName, - detail: extensionId, - run: (openMode: QuickOpenMode) => { - if (openMode === QuickOpenMode.OPEN) { - if (deferred) { - deferred.resolve(formatter); - deferred = undefined; - } - - this.quickOpenService.hide(); - - this.setDefaultFormatter(languageId, extensionId ? extensionId : ''); - return true; - } - - return false; - } - }); - }) - .sort((a, b) => a.getLabel()!.localeCompare(b.getLabel()!)); - - const model: QuickOpenModel = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(items); - } - }; - - this.quickOpenService.open(model, - { - fuzzyMatchDescription: true, - fuzzyMatchLabel: true, - fuzzyMatchDetail: true, - placeholder: 'Select formatter for the current document', - ignoreFocusOut: false, - - onClose: () => { - if (deferred) { - deferred.resolve(undefined); - deferred = undefined; - } - } - }); - - return deferred.promise; + return new Promise(async (resolve, reject) => { + const items = formatters + .filter(formatter => formatter.displayName) + .map(formatter => ({ + label: formatter.displayName!, + detail: formatter.extensionId ? formatter.extensionId.value : undefined, + value: formatter, + })) + .sort((a, b) => a.label!.localeCompare(b.label!)); + + const selectedFormatter = await this.monacoQuickInputService.showQuickPick(items, { placeholder: 'Select formatter for the current document' }); + this.setDefaultFormatter(languageId, selectedFormatter.detail ? selectedFormatter.detail : ''); + resolve(selectedFormatter.value); + }); } - } diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index ffd8d7c1d49a1..8af457d644a53 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -17,12 +17,12 @@ import '../../src/browser/style/index.css'; import '../../src/browser/style/symbol-sprite.svg'; import '../../src/browser/style/symbol-icons.css'; - import { ContainerModule, decorate, injectable, interfaces } from '@theia/core/shared/inversify'; import { MenuContribution, CommandContribution } from '@theia/core/lib/common'; import { - QuickOpenService, FrontendApplicationContribution, KeybindingContribution, - PreferenceService, PreferenceSchemaProvider, createPreferenceProxy, QuickOpenContribution, PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN + FrontendApplicationContribution, KeybindingContribution, + PreferenceService, PreferenceSchemaProvider, createPreferenceProxy, + PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService, QuickCommandService, QuickHelpService, QuickViewService, QuickEditorService } from '@theia/core/lib/browser'; import { TextEditorProvider, DiffNavigatorProvider } from '@theia/editor/lib/browser'; import { StrictEditorTextFocusContext } from '@theia/editor/lib/browser/editor-keybinding-contexts'; @@ -39,7 +39,6 @@ import { MonacoOutlineContribution } from './monaco-outline-contribution'; import { MonacoStatusBarContribution } from './monaco-status-bar-contribution'; import { MonacoCommandService, MonacoCommandServiceFactory } from './monaco-command-service'; import { MonacoCommandRegistry } from './monaco-command-registry'; -import { MonacoQuickOpenService } from './monaco-quick-open-service'; import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { MonacoStrictEditorTextFocusContext } from './monaco-keybinding-contexts'; import { MonacoFrontendApplicationContribution } from './monaco-frontend-application-contribution'; @@ -62,6 +61,12 @@ import { LanguageService } from '@theia/core/lib/browser/language-service'; import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter'; import { MonacoFormattingConflictsContribution } from './monaco-formatting-conflicts'; +import { MonacoQuickInputService } from './monaco-quick-input-service'; +import { MonacoQuickCommandService } from './monaco-quick-command-service'; +import { MonacoQuickHelpService } from './monaco-quick-help-service'; +import { MonacoQuickViewService } from './monaco-quick-view-service'; +import { MonacoQuickEditorService } from './monaco-quick-editor-service'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; decorate(injectable(), monaco.contextKeyService.ContextKeyService); @@ -82,7 +87,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoLanguages).toSelf().inSingletonScope(); rebind(LanguageService).toService(MonacoLanguages); bind(WorkspaceSymbolCommand).toSelf().inSingletonScope(); - for (const identifier of [CommandContribution, KeybindingContribution, QuickOpenContribution]) { + for (const identifier of [CommandContribution, KeybindingContribution, QuickAccessContribution]) { bind(identifier).toService(WorkspaceSymbolCommand); } @@ -94,6 +99,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(monaco.contextKeyService.ContextKeyService).toDynamicValue(({ container }) => new monaco.contextKeyService.ContextKeyService(container.get(MonacoConfigurationService)) ).inSingletonScope(); + bind(MonacoBulkEditService).toSelf().inSingletonScope(); bind(MonacoEditorService).toSelf().inSingletonScope(); bind(MonacoTextModelService).toSelf().inSingletonScope(); @@ -131,8 +137,20 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(KeybindingContribution).toService(MonacoKeybindingContribution); rebind(StrictEditorTextFocusContext).to(MonacoStrictEditorTextFocusContext).inSingletonScope(); - bind(MonacoQuickOpenService).toSelf().inSingletonScope(); - rebind(QuickOpenService).toService(MonacoQuickOpenService); + bind(MonacoQuickInputService).toSelf().inSingletonScope(); + bind(QuickInputService).toService(MonacoQuickInputService); + + bind(MonacoQuickCommandService).toSelf().inSingletonScope(); + bind(QuickCommandService).toService(MonacoQuickCommandService); + + bind(MonacoQuickHelpService).toSelf().inSingletonScope(); + bind(QuickHelpService).toService(MonacoQuickHelpService); + + bind(MonacoQuickViewService).toSelf().inSingletonScope(); + bind(QuickViewService).toService(MonacoQuickViewService); + + bind(MonacoQuickEditorService).toSelf().inSingletonScope(); + bind(QuickEditorService).toService(MonacoQuickEditorService); MonacoTextmateModuleBinder(bind, unbind, isBound, rebind); diff --git a/packages/monaco/src/browser/monaco-loader.ts b/packages/monaco/src/browser/monaco-loader.ts index ceb3fcaa2c3b2..b086ab278b71d 100644 --- a/packages/monaco/src/browser/monaco-loader.ts +++ b/packages/monaco/src/browser/monaco-loader.ts @@ -56,8 +56,13 @@ export function loadMonaco(vsRequire: any): Promise { 'vs/editor/standalone/browser/simpleServices', 'vs/editor/standalone/browser/standaloneServices', 'vs/editor/standalone/browser/standaloneLanguages', - 'vs/base/parts/quickopen/browser/quickOpenWidget', - 'vs/base/parts/quickopen/browser/quickOpenModel', + 'vs/base/parts/quickinput/browser/quickInput', + 'vs/platform/quickinput/browser/quickInput', + 'vs/platform/quickinput/common/quickAccess', + 'vs/platform/quickinput/browser/quickAccess', + 'vs/platform/quickinput/browser/pickerQuickAccess', + 'vs/base/browser/ui/list/listWidget', + 'vs/platform/registry/common/platform', 'vs/base/common/filters', 'vs/platform/theme/common/themeService', 'vs/platform/theme/common/styler', @@ -86,7 +91,9 @@ export function loadMonaco(vsRequire: any): Promise { ], (commands: any, actions: any, keybindingsRegistry: any, keybindingResolver: any, resolvedKeybinding: any, keybindingLabels: any, keyCodes: any, mime: any, editorExtensions: any, simpleServices: any, - standaloneServices: any, standaloneLanguages: any, quickOpenWidget: any, quickOpenModel: any, + standaloneServices: any, standaloneLanguages: any, quickInput: any, quickInputPlatform: any, + quickAccess: any, quickAccessBrowser: any, pickerQuickAccess: any, listWidget: any, // helpQuickAccess: any, commandsQuickAccess: any, + platformRegistry: any, filters: any, themeService: any, styler: any, colorRegistry: any, color: any, platform: any, modes: any, suggest: any, snippetParser: any, format: any, @@ -105,11 +112,12 @@ export function loadMonaco(vsRequire: any): Promise { global.monaco.services = Object.assign({}, simpleServices, standaloneServices, standaloneLanguages, configuration, configurationModels, resolverService, codeEditorService, codeEditorServiceImpl, markerService, openerService); - global.monaco.quickOpen = Object.assign({}, quickOpenWidget, quickOpenModel); + global.monaco.quickInput = Object.assign({}, quickInput, quickAccess, quickAccessBrowser, quickInputPlatform, + pickerQuickAccess); global.monaco.filters = filters; global.monaco.theme = Object.assign({}, themeService, styler); global.monaco.color = Object.assign({}, colorRegistry, color); - global.monaco.platform = platform; + global.monaco.platform = Object.assign({}, platform, platformRegistry); global.monaco.editorExtensions = editorExtensions; global.monaco.modes = modes; global.monaco.suggest = suggest; @@ -124,6 +132,7 @@ export function loadMonaco(vsRequire: any): Promise { global.monaco.textModel = textModel; global.monaco.strings = strings; global.monaco.async = async; + global.monaco.list = listWidget; resolve(); }); }); diff --git a/packages/monaco/src/browser/monaco-menu.ts b/packages/monaco/src/browser/monaco-menu.ts index 247a79e7c96d8..e0c02baaf4f44 100644 --- a/packages/monaco/src/browser/monaco-menu.ts +++ b/packages/monaco/src/browser/monaco-menu.ts @@ -37,7 +37,7 @@ export class MonacoEditorMenuContribution implements MenuContribution { ) { } registerMenus(registry: MenuModelRegistry): void { - for (const item of MenuRegistry.getMenuItems(7)) { + for (const item of MenuRegistry.getMenuItems(monaco.actions.MenuId.EditorContext)) { if (!monaco.actions.isIMenuItem(item)) { continue; } @@ -51,7 +51,7 @@ export class MonacoEditorMenuContribution implements MenuContribution { this.registerPeekSubmenu(registry); registry.registerSubmenu(MonacoMenus.SELECTION, 'Selection'); - for (const item of MenuRegistry.getMenuItems(25)) { + for (const item of MenuRegistry.getMenuItems(monaco.actions.MenuId.MenubarSelectionMenu)) { if (!monaco.actions.isIMenuItem(item)) { continue; } @@ -69,7 +69,7 @@ export class MonacoEditorMenuContribution implements MenuContribution { protected registerPeekSubmenu(registry: MenuModelRegistry): void { registry.registerSubmenu(MonacoMenus.PEEK_CONTEXT_SUBMENU, 'Peek'); - for (const item of MenuRegistry.getMenuItems(8)) { + for (const item of MenuRegistry.getMenuItems(monaco.actions.MenuId.EditorContextPeek)) { if (!monaco.actions.isIMenuItem(item)) { continue; } diff --git a/packages/monaco/src/browser/monaco-quick-command-service.ts b/packages/monaco/src/browser/monaco-quick-command-service.ts new file mode 100644 index 0000000000000..182ec816afae7 --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-command-service.ts @@ -0,0 +1,132 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { KeybindingRegistry, KeySequence, QuickCommandService } from '@theia/core/lib/browser'; +import { Command } from '@theia/core/lib/common/command'; +import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; +import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; + +@injectable() +export class MonacoQuickCommandService extends QuickCommandService implements monaco.quickInput.IQuickAccessDataService { + + private recentItems: Array = []; + private otherItems: Array = []; + + @inject(KeybindingRegistry) + protected readonly keybindingRegistry: KeybindingRegistry; + + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: CommandsQuickAccessProvider, + prefix: CommandsQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Quick Command', needsEditor: false }] + }); + CommandsQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; + } + + reset(): void { + const { recent, other } = this.getCommands(); + this.recentItems = []; + this.otherItems = []; + this.recentItems.push(...recent.map(command => this.toItem(command))); + this.otherItems.push(...other.map(command => this.toItem(command))); + } + + getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { + const items: Array = []; + if (this.recentItems.length === 0 && this.otherItems.length === 0) { + this.reset(); + } + const recentItems = filterItems(this.recentItems.slice(), filter); + const otherItems = filterItems(this.otherItems.slice(), filter); + + if (recentItems.length > 0) { + items.push({ type: 'separator', label: 'recently used' }, ...recentItems); + } + + if (otherItems.length > 0) { + items.push({ type: 'separator', label: 'other commands' }, ...otherItems); + } + return items; + } + + private toItem(command: Command): monaco.quickInput.IAnythingQuickPickItem { + const label = (command.category) ? `${command.category}: ` + command.label! : command.label!; + const iconClasses = this.getItemIconClasses(command); + const activeElement = window.document.activeElement as HTMLElement; + + return { + label, + iconClasses, + alwaysShow: !!this.commandRegistry.getActiveHandler(command.id), + keybinding: this.getKeybinding(command), + accept: () => { + activeElement.focus({ preventScroll: true }); + this.commandRegistry.executeCommand(command.id); + this.commandRegistry.addRecentCommand(command); + } + }; + } + + private getKeybinding(command: Command): monaco.keybindings.ResolvedKeybinding | undefined { + const keybindings = this.keybindingRegistry.getKeybindingsForCommand(command.id); + if (!keybindings || keybindings.length === 0) { + return undefined; + } + + let keySequence: KeySequence; + try { + keySequence = this.keybindingRegistry.resolveKeybinding(keybindings[0]); + } catch (error) { + return undefined; + } + return new MonacoResolvedKeybinding(keySequence, this.keybindingRegistry); + } + + private getItemIconClasses(command: Command): string[] | undefined { + const toggledHandler = this.commandRegistry.getToggledHandler(command.id); + if (toggledHandler) { + return ['fa fa-check']; + } + return undefined; + } +} + +export class CommandsQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = '>'; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(CommandsQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: CommandsQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return CommandsQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/monaco/src/browser/monaco-quick-editor-service.ts b/packages/monaco/src/browser/monaco-quick-editor-service.ts new file mode 100644 index 0000000000000..03effbda8d380 --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-editor-service.ts @@ -0,0 +1,104 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { QuickEditorService, filterItems } from '@theia/core/lib/browser'; +import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; +import URI from '@theia/core/lib/common/uri'; + +@injectable() +export class MonacoQuickEditorService extends QuickEditorService implements monaco.quickInput.IQuickAccessDataService { + + @inject(EditorManager) + protected readonly editorManager: EditorManager; + + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: EditorQuickAccessProvider, + prefix: EditorQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Show All Opened Editors', needsEditor: false }] + }); + EditorQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; + } + + getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { + const editorItems: Array = []; + + // Get the alphabetically sorted list of URIs of all currently opened editor widgets. + const widgets: URI[] = this.editorManager.all + .map((w: EditorWidget) => w.editor.uri) + .sort(); + + if (widgets.length === 0) { + editorItems.push(({ + label: 'List of opened editors is currently empty' + })); + } else { + for (const uri of widgets) { + const item = this.toItem(uri); + editorItems.push(item); + } + } + + return filterItems(editorItems.slice(), filter); + } + + protected toItem(uri: URI): monaco.quickInput.IAnythingQuickPickItem { + const description = this.labelProvider.getLongName(uri.parent); + const icon = this.labelProvider.getIcon(uri); + const iconClasses = icon === '' ? undefined : [icon + ' file-icon']; + + return { + label: this.labelProvider.getName(uri), + description: description, + iconClasses, + ariaLabel: uri.path.toString(), + resource: uri, + alwaysShow: true, + accept: () => this.openFile(uri) + }; + } + + protected openFile(uri: URI): void { + this.openerService.getOpener(uri) + .then(opener => opener.open(uri)); + } +} + +export class EditorQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = 'edt '; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(EditorQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: EditorQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return EditorQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/monaco/src/browser/monaco-quick-help-service.ts b/packages/monaco/src/browser/monaco-quick-help-service.ts new file mode 100644 index 0000000000000..dcd8542f71abc --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-help-service.ts @@ -0,0 +1,106 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { QuickHelpService } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { MonacoQuickInputService } from './monaco-quick-input-service'; + +@injectable() +export class MonacoQuickHelpService extends QuickHelpService implements monaco.quickInput.IQuickAccessDataService { + + @inject(MonacoQuickInputService) + protected readonly monacoQuickInputService: MonacoQuickInputService; + + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: HelpQuickAccessProvider, + prefix: HelpQuickAccessProvider.PREFIX, + placeholder: 'Type "?" to get help on the actions you can take from here.', + helpEntries: [{ description: 'Show all Quick Access Providers', needsEditor: false }] + }); + HelpQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; + } + + getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { + const { editorProviders, globalProviders } = this.getQuickAccessProviders(); + const result = editorProviders.length === 0 || globalProviders.length === 0 ? + // Without groups + [ + ...(editorProviders.length === 0 ? globalProviders : editorProviders) + ] : + + // With groups + [ + { label: 'global commands', type: 'separator' }, + ...globalProviders, + { label: 'editor commands', type: 'separator' }, + ...editorProviders + ]; + return result as monaco.quickInput.Picks; + } + + private getQuickAccessProviders(): { editorProviders: monaco.quickInput.IHelpQuickAccessPickItem[], globalProviders: monaco.quickInput.IHelpQuickAccessPickItem[] } { + const globalProviders: monaco.quickInput.IHelpQuickAccessPickItem[] = []; + const editorProviders: monaco.quickInput.IHelpQuickAccessPickItem[] = []; + + const providers = monaco.platform.Registry.as('workbench.contributions.quickaccess').getQuickAccessProviders(); + + for (const provider of providers.sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) { + if (provider.prefix === HelpQuickAccessProvider.PREFIX) { + continue; // exclude help which is already active + } + + for (const helpEntry of provider.helpEntries) { + const prefix = helpEntry.prefix || provider.prefix; + const label = prefix || '\u2026' /* ... */; + + (helpEntry.needsEditor ? editorProviders : globalProviders).push({ + prefix, + label, + ariaLabel: `${label}, ${helpEntry.description}`, + description: helpEntry.description, + accept: () => this.monacoQuickInputService.open(prefix) + }); + } + } + + return { editorProviders, globalProviders }; + } +} + +export class HelpQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = '?'; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(HelpQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: HelpQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + protected getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return HelpQuickAccessProvider.dataService.getPicks(filter, token); + } +} diff --git a/packages/monaco/src/browser/monaco-quick-input-service.ts b/packages/monaco/src/browser/monaco-quick-input-service.ts new file mode 100644 index 0000000000000..9675f54d683ea --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-input-service.ts @@ -0,0 +1,235 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 + ********************************************************************************/ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { QuickInputService } from '@theia/core/lib/browser'; +import { CancellationToken, Event } from '@theia/core/lib/common'; +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; + +@injectable() +export class MonacoQuickInputService extends QuickInputService implements monaco.quickInput.IQuickInputService { + + @inject(monaco.contextKeyService.ContextKeyService) + protected readonly contextKeyService: monaco.contextKeyService.ContextKeyService; + + controller: monaco.quickInput.QuickInputController; + quickAccess: monaco.quickInput.IQuickAccessController; + + protected container: HTMLElement; + private quickInputList: monaco.list.List; + + get backButton(): monaco.quickInput.IQuickInputButton { return this.controller.backButton; } + + constructor() { + super(); + this.initContainer(); + this.initController(); + } + + @postConstruct() + protected async init(): Promise { + this.quickAccess = new monaco.quickInput.QuickAccessController(this, monaco.services.StaticServices.instantiationService.get()); + } + + setContextKey(key: string | undefined): void { + if (key) { + this.contextKeyService.createKey(key, undefined); + } + } + + createQuickPick(): monaco.quickInput.IQuickPick { + return this.controller.createQuickPick(); + } + + createInputBox(): monaco.quickInput.IInputBox { + return this.controller.createInputBox(); + } + + open(filter: string): void { + this.quickAccess.show(filter); + setTimeout(() => { + this.quickInputList.focusNth(0); + }, 300); + } + + showQuickPick(items: Array, options?: monaco.quickInput.IQuickPickOptions): Promise { + return new Promise((resolve, reject) => { + const quickPick = this.createQuickPick(); + + if (options) { + quickPick.ariaLabel = options.ariaLabel; + quickPick.canAcceptInBackground = !!options.canAcceptInBackground; + quickPick.canSelectMany = !!options.canSelectMany; + quickPick.contextKey = options.contextKey; + quickPick.customButton = !!options.customButton; + quickPick.customHover = options.customHover; + quickPick.customLabel = options.customLabel; + quickPick.description = options.description; + quickPick.enabled = options.enabled ?? true; + quickPick.hideCheckAll = !!options.hideCheckAll; + quickPick.hideInput = !!options.hideInput; + quickPick.ignoreFocusOut = !!options.ignoreFocusOut; + quickPick.autoFocusOnList = options.autoFocusOnList ?? true; + quickPick.matchOnDescription = options.matchOnDescription ?? true; + quickPick.matchOnDetail = options.matchOnDetail ?? true; + quickPick.matchOnLabel = options.matchOnLabel ?? true; + quickPick.placeholder = options.placeholder; + quickPick.sortByLabel = options.sortByLabel ?? true; + quickPick.step = options.step; + quickPick.title = options.title; + quickPick.totalSteps = options.totalSteps; + quickPick.validationMessage = options.validationMessage; + + if (options.activeItem) { + quickPick.activeItems = [options.activeItem]; + } + + quickPick.onDidAccept((event: Event) => { + if (options?.onDidAccept) { + options.onDidAccept(); + } + }); + + quickPick.onDidHide(() => { + if (options.onDidHide) { + options.onDidHide(); + }; + quickPick.dispose(); + }); + quickPick.onDidChangeValue((filter: string) => { + if (options.onDidChangeValue) { + options.onDidChangeValue(quickPick, filter); + } + }); + quickPick.onDidChangeActive((activeItems: Array) => { + if (options.onDidChangeActive) { + options.onDidChangeActive(quickPick, activeItems); + } + }); + quickPick.onDidTriggerButton((btn: monaco.quickInput.IQuickInputButton) => { + if (options.onDidTriggerButton) { + options.onDidTriggerButton(btn); + } + }); + quickPick.onDidChangeSelection((selectedItems: Array) => { + if (options.onDidChangeSelection) { + options.onDidChangeSelection(quickPick, selectedItems); + } + if (selectedItems[0].execute) { + selectedItems[0].execute(selectedItems[0], quickPick.value); + } + quickPick.hide(); + resolve(selectedItems[0]); + }); + } + + quickPick.items = items; + quickPick.show(); + }); + } + + input(options?: monaco.quickInput.IInputOptions, token?: CancellationToken): Promise { + return this.controller.input(options, token); + } + + pick>( + picks: Promise | T[], options: O = {}, token?: CancellationToken): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { + return this.controller.pick(picks, options, token); + } + + hide(): void { + this.controller.hide(); + } + + focus(): void { + this.controller.focus(); + } + + toggle(): void { + this.controller.toggle(); + } + + applyStyles(styles: monaco.quickInput.IQuickInputStyles): void { + this.controller.applyStyles(styles); + } + + layout(dimension: monaco.editor.IDimension, titleBarOffset: number): void { + this.controller.layout(dimension, titleBarOffset); + } + + navigate?(next: boolean, quickNavigate?: monaco.quickInput.IQuickNavigateConfiguration): void { + this.controller.navigate(next, quickNavigate); + } + + dispose(): void { + this.controller.dispose(); + } + + async cancel(): Promise { + this.controller.cancel(); + } + + async back(): Promise { + this.controller.back(); + } + + async accept?(keyMods?: monaco.quickInput.IKeyMods): Promise { + this.controller.accept(keyMods); + } + + private initContainer(): void { + const overlayWidgets = document.createElement('div'); + overlayWidgets.classList.add('quick-input-overlay'); + document.body.appendChild(overlayWidgets); + const container = this.container = document.createElement('quick-input-container'); + container.style.position = 'absolute'; + container.style.top = '0px'; + container.style.right = '50%'; + container.style.zIndex = '1000000'; + overlayWidgets.appendChild(container); + } + + private initController(): void { + this.controller = new monaco.quickInput.QuickInputController(this.getOptions()); + this.controller.layout({ width: 600, height: 600 }, 0); + } + + private getOptions(): monaco.quickInput.IQuickInputOptions { + return { + idPrefix: 'quickInput_', + container: this.container, + ignoreFocusOut: () => false, + isScreenReaderOptimized: () => true, + backKeybindingLabel: () => undefined, + setContextKey: (id?: string) => this.setContextKey(id), + returnFocus: () => this.container.focus(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + createList: (user: string, containr: HTMLElement, delegate: any, renderers: any, options: any) => { + this.quickInputList = new monaco.list.List(user, containr, delegate, renderers, options); + return this.quickInputList; + }, + styles: { + widget: {}, + list: {}, + inputBox: {}, + countBadge: {}, + button: {}, + progressBar: {} + } + }; + } +} + diff --git a/packages/monaco/src/browser/monaco-quick-open-service.ts b/packages/monaco/src/browser/monaco-quick-open-service.ts deleted file mode 100644 index 77faf49a27006..0000000000000 --- a/packages/monaco/src/browser/monaco-quick-open-service.ts +++ /dev/null @@ -1,573 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 TypeFox 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, inject, optional, postConstruct } from '@theia/core/shared/inversify'; -import { MessageType } from '@theia/core/lib/common/message-service-protocol'; -import { - QuickOpenService, QuickOpenOptions, QuickOpenItem, QuickOpenGroupItem, - QuickOpenMode, KeySequence, KeybindingRegistry -} from '@theia/core/lib/browser'; -import { QuickOpenModel, QuickOpenActionProvider, QuickOpenAction } from '@theia/core/lib/common/quick-open-model'; -import { ContextKey } from '@theia/core/lib/browser/context-key-service'; -import { MonacoContextKeyService } from './monaco-context-key-service'; -import { QuickOpenHideReason } from '@theia/core/lib/common/quick-open-service'; -import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; -import { BrowserMenuBarContribution } from '@theia/core/lib/browser/menu/browser-menu-plugin'; -import { compareEntries, setFileNameComparer } from './monaco-comparers'; - -export interface MonacoQuickOpenControllerOpts extends monaco.quickOpen.IQuickOpenControllerOpts { - valueSelection?: Readonly<[number, number]>; - enabled?: boolean; - readonly prefix?: string; - readonly password?: boolean; - readonly ignoreFocusOut?: boolean; - onType?(lookFor: string, acceptor: (model: monaco.quickOpen.QuickOpenModel) => void): void; - onClose?(canceled: boolean): void; -} - -@injectable() -export class MonacoQuickOpenService extends QuickOpenService { - - protected readonly container: HTMLElement; - protected _widget: monaco.quickOpen.QuickOpenWidget | undefined; - protected opts: MonacoQuickOpenControllerOpts | undefined; - protected previousActiveElement: Element | undefined; - protected _widgetNode: HTMLElement; - - @inject(MonacoContextKeyService) - protected readonly contextKeyService: MonacoContextKeyService; - - @inject(KeybindingRegistry) - protected readonly keybindingRegistry: KeybindingRegistry; - - @inject(BrowserMenuBarContribution) @optional() - protected readonly browserMenuBarContribution?: BrowserMenuBarContribution; - - protected inQuickOpenKey: ContextKey; - - constructor() { - super(); - const overlayWidgets = document.createElement('div'); - overlayWidgets.classList.add('quick-open-overlay'); - document.body.appendChild(overlayWidgets); - - const container = this.container = document.createElement('quick-open-container'); - container.style.position = 'absolute'; - container.style.top = '0px'; - container.style.right = '50%'; - container.style.zIndex = '1000000'; - overlayWidgets.appendChild(container); - } - - @postConstruct() - protected init(): void { - this.inQuickOpenKey = this.contextKeyService.createKey('inQuickOpen', false); - - setFileNameComparer(new monaco.async.IdleValue(() => { - const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); - const isNumeric = collator.resolvedOptions().numeric; - return { - collator: collator, - collatorIsNumeric: isNumeric - }; - })); - } - - open(model: QuickOpenModel, options?: QuickOpenOptions): void { - this.internalOpen(new MonacoQuickOpenControllerOptsImpl(model, this.keybindingRegistry, options)); - } - - hide(reason?: QuickOpenHideReason): void { - let hideReason: monaco.quickOpen.HideReason | undefined; - switch (reason) { - case QuickOpenHideReason.ELEMENT_SELECTED: - hideReason = monaco.quickOpen.HideReason.ELEMENT_SELECTED; - break; - case QuickOpenHideReason.FOCUS_LOST: - hideReason = monaco.quickOpen.HideReason.FOCUS_LOST; - break; - case QuickOpenHideReason.CANCELED: - hideReason = monaco.quickOpen.HideReason.CANCELED; - break; - } - this.widget.hide(hideReason); - } - - showDecoration(type: MessageType): void { - let decoration = monaco.MarkerSeverity.Info; - if (type === MessageType.Warning) { - decoration = monaco.MarkerSeverity.Warning; - } else if (type === MessageType.Error) { - decoration = monaco.MarkerSeverity.Error; - } - this.showInputDecoration(decoration); - } - hideDecoration(): void { - this.clearInputDecoration(); - } - - refresh(): void { - const inputBox = this.widget.inputBox; - if (inputBox) { - this.onType(inputBox.inputElement.value); - } - } - - internalOpen(opts: MonacoQuickOpenControllerOpts): void { - const browserMenuBarContribution = this.browserMenuBarContribution; - if (browserMenuBarContribution) { - const browserMenuBar = browserMenuBarContribution.menuBar; - if (browserMenuBar) { - const activeMenu = browserMenuBar.activeMenu; - if (activeMenu) { - activeMenu.close(); - } - } - } - - // eslint-disable-next-line no-null/no-null - if (this.widgetNode && this.widgetNode.offsetParent !== null) { - this.hide(); - } - this.opts = opts; - const activeContext = window.document.activeElement || undefined; - if (!activeContext || !this.container.contains(activeContext)) { - this.previousActiveElement = activeContext; - this.contextKeyService.activeContext = activeContext instanceof HTMLElement ? activeContext : undefined; - } - - this.hideDecoration(); - this.widget.show(this.opts.prefix || ''); - this.setPlaceHolder(opts.inputAriaLabel); - this.setPassword(opts.password ? true : false); - this.setEnabled(opts.enabled); - this.setValueSelected(opts.inputAriaLabel, opts.valueSelection); - this.inQuickOpenKey.set(true); - - const widget = this.widget; - if (widget.inputBox) { - widget.inputBox.inputElement.tabIndex = 1; - // Position the cursor at the end of the input unless a user has made a selection. - if (widget.inputBox.inputElement.selectionStart === widget.inputBox.inputElement.selectionEnd) { - widget.inputBox.inputElement.selectionStart = widget.inputBox.inputElement.value.length; - } - } - } - - setValueSelected(value: string | undefined, selectLocation: Readonly<[number, number]> | undefined): void { - if (!value) { - return; - } - - const widget = this.widget; - if (widget.inputBox) { - - if (!selectLocation) { - widget.inputBox.inputElement.setSelectionRange(0, value.length); - return; - } - - if (selectLocation[0] === selectLocation[1]) { - widget.inputBox.inputElement.setSelectionRange(selectLocation[0], selectLocation[0]); - return; - } - - widget.inputBox.inputElement.setSelectionRange(selectLocation[0], selectLocation[1]); - } - } - - setEnabled(isEnabled: boolean | undefined): void { - const widget = this.widget; - if (widget.inputBox) { - widget.inputBox.inputElement.readOnly = (isEnabled !== undefined) ? !isEnabled : false; - } - } - - setValue(value: string | undefined): void { - if (this.widget && this.widget.inputBox) { - this.widget.inputBox.inputElement.value = (value !== undefined) ? value : ''; - } - } - - setPlaceHolder(placeHolder: string): void { - const widget = this.widget; - if (widget.inputBox) { - widget.inputBox.setPlaceHolder(placeHolder); - } - } - - setPassword(isPassword: boolean): void { - const widget = this.widget; - if (widget.inputBox) { - widget.inputBox.inputElement.type = isPassword ? 'password' : 'text'; - } - } - - showInputDecoration(decoration: monaco.MarkerSeverity): void { - const widget = this.widget; - if (widget.inputBox) { - const type = decoration === monaco.MarkerSeverity.Info ? 1 : - decoration === monaco.MarkerSeverity.Warning ? 2 : 3; - widget.inputBox.showMessage({ type, content: '' }); - } - } - - clearInputDecoration(): void { - const widget = this.widget; - if (widget.inputBox) { - widget.inputBox.hideMessage(); - } - } - - protected get widget(): monaco.quickOpen.QuickOpenWidget { - if (this._widget) { - return this._widget; - } - const widget = this._widget = new monaco.quickOpen.QuickOpenWidget(this.container, { - onOk: () => { - this.previousActiveElement = undefined; - this.contextKeyService.activeContext = undefined; - this.onClose(false); - }, - onCancel: () => { - if (this.previousActiveElement instanceof HTMLElement) { - this.previousActiveElement.focus({ preventScroll: true }); - } - this.previousActiveElement = undefined; - this.contextKeyService.activeContext = undefined; - this.onClose(true); - }, - onType: lookFor => this.onType(lookFor || ''), - onFocusLost: () => { - if (this.opts && this.opts.ignoreFocusOut !== undefined) { - if (this.opts.ignoreFocusOut === false) { - this.onClose(true); - } - return this.opts.ignoreFocusOut; - } else { - return false; - } - } - }, {}); - this.attachQuickOpenStyler(); - this._widgetNode = widget.create(); - widget.tree.onDidChangeFocus(() => this.onDidChangeActiveEmitter.fire(this.getActive())); - return widget; - } - - get widgetNode(): HTMLElement { - return this._widgetNode; - } - - getActive(): QuickOpenItem[] { - if (this._widget && this._widget.isVisible()) { - const focus = this._widget.tree.getFocus(); - if (focus instanceof QuickOpenEntry) { - return [focus.item]; - } - } - return []; - } - - protected attachQuickOpenStyler(): void { - if (!this._widget) { - return; - } - const themeService = monaco.services.StaticServices.standaloneThemeService.get(); - const detach = monaco.theme.attachQuickOpenStyler(this._widget, themeService); - const dispose = themeService.onThemeChange(() => { - detach.dispose(); - this.attachQuickOpenStyler(); - dispose.dispose(); - }); - } - - protected onClose(cancelled: boolean): void { - if (this.opts && this.opts.onClose) { - this.opts.onClose(cancelled); - } - this.inQuickOpenKey.set(false); - } - - protected async onType(lookFor: string): Promise { - const opts = this.opts; - if (this.widget && opts) { - if (opts.onType) { - opts.onType(lookFor, model => - this.widget.setInput(model, opts.getAutoFocus(lookFor), opts.inputAriaLabel)); - } else { - const m = opts.getModel(lookFor); - this.widget.setInput(m, opts.getAutoFocus(lookFor), opts.inputAriaLabel); - } - } - } - -} - -export class MonacoQuickOpenControllerOptsImpl implements MonacoQuickOpenControllerOpts { - - protected readonly options: QuickOpenOptions.Resolved; - readonly password?: boolean; - - constructor( - protected readonly model: QuickOpenModel, - protected readonly keybindingService: KeybindingRegistry, - options?: QuickOpenOptions - ) { - this.model = model; - this.options = QuickOpenOptions.resolve(options); - this.password = this.options.password; - } - - get enabled(): boolean { - return this.options.enabled; - } - - get prefix(): string { - return this.options.prefix; - } - - get ignoreFocusOut(): boolean { - return this.options.ignoreFocusOut; - } - - get inputAriaLabel(): string { - return this.options.placeholder || ''; - } - - get valueSelection(): Readonly<[number, number]> { - return this.options.valueSelection || [-1, -1]; - } - - onClose(cancelled: boolean): void { - this.options.onClose(cancelled); - } - - protected toOpenModel(lookFor: string, items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider): monaco.quickOpen.QuickOpenModel { - const entries: monaco.quickOpen.QuickOpenEntry[] = []; - for (const item of items) { - const entry = this.createEntry(item, lookFor); - if (entry) { - entries.push(entry); - } - } - if (this.options.fuzzySort) { - entries.sort((a, b) => compareEntries(a, b, lookFor)); - } - return new monaco.quickOpen.QuickOpenModel(entries, actionProvider ? new MonacoQuickOpenActionProvider(actionProvider) : undefined); - } - - getModel(lookFor: string): monaco.quickOpen.QuickOpenModel { - throw new Error('getModel not supported!'); - } - - onType(lookFor: string, acceptor: (model: monaco.quickOpen.QuickOpenModel) => void): void { - this.model.onType(lookFor, (items, actionProvider) => { - const result = this.toOpenModel(lookFor, items, actionProvider); - acceptor(result); - }); - } - - protected createEntry(item: QuickOpenItem, lookFor: string): monaco.quickOpen.QuickOpenEntry | undefined { - if (this.options.skipPrefix) { - lookFor = lookFor.substr(this.options.skipPrefix); - } - if (this.options.trimInput) { - lookFor = lookFor.trim(); - } - const { fuzzyMatchLabel, fuzzyMatchDescription, fuzzyMatchDetail } = this.options; - const labelHighlights = fuzzyMatchLabel ? this.matchesFuzzy(lookFor, item.getLabel(), fuzzyMatchLabel) : item.getLabelHighlights(); - const descriptionHighlights = fuzzyMatchDescription ? this.matchesFuzzy(lookFor, item.getDescription(), fuzzyMatchDescription) : item.getDescriptionHighlights(); - const detailHighlights = fuzzyMatchDetail ? this.matchesFuzzy(lookFor, item.getDetail(), fuzzyMatchDetail) : item.getDetailHighlights(); - if ((lookFor && !labelHighlights && !descriptionHighlights && !detailHighlights) - && !this.options.showItemsWithoutHighlight) { - return undefined; - } - const entry = item instanceof QuickOpenGroupItem - ? new QuickOpenEntryGroup(item, this.keybindingService) - : new QuickOpenEntry(item, this.keybindingService); - entry.setHighlights(labelHighlights || [], descriptionHighlights, detailHighlights); - return entry; - } - - protected matchesFuzzy(lookFor: string, value: string | undefined, options?: QuickOpenOptions.FuzzyMatchOptions | boolean): monaco.quickOpen.IHighlight[] | undefined { - if (!lookFor || !value) { - return undefined; - } - const enableSeparateSubstringMatching = typeof options === 'object' && options.enableSeparateSubstringMatching; - return monaco.filters.matchesFuzzy(lookFor, value, enableSeparateSubstringMatching); - } - - getAutoFocus(lookFor: string): monaco.quickOpen.IAutoFocus { - if (this.options.selectIndex) { - const idx = this.options.selectIndex(lookFor); - if (idx >= 0) { - return { - autoFocusIndex: idx - }; - } - } - return { - autoFocusFirstEntry: true, - autoFocusPrefixMatch: lookFor - }; - } - -} - -export class QuickOpenEntry extends monaco.quickOpen.QuickOpenEntry { - - constructor( - public readonly item: QuickOpenItem, - protected readonly keybindingService: KeybindingRegistry - ) { - super(); - } - - getLabel(): string | undefined { - return this.item.getLabel(); - } - - getAriaLabel(): string { - return this.item.getTooltip() || ''; - } - - getDetail(): string | undefined { - return this.item.getDetail(); - } - - getDescription(): string | undefined { - return this.item.getDescription(); - } - - isHidden(): boolean { - return super.isHidden() || this.item.isHidden(); - } - - getResource(): monaco.Uri | undefined { - const uri = this.item.getUri(); - return uri ? monaco.Uri.parse(uri.toString()) : undefined; - } - - getIcon(): string | undefined { - return this.item.getIconClass(); - } - - getKeybinding(): monaco.keybindings.ResolvedKeybinding | undefined { - const keybinding = this.item.getKeybinding(); - if (!keybinding) { - return undefined; - } - - let keySequence: KeySequence; - try { - keySequence = this.keybindingService.resolveKeybinding(keybinding); - } catch (error) { - return undefined; - } - return new MonacoResolvedKeybinding(keySequence, this.keybindingService); - } - - run(mode: monaco.quickOpen.Mode): boolean { - if (mode === 1) { - return this.item.run(QuickOpenMode.OPEN); - } - if (mode === 2) { - return this.item.run(QuickOpenMode.OPEN_IN_BACKGROUND); - } - if (mode === 0) { - return this.item.run(QuickOpenMode.PREVIEW); - } - return false; - } - -} - -export class QuickOpenEntryGroup extends monaco.quickOpen.QuickOpenEntryGroup { - - constructor( - public readonly item: QuickOpenGroupItem, - protected readonly keybindingService: KeybindingRegistry - ) { - super(new QuickOpenEntry(item, keybindingService)); - } - - getGroupLabel(): string { - return this.item.getGroupLabel() || ''; - } - - showBorder(): boolean { - return this.item.showBorder(); - } - - getKeybinding(): monaco.keybindings.ResolvedKeybinding | undefined { - const entry = this.getEntry(); - return entry ? entry.getKeybinding() : super.getKeybinding(); - } - -} - -export class MonacoQuickOpenAction implements monaco.editor.IAction { - constructor(public readonly action: QuickOpenAction) { } - - get id(): string { - return this.action.id; - } - - get label(): string { - return this.action.label || ''; - } - - get tooltip(): string { - return this.action.tooltip || ''; - } - - get class(): string | undefined { - return this.action.class; - } - - get enabled(): boolean { - return this.action.enabled || true; - } - - get checked(): boolean { - return this.action.checked || false; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - run(entry: QuickOpenEntry | QuickOpenEntryGroup): Promise { - return this.action.run(entry.item); - } - - dispose(): void { - this.action.dispose(); - } -} - -export class MonacoQuickOpenActionProvider implements monaco.quickOpen.IActionProvider { - constructor(public readonly provider: QuickOpenActionProvider) { } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - hasActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): boolean { - return this.provider.hasActions(entry.item); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): ReadonlyArray { - const actions = this.provider.getActions(entry.item); - return actions.map(action => new MonacoQuickOpenAction(action)); - } -} diff --git a/packages/monaco/src/browser/monaco-quick-view-service.ts b/packages/monaco/src/browser/monaco-quick-view-service.ts new file mode 100644 index 0000000000000..ababfaa6c5c9b --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-view-service.ts @@ -0,0 +1,97 @@ +/******************************************************************************** + * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { QuickViewItem, QuickViewService } from '@theia/core/lib/browser/quick-input/quick-view-service'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { Disposable } from '@theia/core/lib/common'; +import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; + +@injectable() +export class MonacoQuickViewService extends QuickViewService implements monaco.quickInput.IQuickAccessDataService { + protected readonly items: (monaco.quickInput.IAnythingQuickPickItem & { when?: string })[] = []; + private hiddenItemLabels = new Set(); + + @inject(ContextKeyService) + protected readonly contextKexService: ContextKeyService; + + registerItem(item: QuickViewItem): Disposable { + const quickOpenItem = { + label: item.label, + accept: () => item.open(), + when: item.when + }; + this.items.push(quickOpenItem); + this.items.sort((a, b) => a.label!.localeCompare(b.label!)); + + return Disposable.create(() => { + const index = this.items.indexOf(quickOpenItem); + if (index !== -1) { + this.items.splice(index, 1); + } + }); + } + + hideItem(label: string): void { + this.hiddenItemLabels.add(label); + } + + showItem(label: string): void { + this.hiddenItemLabels.delete(label); + } + + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: ViewQuickAccessProvider, + prefix: ViewQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Open View', needsEditor: false }] + }); + ViewQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; + } + + getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { + const items = this.items.filter(item => + (item.when === undefined || this.contextKexService.match(item.when)) && + (!this.hiddenItemLabels.has(item.label)) + ); + return filterItems(items, filter); + } +} + +export class ViewQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = 'view '; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching views' + }; + + constructor() { + super(ViewQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: ViewQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return ViewQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/monaco/src/browser/monaco-resolved-keybinding.ts b/packages/monaco/src/browser/monaco-resolved-keybinding.ts index a5124cf748cef..a76c6268a1474 100644 --- a/packages/monaco/src/browser/monaco-resolved-keybinding.ts +++ b/packages/monaco/src/browser/monaco-resolved-keybinding.ts @@ -73,6 +73,10 @@ export class MonacoResolvedKeybinding extends monaco.keybindings.ResolvedKeybind return this.keySequence.map(keyCode => monaco.keybindings.USLayoutResolvedKeybinding.getDispatchStr(this.toKeybinding(keyCode))); } + public getSingleModifierDispatchParts(): (string | null)[] { + return []; /* NOOP */ + } + private toKeybinding(keyCode: KeyCode): monaco.keybindings.SimpleKeybinding { return new monaco.keybindings.SimpleKeybinding( keyCode.ctrl, diff --git a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts index eb70b925256fe..ef8deec4a59ce 100644 --- a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts +++ b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts @@ -101,7 +101,7 @@ export class MonacoSnippetSuggestProvider implements monaco.languages.Completion return { suggestions }; } - resolveCompletionItem(textModel: monaco.editor.ITextModel, position: monaco.Position, item: monaco.languages.CompletionItem): monaco.languages.CompletionItem { + resolveCompletionItem?(item: monaco.languages.CompletionItem, token: monaco.CancellationToken): monaco.languages.CompletionItem { return item instanceof MonacoSnippetSuggestion ? item.resolve() : item; } diff --git a/packages/monaco/src/browser/monaco-theming-service.ts b/packages/monaco/src/browser/monaco-theming-service.ts index 6e5a14923f8aa..4e38a3409246d 100644 --- a/packages/monaco/src/browser/monaco-theming-service.ts +++ b/packages/monaco/src/browser/monaco-theming-service.ts @@ -138,7 +138,7 @@ export class MonacoThemingService { static init(): void { this.updateBodyUiTheme(); - ThemeService.get().onThemeChange(() => this.updateBodyUiTheme()); + ThemeService.get().onDidColorThemeChange(() => this.updateBodyUiTheme()); this.restore(); } diff --git a/packages/monaco/src/browser/style/index.css b/packages/monaco/src/browser/style/index.css index 9870a6feb7a5a..2e87b701dfcbc 100644 --- a/packages/monaco/src/browser/style/index.css +++ b/packages/monaco/src/browser/style/index.css @@ -1,117 +1,169 @@ .monaco-editor { - padding-bottom: 5.6px; - font-family: var(--theia-code-font-family); - font-size: inherit !important; + padding-bottom: 5.6px; + font-family: var(--theia-code-font-family); + font-size: inherit !important; } /* * set z-index to 0, so tabs are not above overlay widgets */ .p-TabBar.theia-app-centers { - z-index: 0; - display: flex; -} - -/* - * we need to disable the background image when using font awesome icons and file-icons - */ -.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.fa, -.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file-icon { - display: flex; - justify-content: center; - align-items: center; - background-image: none; - margin-right: 0px; -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry .codicon { - vertical-align: sub; -} - -.monaco-quick-open-widget { - background-color: var(--theia-quickInput-background) !important; - color: var(--theia-quickInput-foreground) !important; -} - -.quick-open-entry .quick-open-row .monaco-icon-label .monaco-icon-label-description-container .label-description { - font-size: calc(var(--theia-ui-font-size0) * 0.95); + z-index: 0; + display: flex; } .monaco-editor .zone-widget { - position: absolute; - z-index: 10; - background-color: var(--theia-editorWidget-background); + position: absolute; + z-index: 10; + background-color: var(--theia-editorWidget-background); } .monaco-editor .zone-widget .zone-widget-container { - border-top-style: solid; - border-bottom-style: solid; - border-top-width: 0; - border-bottom-width: 0; - border-top-color: var(--theia-peekView-border); - border-bottom-color: var(--theia-peekView-border); - position: relative; + border-top-style: solid; + border-bottom-style: solid; + border-top-width: 0; + border-bottom-width: 0; + border-top-color: var(--theia-peekView-border); + border-bottom-color: var(--theia-peekView-border); + position: relative; } .monaco-editor .parameter-hints-widget > .wrapper { - overflow: hidden; + overflow: hidden; } /* List highlight, see https://github.com/microsoft/vscode/blob/ff5f581425da6230b6f9216ecf19abf6c9d285a6/src/vs/workbench/browser/style.ts#L50 */ .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight, .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { - color: var(--theia-list-highlightForeground) !important; + color: var(--theia-list-highlightForeground) !important; } /* Scrollbars, see https://github.com/microsoft/vscode/blob/ff5f581425da6230b6f9216ecf19abf6c9d285a6/src/vs/workbench/browser/style.ts#L65 */ .monaco-scrollable-element > .shadow.top { - box-shadow: var(--theia-scrollbar-shadow) 0 6px 6px -6px inset !important; + box-shadow: var(--theia-scrollbar-shadow) 0 6px 6px -6px inset !important; } .monaco-scrollable-element > .shadow.left { - box-shadow: var(--theia-scrollbar-shadow) 6px 0 6px -6px inset !important; + box-shadow: var(--theia-scrollbar-shadow) 6px 0 6px -6px inset !important; } .monaco-scrollable-element > .shadow.top.left { - box-shadow: var(--theia-scrollbar-shadow) 6px 6px 6px -6px inset !important; + box-shadow: var(--theia-scrollbar-shadow) 6px 6px 6px -6px inset !important; } .monaco-scrollable-element > .scrollbar > .slider { - background: var(--theia-scrollbarSlider-background) !important; + background: var(--theia-scrollbarSlider-background) !important; } .monaco-scrollable-element > .scrollbar > .slider:hover { - background: var(--theia-scrollbarSlider-hoverBackground) !important; + background: var(--theia-scrollbarSlider-hoverBackground) !important; } .monaco-scrollable-element > .scrollbar > .slider.active { - background: var(--theia-scrollbarSlider-activeBackground) !important; + background: var(--theia-scrollbarSlider-activeBackground) !important; } .monaco-scrollable-element > .scrollbar.vertical > .slider { - width: var(--theia-scrollbar-width) !important; + width: var(--theia-scrollbar-width) !important; } .monaco-scrollable-element > .scrollbar.horizontal > .slider { - height: var(--theia-scrollbar-width) !important; + height: var(--theia-scrollbar-width) !important; } .monaco-editor .reference-zone-widget .ref-tree .referenceMatch .highlight { - color: unset !important; + color: unset !important; } .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { - outline: var(--theia-border-width) solid; - outline-offset: calc(-1*var(--theia-border-width)); - outline-color: var(--theia-focusBorder); + outline: var(--theia-border-width) solid; + outline-offset: calc(-1 * var(--theia-border-width)); + outline-color: var(--theia-focusBorder); } .monaco-editor .rename-box input { - color: var(--theia-editor-foreground); + color: var(--theia-editor-foreground); } .monaco-editor .rename-box .rename-label { - opacity: .8; - padding: 3px; - font-family: sans-serif; + opacity: 0.8; + padding: 3px; + font-family: sans-serif; +} + +/* Monaco Quick Input */ +.quick-input-widget { + width: 600px !important; + margin-left: -300px !important; + background-color: var(--theia-quickInput-background) !important; + color: var(--theia-quickInput-foreground) !important; +} + +.monaco-icon-label, +.monaco-icon-label-container, +.monaco-icon-name-container, +.monaco-highlighted-label, +.quick-input-list-row { + line-height: var(--theia-content-line-height) !important; + height: var(--theia-content-line-height) !important; +} + +.monaco-icon-label-container { + font-family: var(--theia-ui-font-family) !important; +} + +.quick-input-list + .quick-input-list-rows + > .quick-input-list-row + .monaco-icon-label, +.quick-input-list + .quick-input-list-rows + > .quick-input-list-row + .monaco-icon-label + .monaco-icon-label-container, +.quick-input-list + .quick-input-list-rows + > .quick-input-list-row + .monaco-icon-label + .monaco-icon-label-container + > .monaco-icon-name-container { + display: flex !important; +} + +.quick-input-list .monaco-list-row.focused { + background-color: var(--theia-quickInput-list-focusBackground) !important; +} + +.monaco-list-rows + .monaco-list-row:not(:first-child) + .quick-input-list-entry.quick-input-list-separator-border { + border-top: 1px solid var(--theia-pickerGroup-border) !important; +} +.quick-input-list .quick-input-list-separator { + color: var(--theia-pickerGroup-foreground) !important; +} + +.monaco-icon-label > .monaco-icon-label-container { + flex: 1 !important; +} + +.quick-input-list + .quick-input-list-rows + > .quick-input-list-row + .monaco-icon-label + .monaco-icon-label-container + > .monaco-icon-name-container { + flex: 0 !important; +} + +.quick-input-list-rows + .quick-input-list-row + .monaco-icon-label + .monaco-icon-label-description-container + .label-description { + font-size: calc(var(--theia-ui-font-size0) * 0.95) !important; +} + +.quick-input-list .quick-input-list-label { + cursor: pointer !important; } diff --git a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts index 49737917faf81..185494517cb54 100644 --- a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts +++ b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts @@ -108,7 +108,7 @@ export class MonacoTextmateService implements FrontendApplicationContribution { }); this.updateTheme(); - this.themeService.onThemeChange(() => this.updateTheme()); + this.themeService.onDidColorThemeChange(() => this.updateTheme()); for (const id of this.textmateRegistry.languages) { this.activateLanguage(id); diff --git a/packages/monaco/src/browser/textmate/monaco-theme-registry.ts b/packages/monaco/src/browser/textmate/monaco-theme-registry.ts index 89744d08c2d55..569a62a708cc2 100644 --- a/packages/monaco/src/browser/textmate/monaco-theme-registry.ts +++ b/packages/monaco/src/browser/textmate/monaco-theme-registry.ts @@ -43,7 +43,7 @@ export class MonacoThemeRegistry { protected doGetTheme(name: string | undefined): MixStandaloneTheme | undefined { const standaloneThemeService = monaco.services.StaticServices.standaloneThemeService.get(); - const theme = !name ? standaloneThemeService.getTheme() : standaloneThemeService._knownThemes.get(name); + const theme = !name ? standaloneThemeService.getColorTheme() : standaloneThemeService._knownThemes.get(name); return theme as MixStandaloneTheme | undefined; } diff --git a/packages/monaco/src/browser/workspace-symbol-command.ts b/packages/monaco/src/browser/workspace-symbol-command.ts index fd0df771efd34..8131911ef7065 100644 --- a/packages/monaco/src/browser/workspace-symbol-command.ts +++ b/packages/monaco/src/browser/workspace-symbol-command.ts @@ -17,21 +17,21 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; import { - PrefixQuickOpenService, QuickOpenModel, QuickOpenItem, OpenerService, - QuickOpenMode, KeybindingContribution, KeybindingRegistry, QuickOpenHandler, QuickOpenOptions, QuickOpenContribution, QuickOpenHandlerRegistry + KeybindingContribution, KeybindingRegistry, QuickAccessContribution, OpenerService, LabelProvider } from '@theia/core/lib/browser'; -import { CancellationTokenSource, CommandRegistry, CommandHandler, Command, SelectionService, CancellationToken } from '@theia/core'; -import URI from '@theia/core/lib/common/uri'; +import { + CommandRegistry, CommandHandler, Command, SelectionService, CancellationToken +} from '@theia/core'; import { CommandContribution } from '@theia/core/lib/common'; import { Range, Position, SymbolInformation } from '@theia/core/shared/vscode-languageserver-types'; import { WorkspaceSymbolParams } from '@theia/core/shared/vscode-languageserver-protocol'; import { MonacoLanguages, WorkspaceSymbolProvider } from './monaco-languages'; +import { MonacoQuickInputService } from './monaco-quick-input-service'; +import URI from '@theia/core/lib/common/uri'; @injectable() -export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContribution, KeybindingContribution, CommandHandler, QuickOpenHandler, QuickOpenContribution { - +export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDataService, CommandContribution, KeybindingContribution, CommandHandler, QuickAccessContribution { readonly prefix = '#'; - readonly description = 'Go to Symbol in Workspace'; private command: Command = { id: 'languages.workspace.symbol', @@ -40,29 +40,16 @@ export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContributi @inject(MonacoLanguages) protected readonly languages: MonacoLanguages; @inject(OpenerService) protected readonly openerService: OpenerService; - @inject(PrefixQuickOpenService) protected quickOpenService: PrefixQuickOpenService; + @inject(MonacoQuickInputService) protected monacoQuickInputService: MonacoQuickInputService; @inject(SelectionService) protected selectionService: SelectionService; + @inject(LabelProvider) protected readonly labelProvider: LabelProvider; isEnabled(): boolean { return this.languages.workspaceSymbolProviders !== undefined; } execute(): void { - this.quickOpenService.open(this.prefix); - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - fuzzyMatchLabel: true, - showItemsWithoutHighlight: true, - onClose: () => { - this.cancellationSource.cancel(); - } - }; + this.monacoQuickInputService.open(this.prefix); } registerCommands(commands: CommandRegistry): void { @@ -80,82 +67,85 @@ export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContributi }); } - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this); + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: SymbolsQuickAccessProvider, + prefix: SymbolsQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Go to Symbol in Workspace', needsEditor: false }] + }); + SymbolsQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } - private cancellationSource = new CancellationTokenSource(); - - async onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { + async getPicks(filter: string, token: CancellationToken): Promise> { + const items: Array = []; if (this.languages.workspaceSymbolProviders) { - this.cancellationSource.cancel(); - const newCancellationSource = new CancellationTokenSource(); - this.cancellationSource = newCancellationSource; - const param: WorkspaceSymbolParams = { - query: lookFor + query: filter }; - const items: QuickOpenItem[] = []; - const workspaceProviderPromises = []; for (const provider of this.languages.workspaceSymbolProviders) { workspaceProviderPromises.push((async () => { - const symbols = await provider.provideWorkspaceSymbols(param, newCancellationSource.token); - if (symbols && !newCancellationSource.token.isCancellationRequested) { + const symbols = await provider.provideWorkspaceSymbols(param, token); + if (symbols && !token.isCancellationRequested) { for (const symbol of symbols) { - items.push(this.createItem(symbol, provider, newCancellationSource.token)); + items.push(this.createItem(symbol, provider, token)); } - acceptor(items); } return symbols; })()); } - Promise.all(workspaceProviderPromises.map(p => p.then(sym => sym, _ => undefined))).then(symbols => { - const filteredSymbols = symbols.filter(el => el && el.length !== 0); - if (filteredSymbols.length === 0) { - items.push(new QuickOpenItem({ - label: lookFor.length === 0 ? 'Type to search for symbols' : 'No symbols matching', - run: () => false - })); - acceptor(items); - } - }).catch(); + await Promise.all(workspaceProviderPromises.map(p => p.then(sym => sym, _ => undefined))) + .then(symbols => { + const filteredSymbols = symbols.filter(el => el && el.length !== 0); + if (filteredSymbols.length === 0) { + items.push({ + label: filter.length === 0 ? 'Type to search for symbols' : 'No symbols matching', + }); + } + }).catch(); } + return items; } - protected createItem(sym: SymbolInformation, provider: WorkspaceSymbolProvider, token: CancellationToken): QuickOpenItem { + protected createItem(sym: SymbolInformation, provider: WorkspaceSymbolProvider, token: CancellationToken): monaco.quickInput.IAnythingQuickPickItem { const uri = new URI(sym.location.uri); - const icon = this.toCssClassName(sym.kind) || 'unknown'; + const iconClasses = this.toCssClassName(sym.kind); let parent = sym.containerName; if (parent) { parent += ' - '; } - parent = (parent || '') + uri.displayName; - return new SimpleOpenItem(sym.name, icon, parent, uri.toString(), () => { - - if (provider.resolveWorkspaceSymbol) { - provider.resolveWorkspaceSymbol(sym, token).then(resolvedSymbol => { - if (resolvedSymbol) { - this.openURL(uri, resolvedSymbol.location.range.start, resolvedSymbol.location.range.end); - } else { - // the symbol didn't resolve -> use given symbol - this.openURL(uri, sym.location.range.start, sym.location.range.end); - } - }); - } else { - // resolveWorkspaceSymbol wasn't specified - this.openURL(uri, sym.location.range.start, sym.location.range.end); + parent = (parent || '') + this.labelProvider.getName(uri); + return ({ + label: sym.name, + description: parent, + ariaLabel: uri.toString(), + iconClasses, + accept: () => { + if (provider.resolveWorkspaceSymbol) { + provider.resolveWorkspaceSymbol(sym, token).then(resolvedSymbol => { + if (resolvedSymbol) { + this.openURL(uri, resolvedSymbol.location.range.start, resolvedSymbol.location.range.end); + } else { + // the symbol didn't resolve -> use given symbol + this.openURL(uri, sym.location.range.start, sym.location.range.end); + } + }); + } else { + // resolveWorkspaceSymbol wasn't specified + this.openURL(uri, sym.location.range.start, sym.location.range.end); + } } }); } - protected toCssClassName(symbolKind: SymbolKind, inline?: boolean): string | undefined { + protected toCssClassName(symbolKind: SymbolKind, inline?: boolean): string[] | undefined { const kind = SymbolKind[symbolKind]; if (!kind) { return undefined; } - return `codicon ${inline ? 'inline' : 'block'} codicon-symbol-${kind.toLowerCase() || 'property'}`; + return [`codicon ${inline ? 'inline' : 'block'} codicon-symbol-${kind.toLowerCase() || 'property'}`]; } private openURL(uri: URI, start: Position, end: Position): void { @@ -165,52 +155,6 @@ export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContributi } } -class SimpleOpenItem extends QuickOpenItem { - - constructor( - protected readonly label: string, - protected readonly icon: string, - protected readonly parent: string, - protected readonly toolTip: string, - protected readonly onOpen: () => void, - protected readonly onSelect?: () => void - ) { - super(); - } - - getLabel(): string { - return this.label; - } - - isHidden(): boolean { - return false; - } - - getTooltip(): string { - return this.toolTip; - } - - getDescription(): string { - return this.parent; - } - - getIconClass(): string { - return this.icon; - } - - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - if (!this.onSelect) { - return false; - } - this.onSelect(); - return true; - } - this.onOpen(); - return true; - } -} - enum SymbolKind { File = 1, Module = 2, @@ -239,3 +183,27 @@ enum SymbolKind { Operator = 25, TypeParameter = 26 } + +export class SymbolsQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = '#'; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(SymbolsQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: SymbolsQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return SymbolsQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/monaco/src/electron-browser/monaco-electron-module.ts b/packages/monaco/src/electron-browser/monaco-electron-module.ts index 751eddedcaa27..9a84ed7822b83 100644 --- a/packages/monaco/src/electron-browser/monaco-electron-module.ts +++ b/packages/monaco/src/electron-browser/monaco-electron-module.ts @@ -47,5 +47,9 @@ export default loadVsRequire(global) s.process.browser = true; return loadMonaco(vsRequire); }) + .then(() => + // Clear Monaco QuickAccessRegistry as it currently includes monaco internal providers and not Theia's providers + monaco.platform.Registry.as('workbench.contributions.quickaccess').clear() + ) .then(() => import('../browser/monaco-frontend-module')) .then(module => module.default); diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts index a0f65fe3e9987..4071ee3581802 100644 --- a/packages/monaco/src/typings/monaco/index.d.ts +++ b/packages/monaco/src/typings/monaco/index.d.ts @@ -13,7 +13,6 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable max-len */ @@ -62,10 +61,14 @@ declare module monaco.format { } declare module monaco.instantiation { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/instantiation/common/instantiation.ts#L86 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/instantiation/common/instantiation.ts#L86 export interface IInstantiationService { invokeFunction: (fn: any, ...args: any) => any } + + export class InstantiationService implements IInstantiationService { + invokeFunction: (fn: any, ...args: any) => any; + } } declare module monaco.textModel { @@ -75,13 +78,13 @@ declare module monaco.textModel { on(event: 'end', callback: () => void): void; on(event: string, callback: any): void; } - // https://github.com/microsoft/vscode/blob/e683ace9e5acadba0e8bde72d793cb2cb83e58a7/src/vs/editor/common/model/textModel.ts#L58 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model/textModel.ts#L62 export function createTextBufferFactoryFromStream(stream: ITextStream, filter?: (chunk: any) => string, validator?: (chunk: any) => Error | undefined): Promise; } declare module monaco.editor { - // https://github.com/microsoft/vscode/blob/e683ace9e5acadba0e8bde72d793cb2cb83e58a7/src/vs/editor/common/model.ts#L1263 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model.ts#L1264 export interface ITextBufferFactory { getFirstLineText(lengthLimit: number): string; } @@ -92,40 +95,67 @@ declare module monaco.editor { /** * @internal */ - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/browser/editorBrowser.ts#L644-L647 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorBrowser.ts#L736 setDecorations(decorationTypeKey: string, ranges: editorCommon.IDecorationOptions[]): void; /** * @internal */ - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/browser/editorBrowser.ts#L654-L657 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorBrowser.ts#L746 removeDecorations(decorationTypeKey: string): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/bulkEditService.ts#L14 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/undoRedo/common/undoRedo.ts#L92-L111 + export class UndoRedoSource { + private static _ID = 0; + + public readonly id: number; + private order: number; + + constructor() { + this.id = UndoRedoSource._ID++; + this.order = 1; + } + + public nextOrder(): number { + if (this.id === 0) { + return 0; + } + return this.order++; + } + + public static None = new UndoRedoSource(); + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/bulkEditService.ts#L67 export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; + token?: CancellationToken; showPreview?: boolean; label?: string; + quotableLabel?: string; + undoRedoSource?: UndoRedoSource; + undoRedoGroupId?: number; + confirmBeforeUndo?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/bulkEditService.ts#L21 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/bulkEditService.ts#L79 export interface IBulkEditResult { ariaSummary: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/bulkEditService.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/bulkEditService.ts#L83 export type IBulkEditPreviewHandler = (edit: monaco.languages.WorkspaceEdit, options?: IBulkEditOptions) => Promise; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/bulkEditService.ts#L27 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/bulkEditService.ts#L85 export interface IBulkEditService { - apply(edit: monaco.languages.WorkspaceEdit): Promise; + apply(edit: monaco.languages.WorkspaceEdit, options?: IBulkEditOptions): Promise; hasPreviewHandler(): boolean; setPreviewHandler(handler: IBulkEditPreviewHandler): monaco.IDisposable; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/widget/diffNavigator.ts#L43 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/widget/diffNavigator.ts#L43 export interface IDiffNavigator { readonly ranges: IDiffRange[]; readonly nextIdx: number; @@ -133,12 +163,12 @@ declare module monaco.editor { _initIdx(fwd: boolean): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/widget/diffNavigator.ts#L16 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/widget/diffNavigator.ts#L16 export interface IDiffRange { readonly range: Range; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneCodeEditor.ts#L206 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneCodeEditor.ts#L245 export interface IStandaloneCodeEditor extends CommonCodeEditor { setDecorations(decorationTypeKey: string, ranges: IDecorationOptions[]): void; setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void; @@ -152,12 +182,12 @@ declare module monaco.editor { } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/widget/codeEditorWidget.ts#L106 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/widget/codeEditorWidget.ts#L107 export interface CommonCodeEditor { readonly _commandService: monaco.commands.ICommandService; readonly _instantiationService: monaco.instantiation.IInstantiationService; readonly _contributions: { - 'editor.controller.quickOpenController': monaco.quickOpen.QuickOpenController + // 'editor.controller.quickOpenController': monaco.quickOpen.QuickOpenController 'editor.contrib.referencesController': monaco.referenceSearch.ReferencesController 'editor.contrib.hover': ModesHoverController 'css.editor.codeLens': CodeLensContribution @@ -169,50 +199,50 @@ declare module monaco.editor { } | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codeAction/codeActionCommands.ts#L68 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codeAction/codeActionCommands.ts#L68 export interface QuickFixController { readonly _ui: { rawValue?: CodeActionUi } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codeAction/codeActionUi.ts#L22 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codeAction/codeActionUi.ts#L21 export interface CodeActionUi { readonly _lightBulbWidget: { rawValue?: LightBulbWidget } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codeAction/lightBulbWidget.ts#L47 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codeAction/lightBulbWidget.ts#L48 export interface LightBulbWidget { readonly _domNode: HTMLDivElement; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codelens/codelensController.ts#L24 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codelens/codelensController.ts#L28 export interface CodeLensContribution { readonly _lenses: CodeLensWidget[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codelens/codelensWidget.ts#L182 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codelens/codelensWidget.ts#L178 export interface CodeLensWidget { readonly _contentWidget?: CodeLensContentWidget; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/codelens/codelensWidget.ts#L50 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/codelens/codelensWidget.ts#L49 export interface CodeLensContentWidget { readonly _domNode: HTMLElement; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/hover/hover.ts#L31 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/hover/hover.ts#L30 export interface ModesHoverController { readonly contentWidget: ModesContentHoverWidget; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/hover/modesContentHover.ts#L195 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/hover/modesContentHover.ts#L177 export interface ModesContentHoverWidget { readonly isVisible: boolean; readonly _domNode: HTMLElement; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/controller/cursor.ts#L169 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/controller/cursor.ts#L122 export interface ICursor { trigger(source: string, handlerId: string, payload: any): void; } @@ -226,7 +256,7 @@ declare module monaco.editor { contextKeyService?: monaco.contextKeyService.IContextKeyService; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/editor/common/editor.ts#L63 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/editor/common/editor.ts#L68 export interface IResourceInput { resource: monaco.Uri; options?: IResourceInputOptions; @@ -237,24 +267,25 @@ declare module monaco.editor { * Tells the editor to not receive keyboard focus when the editor is being opened. By default, * the editor will receive keyboard focus on open. */ - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/editor/common/editor.ts#L132 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/editor/common/editor.ts#L150 readonly preserveFocus?: boolean; /** * Will reveal the editor if it is already opened and visible in any of the opened editor groups. */ - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/editor/common/editor.ts#L157 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/editor/common/editor.ts#L175 readonly revealIfVisible?: boolean; /** * Text editor selection. */ - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/editor/common/editor.ts#L223 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/editor/common/editor.ts#L270 readonly selection?: Partial; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/codeEditorService.ts#L15 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/codeEditorService.ts#L16 export interface ICodeEditorService { + getFocusedCodeEditor(): monaco.editor.ICodeEditor | undefined; getActiveCodeEditor(): monaco.editor.ICodeEditor | undefined; openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, sideBySide?: boolean): Promise; registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: monaco.editor.ICodeEditor): void; @@ -262,12 +293,12 @@ declare module monaco.editor { resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/lifecycle.ts#L209 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/lifecycle.ts#L253 export interface IReference extends monaco.IDisposable { readonly object: T; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/resolverService.ts#L14 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/resolverService.ts#L14 export interface ITextModelService { /** * Provided a resource URI, it will return a model reference @@ -281,7 +312,7 @@ declare module monaco.editor { registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): monaco.IDisposable; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/resolverService.ts#L34 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/resolverService.ts#L34 export interface ITextModelContentProvider { /** * Given a resource, return the content of the resource as IModel. @@ -289,8 +320,8 @@ declare module monaco.editor { provideTextContent(resource: monaco.Uri): Promise | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/resolverService.ts#L42 && - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/editor/common/editor.ts#L9 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/resolverService.ts#L42 && + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/editor/common/editor.ts#L9 export interface ITextEditorModel { readonly onDispose: monaco.IEvent; /** @@ -308,7 +339,7 @@ declare module monaco.editor { textEditorModel: monaco.editor.IModel; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/browser/contextmenu.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/contextmenu.ts#L18 export interface IContextMenuDelegate { /** * Returns with an HTML element or the client coordinates as the anchor of the context menu to open. @@ -326,7 +357,7 @@ declare module monaco.editor { onHide(wasCancelled: boolean): void } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/actions.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/actions.ts#L26 export interface IAction extends IDisposable { readonly id: string; label: string; @@ -337,7 +368,7 @@ declare module monaco.editor { run(event?: any): Promise; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextview/browser/contextView.ts#L38 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextview/browser/contextView.ts#L39 export interface IContextMenuService { /** * Shows the native Monaco context menu in the editor. @@ -345,26 +376,26 @@ declare module monaco.editor { showContextMenu(delegate: IContextMenuDelegate): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L623 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L680 export interface IDecorationOptions { range: IRange; hoverMessage?: IMarkdownString | IMarkdownString[]; renderOptions?: IDecorationInstanceRenderOptions; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L607 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L664 export interface IThemeDecorationInstanceRenderOptions { before?: IContentDecorationRenderOptions; after?: IContentDecorationRenderOptions; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L615 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L672 export interface IDecorationInstanceRenderOptions extends IThemeDecorationInstanceRenderOptions { light?: IThemeDecorationInstanceRenderOptions; dark?: IThemeDecorationInstanceRenderOptions; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L575 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L628 export interface IContentDecorationRenderOptions { contentText?: string; contentIconPath?: UriComponents; @@ -383,7 +414,7 @@ declare module monaco.editor { height?: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L595 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L652 export interface IDecorationRenderOptions extends IThemeDecorationRenderOptions { isWholeLine?: boolean; rangeBehavior?: TrackedRangeStickiness; @@ -393,7 +424,7 @@ declare module monaco.editor { dark?: IThemeDecorationRenderOptions; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/editorCommon.ts#L540 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/editorCommon.ts#L592 export interface IThemeDecorationRenderOptions { backgroundColor?: string | ThemeColor; @@ -426,7 +457,7 @@ declare module monaco.editor { after?: IContentDecorationRenderOptions; } - // https://github.com/microsoft/vscode/blob/e683ace9e5acadba0e8bde72d793cb2cb83e58a7/src/vs/editor/common/model.ts#L522 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model.ts#L522 export interface ITextSnapshot { read(): string | null; } @@ -437,17 +468,17 @@ declare module monaco.editor { * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. * @internal */ - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/common/model.ts#L826-L831 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model.ts#L890 getLineTokens(lineNumber: number): LineTokens; /** * Force tokenization information for `lineNumber` to be accurate. * @internal */ - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/common/model.ts#L806-L810 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model.ts#L869 forceTokenization(lineNumber: number): void; - // https://github.com/microsoft/vscode/blob/e683ace9e5acadba0e8bde72d793cb2cb83e58a7/src/vs/editor/common/model.ts#L623 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model.ts#L617 createSnapshot(): ITextSnapshot | null; /** @@ -463,18 +494,18 @@ declare module monaco.editor { declare module monaco.commands { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/commands/common/commands.ts#L55 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/commands/common/commands.ts#L60 export const CommandsRegistry: { getCommands(): Map any }>; }; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/commands/common/commands.ts#L16 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/commands/common/commands.ts#L16 export interface ICommandEvent { commandId: string; args: any[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/commands/common/commands.ts#L21 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/commands/common/commands.ts#L21 export interface ICommandService { onWillExecuteCommand: monaco.Event; onDidExecuteCommand: monaco.Event; @@ -485,13 +516,13 @@ declare module monaco.commands { declare module monaco.actions { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L17 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L21 export interface ILocalizedString { value: string; original: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L22 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L41 export interface ICommandAction { id: string; title: string | ILocalizedString; @@ -501,7 +532,7 @@ declare module monaco.actions { toggled?: monaco.contextkey.ContextKeyExpr; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L35 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L53 export interface IMenuItem { command: ICommandAction; when?: monaco.contextkey.ContextKeyExpr; @@ -510,7 +541,7 @@ declare module monaco.actions { alt?: ICommandAction; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L43 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L61 export interface ISubmenuItem { title: string | ILocalizedString; submenu: number; // enum MenuId @@ -519,46 +550,150 @@ declare module monaco.actions { order?: number; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L137 + export class MenuId { + + private static _idPool = 0; + + static readonly CommandPalette = new MenuId('CommandPalette'); + static readonly DebugBreakpointsContext = new MenuId('DebugBreakpointsContext'); + static readonly DebugCallStackContext = new MenuId('DebugCallStackContext'); + static readonly DebugConsoleContext = new MenuId('DebugConsoleContext'); + static readonly DebugVariablesContext = new MenuId('DebugVariablesContext'); + static readonly DebugWatchContext = new MenuId('DebugWatchContext'); + static readonly DebugToolBar = new MenuId('DebugToolBar'); + static readonly EditorContext = new MenuId('EditorContext'); + static readonly EditorContextPeek = new MenuId('EditorContextPeek'); + static readonly EditorTitle = new MenuId('EditorTitle'); + static readonly EditorTitleRun = new MenuId('EditorTitleRun'); + static readonly EditorTitleContext = new MenuId('EditorTitleContext'); + static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext'); + static readonly ExplorerContext = new MenuId('ExplorerContext'); + static readonly ExtensionContext = new MenuId('ExtensionContext'); + static readonly GlobalActivity = new MenuId('GlobalActivity'); + static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu'); + static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu'); + static readonly MenubarEditMenu = new MenuId('MenubarEditMenu'); + static readonly MenubarFileMenu = new MenuId('MenubarFileMenu'); + static readonly MenubarGoMenu = new MenuId('MenubarGoMenu'); + static readonly MenubarHelpMenu = new MenuId('MenubarHelpMenu'); + static readonly MenubarLayoutMenu = new MenuId('MenubarLayoutMenu'); + static readonly MenubarNewBreakpointMenu = new MenuId('MenubarNewBreakpointMenu'); + static readonly MenubarPreferencesMenu = new MenuId('MenubarPreferencesMenu'); + static readonly MenubarRecentMenu = new MenuId('MenubarRecentMenu'); + static readonly MenubarSelectionMenu = new MenuId('MenubarSelectionMenu'); + static readonly MenubarSwitchEditorMenu = new MenuId('MenubarSwitchEditorMenu'); + static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu'); + static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu'); + static readonly MenubarViewMenu = new MenuId('MenubarViewMenu'); + static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu'); + static readonly OpenEditorsContext = new MenuId('OpenEditorsContext'); + static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); + static readonly SCMChangeContext = new MenuId('SCMChangeContext'); + static readonly SCMResourceContext = new MenuId('SCMResourceContext'); + static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext'); + static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext'); + static readonly SCMSourceControl = new MenuId('SCMSourceControl'); + static readonly SCMTitle = new MenuId('SCMTitle'); + static readonly SearchContext = new MenuId('SearchContext'); + static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu'); + static readonly TestItem = new MenuId('TestItem'); + static readonly TouchBarContext = new MenuId('TouchBarContext'); + static readonly TitleBarContext = new MenuId('TitleBarContext'); + static readonly TunnelContext = new MenuId('TunnelContext'); + static readonly TunnelPortInline = new MenuId('TunnelInline'); + static readonly TunnelTitle = new MenuId('TunnelTitle'); + static readonly TunnelLocalAddressInline = new MenuId('TunnelLocalAddressInline'); + static readonly TunnelOriginInline = new MenuId('TunnelOriginInline'); + static readonly ViewItemContext = new MenuId('ViewItemContext'); + static readonly ViewContainerTitle = new MenuId('ViewContainerTitle'); + static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext'); + static readonly ViewTitle = new MenuId('ViewTitle'); + static readonly ViewTitleContext = new MenuId('ViewTitleContext'); + static readonly CommentThreadTitle = new MenuId('CommentThreadTitle'); + static readonly CommentThreadActions = new MenuId('CommentThreadActions'); + static readonly CommentTitle = new MenuId('CommentTitle'); + static readonly CommentActions = new MenuId('CommentActions'); + static readonly NotebookCellTitle = new MenuId('NotebookCellTitle'); + static readonly NotebookCellInsert = new MenuId('NotebookCellInsert'); + static readonly NotebookCellBetween = new MenuId('NotebookCellBetween'); + static readonly NotebookCellListTop = new MenuId('NotebookCellTop'); + static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle'); + static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle'); + static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle'); + static readonly BulkEditTitle = new MenuId('BulkEditTitle'); + static readonly BulkEditContext = new MenuId('BulkEditContext'); + static readonly TimelineItemContext = new MenuId('TimelineItemContext'); + static readonly TimelineTitle = new MenuId('TimelineTitle'); + static readonly TimelineTitleContext = new MenuId('TimelineTitleContext'); + static readonly AccountsContext = new MenuId('AccountsContext'); + static readonly PanelTitle = new MenuId('PanelTitle'); + static readonly TerminalContext = new MenuId('TerminalContext'); + + readonly id: number; + readonly _debugName: string; + + constructor(debugName: string) { + this.id = MenuId._idPool++; + this._debugName = debugName; + } + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L191 export interface IMenuRegistry { /** * Retrieves all the registered menu items for the given menu. - * @param menuId - see https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L67 + * @param menuId - see https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L89 */ - getMenuItems(menuId: 7 /* EditorContext */ | 8 /* EditorContextPeek */ | 25 /* MenubarSelectionMenu */): IMenuItem | ISubmenuItem[]; + getMenuItems(menuId: MenuId): Array; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L51 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L70 export function isIMenuItem(item: IMenuItem | ISubmenuItem): item is IMenuItem; /** * The shared menu registry singleton. */ - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L146 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L202 export const MenuRegistry: IMenuRegistry; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/actions/common/actions.ts#L250 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/actions/common/actions.ts#L356 export class MenuItemAction { } } declare module monaco.platform { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/platform.ts#L206 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/platform.ts#L245 export const enum OperatingSystem { Windows = 1, Macintosh = 2, Linux = 3 } export const OS: OperatingSystem; + + export class Registry { + static add(id: string, data: any): void; + + /** + * Returns true iff there is an extension with the provided id. + * @param id an extension identifier + */ + static knows(id: string): boolean; + + /** + * Returns the extension functions and properties defined by the specified key or null. + * @param id an extension identifier + */ + static as(id: string): T; + } } declare module monaco.keybindings { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/keybinding/common/keybindingResolver.ts#L20 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/keybinding/common/keybindingResolver.ts#L19 export class KeybindingResolver { static contextMatchesRules(context: monaco.contextKeyService.IContext, rules: monaco.contextkey.ContextKeyExpr | null | undefined): boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keyCodes.ts#L443 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keyCodes.ts#L443 export class SimpleKeybinding { public readonly ctrlKey: boolean; public readonly shiftKey: boolean; @@ -570,22 +705,22 @@ declare module monaco.keybindings { toChord(): ChordKeybinding; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keyCodes.ts#L503 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keyCodes.ts#L503 export class ChordKeybinding { readonly parts: SimpleKeybinding[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keyCodes.ts#L540 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keyCodes.ts#L540 export type Keybinding = ChordKeybinding; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L12 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L12 export interface IKeybindingItem { keybinding: Keybinding; command: string; when?: monaco.contextkey.ContextKeyExpr; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L69 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L73 export interface IKeybindingsRegistry { /** * Returns with all the default, static keybindings. @@ -593,10 +728,10 @@ declare module monaco.keybindings { getDefaultKeybindings(): IKeybindingItem[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L76 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L243 export const KeybindingsRegistry: IKeybindingsRegistry; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keyCodes.ts#L542 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keyCodes.ts#L542 export class ResolvedKeybindingPart { readonly ctrlKey: boolean; readonly shiftKey: boolean; @@ -609,7 +744,7 @@ declare module monaco.keybindings { constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, kbLabel: string | null, kbAriaLabel: string | null); } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keyCodes.ts#L564 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keyCodes.ts#L564 export abstract class ResolvedKeybinding { public abstract getLabel(): string | null; public abstract getAriaLabel(): string | null; @@ -619,14 +754,15 @@ declare module monaco.keybindings { public abstract isChord(): boolean; public abstract getParts(): ResolvedKeybindingPart[]; public abstract getDispatchParts(): (string | null)[]; + public abstract getSingleModifierDispatchParts(): (string | null)[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts#L13 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts#L13 export class USLayoutResolvedKeybinding { public static getDispatchStr(keybinding: SimpleKeybinding): string | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L17 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L17 export interface Modifiers { readonly ctrlKey: boolean; readonly shiftKey: boolean; @@ -634,76 +770,76 @@ declare module monaco.keybindings { readonly metaKey: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L24 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L24 export interface KeyLabelProvider { (keybinding: T): string | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L28 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L28 export interface ModifierLabelProvider { toLabel(OS: monaco.platform.OperatingSystem, parts: T[], keyLabelProvider: KeyLabelProvider): string | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L61 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L61 export const UILabelProvider: ModifierLabelProvider; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L88 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L88 export const AriaLabelProvider: ModifierLabelProvider; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L116 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L116 export const ElectronAcceleratorLabelProvider: ModifierLabelProvider; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/keybindingLabels.ts#L136 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/keybindingLabels.ts#L136 export const UserSettingsLabelProvider: ModifierLabelProvider; } declare module monaco.services { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneLanguages.ts#L101 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneLanguages.ts#L107 export class TokenizationSupport2Adapter implements monaco.modes.ITokenizationSupport { constructor(standaloneThemeService: IStandaloneThemeService, languageIdentifier: LanguageIdentifier, actual: monaco.languages.TokensProvider) tokenize(line: string, state: monaco.languages.IState, offsetDelta: number): any; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/resolverService.ts#L12 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/resolverService.ts#L12 export const ITextModelService: any; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/opener/common/opener.ts#L13 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/opener/common/opener.ts#L15 export interface OpenInternalOptions { readonly openToSide?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/opener/common/opener.ts#L28 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/opener/common/opener.ts#L35 export interface OpenExternalOptions { readonly openExternal?: boolean } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/opener/common/opener.ts#L38 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/opener/common/opener.ts#L49 export interface IOpener { open(resource: monaco.Uri | string, options?: OpenInternalOptions | OpenExternalOptions): Promise; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/openerService.ts#L89 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/openerService.ts#L94 export class OpenerService { constructor(editorService: monaco.editor.ICodeEditorService, commandService: monaco.commands.ICommandService); registerOpener(opener: IOpener): monaco.IDisposable; open(resource: monaco.Uri | string, options?: OpenInternalOptions & OpenExternalOptions): Promise; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/codeEditorService.ts#L13 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/codeEditorService.ts#L14 export const ICodeEditorService: any; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/configuration/common/configuration.ts#L16 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configuration.ts#L16 export const IConfigurationService: any; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/configuration/common/configurationModels.ts#L337 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configurationModels.ts#L377 export interface Configuration { getValue(section: string | undefined, overrides: any, workspace: any | undefined): any; toData(): any; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/configuration/common/configuration.ts#L30-L37 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configuration.ts#L30-L38 export const enum ConfigurationTarget { USER = 1, USER_LOCAL, @@ -714,13 +850,13 @@ declare module monaco.services { MEMORY } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/configuration/common/configuration.ts#L51 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configuration.ts#L51 export interface IConfigurationChange { keys: string[]; overrides: [string, string[]][]; } - // https://github.com/theia-ide/vscode/blob/b0b47123a5da83d42c2675f2bfff5bb9f1b2673c/src/vs/platform/configuration/common/configuration.ts#L56 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configuration.ts#L56 export class IConfigurationChangeEvent { readonly source: ConfigurationTarget; @@ -730,28 +866,29 @@ declare module monaco.services { affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/configuration/common/configuration.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/configuration/common/configuration.ts#L25 export interface IConfigurationOverrides { overrideIdentifier?: string | null; resource?: monaco.Uri | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/simpleServices.ts#L434 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/simpleServices.ts#L445 export interface IConfigurationService { _onDidChangeConfiguration: monaco.Emitter; _configuration: Configuration; } - // https://github.com/microsoft/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/textResourceConfigurationService.ts#L71 + // https://github.com/microsoft/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/textResourceConfigurationService.ts#L71 export interface ITextResourcePropertiesService { getEOL(resource: monaco.Uri | undefined, language?: string): string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/services/codeEditorServiceImpl.ts#L58 - export abstract class CodeEditorServiceImpl implements monaco.editor.ICodeEditorService { - constructor(themeService: IStandaloneThemeService); - abstract getActiveCodeEditor(): monaco.editor.ICodeEditor | undefined; - abstract openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/services/codeEditorServiceImpl.ts#L84 + export class CodeEditorServiceImpl implements monaco.editor.ICodeEditorService { + constructor(styleSheet: any, themeService: IStandaloneThemeService); + + getActiveCodeEditor(): monaco.editor.ICodeEditor | undefined; + openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, sideBySide?: boolean): Promise; registerDecorationType: monaco.editor.ICodeEditorService['registerDecorationType']; removeDecorationType: monaco.editor.ICodeEditorService['removeDecorationType']; @@ -763,7 +900,7 @@ declare module monaco.services { getFocusedCodeEditor(): monaco.editor.ICodeEditor | undefined; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/simpleServices.ts#L249 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/simpleServices.ts#L264 export class StandaloneCommandService implements monaco.commands.ICommandService { constructor(instantiationService: monaco.instantiation.IInstantiationService); private readonly _onWillExecuteCommand: monaco.Emitter; @@ -773,12 +910,12 @@ declare module monaco.services { executeCommand(commandId: string, ...args: any[]): Promise; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneServices.ts#L60 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneServices.ts#L66 export class LazyStaticService { get(overrides?: monaco.editor.IEditorOverrideServices): T; } - // https://github.com/microsoft/vscode/blob/0eb3a02ca2bcfab5faa3dc6e52d7c079efafcab0/src/vs/platform/theme/common/themeService.ts#L78 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/themeService.ts#L86 export interface ITokenStyle { readonly foreground?: number; readonly bold?: boolean; @@ -786,55 +923,67 @@ declare module monaco.services { readonly italic?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/common/standaloneThemeService.ts#L28 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/common/standaloneThemeService.ts#L29 export interface IStandaloneThemeService extends monaco.theme.IThemeService { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L178 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L200 readonly _knownThemes: Map; + setTheme(themeName: string): void; + setAutoDetectHighContrast(autoDetectHighContrast: boolean): void; + defineTheme(themeName: string, themeData: monaco.editor.IStandaloneThemeData): void; + getColorTheme(): IStandaloneTheme; + setColorMapOverride(colorMapOverride: monaco.color.Color[] | null): void; + } - getTheme(): IStandaloneTheme; + export class StandaloneThemeServiceImpl implements IStandaloneThemeService { + readonly _knownThemes: Map; + setTheme(themeName: string): void { } + setAutoDetectHighContrast(autoDetectHighContrast: boolean): void { } + defineTheme(themeName: string, themeData: monaco.editor.IStandaloneThemeData): void { } + getColorTheme(): IStandaloneTheme { } + setColorMapOverride(colorMapOverride: monaco.color.Color[] | null): void { } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/common/standaloneThemeService.ts#L23 - export interface IStandaloneTheme { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L31 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/common/standaloneThemeService.ts#L24 + export interface IStandaloneTheme extends monaco.theme.IColorTheme { + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L33 themeData: monaco.editor.IStandaloneThemeData tokenTheme: TokenTheme; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/themeService.ts#L98 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/themeService.ts#L105 getColor(color: string, useDefault?: boolean): monaco.color.Color | undefined; getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes/supports/tokenization.ts#L188 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes/supports/tokenization.ts#L188 export interface TokenTheme { match(languageId: LanguageId, scope: string): number; _match(token: string): any; getColorMap(): monaco.color.Color[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L27 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L27 export const enum LanguageId { Null = 0, PlainText = 1 } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L35 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L35 export class LanguageIdentifier { public readonly id: LanguageId; readonly language: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L58 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L58 export interface IMode { getId(): string; getLanguageIdentifier(): LanguageIdentifier; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/modeService.ts#L30 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/modeService.ts#L30 export interface IModeService { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/modeServiceImpl.ts#L46 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/modeServiceImpl.ts#L46 private readonly _instantiatedModes: { [modeId: string]: IMode; }; private readonly _onLanguagesMaybeChanged: Emitter; readonly onDidCreateMode: monaco.IEvent; @@ -842,17 +991,17 @@ declare module monaco.services { getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/modeService.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/modeService.ts#L25 export interface ILanguageSelection { readonly languageIdentifier: LanguageIdentifier; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/instantiation/common/serviceCollection.ts#L9 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/instantiation/common/serviceCollection.ts#L9 export interface ServiceCollection { set(id: any, instanceOrDescriptor: T): T; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/markers/common/markers.ts#L12 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/markers/common/markers.ts#L12 export interface IMarkerService { read(filter?: { owner?: string; resource?: monaco.Uri; severities?: number, take?: number; }): editor.IMarker[]; } @@ -863,12 +1012,12 @@ declare module monaco.services { updateModel(model: monaco.editor.ITextModel, value: string | monaco.editor.ITextBufferFactory): void; } - // https://github.com/microsoft/vscode/blob/standalone/0.20.x/src/vs/editor/common/services/editorWorkerService.ts#L21 + // https://github.com/microsoft/vscode/blob/standalone/0.23.x/src/vs/editor/common/services/editorWorkerService.ts#L21 export interface IEditorWorkerService { computeMoreMinimalEdits(resource: monaco.Uri, edits: monaco.languages.TextEdit[] | null | undefined): Promise; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneServices.ts#L56 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneServices.ts#L62 export module StaticServices { export function init(overrides: monaco.editor.IEditorOverrideServices): [ServiceCollection, monaco.instantiation.IInstantiationService]; export const standaloneThemeService: LazyStaticService; @@ -884,21 +1033,66 @@ declare module monaco.services { } declare module monaco.theme { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/themeService.ts#L89 - export interface ITheme { } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/themeService.ts#L93 + + export type ColorIdentifier = string; + + export interface IColorTheme { + readonly type: ColorScheme; + + readonly label: string; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/themeService.ts#L131 + /** + * Resolves the color of the given color identifier. If the theme does not + * specify the color, the default color is returned unless useDefault is set to false. + * @param color the id of the color + * @param useDefault specifies if the default color should be used. If not set, the default is used. + */ + getColor(color: ColorIdentifier, useDefault?: boolean): monaco.color.Color | undefined; + + /** + * Returns whether the theme defines a value for the color. If not, that means the + * default color will be used. + */ + defines(color: ColorIdentifier): boolean; + + /** + * Returns the token style for a given classification. The result uses the MetadataConsts format + */ + getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): monaco.services.ITokenStyle | undefined; + + /** + * List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list. + */ + readonly tokenColorMap: string[]; + + /** + * Defines whether semantic highlighting should be enabled for the theme. + */ + readonly semanticHighlighting: boolean; + } + + export interface IFileIconTheme { + readonly hasFileIcons: boolean; + readonly hasFolderIcons: boolean; + readonly hidesExplorerArrows: boolean; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/themeService.ts#L143 export interface IThemeService { - readonly onThemeChange: monaco.IEvent; + getColorTheme(): IColorTheme; + getFileIconTheme(): IFileIconTheme; + readonly onDidColorThemeChange: monaco.IEvent; + readonly onDidFileIconThemeChange: monaco.IEvent; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/styler.ts#L10 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/styler.ts#L10 export interface IThemable { } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/styler.ts#L166 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/styler.ts#L171 export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeService): monaco.IDisposable; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/themeService.ts#L25 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/themeService.ts#L38 export interface ThemeIcon { readonly id: string; } @@ -910,7 +1104,7 @@ declare module monaco.theme { declare module monaco.color { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/color.ts#L13 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/color.ts#L13 export class RGBA { readonly r: number; readonly g: number; @@ -920,7 +1114,7 @@ declare module monaco.color { constructor(r: number, g: number, b: number, a?: number); } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/color.ts#L48 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/color.ts#L48 export class HSLA { readonly h: number; readonly s: number; @@ -930,65 +1124,65 @@ declare module monaco.color { constructor(h: number, s: number, l: number, a: number); } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/color.ts#L256 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/color.ts#L256 export class Color { readonly rgba: RGBA; constructor(arg: RGBA | HSLA); } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L20 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L19 export interface ColorContribution { readonly id: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L29 - export type ColorFunction = (theme: monaco.theme.ITheme) => Color | undefined; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L28 + export type ColorFunction = (theme: monaco.theme.IColorTheme) => Color | undefined; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L42 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L41 export type ColorValue = string | Color | ColorFunction; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L33 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L32 export interface ColorDefaults { light?: ColorValue; dark?: ColorValue; hc?: ColorValue; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L49 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L48 export interface IColorRegistry { getColors(): ColorContribution[]; registerColor(id: string, defaults: ColorDefaults | undefined, description: string): string; deregisterColor(id: string): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L173 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L170 export function getColorRegistry(): IColorRegistry; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L434 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L475 export function darken(colorValue: ColorValue, factor: number): ColorFunction; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L444 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L485 export function lighten(colorValue: ColorValue, factor: number): ColorFunction; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/theme/common/colorRegistry.ts#L454 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/theme/common/colorRegistry.ts#L495 export function transparent(colorValue: ColorValue, factor: number): ColorFunction; } declare module monaco.referenceSearch { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L784 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L943 export interface Location { uri: Uri, range: IRange } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/gotoSymbol/referencesModel.ts#L20 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/gotoSymbol/referencesModel.ts#L22 export interface OneReference { } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/gotoSymbol/referencesModel.ts#L148 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/gotoSymbol/referencesModel.ts#L142 export interface ReferencesModel implements IDisposable { readonly references: OneReference[] } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts#L187 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts#L193 export interface ReferenceWidget { show(range: IRange): void; hide(): void; @@ -1000,13 +1194,13 @@ declare module monaco.referenceSearch { } // it's used as return value for referenceWidget._tree - // see https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts#L198 + // see https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts#L204 export interface ReferenceTree { getFocus(): ReferenceTreeElement[] } export interface ReferenceTreeElement { } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts#L32 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts#L32 export interface ReferencesController extends IDisposable { static readonly ID: string; _widget?: ReferenceWidget; @@ -1019,180 +1213,740 @@ declare module monaco.referenceSearch { } } -declare module monaco.quickOpen { +declare module monaco.quickInput { + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickInput.ts#L18 + export interface IQuickInputService { + readonly _serviceBrand?: undefined; + /** + * Provides access to the back button in quick input. + */ + readonly backButton?: IQuickInputButton; + /** + * Provides access to the quick access providers. + */ + readonly quickAccess: IQuickAccessController; + /** + * Allows to register on the event that quick input is showing. + */ + readonly onShow?: Event; + /** + * Allows to register on the event that quick input is hiding. + */ + readonly onHide?: Event; + /** + * Opens the quick input box for selecting items and returns a promise + * with the user selected item(s) if any. + */ + pick?(picks: Promise | QuickPickItem[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): Promise; + pick?(picks: Promise | QuickPickItem[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): Promise; + pick?(picks: Promise | QuickPickItem[], options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; + /** + * Opens the quick input box for text input and returns a promise with the user typed value if any. + */ + input?(options?: IInputOptions, token?: CancellationToken): Promise; + /** + * Provides raw access to the quick pick controller. + */ + createQuickPick(): IQuickPick; + /** + * Provides raw access to the quick input controller. + */ + createInputBox(): IInputBox; + /** + * Moves focus into quick input. + */ + focus?(): void; + /** + * Toggle the checked state of the selected item. + */ + toggle?(): void; + /** + * Navigate inside the opened quick input list. + */ + navigate?(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void; + /** + * Navigate back in a multi-step quick input. + */ + back?(): Promise; + /** + * Accept the selected item. + * + * @param keyMods allows to override the state of key + * modifiers that should be present when invoking. + */ + accept?(keyMods?: IKeyMods): Promise; + /** + * Cancels quick input and closes it. + */ + cancel?(): Promise; + } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/browser/ui/inputbox/inputBox.ts#L59 - export interface IMessage { - content: string; - formatContent?: boolean; // defaults to false - type?: 1 /* INFO */ | 2 /* WARNING */ | 3 /* ERROR */; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L55 + export interface IQuickNavigateConfiguration { + keybindings: monaco.keybindings.ResolvedKeybinding[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/browser/ui/inputbox/inputBox.ts#L91 - export class InputBox { - inputElement: HTMLInputElement; - setPlaceHolder(placeHolder: string): void; - showMessage(message: IMessage, force?: boolean): void; - hideMessage(): void; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L186 + export enum ItemActivation { + NONE, + FIRST, + SECOND, + LAST } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/tree/browser/tree.ts#L17 - export interface QuickOpenTree { - onDidChangeFocus: monaco.Event; - getFocus(): monaco.quickOpen.QuickOpenEntry; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L151 + export interface IQuickInput extends Disposable { + readonly onDidHide: Event; + readonly onDispose: Event; + title: string | undefined; + description: string | undefined; + step: number | undefined; + totalSteps: number | undefined; + enabled: boolean; + contextKey: string | undefined; + busy: boolean; + ignoreFocusOut: boolean; + show(): void; + hide(): void; + dispose(): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts#L96 - export class QuickOpenWidget implements IDisposable { - inputBox?: InputBox; - tree: QuickOpenTree; - constructor(container: HTMLElement, callbacks: IQuickOpenCallbacks, options: IQuickOpenOptions); + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/browser/quickInput.ts#L1112 + export class QuickInputController implements IDisposable { + constructor(private options: IQuickInputOptions); + readonly backButton: IQuickInputButton; + pick>(picks: Promise[]> | QuickPickValue[], + options: O = {}, + token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined>; + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise; + + createQuickPick(): IQuickPick; + createInputBox(): IInputBox; + + hide(): void; + focus(): void; + toggle(): void; + navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void; + async accept(keyMods: IKeyMods = { alt: false, ctrlCmd: false }): Promise; + async back(): Promise; + async cancel(): Promise; + layout(dimension: monaco.editor.IDimension, titleBarOffset: number): void; + applyStyles(styles: IQuickInputStyles): void; dispose(): void; - isVisible(): boolean; - create(): HTMLElement; - setInput(input: IModel, autoFocus?: IAutoFocus, ariaLabel?: string): void; - layout(dimension: monaco.editor.IDimension): void; - show(prefix: string, options?: IShowOptions): void; - show(input: IModel, options?: IShowOptions): void; - hide(reason?: HideReason): void; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts#L79 - export enum HideReason { - ELEMENT_SELECTED, - FOCUS_LOST, - CANCELED - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts#L28 - export interface IQuickOpenCallbacks { - onOk: () => void; - onCancel: () => void; - onType: (lookFor: string) => void; - onShow?: () => void; - onHide?: (reason: HideReason) => void; - onFocusLost?: () => boolean /* veto close */; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts#L37 - export interface IQuickOpenOptions /* extends IQuickOpenStyles */ { - minItemsToShow?: number; - maxItemsToShow?: number; - inputPlaceHolder?: string; - inputAriaLabel?: string; - actionProvider?: IActionProvider; - keyboardSupport?: boolean; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts#L57 - export interface IShowOptions { - quickNavigateConfiguration?: IQuickNavigateConfiguration; - autoFocus?: IAutoFocus; - inputSelection?: IRange; - value?: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L8 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/browser/quickInput.ts#L35 + export interface IQuickInputOptions { + idPrefix: string; + container: HTMLElement; + ignoreFocusOut?(): boolean; + isScreenReaderOptimized?(): boolean; + backKeybindingLabel?(): string | undefined; + setContextKey?(id?: string): void; + returnFocus?(): void; + createList?( + user: string, + container: HTMLElement, + delegate: monaco.list.IListVirtualDelegate, + renderers: monaco.list.IListRenderer[], + options: monaco.list.IListOptions, + ): QuickInputItemList; + styles?: IQuickInputStyles; + } + + export class QuickInputOptions implements monaco.quickInput.IQuickInputOptions { + idPrefix: string; + container: HTMLElement; + styles?: IQuickInputStyles; + ignoreFocusOut: monaco.quickInput.IQuickInputOptions['ignoreFocusOut']; + isScreenReaderOptimized: monaco.quickInput.IQuickInputOptions['isScreenReaderOptimized']; + backKeybindingLabel: monaco.quickInput.IQuickInputOptions['backKeybindingLabel']; + setContextKey: monaco.quickInput.IQuickInputOptions['setContextKey']; + returnFocus: monaco.quickInput.IQuickInputOptions['returnFocus']; + createList: monaco.quickInput.IQuickInputOptions['createList']; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/browser/quickInput.ts#L53 + export interface IQuickInputStyles { + widget: IQuickInputWidgetStyles; + inputBox: IInputBoxStyles; + countBadge: ICountBadgeStyles; + button: IButtonStyles; + progressBar: IProgressBarStyles; + list: monaco.list.IListStyles & { pickerGroupBorder?: monaco.color.Color; pickerGroupForeground?: monaco.color.Color; }; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/browser/quickInput.ts#L62 + export interface IQuickInputWidgetStyles { + quickInputBackground?: monaco.color.Color; + quickInputForeground?: monaco.color.Color; + quickInputTitleBackground?: monaco.color.Color; + contrastBorder?: monaco.color.Color; + widgetShadow?: monaco.color.Color; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L14 + export interface IQuickPickItemHighlights { + label?: monaco.filters.IMatch[]; + description?: monaco.filters.IMatch[]; + detail?: monaco.filters.IMatch[]; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/helpQuickAccess.ts#L12 + interface IHelpQuickAccessPickItem extends monaco.quickInput.IAnythingQuickPickItem { + prefix: string; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L333 + export type IQuickPickItemWithResource = IQuickPickItem & { resource?: URI }; + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts#L55 + export interface IAnythingQuickPickItem extends IPickerQuickAccessItem, IQuickPickItemWithResource { } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L20 + export interface IQuickPickItem { + type?: 'item' | 'separator'; + id?: string; + label: string; + meta?: string; + ariaLabel?: string; + description?: string; + detail?: string; + /** + * Allows to show a keybinding next to the item to indicate + * how the item can be triggered outside of the picker using + * keyboard shortcut. + */ + keybinding?: monaco.keybindings.ResolvedKeybinding; + iconClasses?: string[]; + italic?: boolean; + strikethrough?: boolean; + highlights?: IQuickPickItemHighlights; + buttons?: IQuickInputButton[]; + picked?: boolean; + alwaysShow?: boolean; + execute?: (item: IQuickPickItem, lookFor: string) => void + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L306 + export interface IQuickInputButton { + /** iconPath or iconClass required */ + iconPath?: URI | { light: URI; dark: URI } | { id: string }; // { dark: URI; light: URI; }; + /** iconPath or iconClass required */ + iconClass?: string; + tooltip?: string; + /** + * Whether to always show the button. By default buttons + * are only visible when hovering over them with the mouse + */ + alwaysVisible?: boolean; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L43 + export interface IQuickPickSeparator { + type: 'separator'; + label?: string; + } + + export type QuickPickValue = T | IQuickPickSeparator; + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L48 + export interface IKeyMods { + readonly ctrlCmd: boolean; + readonly alt: boolean; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L55 export interface IQuickNavigateConfiguration { keybindings: monaco.keybindings.ResolvedKeybinding[]; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L12 - export interface IAutoFocus { + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L193 + export interface IQuickPick extends IQuickInput { + value: string; + /** + * A method that allows to massage the value used + * for filtering, e.g, to remove certain parts. + */ + filterValue: (value: string) => string; + ariaLabel: string | undefined; + placeholder: string | undefined; + readonly onDidChangeValue: Event; + readonly onDidAccept: Event; + /** + * If enabled, will fire the `onDidAccept` event when + * pressing the arrow-right key with the idea of accepting + * the selected item without closing the picker. + */ + canAcceptInBackground: boolean; + ok: boolean | 'default'; + readonly onDidCustom: Event; + customButton: boolean; + customLabel: string | undefined; + customHover: string | undefined; + buttons: ReadonlyArray; + readonly onDidTriggerButton: Event; + readonly onDidTriggerItemButton: Event>; + items: ReadonlyArray; + canSelectMany: boolean; + matchOnDescription: boolean; + matchOnDetail: boolean; + matchOnLabel: boolean; + sortByLabel: boolean; + autoFocusOnList: boolean; + quickNavigate: IQuickNavigateConfiguration | undefined; + activeItems: ReadonlyArray; + readonly onDidChangeActive: Event; + /** + * Allows to control which entry should be activated by default. + */ + itemActivation: ItemActivation; + selectedItems: ReadonlyArray; + readonly onDidChangeSelection: Event; + readonly keyMods: IKeyMods; + valueSelection: Readonly<[number, number]> | undefined; + validationMessage: string | undefined; + inputHasFocus(): boolean; + focusOnInput(): void; + /** + * Hides the input box from the picker UI. This is typically used + * in combination with quick-navigation where no search UI should + * be presented. + */ + hideInput: boolean; + hideCheckAll: boolean; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L283 + export interface IInputBox extends IQuickInput { + value: string; + valueSelection: Readonly<[number, number]> | undefined; + placeholder: string | undefined; + password: boolean; + readonly onDidChangeValue: Event; + readonly onDidAccept: Event; + buttons: ReadonlyArray; + readonly onDidTriggerButton: Event; + prompt: string | undefined; + validationMessage: string | undefined; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L116 + export interface IInputOptions { + /** + * the value to prefill in the input box + */ + value?: string; + /** + * the selection of value, default to the whole word + */ + valueSelection?: [number, number]; + /** + * the text to display underneath the input box + */ + prompt?: string; + /** + * an optional string to show as placeholder in the input box to guide the user what to type + */ + placeHolder?: string; + /** + * Controls if a password input is shown. Password input hides the typed text. + */ + password?: boolean; + + ignoreFocusLost?: boolean; + /** + * an optional function that is used to validate user input. + */ + validateInput?: (input: string) => Promise; + } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/inputbox/inputBox.ts#L39 + export interface IInputBoxStyles { + readonly inputBackground?: monaco.color.Color; + readonly inputForeground?: monaco.color.Color; + readonly inputBorder?: monaco.color.Color; + readonly inputValidationInfoBorder?: monaco.color.Color; + readonly inputValidationInfoBackground?: monaco.color.Color; + readonly inputValidationInfoForeground?: monaco.color.Color; + readonly inputValidationWarningBorder?: monaco.color.Color; + readonly inputValidationWarningBackground?: monaco.color.Color; + readonly inputValidationWarningForeground?: monaco.color.Color; + readonly inputValidationErrorBorder?: monaco.color.Color; + readonly inputValidationErrorBackground?: monaco.color.Color; + readonly inputValidationErrorForeground?: monaco.color.Color; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/countBadge/countBadge.ts#L19 + export interface ICountBadgeStyles { + badgeBackground?: monaco.color.Color; + badgeForeground?: monaco.color.Color; + badgeBorder?: monaco.color.Color; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/button/button.ts#L26 + export interface IButtonStyles { + buttonBackground?: monaco.color.Color; + buttonHoverBackground?: monaco.color.Color; + buttonForeground?: monaco.color.Color; + buttonSecondaryBackground?: monaco.color.Color; + buttonSecondaryHoverBackground?: monaco.color.Color; + buttonSecondaryForeground?: monaco.color.Color; + buttonBorder?: monaco.color.Color; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/progressbar/progressbar.ts#L22 + export interface IProgressBarStyles { + progressBarBackground?: monaco.color.Color; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L59 + export interface IPickOptions { + /** + * an optional string to show as placeholder in the input box to guide the user what she picks on + */ + placeHolder?: string; + /** + * an optional flag to include the description when filtering the picks + */ + matchOnDescription?: boolean; + /** + * an optional flag to include the detail when filtering the picks + */ + matchOnDetail?: boolean; + /** + * an optional flag to filter the picks based on label. Defaults to true. + */ + matchOnLabel?: boolean; + /** + * an option flag to control whether focus is always automatically brought to a list item. Defaults to true. + */ + autoFocusOnList?: boolean; + /** + * an optional flag to not close the picker on focus lost + */ + ignoreFocusLost?: boolean; + /** + * an optional flag to make this picker multi-select + */ + canPickMany?: boolean; + /** + * enables quick navigate in the picker to open an element without typing + */ + quickNavigate?: IQuickNavigateConfiguration; + /** + * a context key to set when this picker is active + */ + contextKey?: string; + /** + * an optional property for the item to focus initially. + */ + activeItem?: Promise | T; + onKeyMods?: (keyMods: IKeyMods) => void; + onDidFocus?: (entry: T) => void; + onDidTriggerItemButton?: (context: IQuickPickItemButtonContext) => void; + } + + export interface IQuickPickOptions { + busy?: boolean; + enabled?: boolean; + title?: string; + description?: string; + value?: string; + filterValue?: (value: string) => string; + ariaLabel?: string; + buttons?: Array; + placeholder?: string; + canAcceptInBackground?: boolean; + customButton?: boolean; + customLabel?: string; + customHover?: string; + canSelectMany?: boolean; + matchOnDescription?: boolean; + matchOnDetail?: boolean; + matchOnLabel?: boolean; + sortByLabel?: boolean; + autoFocusOnList?: boolean; + ignoreFocusOut?: boolean; + quickNavigate?: monaco.quickInput.IQuickNavigateConfiguration; + itemActivation?: monaco.quickInput.ItemActivation; + valueSelection?: Readonly<[number, number]>; + validationMessage?: string; + hideInput?: boolean; + hideCheckAll?: boolean; + runIfSingle?: boolean + contextKey?: string; + activeItem?: T, + step?: number; + totalSteps?: number; + + onDidAccept?: () => void, + onDidChangeActive?: (quickPick: IQuickPick, activeItems: Array) => void, + onDidChangeSelection?: (quickPick: IQuickPick, selectedItems: Array) => void, + onDidChangeValue?: (quickPick: IQuickPick, filter: string) => void, + onDidCustom?: () => void, + onDidHide?: () => void, + onDidTriggerButton?: Event; + onDidTriggerItemButton?: Event>; + } + + export interface IQuickAccessDataService { + getPicks(filter: string, token: CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null; + registerQuickAccessProvider(): void; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L75 + export type Pick = T | IQuickPickSeparator; + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + export type PicksWithActive = { items: Array>, active?: T }; + export type Picks = Array> | PicksWithActive; + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + export type FastAndSlowPicks = { picks: Picks, additionalPicks: Promise> }; + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L92 + export class PickerQuickAccessProvider extends Disposable implements IQuickAccessProvider { + constructor(private prefix: string, protected options?: IPickerQuickAccessProviderOptions) { + super(); + } + provide(picker: IQuickPick, token: CancellationToken): IDisposable; + protected getPicks(filter: string, disposables: any, token: CancellationToken): Picks | Promise> | FastAndSlowPicks | null; + } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L62 + export interface IPickerQuickAccessProviderOptions { /** - * The index of the element to focus in the result list. + * Enables support for opening picks in the background via gesture. */ - autoFocusIndex?: number; + canAcceptInBackground?: boolean; /** - * If set to true, will automatically select the first entry from the result list. + * Enables to show a pick entry when no results are returned from a search. */ - autoFocusFirstEntry?: boolean; + noResultsPick?: T; + } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L36 + export interface IPickerQuickAccessItem extends IQuickPickItem { /** - * If set to true, will automatically select the second entry from the result list. + * A method that will be executed when the pick item is accepted from + * the picker. The picker will close automatically before running this. + * + * @param keyMods the state of modifier keys when the item was accepted. + * @param event the underlying event that caused the accept to trigger. */ - autoFocusSecondEntry?: boolean; + accept?(keyMods?: IKeyMods, event?: IQuickPickAcceptEvent): void; /** - * If set to true, will automatically select the last entry from the result list. + * A method that will be executed when a button of the pick item was + * clicked on. + * + * @param buttonIndex index of the button of the item that + * was clicked. + * + * @param the state of modifier keys when the button was triggered. + * + * @returns a value that indicates what should happen after the trigger + * which can be a `Promise` for long running operations. */ - autoFocusLastEntry?: boolean; + trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise; + } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L177 + export interface IQuickPickAcceptEvent { /** - * If set to true, will automatically select any entry whose label starts with the search - * value. Since some entries to the top might match the query but not on the prefix, this - * allows to select the most accurate match (matching the prefix) while still showing other - * elements. + * Signals if the picker item is to be accepted + * in the background while keeping the picker open. */ - autoFocusPrefixMatch?: string; + inBackground: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L43-L47 - export type Mode = 0 /* PREVIEW */ | 1 /* OPEN */ | 2 /* OPEN_IN_BACKGROUND */; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L41 + export enum DefaultQuickAccessFilterValue { + /** + * Keep the value as it is given to quick access. + */ + PRESERVE = 0, - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L49 - export interface IEntryRunContext { - event: any; - keymods: IKeyMods; - quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; + /** + * Use the value that was used last time something was accepted from the picker. + */ + LAST = 1 } + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L13 + export enum TriggerAction { + /** + * Do nothing after the button was clicked. + */ + NO_ACTION, - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L55 - export interface IKeyMods { - ctrlCmd: boolean; - alt: boolean; + /** + * Close the picker. + */ + CLOSE_PICKER, + + /** + * Update the results of the picker. + */ + REFRESH_PICKER, + + /** + * Remove the item from the picker. + */ + REMOVE_ITEM } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L60 - export interface IDataSource { - getId(entry: T): string; - getLabel(entry: T): string | null; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L54 + export interface IQuickAccessProvider { + /** + * Allows to set a default filter value when the provider opens. This can be: + * - `undefined` to not specify any default value + * - `DefaultFilterValues.PRESERVE` to use the value that was last typed + * - `string` for the actual value to use + * + * Note: the default filter will only be used if quick access was opened with + * the exact prefix of the provider. Otherwise the filter value is preserved. + */ + readonly defaultFilterValue?: string | DefaultQuickAccessFilterValue; + + /** + * Called whenever a prefix was typed into quick pick that matches the provider. + * + * @param picker the picker to use for showing provider results. The picker is + * automatically shown after the method returns, no need to call `show()`. + * @param token providers have to check the cancellation token everytime after + * a long running operation or from event handlers because it could be that the + * picker has been closed or changed meanwhile. The token can be used to find out + * that the picker was closed without picking an entry (e.g. was canceled by the user). + * @return a disposable that will automatically be disposed when the picker + * closes or is replaced by another picker. + */ + provide(picker: monaco.quickInput.IQuickPick, token: CancellationToken): IDisposable; } - /** - * See vs/base/parts/tree/browser/tree.ts - IRenderer - */ - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L68 - export interface IRenderer { - getHeight(entry: T): number; - getTemplateId(entry: T): string; - renderTemplate(templateId: string, container: HTMLElement, styles: any): any; - renderElement(entry: T, templateId: string, templateData: any, styles: any): void; - disposeTemplate(templateId: string, templateData: any): void; + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L101 + export interface IQuickAccessProviderDescriptor { + /** + * The actual provider that will be instantiated as needed. + */ + readonly ctor: { new(...services: any /* TS BrandedService but no clue how to type this properly */[]): IQuickAccessProvider }; + + /** + * The prefix for quick access picker to use the provider for. + */ + readonly prefix: string; + + /** + * A placeholder to use for the input field when the provider is active. + * This will also be read out by screen readers and thus helps for + * accessibility. + */ + readonly placeholder?: string; + + /** + * Documentation for the provider in the quick access help. + */ + readonly helpEntries: IQuickAccessProviderHelp[]; + + /** + * A context key that will be set automatically when the + * picker for the provider is showing. + */ + readonly contextKey?: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L76 - export interface IFilter { - isVisible(entry: T): boolean; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L136 + export interface IQuickAccessRegistry { + /** + * Registers a quick access provider to the platform. + */ + registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable; + + /** + * Get all registered quick access providers. + */ + getQuickAccessProviders(): IQuickAccessProviderDescriptor[]; + + /** + * Get a specific quick access provider for a given prefix. + */ + getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined; + + /** + * Clear all existing quick access providers + */ + clear(): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L80 - export interface IAccessiblityProvider { - getAriaLabel(entry: T): string; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L154 + export class QuickAccessRegistry implements IQuickAccessRegistry { + registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable; + getQuickAccessProviders(): IQuickAccessProviderDescriptor[]; + getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L33 + export interface IQuickAccessController { + /** + * Open the quick access picker with the optional value prefilled. + */ + show(value?: string, options?: IQuickAccessOptions): void; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L13 + export interface IQuickAccessOptions { + /** + * Allows to enable quick navigate support in quick input. + */ + quickNavigateConfiguration?: IQuickNavigateConfiguration; + + /** + * Allows to configure a different item activation strategy. + * By default the first item in the list will get activated. + */ + itemActivation?: ItemActivation; + + /** + * Whether to take the input value as is and not restore it + * from any existing value if quick access is visible. + */ + preserveValue?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L84 - export interface IRunner { - run(entry: T, mode: Mode, context: IEntryRunContext): boolean; + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/quickAccess.ts#L14 + export class QuickAccessController extends Disposable implements IQuickAccessController { + constructor(quickInputService: monaco.quickInput.IQuickInputService, + instantiationService: monaco.instantiation.IInstantiationService + ) { + } + show(value?: string, options?: IQuickAccessOptions): void { } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/common/quickOpen.ts#L88 - export interface IModel { - entries: T[]; - dataSource: IDataSource; - renderer: IRenderer; - runner: IRunner; - filter?: IFilter; - accessibilityProvider?: IAccessiblityProvider; + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L319 + export interface IQuickPickItemButtonEvent { + button: IQuickInputButton; + item: T; } +} - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenModel.ts#L29 - export interface IHighlight { - start: number; - end: number; +declare module monaco.quickOpen { + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/inputbox/inputBox.ts#L58 + export interface IMessage { + content: string; + formatContent?: boolean; // defaults to false + type?: 1 /* INFO */ | 2 /* WARNING */ | 3 /* ERROR */; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/browser/ui/iconLabel/iconLabel.ts#L20 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/inputbox/inputBox.ts#L90 + export class InputBox { + inputElement: HTMLInputElement; + setPlaceHolder(placeHolder: string): void; + showMessage(message: IMessage, force?: boolean): void; + hideMessage(): void; + } + + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/browser/ui/iconLabel/iconLabel.ts#L34 export interface IIconLabelValueOptions { title?: string; descriptionTitle?: string; @@ -1205,120 +1959,47 @@ declare module monaco.quickOpen { readonly separator?: string; readonly domId?: string; } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenModel.ts#L55 - export class QuickOpenEntry { - constructor(highlights?: IHighlight[]); - getLabel(): string | undefined; - getLabelOptions(): IIconLabelValueOptions | undefined; - getAriaLabel(): string; - getDetail(): string | undefined; - getIcon(): string | undefined; - getDescription(): string | undefined; - getKeybinding(): monaco.keybindings.ResolvedKeybinding | undefined; - getResource(): Uri | undefined; - isHidden(): boolean; - setHidden(hidden: boolean): void; - setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void; - getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */]; - run(mode: Mode, context: IEntryRunContext): boolean; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenModel.ts#L197 - export class QuickOpenEntryGroup extends QuickOpenEntry { - constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean); - getGroupLabel(): string | undefined; - setGroupLabel(groupLabel: string | undefined): void; - showBorder(): boolean; - setShowBorder(showBorder: boolean): void; - getEntry(): QuickOpenEntry | undefined; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/tree/browser/tree.ts#L571 - export interface IActionProvider { - hasActions(element: any, item: any): boolean; - getActions(element: any, item: any): ReadonlyArray | null; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/parts/quickopen/browser/quickOpenModel.ts#L489 - export class QuickOpenModel implements - IModel, - IDataSource, - IFilter, - IRunner, - IAccessiblityProvider { - - constructor(entries?: QuickOpenEntry[], actionProvider?: IActionProvider); - addEntries(entries: QuickOpenEntry[]): void; - entries: QuickOpenEntry[]; - dataSource: IDataSource; - renderer: IRenderer; - runner: IRunner; - filter?: IFilter; - accessibilityProvider?: IAccessiblityProvider; - getId(entry: QuickOpenEntry): string; - getLabel(entry: QuickOpenEntry): string | null; - isVisible(entry: QuickOpenEntry): boolean; - run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts#L19 - export interface IQuickOpenControllerOpts { - readonly inputAriaLabel: string; - getModel(lookFor: string): QuickOpenModel; - getAutoFocus(lookFor: string): IAutoFocus; - } - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts#L25 - export interface QuickOpenController extends IDisposable { - static readonly ID: string; - dispose(): void; - run(opts: IQuickOpenControllerOpts): void; - - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts#L169 - clearDecorations(): void; - } } declare module monaco.filters { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/filters.ts#L15 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/filters.ts#L15 export interface IMatch { start: number; end: number; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/filters.ts#L337 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/filters.ts#L337 export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching?: boolean): IMatch[] | undefined; } declare module monaco.editorExtensions { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/editorExtensions.ts#L141 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorExtensions.ts#L215 export abstract class EditorCommand { } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/editorExtensions.ts#L205 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorExtensions.ts#L279 export abstract class EditorAction extends EditorCommand { id: string; label: string; } export module EditorExtensionsRegistry { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/editorExtensions.ts#L395 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorExtensions.ts#L497 export function getEditorActions(): EditorAction[]; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/browser/editorExtensions.ts#L391 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/browser/editorExtensions.ts#L493 export function getEditorCommand(commandId: string): EditorCommand | undefined; } } declare module monaco.modes { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L209 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L209 export interface ITokenizationSupport { tokenize(line: string, state: monaco.languages.IState, offsetDelta: number): any; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L1692 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L1927 export interface TokenizationRegistry { get(language: string): ITokenizationSupport | null; getColorMap(): monaco.color.Color[] | null; @@ -1326,7 +2007,7 @@ declare module monaco.modes { } export const TokenizationRegistry: TokenizationRegistry; - // https://github.com/microsoft/vscode/blob/0eb3a02ca2bcfab5faa3dc6e52d7c079efafcab0/src/vs/editor/common/modes.ts#L66-L76 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L70 export const enum FontStyle { NotSet = -1, None = 0, @@ -1335,7 +2016,7 @@ declare module monaco.modes { Underline = 4 } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L148 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L148 export class TokenMetadata { public static getLanguageId(metadata: number): number; @@ -1351,13 +2032,13 @@ declare module monaco.modes { public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/glob.ts#L18 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/glob.ts#L17 export interface IRelativePattern { base: string; pattern: string; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes/languageSelector.ts#L9 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes/languageSelector.ts#L10 export interface LanguageFilter { language?: string; scheme?: string; @@ -1369,10 +2050,10 @@ declare module monaco.modes { exclusive?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes/languageSelector.ts#L20 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes/languageSelector.ts#L21 export type LanguageSelector = string | LanguageFilter | Array; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes/languageFeatureRegistry.ts#L29 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes/languageFeatureRegistry.ts#L32 export interface LanguageFeatureRegistry { has(model: monaco.editor.ITextModel): boolean; all(model: monaco.editor.ITextModel): T[]; @@ -1380,46 +2061,46 @@ declare module monaco.modes { readonly onDidChange: monaco.IEvent; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L1599 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L1824 export const DocumentSymbolProviderRegistry: LanguageFeatureRegistry; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L1584 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L1799 export const CompletionProviderRegistry: LanguageFeatureRegistry; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/common/modes.ts#L1634 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/modes.ts#L1869 export const CodeActionProviderRegistry: LanguageFeatureRegistry; } declare module monaco.suggest { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggest.ts#L115 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggest.ts#L145 export const enum SnippetSortOrder { Top, Inline, Bottom } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggestController.ts#L97 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggestController.ts#L99 export interface SuggestController { readonly widget: { getValue(): SuggestWidget } } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggestWidget.ts#L462 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggestWidget.ts#L635 export interface SuggestWidget { getFocusedItem(): ISelectedSuggestion | undefined; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggestWidget.ts#L456 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggestWidget.ts#L55 export interface ISelectedSuggestion { item: CompletionItem; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggest.ts#L28 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggest.ts#L38 export interface CompletionItem { completion: monaco.languages.CompletionItem; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggest.ts#L119 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggest.ts#L149 export class CompletionOptions { constructor( @@ -1430,7 +2111,7 @@ declare module monaco.suggest { } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggest.ts#L142 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggest.ts#L192 export function provideSuggestionItems( model: monaco.editor.ITextModel, position: Position, @@ -1439,12 +2120,12 @@ declare module monaco.suggest { token?: monaco.CancellationToken ): Promise; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/suggest/suggest.ts#L136 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/suggest/suggest.ts#L166 export function setSnippetSuggestSupport(support: monaco.languages.CompletionItemProvider): monaco.languages.CompletionItemProvider; } declare module monaco.snippetParser { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/contrib/snippet/snippetParser.ts#L583 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/contrib/snippet/snippetParser.ts#L583 export class SnippetParser { parse(value: string): TextmateSnippet; } @@ -1454,51 +2135,65 @@ declare module monaco.snippetParser { declare module monaco.contextKeyService { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L803 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1313 export interface IContextKey { set(value: T): void; reset(): void; get(): T | undefined; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L827 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1337 export interface IContextKeyService { - // vs code has another object as argument https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L809 - // which contains restcicted number of HTMLElement methods - createScoped(target?: HTMLElement): IContextKeyService; - getContext(target?: HTMLElement): IContext; + // vs code has another object as argument https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1348 + // which contains restricted number of HTMLElement methods + onDidChangeContext: monaco.IEvent; + bufferChangeEvents(callback: Function): void; + createKey(key: string, defaultValue: T | undefined): IContextKey; contextMatchesRules(rules: monaco.contextkey.ContextKeyExpr | undefined): boolean; - onDidChangeContext: monaco.IEvent; + getContextKeyValue(key: string): T | undefined; + + createScoped(target?: HTMLElement): IContextKeyService; + createOverlay(overlay: Iterable<[string, any]>): IContextKeyService; + getContext(target?: HTMLElement): IContext; + + updateParent(parentContextKeyService: IContextKeyService): void; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L799 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1309 export interface IContext { getValue(key: string): T | undefined; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L823 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1333 export interface IContextKeyChangeEvent { affectsSome(keys: Set): boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/browser/contextKeyService.ts#L321 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/browser/contextKeyService.ts#L352 export class ContextKeyService implements IContextKeyService { constructor(configurationService: monaco.services.IConfigurationService); - createScoped(target?: HTMLElement): IContextKeyService; - getContext(target?: HTMLElement): IContext; + onDidChangeContext: monaco.IEvent; + bufferChangeEvents(callback: Function): void; + createKey(key: string, defaultValue: T | undefined): IContextKey; contextMatchesRules(rules: monaco.contextkey.ContextKeyExpr | undefined): boolean; - onDidChangeContext: monaco.IEvent; + getContextKeyValue(key: string): T | undefined; + + createScoped(target?: HTMLElement): IContextKeyService; + createOverlay(overlay: Iterable<[string, any]>): IContextKeyService; + getContext(target?: HTMLElement): IContext; + + updateParent(parentContextKeyService: IContextKeyService): void; } } declare module monaco.contextkey { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L817 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L1327 export const IContextKeyService: any; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/platform/contextkey/common/contextkey.ts#L29 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/contextkey/common/contextkey.ts#L79 export class ContextKeyExpr { keys(): string[]; static deserialize(when: string): ContextKeyExpr; @@ -1508,7 +2203,7 @@ declare module monaco.contextkey { declare module monaco.mime { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/mime.ts#L18 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/mime.ts#L17 export interface ITextMimeAssociation { readonly id: string; readonly mime: string; @@ -1519,30 +2214,30 @@ declare module monaco.mime { readonly userConfigured?: boolean; } - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/mime.ts#L42 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/mime.ts#L41 export function registerTextMime(association: monaco.mime.ITextMimeAssociation, warnOnOverwrite: boolean): void; - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/mime.ts#L98 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/mime.ts#L97 export function clearTextMimes(onlyUserConfigured?: boolean): void; } declare module monaco.error { - // https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/base/common/errors.ts#L77 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/errors.ts#L79 export function onUnexpectedError(e: any): undefined; } declare module monaco.path { - // https://github.com/microsoft/vscode/blob/320fbada86c113835aef4fb9d7c4bc5b74678166/src/vs/base/common/path.ts#L1494 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/common/path.ts#L1494 export function normalize(uriPath: string): string; } declare module monaco.wordHelper { - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/common/model/wordHelper.ts#L30 + // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/common/model/wordHelper.ts#L30 export const DEFAULT_WORD_REGEXP: RegExp; } declare module monaco.strings { - // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/base/common/strings.ts#L150 + // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/base/common/strings.ts#L384 export function startsWith(haystack: string, needle: string): boolean; // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/base/common/strings.ts#L171 @@ -1560,7 +2255,7 @@ declare module monaco.async { /** * overloading languages register functions to accept LanguageSelector, * check that all register functions passing a selector to registries: - * https://github.com/theia-ide/vscode/blob/standalone/0.20.x/src/vs/editor/standalone/browser/standaloneLanguages.ts#L338-L511 + * https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/editor/standalone/browser/standaloneLanguages.ts#L368-L546 */ declare module monaco.languages { export function registerReferenceProvider(selector: monaco.modes.LanguageSelector, provider: ReferenceProvider): IDisposable; @@ -1586,3 +2281,102 @@ declare module monaco.languages { export function registerDocumentSemanticTokensProvider(selector: monaco.modes.LanguageSelector, provider: DocumentSemanticTokensProvider): IDisposable; export function registerDocumentRangeSemanticTokensProvider(selector: monaco.modes.LanguageSelector, provider: DocumentRangeSemanticTokensProvider): IDisposable; } + +declare module monaco.list { + export class List extends Disposable { + constructor( + private user: string, + container: HTMLElement, + virtualDelegate: IListVirtualDelegate, + renderers: IListRenderer[], + private _options: IListOptions = DefaultOptions + ) + + focusNth(n: number, browserEvent?: UIEvent, filter?: (element: T) => boolean): void; + } + + export interface IListStyles { + listBackground?: monaco.color.Color; + listFocusBackground?: monaco.color.Color; + listFocusForeground?: monaco.color.Color; + listActiveSelectionBackground?: monaco.color.Color; + listActiveSelectionForeground?: monaco.color.Color; + listFocusAndSelectionBackground?: monaco.color.Color; + listFocusAndSelectionForeground?: monaco.color.Color; + listInactiveSelectionBackground?: monaco.color.Color; + listInactiveSelectionForeground?: monaco.color.Color; + listInactiveFocusForeground?: monaco.color.Color; + listInactiveFocusBackground?: monaco.color.Color; + listHoverBackground?: monaco.color.Color; + listHoverForeground?: monaco.color.Color; + listDropBackground?: monaco.color.Color; + listFocusOutline?: monaco.color.Color; + listInactiveFocusOutline?: monaco.color.Color; + listSelectionOutline?: monaco.color.Color; + listHoverOutline?: monaco.color.Color; + listFilterWidgetBackground?: monaco.color.Color; + listFilterWidgetOutline?: monaco.color.Color; + listFilterWidgetNoMatchesOutline?: monaco.color.Color; + listMatchesShadow?: monaco.color.Color; + treeIndentGuidesStroke?: monaco.color.Color; + tableColumnsBorder?: monaco.color.Color; + } + + export interface IListOptions { + readonly identityProvider?: IIdentityProvider; + readonly dnd?: IListDragAndDrop; + readonly enableKeyboardNavigation?: boolean; + readonly automaticKeyboardNavigation?: boolean; + readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; + readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly multipleSelectionController?: IMultipleSelectionController; + readonly styleController?: (suffix: string) => IStyleController; + readonly accessibilityProvider?: IListAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly setRowHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; + readonly additionalScrollHeight?: number; + readonly transformOptimization?: boolean; + readonly smoothScrolling?: boolean; + readonly alwaysConsumeMouseWheel?: boolean; + } + + export interface IListRenderer { + readonly templateId: string; + renderTemplate(container: HTMLElement): TTemplateData; + renderElement(element: T, index: number, templateData: TTemplateData, height: number | undefined): void; + disposeElement?(element: T, index: number, templateData: TTemplateData, height: number | undefined): void; + disposeTemplate(templateData: TTemplateData): void; + } + + export interface IListVirtualDelegate { + getHeight(element: T): number; + getTemplateId(element: T): string; + hasDynamicHeight?(element: T): boolean; + setDynamicHeight?(element: T, height: number): void; + } + + export interface IListElement { + readonly index: number; + readonly item: monaco.quickInput.IQuickPickItem; + readonly saneLabel: string; + readonly saneMeta?: string; + readonly saneAriaLabel: string; + readonly saneDescription?: string; + readonly saneDetail?: string; + readonly labelHighlights?: monaco.filters.IMatch[]; + readonly descriptionHighlights?: monaco.filters.IMatch[]; + readonly detailHighlights?: monaco.filters.IMatch[]; + readonly checked: boolean; + readonly separator?: monaco.quickInput.IQuickPickSeparator; + readonly fireButtonTriggered: (event: monaco.quickInput.IQuickPickItemButtonEvent) => void; + } +} diff --git a/packages/output/src/browser/output-contribution.ts b/packages/output/src/browser/output-contribution.ts index adada8c262faf..9e992fb18592f 100644 --- a/packages/output/src/browser/output-contribution.ts +++ b/packages/output/src/browser/output-contribution.ts @@ -18,7 +18,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify' import URI from '@theia/core/lib/common/uri'; import { Widget } from '@theia/core/lib/browser/widgets/widget'; import { MaybePromise } from '@theia/core/lib/common/types'; -import { CommonCommands, quickCommand, OpenHandler, open, OpenerOptions, OpenerService } from '@theia/core/lib/browser'; +import { CommonCommands, quickCommand, OpenHandler, open, OpenerOptions, OpenerService, QuickPickItem, QuickPickValue } from '@theia/core/lib/browser'; import { CommandRegistry, MenuModelRegistry, CommandService } from '@theia/core/lib/common'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { OutputWidget } from './output-widget'; @@ -27,7 +27,7 @@ import { OutputUri } from '../common/output-uri'; import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { OutputChannelManager, OutputChannel } from '../common/output-channel'; import { OutputCommands } from './output-commands'; -import { QuickPickService, QuickPickItem } from '@theia/core/lib/common/quick-pick-service'; +import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; @injectable() export class OutputContribution extends AbstractViewContribution implements OpenHandler { @@ -255,7 +255,7 @@ export class OutputContribution extends AbstractViewContribution i } protected async pick({ channels, placeholder }: { channels: OutputChannel[], placeholder: string }): Promise { - const items: QuickPickItem[] = []; + const items: Array | QuickPickItem> = []; for (let i = 0; i < channels.length; i++) { const channel = channels[i]; if (i === 0) { @@ -265,7 +265,7 @@ export class OutputContribution extends AbstractViewContribution i } items.push({ label: channel.name, value: channel }); } - return this.quickPickService.show(items, { placeholder }); + const selectedItem = await this.quickPickService.show(items, { placeholder }); + return selectedItem && ('value' in selectedItem) ? selectedItem.value : undefined; } - } diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index c471856b1906f..371877db16d31 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -21,7 +21,7 @@ import { NavigatableWidget, open, OpenerService, - PrefixQuickOpenService, + QuickInputService, Saveable } from '@theia/core/lib/browser'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; @@ -50,7 +50,7 @@ import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl'; import { WorkspaceCommands } from '@theia/workspace/lib/browser'; import { WorkspaceService, WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service'; import { DiffService } from '@theia/workspace/lib/browser/diff-service'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc'; import { URI } from '@theia/core/shared/vscode-uri'; import { PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol'; @@ -101,8 +101,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly openerService: OpenerService; @inject(ApplicationShellMouseTracker) protected readonly mouseTracker: ApplicationShellMouseTracker; - @inject(PrefixQuickOpenService) - protected readonly quickOpen: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInput: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(TerminalFrontendContribution) @@ -221,7 +221,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution { execute: () => commands.executeCommand('editor.action.gotoLine') }); commands.registerCommand({ id: 'workbench.action.quickOpen' }, { - execute: () => this.quickOpen.open('') + execute: () => this.quickInput?.open('') }); commands.registerCommand({ id: 'workbench.action.openSettings' }, { execute: () => commands.executeCommand(CommonCommands.OPEN_PREFERENCES.id) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts index 4b3468bce9cae..e4db6655f9e47 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts @@ -102,6 +102,7 @@ export enum CompletionItemInsertTextRule { export interface Completion { label: string; + label2?: string; kind: CompletionItemKind; detail?: string; documentation?: string | MarkdownString; @@ -313,7 +314,11 @@ export interface ReferenceContext { includeDeclaration: boolean; } +export type CacheId = number; +export type ChainedCacheId = [CacheId, CacheId]; + export interface DocumentLink { + cacheId?: ChainedCacheId, range: Range; url?: UriComponents | string; tooltip?: string; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 70a6b8c97c1d0..68d12c4335543 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -26,8 +26,7 @@ import { EndOfLine, OverviewRulerLane, IndentAction, - FileOperationOptions, - QuickInputButton + FileOperationOptions } from '../plugin/types-impl'; import { UriComponents } from './uri-components'; import { ConfigurationTarget } from '../plugin/types-impl'; @@ -43,6 +42,7 @@ import { Hover, DocumentHighlight, FormattingOptions, + ChainedCacheId, Definition, DocumentLink, CodeLensSymbol, @@ -81,8 +81,6 @@ import { DebuggerDescription } from '@theia/debug/lib/common/debug-service'; import { DebugProtocol } from 'vscode-debugprotocol'; import { SymbolInformation } from '@theia/core/shared/vscode-languageserver-types'; import { ArgumentProcessor } from '../plugin/command-registry'; -import { MaybePromise } from '@theia/core/lib/common/types'; -import { QuickTitleButton } from '@theia/core/lib/common/quick-open-model'; import * as files from '@theia/filesystem/lib/common/files'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { ResourceLabelFormatter } from '@theia/core/lib/common/label-protocol'; @@ -95,6 +93,7 @@ import type { import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/base-terminal-protocol'; import { ThemeType } from '@theia/core/lib/browser/theming'; import { Disposable } from '@theia/core/lib/common/disposable'; +import { IPickOptions, QuickInputButtonHandle, QuickPickItem } from '@theia/core/lib/browser'; export interface PreferenceData { [scope: number]: any; @@ -376,27 +375,6 @@ export interface AutoFocus { // TODO } -export interface PickOptions { - placeHolder?: string; - autoFocus?: AutoFocus; - matchOnDescription?: boolean; - matchOnDetail?: boolean; - ignoreFocusLost?: boolean; - quickNavigationConfiguration?: {}; // TODO - contextKey?: string; - canSelectMany?: boolean; -} - -export interface PickOpenItem { - handle: number; - label: string; - description?: string; - detail?: string; - picked?: boolean; - groupLabel?: string; - showBorder?: boolean; -} - export enum MainMessageType { Error, Warning, @@ -430,16 +408,30 @@ export interface StatusBarMessageRegistryMain { $dispose(id: string): void; } +export type Item = string | theia.QuickPickItem; + export interface QuickOpenExt { $onItemSelected(handle: number): void; - $validateInput(input: string): PromiseLike | undefined; + $validateInput(input: string): Promise | undefined; - $acceptOnDidAccept(quickInputNumber: number): Promise; - $acceptDidChangeValue(quickInputNumber: number, changedValue: string): Promise; - $acceptOnDidHide(quickInputNumber: number): Promise; - $acceptOnDidTriggerButton(quickInputNumber: number, btn: QuickTitleButton): Promise; + $acceptOnDidAccept(sessionId: number): Promise; + $acceptDidChangeValue(sessionId: number, changedValue: string): Promise; + $acceptOnDidHide(sessionId: number): Promise; + $acceptOnDidTriggerButton(sessionId: number, btn: QuickInputButtonHandle): Promise; $onDidChangeActive(sessionId: number, handles: number[]): void; $onDidChangeSelection(sessionId: number, handles: number[]): void; + + /* eslint-disable max-len */ + showQuickPick(itemsOrItemsPromise: Array | Promise>, options: theia.QuickPickOptions & { canPickMany: true; }, + token?: theia.CancellationToken): Promise | undefined>; + showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + + showInput(options?: theia.InputBoxOptions, token?: theia.CancellationToken): PromiseLike; + // showWorkspaceFolderPick(options?: theia.WorkspaceFolderPickOptions, token?: theia.CancellationToken): Promise + createQuickPick(plugin: Plugin): theia.QuickPick; + createInputBox(plugin: Plugin): theia.InputBox; } /** @@ -554,51 +546,70 @@ export interface WorkspaceFolderPickOptionsMain { ignoreFocusOut?: boolean; } -export interface QuickInputTitleButtonHandle extends QuickTitleButton { - index: number; // index of where they are in buttons array if QuickInputButton or -1 if QuickInputButtons.Back +export interface TransferQuickPickItems extends theia.QuickPickItem { + handle: number; } -export interface TransferQuickInput { +export interface TransferQuickInputButton extends theia.QuickInputButton { + handle?: number; +} + +export type TransferQuickInput = TransferQuickPick | TransferInputBox; + +export interface BaseTransferQuickInput { + [key: string]: any; id: number; - title: string | undefined; - step: number | undefined; - totalSteps: number | undefined; - enabled: boolean; - busy: boolean; - ignoreFocusOut: boolean; -} - -export interface TransferInputBox extends TransferQuickInput { - value: string; - placeholder: string | undefined; - password: boolean; - buttons: ReadonlyArray; - prompt: string | undefined; - validationMessage: string | undefined; - validateInput(value: string): MaybePromise; -} - -export interface TransferQuickPick extends TransferQuickInput { - value: string; - placeholder: string | undefined; - buttons: ReadonlyArray; - items: PickOpenItem[]; - canSelectMany: boolean; - matchOnDescription: boolean; - matchOnDetail: boolean; - activeItems: ReadonlyArray; - selectedItems: ReadonlyArray; + type?: 'quickPick' | 'inputBox'; + enabled?: boolean; + busy?: boolean; + visible?: boolean; +} + +export interface TransferQuickPick extends BaseTransferQuickInput { + type?: 'quickPick'; + value?: string; + placeholder?: string; + buttons?: TransferQuickInputButton[]; + items?: TransferQuickPickItems[]; + activeItems?: ReadonlyArray; + selectedItems?: ReadonlyArray; + canSelectMany?: boolean; + ignoreFocusOut?: boolean; + matchOnDescription?: boolean; + matchOnDetail?: boolean; + sortByLabel?: boolean; +} + +export interface TransferInputBox extends BaseTransferQuickInput { + type?: 'inputBox'; + value?: string; + placeholder?: string; + password?: boolean; + buttons?: TransferQuickInputButton[]; + prompt?: string; + validationMessage?: string; +} + +export interface IInputBoxOptions { + value?: string; + valueSelection?: [number, number]; + prompt?: string; + placeHolder?: string; + password?: boolean; + ignoreFocusOut?: boolean; } export interface QuickOpenMain { - $show(options: PickOptions, token: CancellationToken): Promise; - $setItems(items: PickOpenItem[]): Promise; + $show(instance: number, options: IPickOptions, token: CancellationToken): Promise; + $setItems(instance: number, items: TransferQuickPickItems[]): Promise; + $setError(instance: number, error: Error): Promise; $input(options: theia.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise; + $createOrUpdate(params: TransferQuickInput): Promise; + $dispose(id: number): Promise; + $hide(): void; - $showInputBox(inputBox: TransferInputBox, validateInput: boolean): void; - $showCustomQuickPick(inputBox: TransferQuickPick): void; - $setQuickInputChanged(changed: object): void; - $refreshQuickInput(): void; + $showInputBox(options: TransferInputBox, validateInput: boolean): Promise; + $showCustomQuickPick(options: TransferQuickPick): void; } export interface WorkspaceMain { @@ -1391,7 +1402,7 @@ export interface PluginInfo { export interface LanguagesExt { $provideCompletionItems(handle: number, resource: UriComponents, position: Position, context: CompletionContext, token: CancellationToken): Promise; - $resolveCompletionItem(handle: number, parentId: number, id: number, token: CancellationToken): Promise; + $resolveCompletionItem(handle: number, chainedId: ChainedCacheId, token: CancellationToken): Promise; $releaseCompletionItems(handle: number, id: number): void; $provideImplementation(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; $provideTypeDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; diff --git a/packages/plugin-ext/src/main/browser/authentication-main.ts b/packages/plugin-ext/src/main/browser/authentication-main.ts index 088c1ae677ec8..6f779a73b9a3f 100644 --- a/packages/plugin-ext/src/main/browser/authentication-main.ts +++ b/packages/plugin-ext/src/main/browser/authentication-main.ts @@ -30,11 +30,12 @@ import { AuthenticationService, readAllowedExtensions } from '@theia/core/lib/browser/authentication-service'; -import { QuickPickItem, QuickPickService } from '@theia/core/lib/common/quick-pick-service'; +import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { AuthenticationSession, AuthenticationSessionsChangeEvent } from '../../common/plugin-api-rpc-model'; +import { QuickPickValue } from '@theia/core/lib/browser/quick-input/quick-input-service'; export class AuthenticationMainImpl implements AuthenticationMain { private readonly proxy: AuthenticationExt; @@ -143,7 +144,7 @@ export class AuthenticationMainImpl implements AuthenticationMain { } return new Promise(async (resolve, reject) => { - const items: QuickPickItem<{ session?: AuthenticationSession }>[] = potentialSessions.map(session => ({ + const items: QuickPickValue<{ session?: AuthenticationSession }>[] = potentialSessions.map(session => ({ label: session.account.label, value: { session } })); @@ -151,13 +152,13 @@ export class AuthenticationMainImpl implements AuthenticationMain { label: 'Sign in to another account', value: { session: undefined } }); - const selected = await this.quickPickService.show<{ session?: AuthenticationSession }>(items, + const selected = await this.quickPickService.show(items, { title: `The extension '${extensionName}' wants to access a ${providerName} account`, ignoreFocusOut: true }); if (selected) { - const session = selected.session ?? await this.authenticationService.login(providerId, scopes); + const session = selected.value?.session ?? await this.authenticationService.login(providerId, scopes); const accountName = session.account.label; const allowList = await readAllowedExtensions(this.storageService, providerId, accountName); diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts index 23e714dd201d8..17d763588d72d 100644 --- a/packages/plugin-ext/src/main/browser/languages-main.ts +++ b/packages/plugin-ext/src/main/browser/languages-main.ts @@ -130,7 +130,7 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { triggerCharacters, provideCompletionItems: (model, position, context, token) => this.provideCompletionItems(handle, model, position, context, token), resolveCompletionItem: supportsResolveDetails - ? (model, position, suggestion, token) => Promise.resolve(this.resolveCompletionItem(handle, model, position, suggestion, token)) + ? (suggestion, token) => Promise.resolve(this.resolveCompletionItem(handle, suggestion, token)) : undefined })); } @@ -151,10 +151,10 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { }); } - protected resolveCompletionItem(handle: number, model: monaco.editor.ITextModel, position: monaco.Position, + protected resolveCompletionItem(handle: number, item: monaco.languages.CompletionItem, token: monaco.CancellationToken): monaco.languages.ProviderResult { const { parentId, id } = item as CompletionDto; - return this.proxy.$resolveCompletionItem(handle, parentId, id, token).then(resolved => { + return this.proxy.$resolveCompletionItem(handle, [parentId, id], token).then(resolved => { if (resolved) { mixin(item, resolved, true); } diff --git a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts index b99fdc472bc68..f2b105740afa7 100644 --- a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts @@ -17,14 +17,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { URI as CodeUri } from '@theia/core/shared/vscode-uri'; -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { MenuPath, ILogger, CommandRegistry, Command, Mutable, MenuAction, SelectionService, CommandHandler, Disposable, DisposableCollection } from '@theia/core'; import { EDITOR_CONTEXT_MENU, EditorWidget } from '@theia/editor/lib/browser'; import { MenuModelRegistry } from '@theia/core/lib/common'; import { Emitter } from '@theia/core/lib/common/event'; import { TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution'; -import { QuickCommandService } from '@theia/core/lib/browser/quick-open/quick-command-service'; import { VIEW_ITEM_CONTEXT_MENU, TreeViewWidget, VIEW_ITEM_INLINE_MENU } from '../view/tree-view-widget'; import { DeployedPlugin, Menu, ScmCommandArg, TimelineCommandArg, TreeViewSelection } from '../../../common'; import { DebugStackFramesWidget } from '@theia/debug/lib/browser/view/debug-stack-frames-widget'; @@ -44,6 +43,7 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { TIMELINE_ITEM_CONTEXT_MENU } from '@theia/timeline/lib/browser/timeline-tree-widget'; import { TimelineItem } from '@theia/timeline/lib/common/timeline-model'; import { COMMENT_CONTEXT, COMMENT_THREAD_CONTEXT, COMMENT_TITLE } from '../comments/comment-thread-widget'; +import { QuickCommandService } from '@theia/core/lib/browser'; type CodeEditorWidget = EditorWidget | WebviewWidget; @injectable() @@ -72,7 +72,7 @@ export class MenusContributionPointHandler { @inject(ScmService) protected readonly scmService: ScmService; - @inject(QuickCommandService) + @inject(QuickCommandService) @optional() protected readonly quickCommandService: QuickCommandService; @inject(TabBarToolbarRegistry) @@ -524,14 +524,14 @@ export class MenusContributionPointHandler { const commandId = this.createSyntheticCommandId(menu.command, { prefix: '__plugin.menu.action.' }); const command: Command = { id: commandId }; toDispose.push(this.commands.registerCommand(command, handler(menu.command))); - toDispose.push(this.quickCommandService.pushCommandContext(commandId, 'false')); + toDispose.push(this.quickCommandService?.pushCommandContext(commandId, 'false')); let altId: string | undefined; if (menu.alt) { altId = this.createSyntheticCommandId(menu.alt, { prefix: '__plugin.menu.action.' }); const alt: Command = { id: altId }; toDispose.push(this.commands.registerCommand(alt, handler(menu.alt))); - toDispose.push(this.quickCommandService.pushCommandContext(altId, 'false')); + toDispose.push(this.quickCommandService?.pushCommandContext(altId, 'false')); toDispose.push(this.onDidRegisterCommand(menu.alt, pluginCommand => { alt.category = pluginCommand.category; alt.label = pluginCommand.label; diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts b/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts index fe61bed3062ce..8c920dbe03b69 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts @@ -14,92 +14,38 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { QuickOpenService, QuickOpenItem, QuickOpenModel, QuickOpenMode } from '@theia/core/lib/browser'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { PluginServer } from '../../common'; import { Command } from '@theia/core/lib/common/command'; +import { QuickInputService } from '@theia/core/lib/browser'; @injectable() -export class PluginExtDeployCommandService implements QuickOpenModel { - - private items: QuickOpenItem[]; - +export class PluginExtDeployCommandService /* implements QuickOpenModel */ { public static COMMAND: Command = { id: 'plugin-ext:deploy-plugin-id', category: 'Plugin', label: 'Deploy Plugin by Id', - }; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(PluginServer) protected readonly pluginServer: PluginServer; - constructor() { - this.items = []; - } - - /** - * Whether the dialog is currently open. - */ - protected isOpen: boolean = false; - deploy(): void { - const placeholderText = "Plugin's id to deploy."; - - this.isOpen = true; - - this.quickOpenService.open(this, { - placeholder: placeholderText, - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, - fuzzySort: true, - onClose: () => { - this.isOpen = false; - }, - }); - } - - public async onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - this.items = []; - if (lookFor || lookFor.length > 0) { - this.items.push(this.createDeployQuickOpenItem(lookFor, 'Deploy this plugin')); - } - acceptor(this.items); - } - - protected createDeployQuickOpenItem(name: string, description: string): DeployQuickOpenItem { - return new DeployQuickOpenItem(name, this.pluginServer, description); + this.quickInputService?.showQuickPick([], + { + placeholder: "Plugin's id to deploy.", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onDidChangeValue: (quickPick: any, filter: string) => { + quickPick.items = [{ + label: filter, + detail: 'Deploy this plugin', + execute: () => this.pluginServer.deploy(filter) + }]; + } + } + ); } - -} - -export class DeployQuickOpenItem extends QuickOpenItem { - - constructor( - protected readonly name: string, - protected readonly pluginServer: PluginServer, - protected readonly description?: string - ) { - super(); - } - - getLabel(): string { - return this.name; - } - - getDetail(): string { - return this.description || ''; - } - - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.pluginServer.deploy(this.name); - return true; - } - } diff --git a/packages/plugin-ext/src/main/browser/plugin-shared-style.ts b/packages/plugin-ext/src/main/browser/plugin-shared-style.ts index 9f2ac49e18636..1c1120c04b491 100644 --- a/packages/plugin-ext/src/main/browser/plugin-shared-style.ts +++ b/packages/plugin-ext/src/main/browser/plugin-shared-style.ts @@ -41,7 +41,7 @@ export class PluginSharedStyle { constructor() { this.update(); - ThemeService.get().onThemeChange(() => this.update()); + ThemeService.get().onDidColorThemeChange(() => this.update()); } protected readonly toUpdate = new DisposableCollection(); diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index e510cd935a776..4737a6088a318 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -13,346 +13,362 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { InputBoxOptions, QuickPickItem as QuickPickItemExt } from '@theia/plugin'; import { interfaces } from '@theia/core/shared/inversify'; -import { - QuickOpenModel, - QuickOpenItem, - QuickOpenMode -} from '@theia/core/lib/browser/quick-open/quick-open-model'; import { RPCProtocol } from '../../common/rpc-protocol'; import { QuickOpenExt, QuickOpenMain, MAIN_RPC_CONTEXT, - PickOptions, - PickOpenItem, TransferInputBox, - QuickInputTitleButtonHandle, - TransferQuickPick + TransferQuickPick, + TransferQuickPickItems, + TransferQuickInput, + TransferQuickInputButton } from '../../common/plugin-api-rpc'; -import { MonacoQuickOpenService } from '@theia/monaco/lib/browser/monaco-quick-open-service'; -import { QuickInputService, LabelProvider } from '@theia/core/lib/browser'; -import { PluginSharedStyle } from './plugin-shared-style'; -import { URI } from '@theia/core/shared/vscode-uri'; -import { ThemeIcon, QuickInputButton } from '../../plugin/types-impl'; -import { QuickPickService, QuickPickItem, QuickPickValue } from '@theia/core/lib/common/quick-pick-service'; -import { QuickTitleBar } from '@theia/core/lib/browser/quick-open/quick-title-bar'; +import { IInputOptions, IPickOptions, QuickInputButton, QuickInputService, QuickPickItem, QuickPickValue } from '@theia/core/lib/browser'; +import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; -import { QuickTitleButtonSide, QuickOpenGroupItem } from '@theia/core/lib/common/quick-open-model'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; +import { MonacoQuickInputService } from '@theia/monaco/lib/browser/monaco-quick-input-service'; +import * as theia from '@theia/plugin'; +import { QuickInputButtons } from '../../plugin/types-impl'; +import { getIconUris } from '../../plugin/quick-open'; -export class QuickOpenMainImpl implements QuickOpenMain, QuickOpenModel, Disposable { - - private quickInput: QuickInputService; - private quickPick: QuickPickService; - private quickTitleBar: QuickTitleBar; - private doResolve: (value?: number | number[] | PromiseLike | undefined) => void; - private proxy: QuickOpenExt; - private delegate: MonacoQuickOpenService; - private acceptor: ((items: QuickOpenItem[]) => void) | undefined; - private items: QuickOpenItem[] | undefined; +export interface QuickInputSession { + input: monaco.quickInput.IQuickInput; + handlesToItems: Map; +} - private readonly sharedStyle: PluginSharedStyle; - private readonly labelProvider: LabelProvider; +export class QuickOpenMainImpl implements QuickOpenMain, Disposable { - private activeElement: HTMLElement | undefined; + private quickInputService: QuickInputService; + private quickPickService: QuickPickService; + private proxy: QuickOpenExt; + private delegate: MonacoQuickInputService; + private readonly items: Record = {}; protected readonly toDispose = new DisposableCollection(); constructor(rpc: RPCProtocol, container: interfaces.Container) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.QUICK_OPEN_EXT); - this.delegate = container.get(MonacoQuickOpenService); - this.quickInput = container.get(QuickInputService); - this.quickTitleBar = container.get(QuickTitleBar); - this.quickPick = container.get(QuickPickService); - this.sharedStyle = container.get(PluginSharedStyle); - this.labelProvider = container.get(LabelProvider); + this.delegate = container.get(MonacoQuickInputService); + this.quickInputService = container.get(QuickInputService); + this.quickPickService = container.get(QuickPickService); } dispose(): void { this.toDispose.dispose(); } - private cleanUp(): void { - this.items = undefined; - this.acceptor = undefined; - if (this.activeElement) { - this.activeElement.focus({ preventScroll: true }); - } - this.activeElement = undefined; - } - - $show(options: PickOptions, token: CancellationToken): Promise { - return new Promise((resolve, reject) => { - if (token.isCancellationRequested) { - resolve(undefined); - return; - } - this.doResolve = resolve; - this.activeElement = window.document.activeElement as HTMLElement; - const toDispose = token.onCancellationRequested(() => - this.delegate.hide() - ); - this.delegate.open(this, { - fuzzyMatchDescription: options.matchOnDescription, - fuzzyMatchLabel: true, - fuzzyMatchDetail: options.matchOnDetail, - placeholder: options.placeHolder, - ignoreFocusOut: options.ignoreFocusLost, - onClose: () => { - this.doResolve(undefined); - toDispose.dispose(); - this.cleanUp(); - } - }); + async $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { + const contents = new Promise((resolve, reject) => { + this.items[instance] = { resolve, reject }; }); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - $setItems(items: PickOpenItem[]): Promise { - this.items = []; - for (const i of items) { - let item: QuickOpenItem | QuickOpenGroupItem; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const options: any = { - label: i.label, - description: i.description, - detail: i.detail, - run: (mode: QuickOpenMode) => { - if (mode === QuickOpenMode.OPEN) { - this.proxy.$onItemSelected(i.handle); - this.doResolve(i.handle); - this.cleanUp(); - return true; - } - return false; + options = { + ...options, + onDidFocus: (el: any) => { + if (el) { + this.proxy.$onItemSelected((el).handle); } - }; - - if (i.groupLabel !== undefined || i.showBorder !== undefined) { - options.groupLabel = i.groupLabel; - options.showBorder = i.showBorder; - item = new QuickOpenGroupItem(options); - } else { - item = new QuickOpenItem(options); } + }; - this.items.push(item); + if (options.canPickMany) { + return this.delegate.pick(contents, options as { canPickMany: true }, token).then(items => { + if (items) { + return items.map(item => item.handle); + } + return undefined; + }); + } else { + return this.delegate.pick(contents, options, token).then(item => { + if (item) { + return item.handle; + } + return undefined; + }); } - if (this.acceptor) { - this.acceptor(this.items); + } + + $setItems(instance: number, items: TransferQuickPickItems[]): Promise { + if (this.items[instance]) { + this.items[instance].resolve(items); + delete this.items[instance]; } return Promise.resolve(); } - private convertPickOpenItemToQuickOpenItem(items: PickOpenItem[]): QuickPickItem[] { - const convertedItems: QuickPickValue[] = []; - for (const i of items) { - convertedItems.push({ - label: i.label, - description: i.description, - detail: i.detail, - value: i.handle - }); + $setError(instance: number, error: Error): Promise { + if (this.items[instance]) { + this.items[instance].reject(error); + delete this.items[instance]; } - return convertedItems; + return Promise.resolve(); } $input(options: InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise { - if (validateInput) { - options.validateInput = val => this.proxy.$validateInput(val); - } + const inputOptions: IInputOptions = Object.create(null); - return this.quickInput.open(options, token); - } + if (options) { + inputOptions.password = options.password; + inputOptions.placeHolder = options.placeHolder; + inputOptions.valueSelection = options.valueSelection; + inputOptions.prompt = options.prompt; + inputOptions.value = options.value; + inputOptions.ignoreFocusLost = options.ignoreFocusOut; + } - protected convertQuickInputButton(quickInputButton: QuickInputButton, index: number, toDispose: DisposableCollection): QuickInputTitleButtonHandle { - const currentIconPath = quickInputButton.iconPath; - let newIcon = ''; - let newIconClass: string | undefined; - if ('id' in currentIconPath || currentIconPath instanceof ThemeIcon) { - newIconClass = this.resolveIconClassFromThemeIcon(currentIconPath); - } else if (currentIconPath instanceof URI) { - newIcon = currentIconPath.toString(); - } else { - const { light, dark } = currentIconPath as { light: string | URI, dark: string | URI }; - const themedIconClasses = { - light: light.toString(), - dark: dark.toString() - }; - const reference = this.sharedStyle.toIconClass(themedIconClasses); - toDispose.push(reference); - newIconClass = reference.object.iconClass; + if (validateInput) { + inputOptions.validateInput = (val: string) => this.proxy.$validateInput(val); } - const isDefaultQuickInputButton = 'id' in quickInputButton.iconPath && quickInputButton.iconPath.id === 'Back' ? true : false; - return { - icon: newIcon, - iconClass: newIconClass, - tooltip: quickInputButton.tooltip, - side: isDefaultQuickInputButton ? QuickTitleButtonSide.LEFT : QuickTitleButtonSide.RIGHT, - index: isDefaultQuickInputButton ? -1 : index - }; + return this.quickInputService?.input(inputOptions, token); } - private resolveIconClassFromThemeIcon(themeIcon: ThemeIcon): string | undefined { - switch (themeIcon.id) { - case 'folder': { - return this.labelProvider.folderIcon; + async $showInputBox(options: TransferInputBox, validateInput: boolean): Promise { + return new Promise((resolve, reject) => { + const sessionId = options.id; + const toDispose = new DisposableCollection(); + + const inputBox = this.quickInputService?.createInputBox(); + inputBox.prompt = options.prompt; + inputBox.placeholder = options.placeHolder; + inputBox.value = options.value; + if (options.busy) { + inputBox.busy = options.busy; } - case 'file': { - return this.labelProvider.fileIcon; + if (options.enabled) { + inputBox.enabled = options.enabled; } - case 'Back': { - return 'fa fa-arrow-left'; + inputBox.ignoreFocusOut = options.ignoreFocusOut; + inputBox.contextKey = options.contextKey; + if (options.password) { + inputBox.password = options.password; } - default: { - return monaco.theme.ThemeIcon.asClassName(themeIcon); + inputBox.step = options.step; + inputBox.title = options.title; + inputBox.description = options.description; + inputBox.totalSteps = options.totalSteps; + inputBox.buttons = options.buttons ? this.convertToQuickInputButtons(options.buttons) : []; + inputBox.validationMessage = options.validationMessage; + if (validateInput) { + options.validateInput = (val: string) => { + this.proxy.$validateInput(val); + }; } - } - } - async $showInputBox(inputBox: TransferInputBox, validateInput: boolean): Promise { - if (validateInput) { - inputBox.validateInput = val => this.proxy.$validateInput(val); - } + toDispose.push(inputBox.onDidAccept(() => { + this.proxy.$acceptOnDidAccept(sessionId); + resolve(inputBox.value); + })); + toDispose.push(inputBox.onDidChangeValue((value: string) => { + this.proxy.$acceptDidChangeValue(sessionId, value); + inputBox.validationMessage = options.validateInput(value); + })); + toDispose.push(inputBox.onDidTriggerButton((button: any) => { + this.proxy.$acceptOnDidTriggerButton(sessionId, button); + })); - const toDispose = new DisposableCollection(); - const quickInput = this.quickInput.open({ - busy: inputBox.busy, - enabled: inputBox.enabled, - ignoreFocusOut: inputBox.ignoreFocusOut, - password: inputBox.password, - step: inputBox.step, - title: inputBox.title, - totalSteps: inputBox.totalSteps, - buttons: inputBox.buttons.map((btn, i) => this.convertQuickInputButton(btn, i, toDispose)), - validationMessage: inputBox.validationMessage, - placeHolder: inputBox.placeholder, - value: inputBox.value, - prompt: inputBox.prompt, - validateInput: inputBox.validateInput - }); + toDispose.push(inputBox.onDidHide(() => { + if (toDispose.disposed) { + return; + } + this.proxy.$acceptOnDidHide(sessionId); + toDispose.dispose(); + resolve(undefined); + })); + this.toDispose.push(toDispose); - toDispose.push(this.quickInput.onDidAccept(() => this.proxy.$acceptOnDidAccept(inputBox.id))); - toDispose.push(this.quickInput.onDidChangeValue(changedText => this.proxy.$acceptDidChangeValue(inputBox.id, changedText))); - toDispose.push(this.quickTitleBar.onDidTriggerButton(button => { - this.proxy.$acceptOnDidTriggerButton(inputBox.id, button); - })); - this.toDispose.push(toDispose); - quickInput.then(() => { - if (toDispose.disposed) { - return; - } - this.proxy.$acceptOnDidHide(inputBox.id); - toDispose.dispose(); + inputBox.show(); }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private findChangedKey(key: string, value: any): void { - switch (key) { - case 'title': { - this.quickTitleBar.title = value; - break; - } - case 'step': { - this.quickTitleBar.step = value; - break; - } - case 'totalSteps': { - this.quickTitleBar.totalSteps = value; - break; - } - case 'buttons': { - this.quickTitleBar.buttons = value; - break; - } - case 'value': { - this.delegate.setValue(value); - break; - } - case 'enabled': { - this.delegate.setEnabled(value); - break; - } - case 'password': { - this.delegate.setPassword(value); - break; - } - case 'placeholder': { - this.delegate.setPlaceHolder(value); - break; - } - case 'items': { - this.quickPick.setItems(this.convertPickOpenItemToQuickOpenItem(value)); - break; - } - // TODO selectedItems, activeItems and other properties - // TODO we need better type checking here - } - } + private sessions = new Map(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - $setQuickInputChanged(changed: any): void { - for (const key in changed) { - if (changed.hasOwnProperty(key)) { - const value = changed[key]; - this.findChangedKey(key, value); + $createOrUpdate(params: TransferQuickInput): Promise { + const sessionId = params.id; + const session = this.sessions.get(sessionId); + // if (!session) { + // if (params.type === 'quickPick') { + // const quickPick = this.quickInputService.createQuickPick(); + // quickPick.onDidAccept(() => { + // this.proxy.$acceptOnDidAccept(sessionId); + // }); + // quickPick.onDidChangeActive((items: Array) => { + // this.proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + // }); + // quickPick.onDidChangeSelection((items: Array) => { + // this.proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + // }); + // quickPick.onDidTriggerButton((button: QuickInputButtonHandle) => { + // this.proxy.$acceptOnDidTriggerButton(sessionId, button); + // }); + // quickPick.onDidChangeValue((value: string) => { + // this.proxy.$acceptDidChangeValue(sessionId, value); + // }); + // quickPick.onDidHide(() => { + // this.proxy.$acceptOnDidHide(sessionId); + // }); + // session = { + // input: quickPick, + // handlesToItems: new Map() + // }; + // } else { + // const inputBox = this.quickInputService.createInputBox(); + // inputBox.onDidAccept(() => { + // this.proxy.$acceptOnDidAccept(sessionId); + // }); + // inputBox.onDidTriggerButton((button: QuickInputButtonHandle) => { + // this.proxy.$acceptOnDidTriggerButton(sessionId, button); + // }); + // inputBox.onDidChangeValue((value: string) => { + // this.proxy.$acceptDidChangeValue(sessionId, value); + // }); + // inputBox.onDidHide(() => { + // this.proxy.$acceptOnDidHide(sessionId); + // }); + // session = { + // input: inputBox, + // handlesToItems: new Map() + // }; + // } + // } + if (session) { + const { input, handlesToItems } = session; + for (const param in params) { + if (param === 'id' || param === 'type') { + continue; + } + if (param === 'visible') { + if (params.visible) { + input.show(); + } else { + input.hide(); + } + } else if (param === 'items') { + handlesToItems.clear(); + params[param].forEach((item: TransferQuickPickItems) => { + handlesToItems.set(item.handle, item); + }); + (input as any)[param] = params[param]; + } else if (param === 'activeItems' || param === 'selectedItems') { + (input as any)[param] = params[param] + .filter((handle: number) => handlesToItems.has(handle)) + .map((handle: number) => handlesToItems.get(handle)); + } else if (param === 'buttons') { + (input as any)[param] = params.buttons!.map(button => { + if (button.handle === -1) { + return this.quickInputService.backButton; + } + const { iconPath, tooltip, handle } = button; + if ('id' in iconPath) { + return { + iconClass: monaco.theme.ThemeIcon.asClassName(iconPath), + tooltip, + handle + }; + } else { + const monacoIconPath = (iconPath as unknown as { light: monaco.Uri, dark: monaco.Uri }); + return { + iconPath: { + dark: monaco.Uri.revive(monacoIconPath.dark), + light: monacoIconPath.light && monaco.Uri.revive(monacoIconPath.light) + }, + tooltip, + handle + }; + } + }); + } else { + (input as any)[param] = params[param]; + } } } - } - $refreshQuickInput(): void { - this.quickInput.refresh(); + + return Promise.resolve(undefined); } async $showCustomQuickPick(options: TransferQuickPick): Promise { + const sessionId = options.id; const toDispose = new DisposableCollection(); - const quickPick = this.quickPick.show(this.convertPickOpenItemToQuickOpenItem(options.items), { - buttons: options.buttons.map((btn, i) => this.convertQuickInputButton(btn, i, toDispose)), + + toDispose.push(this.quickPickService.onDidAccept(() => { + this.proxy.$acceptOnDidAccept(sessionId); + })); + toDispose.push(this.quickPickService.onDidChangeActive((e: { quickPick: any, activeItems: Array> }) => { + this.proxy.$onDidChangeActive(sessionId, e.activeItems.map(item => item.value!)); + })); + toDispose.push(this.quickPickService.onDidChangeSelection((e: { quickPick: any, selectedItems: Array> }) => { + this.proxy.$onDidChangeSelection(sessionId, e.selectedItems.map(item => item.value!)); + })); + toDispose.push(this.quickPickService.onDidChangeValue((e: { quickPick: any, filter: string }) => { + this.proxy.$acceptDidChangeValue(sessionId, e.filter); + })); + toDispose.push(this.quickPickService.onDidTriggerButton(button => { + this.proxy.$acceptOnDidTriggerButton(sessionId, button); + })); + toDispose.push(this.quickPickService.onDidHide(() => { + this.proxy.$acceptOnDidHide(sessionId); + if (!toDispose.disposed) { + toDispose.dispose(); + } + })); + this.toDispose.push(toDispose); + + this.quickPickService.show(this.convertToQuickPickItem(options.items), { + buttons: options.buttons ? this.convertToQuickInputButtons(options.buttons) : [], placeholder: options.placeholder, - fuzzyMatchDescription: options.matchOnDescription, - fuzzyMatchLabel: true, + matchOnDescription: options.matchOnDescription, step: options.step, title: options.title, totalSteps: options.totalSteps, ignoreFocusOut: options.ignoreFocusOut, value: options.value, + matchOnLabel: true, runIfSingle: false, }); + } - toDispose.push(this.quickPick.onDidAccept(() => this.proxy.$acceptOnDidAccept(options.id))); - toDispose.push(this.quickPick.onDidChangeActive((elements: QuickPickValue[]) => { - this.proxy.$onDidChangeActive(options.id, elements.map(e => e.value)); - })); - toDispose.push(this.quickPick.onDidChangeSelection((elements: QuickPickValue[]) => { - this.proxy.$onDidChangeSelection(options.id, elements.map(e => e.value)); - })); - toDispose.push(this.quickPick.onDidChangeValue(value => this.proxy.$acceptDidChangeValue(options.id, value))); - toDispose.push(this.quickTitleBar.onDidTriggerButton(button => { - this.proxy.$acceptOnDidTriggerButton(options.id, button); - })); - this.toDispose.push(toDispose); - quickPick.then(() => { - if (toDispose.disposed) { - return; - } - this.proxy.$acceptOnDidHide(options.id); - toDispose.dispose(); - }); + $hide(): void { + this.delegate.hide(); } - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - this.acceptor = acceptor; - if (this.items) { - acceptor(this.items); + $dispose(sessionId: number): Promise { + const session = this.sessions.get(sessionId); + if (session) { + session.input.dispose(); + this.sessions.delete(sessionId); } + return Promise.resolve(undefined); } - $hide(): void { - this.delegate.hide(); + private convertToQuickPickItem(items: TransferQuickPickItems[] | undefined): Array { + const convertedItems: QuickPickValue[] = []; + if (items) { + for (const i of items) { + convertedItems.push({ + label: i.label, + description: i.description, + detail: i.detail, + value: i.handle + }); + } + } + return convertedItems; } + private convertToQuickInputButtons(buttons: Array): Array { + return buttons.map((button, i) => ({ + iconPath: getIconUris(button.iconPath), + tooltip: button.tooltip, + handle: button === QuickInputButtons.Back ? -1 : i, + } as QuickInputButton)); + } } diff --git a/packages/plugin-ext/src/main/browser/theming-main.ts b/packages/plugin-ext/src/main/browser/theming-main.ts index 096591dc6bc9a..6b4d713c98d17 100644 --- a/packages/plugin-ext/src/main/browser/theming-main.ts +++ b/packages/plugin-ext/src/main/browser/theming-main.ts @@ -34,7 +34,7 @@ export class ThemingMainImpl implements ThemingMain { rpc: RPCProtocol ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.THEMING_EXT); - this.themeChangeListener = ThemeService.get().onThemeChange(e => { + this.themeChangeListener = ThemeService.get().onDidColorThemeChange(e => { this.proxy.$onColorThemeChange(e.newTheme.type); }); this.proxy.$onColorThemeChange(ThemeService.get().getCurrentTheme().type); diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 89c2891e3146d..3f2ab4f083def 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject, postConstruct, optional } from '@theia/core/shared/inversify'; import { ApplicationShell, ViewContainer as ViewContainerWidget, WidgetManager, ViewContainerIdentifier, ViewContainerTitleOptions, Widget, FrontendApplicationContribution, @@ -31,7 +31,7 @@ import { DebugFrontendApplicationContribution } from '@theia/debug/lib/browser/d import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { MenuModelRegistry } from '@theia/core/lib/common/menu'; -import { QuickViewService } from '@theia/core/lib/browser/quick-view-service'; +import { QuickViewService } from '@theia/core/lib/browser'; import { Emitter } from '@theia/core/lib/common/event'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { SearchInWorkspaceWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-widget'; @@ -75,7 +75,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { @inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry; - @inject(QuickViewService) + @inject(QuickViewService) @optional() protected readonly quickView: QuickViewService; @inject(ContextKeyService) @@ -256,7 +256,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { commandId: toggleCommandId, label: options.label })); - toDispose.push(this.quickView.registerItem({ + toDispose.push(this.quickView?.registerItem({ label: options.label, open: async () => { const widget = await this.openViewContainer(id); @@ -303,7 +303,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { this.viewClauseContexts.set(view.id, this.contextKeyService.parseKeys(view.when)); toDispose.push(Disposable.create(() => this.viewClauseContexts.delete(view.id))); } - toDispose.push(this.quickView.registerItem({ + toDispose.push(this.quickView?.registerItem({ label: view.name, when: view.when, open: () => this.openView(view.id, { activate: true }) diff --git a/packages/plugin-ext/src/main/browser/workspace-main.ts b/packages/plugin-ext/src/main/browser/workspace-main.ts index 7ed3e0a96adb0..61cab00823fa2 100644 --- a/packages/plugin-ext/src/main/browser/workspace-main.ts +++ b/packages/plugin-ext/src/main/browser/workspace-main.ts @@ -20,8 +20,6 @@ import { WorkspaceExt, StorageExt, MAIN_RPC_CONTEXT, WorkspaceMain, WorkspaceFol import { RPCProtocol } from '../../common/rpc-protocol'; import { URI as Uri } from '@theia/core/shared/vscode-uri'; import { UriComponents } from '../../common/uri-components'; -import { QuickOpenModel, QuickOpenItem, QuickOpenMode } from '@theia/core/lib/browser/quick-open/quick-open-model'; -import { MonacoQuickOpenService } from '@theia/monaco/lib/browser/monaco-quick-open-service'; import { FileSearchService } from '@theia/file-search/lib/common/file-search-service'; import URI from '@theia/core/lib/common/uri'; import { WorkspaceService } from '@theia/workspace/lib/browser'; @@ -32,6 +30,7 @@ import { PluginServer } from '../../common/plugin-protocol'; import { FileSystemPreferences } from '@theia/filesystem/lib/browser'; import { SearchInWorkspaceService } from '@theia/search-in-workspace/lib/browser/search-in-workspace-service'; import { FileStat } from '@theia/filesystem/lib/common/files'; +import { MonacoQuickInputService } from '@theia/monaco/lib/browser/monaco-quick-input-service'; export class WorkspaceMainImpl implements WorkspaceMain, Disposable { @@ -39,7 +38,7 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable { private storageProxy: StorageExt; - private quickOpenService: MonacoQuickOpenService; + private monacoQuickInputService: MonacoQuickInputService; private fileSearchService: FileSearchService; @@ -62,7 +61,7 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable { constructor(rpc: RPCProtocol, container: interfaces.Container) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.WORKSPACE_EXT); this.storageProxy = rpc.getProxy(MAIN_RPC_CONTEXT.STORAGE_EXT); - this.quickOpenService = container.get(MonacoQuickOpenService); + this.monacoQuickInputService = container.get(MonacoQuickInputService); this.fileSearchService = container.get(FileSearchService); this.searchInWorkspaceService = container.get(SearchInWorkspaceService); this.resourceResolver = container.get(TextContentResourceResolver); @@ -127,40 +126,25 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable { const items = this.roots.map(root => { const rootUri = Uri.parse(root); const rootPathName = rootUri.path.substring(rootUri.path.lastIndexOf('/') + 1); - return new QuickOpenItem({ + return { label: rootPathName, detail: rootUri.path, - run: mode => { - if (mode === QuickOpenMode.OPEN) { - returnValue = { - uri: rootUri, - name: rootPathName, - index: 0 - } as theia.WorkspaceFolder; - } - return true; + execute: () => { + returnValue = { + uri: rootUri, + name: rootPathName, + index: 0 + } as theia.WorkspaceFolder; } - }); + }; }); - // Create quick open model - const model = { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(items); - } - } as QuickOpenModel; - // Show pick menu - this.quickOpenService.open(model, { - fuzzyMatchLabel: true, - fuzzyMatchDetail: true, - fuzzyMatchDescription: true, - placeholder: options.placeHolder, - onClose: () => { + this.monacoQuickInputService.showQuickPick(items, { + onDidHide: () => { if (activeElement) { activeElement.focus({ preventScroll: true }); } - resolve(returnValue); } }); diff --git a/packages/plugin-ext/src/plugin/languages.ts b/packages/plugin-ext/src/plugin/languages.ts index 33d0724efaed1..43b0365204541 100644 --- a/packages/plugin-ext/src/plugin/languages.ts +++ b/packages/plugin-ext/src/plugin/languages.ts @@ -59,7 +59,8 @@ import { FoldingRange, SelectionRange, CallHierarchyDefinition, - CallHierarchyReference + CallHierarchyReference, + ChainedCacheId } from '../common/plugin-api-rpc-model'; import { CompletionAdapter } from './languages/completion'; import { Diagnostics } from './languages/diagnostics'; @@ -238,8 +239,8 @@ export class LanguagesExtImpl implements LanguagesExt { return this.withAdapter(handle, CompletionAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined); } - $resolveCompletionItem(handle: number, parentId: number, id: number, token: theia.CancellationToken): Promise { - return this.withAdapter(handle, CompletionAdapter, adapter => adapter.resolveCompletionItem(parentId, id, token), undefined); + $resolveCompletionItem(handle: number, chainedId: ChainedCacheId, token: theia.CancellationToken): Promise { + return this.withAdapter(handle, CompletionAdapter, adapter => adapter.resolveCompletionItem(chainedId, token), undefined); } $releaseCompletionItems(handle: number, id: number): void { diff --git a/packages/plugin-ext/src/plugin/languages/completion.ts b/packages/plugin-ext/src/plugin/languages/completion.ts index 2897f5f7c1a0c..f1446026af787 100644 --- a/packages/plugin-ext/src/plugin/languages/completion.ts +++ b/packages/plugin-ext/src/plugin/languages/completion.ts @@ -20,7 +20,7 @@ import { CompletionItemTag, CompletionList, Range, SnippetString } from '../type import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import { Position } from '../../common/plugin-api-rpc'; -import { CompletionContext, CompletionResultDto, Completion, CompletionDto, CompletionItemInsertTextRule } from '../../common/plugin-api-rpc-model'; +import { CompletionContext, CompletionResultDto, Completion, CompletionDto, CompletionItemInsertTextRule, ChainedCacheId } from '../../common/plugin-api-rpc-model'; import { CommandRegistryImpl } from '../command-registry'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; @@ -88,7 +88,8 @@ export class CompletionAdapter { }); } - async resolveCompletionItem(parentId: number, id: number, token: theia.CancellationToken): Promise { + async resolveCompletionItem(chainedId: ChainedCacheId, token: theia.CancellationToken): Promise { + const [parentId, id] = chainedId; if (typeof this.delegate.resolveCompletionItem !== 'function') { return undefined; } diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index b21f6a87fd210..3e339e447e629 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -13,57 +13,79 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { QuickOpenExt, PLUGIN_RPC_CONTEXT as Ext, QuickOpenMain, TransferInputBox, Plugin, TransferQuickPick, QuickInputTitleButtonHandle } from '../common/plugin-api-rpc'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + QuickOpenExt, PLUGIN_RPC_CONTEXT as Ext, QuickOpenMain, TransferInputBox, Plugin, + Item, TransferQuickInputButton, TransferQuickPickItems, TransferQuickInput, TransferQuickPick +} from '../common/plugin-api-rpc'; import * as theia from '@theia/plugin'; -import { QuickPickOptions, QuickPickItem, InputBoxOptions, InputBox, QuickPick, QuickInput } from '@theia/plugin'; +import { QuickPickItem, InputBoxOptions, InputBox, QuickPick, QuickInput } from '@theia/plugin'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; import { RPCProtocol } from '../common/rpc-protocol'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; -import { QuickInputButtons, QuickInputButton, ThemeIcon, URI } from './types-impl'; +import { QuickInputButtons, ThemeIcon } from './types-impl'; +import { URI } from '@theia/core/shared/vscode-uri'; import * as path from 'path'; -import { quickPickItemToPickOpenItem } from './type-converters'; +import { convertToTransferQuickPickItems } from './type-converters'; import { PluginPackage } from '../common/plugin-protocol'; +import { QuickInputButtonHandle } from '@theia/core/lib/browser'; -export type Item = string | QuickPickItem; +const canceledName = 'Canceled'; +/** + * Checks if the given error is a promise in canceled state + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isPromiseCanceledError(error: any): boolean { + return error instanceof Error && error.name === canceledName && error.message === canceledName; +} + +export function getIconUris(iconPath: theia.QuickInputButton['iconPath']): { dark: URI, light: URI } | { id: string } { + if (iconPath instanceof ThemeIcon) { + return { id: iconPath.id }; + } + const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI; }); + const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI; }); + // Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556 + return { + dark: typeof dark === 'string' ? URI.file(dark) : dark, + light: typeof light === 'string' ? URI.file(light) : light + }; +} + +export function getLightIconUri(iconPath: URI | { light: URI; dark: URI; }): URI { + return typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath; +} + +export function getDarkIconUri(iconPath: URI | { light: URI; dark: URI; }): URI { + return typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath; +} export class QuickOpenExtImpl implements QuickOpenExt { private proxy: QuickOpenMain; - private selectItemHandler: undefined | ((handle: number) => void); - private validateInputHandler: undefined | ((input: string) => string | PromiseLike | undefined); - + private onDidSelectItem: undefined | ((handle: number) => void); + private validateInputHandler: (input: string) => Promise | undefined; private _sessions = new Map(); // Each quickinput will have a number so that we know where to fire events - private currentQuickInputs = 0; + private _instances = 0; constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(Ext.QUICK_OPEN_MAIN); } - $onItemSelected(handle: number): void { - if (this.selectItemHandler) { - this.selectItemHandler(handle); - } - } - $validateInput(input: string): PromiseLike | undefined { - if (this.validateInputHandler) { - return Promise.resolve(this.validateInputHandler(input)); - } - return undefined; - } /* eslint-disable max-len */ - showQuickPick(promiseOrItems: QuickPickItem[] | PromiseLike, options?: QuickPickOptions, token?: theia.CancellationToken): PromiseLike; - showQuickPick(promiseOrItems: QuickPickItem[] | PromiseLike, options?: QuickPickOptions & { canSelectMany: true; }, token?: theia.CancellationToken): PromiseLike; - showQuickPick(promiseOrItems: string[] | PromiseLike, options?: QuickPickOptions, token?: theia.CancellationToken): PromiseLike; - showQuickPick(itemsOrItemsPromise: Item[] | PromiseLike, options?: QuickPickOptions, token: theia.CancellationToken = CancellationToken.None): PromiseLike { - /* eslint-enable max-len */ - this.selectItemHandler = undefined; + showQuickPick(itemsOrItemsPromise: Array | Promise>, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; + showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token: theia.CancellationToken = CancellationToken.None): Promise { + this.onDidSelectItem = undefined; const itemsPromise = >Promise.resolve(itemsOrItemsPromise); - const widgetPromise = this.proxy.$show({ - canSelectMany: options && options.canPickMany, + const instance = ++this._instances; + + const widgetPromise = this.proxy.$show(instance, { + canPickMany: options && options.canPickMany, placeHolder: options && options.placeHolder, - autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: options && options.matchOnDescription, matchOnDetail: options && options.matchOnDetail, ignoreFocusLost: options && options.ignoreFocusOut @@ -76,17 +98,16 @@ export class QuickOpenExtImpl implements QuickOpenExt { if (result === widgetClosedMarker) { return undefined; } - return itemsPromise.then(items => { - - const pickItems = quickPickItemToPickOpenItem(items); + return itemsPromise.then(async items => { + const pickItems: Array = convertToTransferQuickPickItems(items); if (options && typeof options.onDidSelectItem === 'function') { - this.selectItemHandler = handle => { + this.onDidSelectItem = handle => { options.onDidSelectItem!(items[handle]); }; } - this.proxy.$setItems(pickItems); + this.proxy.$setItems(instance, pickItems); return widgetPromise.then(handle => { if (typeof handle === 'number') { @@ -102,76 +123,98 @@ export class QuickOpenExtImpl implements QuickOpenExt { return undefined; }); }); + }).then(undefined, err => { + if (isPromiseCanceledError(err)) { + return undefined; + } + + this.proxy.$setError(instance, err); + + return Promise.reject(err); }); } - showCustomQuickPick(options: TransferQuickPick): void { - this.proxy.$showCustomQuickPick(options); + $onItemSelected(handle: number): void { + if (this.onDidSelectItem) { + this.onDidSelectItem(handle); + } } - createQuickPick(plugin: Plugin): QuickPick { - const newQuickInput = new QuickPickExt(this, this.proxy, plugin, this.currentQuickInputs); - this._sessions.set(this.currentQuickInputs, newQuickInput); - this.currentQuickInputs += 1; - return newQuickInput; - } + // ---- input showInput(options?: InputBoxOptions, token: theia.CancellationToken = CancellationToken.None): PromiseLike { - this.validateInputHandler = options && options.validateInput; - - if (!options) { - options = { - placeHolder: '' - }; + if (options?.validateInput) { + this.validateInputHandler = options.validateInput; } + if (!options) { options = { placeHolder: '' }; } return this.proxy.$input(options, typeof this.validateInputHandler === 'function', token); } - hide(): void { - this.proxy.$hide(); - } - showInputBox(options: TransferInputBox): void { + async showInputBox(options: TransferInputBox): Promise { this.validateInputHandler = options && options.validateInput; - this.proxy.$showInputBox(options, typeof this.validateInputHandler === 'function'); + + return this.proxy.$showInputBox(options, typeof this.validateInputHandler === 'function'); + } + + $validateInput(input: string): Promise | undefined { + if (this.validateInputHandler) { + return Promise.resolve(this.validateInputHandler(input)); + } + return undefined; + } + + // ---- QuickInput + + createQuickPick(plugin: Plugin): QuickPick { + const session: any = new QuickPickExt(this, this.proxy, plugin, () => this._sessions.delete(session._id)); + this._sessions.set(session._id, session); + return session; } createInputBox(plugin: Plugin): InputBox { - const newQuickInput = new InputBoxExt(this, this.proxy, plugin, this.currentQuickInputs); - this._sessions.set(this.currentQuickInputs, newQuickInput); - this.currentQuickInputs += 1; - return newQuickInput; + const session: any = new InputBoxExt(this, this.proxy, plugin, () => this._sessions.delete(session._id)); + this._sessions.set(session._id, session); + return session; + } + + showCustomQuickPick(options: TransferQuickPick): void { + this.proxy.$showCustomQuickPick(options); + } + + hide(): void { + this.proxy.$hide(); } async $acceptOnDidAccept(sessionId: number): Promise { - const currentQuickInput = this._sessions.get(sessionId); - if (currentQuickInput) { - currentQuickInput._fireAccept(); + const session = this._sessions.get(sessionId); + if (session) { + session._fireAccept(); } } async $acceptDidChangeValue(sessionId: number, changedValue: string): Promise { - const currentQuickInput = this._sessions.get(sessionId); - if (currentQuickInput) { - currentQuickInput._fireChangedValue(changedValue); + const session = this._sessions.get(sessionId); + if (session) { + session._fireChangedValue(changedValue); } } async $acceptOnDidHide(sessionId: number): Promise { - const currentQuickInput = this._sessions.get(sessionId); - if (currentQuickInput) { - currentQuickInput._fireHide(); + const session = this._sessions.get(sessionId); + if (session) { + session._fireHide(); } } - async $acceptOnDidTriggerButton(sessionId: number, btn: QuickInputTitleButtonHandle): Promise { - const thisQuickInput = this._sessions.get(sessionId); - if (thisQuickInput) { + async $acceptOnDidTriggerButton(sessionId: number, btn: QuickInputButtonHandle): Promise { + const session = this._sessions.get(sessionId); + if (session) { if (btn.index === -1) { - thisQuickInput._fireButtonTrigger(QuickInputButtons.Back); - } else if (thisQuickInput && (thisQuickInput instanceof InputBoxExt || thisQuickInput instanceof QuickPickExt)) { - const btnFromIndex = thisQuickInput.buttons[btn.index]; - thisQuickInput._fireButtonTrigger(btnFromIndex as QuickInputButton); + session._fireButtonTrigger(QuickInputButtons.Back); + } else if (session && (session instanceof InputBoxExt || session instanceof QuickPickExt)) { + const btnFromIndex = session.buttons[btn.index]; + session._fireButtonTrigger(btnFromIndex as theia.QuickInputButton); } } } @@ -189,11 +232,13 @@ export class QuickOpenExtImpl implements QuickOpenExt { session._fireDidChangeSelection(handles); } } - } export class QuickInputExt implements QuickInput { + private static _nextId = 1; + _id = QuickInputExt._nextId++; + private _busy: boolean; private _enabled: boolean; private _ignoreFocusOut: boolean; @@ -201,9 +246,12 @@ export class QuickInputExt implements QuickInput { private _title: string | undefined; private _totalSteps: number | undefined; private _value: string; - + private _placeholder: string | undefined; + private _buttons: theia.QuickInputButton[] = []; + private _handlesToButtons = new Map(); + protected expectingHide = false; protected visible: boolean; - + private _disposed = false; protected disposableCollection: DisposableCollection; private onDidAcceptEmitter: Emitter; @@ -213,9 +261,11 @@ export class QuickInputExt implements QuickInput { */ private _onDidChangeValueEmitter: Emitter; private onDidHideEmitter: Emitter; - private onDidTriggerButtonEmitter: Emitter; + private onDidTriggerButtonEmitter: Emitter; + private _updateTimeout: any; + private _pendingUpdate: TransferQuickInput = { id: this._id }; - constructor(readonly quickOpen: QuickOpenExtImpl, readonly quickOpenMain: QuickOpenMain, readonly plugin: Plugin) { + constructor(readonly quickOpen: QuickOpenExtImpl, readonly quickOpenMain: QuickOpenMain, readonly plugin: Plugin, private _onDidDispose: () => void) { this.title = undefined; this.step = undefined; this.totalSteps = undefined; @@ -296,34 +346,90 @@ export class QuickInputExt implements QuickInput { this.update({ value }); } + get placeholder(): string | undefined { + return this._placeholder; + } + + set placeholder(placeholder: string | undefined) { + this._placeholder = placeholder; + this.update({ placeholder }); + } + + get buttons(): theia.QuickInputButton[] { + return this._buttons; + } + + set buttons(buttons: theia.QuickInputButton[]) { + this._buttons = buttons.slice(); + this._handlesToButtons.clear(); + buttons.forEach((button, i) => { + const handle = button === QuickInputButtons.Back ? -1 : i; + this._handlesToButtons.set(handle, button); + }); + this.update({ + buttons: buttons.map((button, i) => ({ + iconPath: getIconUris(button.iconPath), + tooltip: button.tooltip, + handle: button === QuickInputButtons.Back ? -1 : i, + })) + }); + } + show(): void { - throw new Error('Method implementation must be provided by extenders'); + this.visible = true; + this.expectingHide = true; + this.update({ visible: true }); } dispose(): void { + if (this._disposed) { + return; + } + this._disposed = true; + this._fireHide(); this.disposableCollection.dispose(); + this._onDidDispose(); } - protected update(changed: object): void { - /** - * The args are just going to be set when we call show for the first time. - * We return early when its invisible to avoid race condition - */ - if (!this.visible || changed === undefined) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + protected update(properties: Record): void { + if (this._disposed) { return; } + for (const key of Object.keys(properties)) { + const value = properties[key]; + this._pendingUpdate[key] = value === undefined ? null : value; + } + + if ('visible' in this._pendingUpdate) { + if (this._updateTimeout) { + clearTimeout(this._updateTimeout); + this._updateTimeout = undefined; + } + this.dispatchUpdate(); + } else if (this.visible && !this._updateTimeout) { + // Defer the update so that multiple changes to setters dont cause a redraw each + this._updateTimeout = setTimeout(() => { + this._updateTimeout = undefined; + this.dispatchUpdate(); + }, 0); + } + } - this.quickOpenMain.$setQuickInputChanged(changed); + private dispatchUpdate(): void { + this.quickOpenMain.$createOrUpdate(this._pendingUpdate); + this._pendingUpdate = { id: this._id }; } hide(): void { - this.quickOpen.hide(); + this.quickOpenMain.$hide(); this.dispose(); } - protected convertURL(iconPath: URI | { light: string | URI; dark: string | URI } | ThemeIcon): URI | { light: string | URI; dark: string | URI } | ThemeIcon { - const toUrl = (arg: string | URI) => { - arg = arg instanceof URI && arg.scheme === 'file' ? arg.fsPath : arg; + protected convertURL(iconPath: monaco.Uri | { light: string | monaco.Uri; dark: string | monaco.Uri } | monaco.theme.ThemeIcon): + URI | { light: string | URI; dark: string | URI } | ThemeIcon { + const toUrl = (arg: string | monaco.Uri) => { + arg = arg instanceof monaco.Uri && arg.scheme === 'file' ? arg.fsPath : arg; if (typeof arg !== 'string') { return arg.toString(true); } @@ -335,10 +441,10 @@ export class QuickInputExt implements QuickInput { }; if ('id' in iconPath || iconPath instanceof ThemeIcon) { return iconPath; - } else if (typeof iconPath === 'string' || iconPath instanceof URI) { + } else if (typeof iconPath === 'string' || iconPath instanceof monaco.Uri) { return URI.parse(toUrl(iconPath)); } else { - const { light, dark } = iconPath as { light: string | URI, dark: string | URI }; + const { light, dark } = iconPath as { light: string | monaco.Uri, dark: string | monaco.Uri }; return { light: toUrl(light), dark: toUrl(dark) @@ -355,10 +461,13 @@ export class QuickInputExt implements QuickInput { } _fireHide(): void { - this.onDidHideEmitter.fire(undefined); + if (this.expectingHide) { + this.expectingHide = false; + this.onDidHideEmitter.fire(undefined); + } } - _fireButtonTrigger(btn: QuickInputButton): void { + _fireButtonTrigger(btn: theia.QuickInputButton): void { this.onDidTriggerButtonEmitter.fire(btn); } @@ -374,7 +483,7 @@ export class QuickInputExt implements QuickInput { return this._onDidChangeValueEmitter.event; } - get onDidTriggerButton(): Event { + get onDidTriggerButton(): Event { return this.onDidTriggerButtonEmitter.event; } } @@ -385,40 +494,22 @@ export class QuickInputExt implements QuickInput { */ export class InputBoxExt extends QuickInputExt implements InputBox { - /** - * Input Box API Start - */ - private _placeholder: string | undefined; private _password: boolean; - - private _buttons: ReadonlyArray; private _prompt: string | undefined; private _validationMessage: string | undefined; - /** - * Input Box API End - */ constructor(readonly quickOpen: QuickOpenExtImpl, readonly quickOpenMain: QuickOpenMain, readonly plugin: Plugin, - readonly quickInputIndex: number) { + onDispose: () => void) { - super(quickOpen, quickOpenMain, plugin); + super(quickOpen, quickOpenMain, plugin, onDispose); this.buttons = []; this.password = false; this.value = ''; } - get buttons(): ReadonlyArray { - return this._buttons; - } - - set buttons(buttons: ReadonlyArray) { - this._buttons = buttons; - this.update({ buttons }); - } - get password(): boolean { return this._password; } @@ -428,15 +519,6 @@ export class InputBoxExt extends QuickInputExt implements InputBox { this.update({ password }); } - get placeholder(): string | undefined { - return this._placeholder; - } - - set placeholder(placeholder: string | undefined) { - this._placeholder = placeholder; - this.update({ placeholder }); - } - get prompt(): string | undefined { return this._prompt; } @@ -454,26 +536,23 @@ export class InputBoxExt extends QuickInputExt implements InputBox { if (this._validationMessage !== validationMessage) { this._validationMessage = validationMessage; this.update({ validationMessage }); - this.quickOpenMain.$refreshQuickInput(); } } - show(): void { - this.visible = true; + async show(): Promise { + super.show(); + const update = (value: string) => { this.value = value; - // this.onDidChangeValueEmitter.fire(value); if (this.validationMessage && this.validationMessage.length > 0) { return this.validationMessage; } }; + this.quickOpen.showInputBox({ - id: this.quickInputIndex, + id: this._id, busy: this.busy, - buttons: this.buttons.map(btn => ({ - 'iconPath': this.convertURL(btn.iconPath), - 'tooltip': btn.tooltip - })), + buttons: this.buttons, enabled: this.enabled, ignoreFocusOut: this.ignoreFocusOut, password: this.password, @@ -484,6 +563,7 @@ export class InputBoxExt extends QuickInputExt implements InputBox { totalSteps: this.totalSteps, validationMessage: this.validationMessage, value: this.value, + visible: this.visible, validateInput(value: string): string | undefined { if (value.length > 0) { return update(value); @@ -497,43 +577,31 @@ export class InputBoxExt extends QuickInputExt implements InputBox { * Base implementation of {@link QuickPick} that uses {@link QuickOpenExt}. * Missing functionality is going to be implemented in the scope of https://github.com/eclipse-theia/theia/issues/5059 */ -export class QuickPickExt extends QuickInputExt implements QuickPick { - - // TODO encapsulate and move up to QuickInputExt - buttons: ReadonlyArray; - // TODO move up to QuickInputExt - private _placeholder: string | undefined; - +export class QuickPickExt extends QuickInputExt implements QuickPick { private _items: T[] = []; private _handlesToItems = new Map(); private _itemsToHandles = new Map(); private _canSelectMany = false; private _matchOnDescription = true; private _matchOnDetail = true; + private _sortByLabel = true; private _activeItems: T[] = []; - private readonly _onDidChangeActiveEmitter = new Emitter(); private _selectedItems: T[] = []; + private readonly _onDidChangeActiveEmitter = new Emitter(); private readonly _onDidChangeSelectionEmitter = new Emitter(); constructor(readonly quickOpen: QuickOpenExtImpl, readonly quickOpenMain: QuickOpenMain, readonly plugin: Plugin, - readonly quickInputIndex: number) { + onDispose: () => void) { - super(quickOpen, quickOpenMain, plugin); + super(quickOpen, quickOpenMain, plugin, onDispose); this.buttons = []; this.disposableCollection.push(this._onDidChangeActiveEmitter); this.disposableCollection.push(this._onDidChangeSelectionEmitter); - } - - get placeholder(): string | undefined { - return this._placeholder; - } - set placeholder(placeholder: string | undefined) { - this._placeholder = placeholder; - this.update({ placeholder }); + this.update({ type: 'quickPick' }); } get items(): T[] { @@ -549,7 +617,7 @@ export class QuickPickExt extends QuickInputExt impleme this._itemsToHandles.set(item, i); }); this.update({ - items: quickPickItemToPickOpenItem(items) + items }); } @@ -580,6 +648,15 @@ export class QuickPickExt extends QuickInputExt impleme this.update({ matchOnDetail }); } + get sortByLabel(): boolean { + return this._sortByLabel; + } + + set sortByLabel(sortByLabel: boolean) { + this._sortByLabel = sortByLabel; + this.update({ sortByLabel }); + } + get activeItems(): T[] { return this._activeItems; } @@ -615,9 +692,9 @@ export class QuickPickExt extends QuickInputExt impleme } show(): void { - this.visible = true; + super.show(); this.quickOpen.showCustomQuickPick({ - id: this.quickInputIndex, + id: this._id, title: this.title, step: this.step, totalSteps: this.totalSteps, @@ -626,11 +703,8 @@ export class QuickPickExt extends QuickInputExt impleme ignoreFocusOut: this.ignoreFocusOut, value: this.value, placeholder: this.placeholder, - buttons: this.buttons.map(btn => ({ - 'iconPath': this.convertURL(btn.iconPath), - 'tooltip': btn.tooltip - })), - items: quickPickItemToPickOpenItem(this.items), + buttons: this.buttons, + items: convertToTransferQuickPickItems(this.items), canSelectMany: this.canSelectMany, matchOnDescription: this.matchOnDescription, matchOnDetail: this.matchOnDetail, @@ -638,5 +712,4 @@ export class QuickPickExt extends QuickInputExt impleme selectedItems: this.selectedItems }); } - } diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 25dd7c5fdd88a..e4737e87e32c7 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -19,12 +19,11 @@ import { Position as P, Range as R, SymbolInformation, SymbolKind as S } from '@ import { URI } from './types-impl'; import * as rpc from '../common/plugin-api-rpc'; import { - DecorationOptions, EditorPosition, PickOpenItem, Plugin, Position, WorkspaceTextEditDto, WorkspaceFileEditDto, Selection, TaskDto, WorkspaceEditDto + DecorationOptions, EditorPosition, Plugin, Position, WorkspaceTextEditDto, WorkspaceFileEditDto, Selection, TaskDto, WorkspaceEditDto } from '../common/plugin-api-rpc'; import * as model from '../common/plugin-api-rpc-model'; import { LanguageFilter, LanguageSelector, RelativePattern } from '@theia/callhierarchy/lib/common/language-selector'; import { isMarkdownString, MarkdownString } from './markdown-string'; -import { Item } from './quick-open'; import * as types from './types-impl'; import { UriComponents } from '../common/uri-components'; import { TaskGroup } from './types-impl'; @@ -1043,20 +1042,20 @@ export function fromColorPresentation(colorPresentation: theia.ColorPresentation }; } -export function quickPickItemToPickOpenItem(items: Item[]): PickOpenItem[] { - const pickItems: PickOpenItem[] = []; +export function convertToTransferQuickPickItems(items: rpc.Item[]): rpc.TransferQuickPickItems[] { + const pickItems: rpc.TransferQuickPickItems[] = []; for (let handle = 0; handle < items.length; handle++) { const item = items[handle]; let label: string; let description: string | undefined; let detail: string | undefined; let picked: boolean | undefined; - let groupLabel: string | undefined; - let showBorder: boolean | undefined; + let alwaysShow: boolean | undefined; + if (typeof item === 'string') { label = item; } else { - ({ label, description, detail, picked, groupLabel, showBorder } = item); + ({ label, description, detail, picked, alwaysShow } = item); } pickItems.push({ @@ -1065,8 +1064,7 @@ export function quickPickItemToPickOpenItem(items: Item[]): PickOpenItem[] { handle, detail, picked, - groupLabel, - showBorder + alwaysShow }); } return pickItems; diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index bf66f68c0a84c..defe4d7b7282e 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1432,15 +1432,12 @@ export enum CommentThreadCollapsibleState { Expanded = 1 } -export interface QuickInputButton { - readonly iconPath: URI | { light: string | URI; dark: string | URI } | ThemeIcon; - readonly tooltip?: string | undefined; -} - export class QuickInputButtons { - static readonly Back: QuickInputButton = { + static readonly Back: theia.QuickInputButton = { iconPath: { - id: 'Back' + id: 'Back', + dark: '', + light: '' }, tooltip: 'Back' }; diff --git a/packages/plugin-metrics/src/browser/plugin-metrics-languages-main.ts b/packages/plugin-metrics/src/browser/plugin-metrics-languages-main.ts index 72036f5c4b735..4e5b32814806b 100644 --- a/packages/plugin-metrics/src/browser/plugin-metrics-languages-main.ts +++ b/packages/plugin-metrics/src/browser/plugin-metrics-languages-main.ts @@ -45,11 +45,11 @@ export class LanguagesMainPluginMetrics extends LanguagesMainImpl { super.provideCompletionItems(handle, model, position, context, token)); } - protected resolveCompletionItem(handle: number, model: monaco.editor.ITextModel, position: monaco.Position, + protected resolveCompletionItem(handle: number, item: monaco.languages.CompletionItem, token: monaco.CancellationToken): monaco.languages.ProviderResult { return this.pluginMetricsResolver.resolveRequest(this.handleToExtensionName(handle), vst.CompletionRequest.type.method, - super.resolveCompletionItem(handle, model, position, item, token)); + super.resolveCompletionItem(handle, item, token)); } protected provideReferences(handle: number, model: monaco.editor.ITextModel, position: monaco.Position, diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 333df0ad3ffc4..3eb01fdf6da38 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -2072,100 +2072,10 @@ declare module '@theia/plugin' { } /** - * A light-weight user input UI that is initially not visible. After - * configuring it through its properties the extension can make it - * visible by calling [QuickInput.show](#QuickInput.show). - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explicit call to [QuickInput.hide](#QuickInput.hide), - * the user pressing Esc, some other input UI opening, etc.) - * - * A user pressing Enter or some other gesture implying acceptance - * of the current state does not automatically hide this UI component. - * It is up to the extension to decide whether to accept the user's input - * and if the UI should indeed be hidden through a call to [QuickInput.hide](#QuickInput.hide). - * - * When the extension no longer needs this input UI, it should - * [QuickInput.dispose](#QuickInput.dispose) it to allow for freeing up - * any resources associated with it. - * - * See [QuickPick](#QuickPick) and [InputBox](#InputBox) for concrete UIs. - */ - export interface QuickInput { - - /** - * An optional title. - */ - title: string | undefined; - - /** - * An optional current step count. - */ - step: number | undefined; - - /** - * An optional total step count. - */ - totalSteps: number | undefined; - - /** - * If the UI should allow for user input. Defaults to true. - * - * Change this to false, e.g., while validating user input or - * loading data for the next step in user input. - */ - enabled: boolean; - - /** - * If the UI should show a progress indicator. Defaults to false. - * - * Change this to true, e.g., while loading more data or validating - * user input. - */ - busy: boolean; - - /** - * If the UI should stay open even when loosing UI focus. Defaults to false. - */ - ignoreFocusOut: boolean; - - /** - * Makes the input UI visible in its current configuration. Any other input - * UI will first fire an [QuickInput.onDidHide](#QuickInput.onDidHide) event. - */ - show(): void; - - /** - * Hides this input UI. This will also fire an [QuickInput.onDidHide](#QuickInput.onDidHide) - * event. - */ - hide(): void; - - /** - * An event signaling when this input UI is hidden. - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explicit call to [QuickInput.hide](#QuickInput.hide), - * the user pressing Esc, some other input UI opening, etc.) - */ - onDidHide: Event; - - /** - * Dispose of this input UI and any associated resources. If it is still - * visible, it is first hidden. After this call the input UI is no longer - * functional and no additional methods or properties on it should be - * accessed. Instead a new input UI should be created. - */ - dispose(): void; - } - - /** - * Something that can be selected from a list of items. + * Represents an item that can be selected from a list of items. */ export interface QuickPickItem { - + type?: 'item' | 'separator'; /** * The item label */ @@ -2186,32 +2096,10 @@ declare module '@theia/plugin' { * not implemented yet */ picked?: boolean; - - /** - * Used to display the group label in the right corner of item - */ - groupLabel?: string; - /** - * Used to display border after item + * Always show this item. */ - showBorder?: boolean; - } - - /** - * Button for an action in a [QuickPick](#QuickPick) or [InputBox](#InputBox). - */ - export interface QuickInputButton { - - /** - * Icon for the button. - */ - readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; - - /** - * An optional tooltip. - */ - readonly tooltip?: string | undefined; + alwaysShow?: boolean; } /** @@ -2395,7 +2283,7 @@ declare module '@theia/plugin' { * @return A human readable string which is presented as diagnostic message. * Return `undefined`, or the empty string when 'value' is valid. */ - validateInput?(value: string): string | undefined | PromiseLike; + validateInput?: (input: string) => Promise | undefined; /** * An optional function that will be called on Enter key. @@ -2605,7 +2493,7 @@ declare module '@theia/plugin' { */ static readonly Folder: ThemeIcon; - private constructor(id: string); + private constructor(public id: string); } /** @@ -3255,39 +3143,6 @@ declare module '@theia/plugin' { readonly logPath: string; } - /** - * A memento represents a storage utility. It can store and retrieve - * values. - */ - export interface Memento { - - /** - * Return a value. - * - * @param key A string. - * @return The stored value or `undefined`. - */ - get(key: string): T | undefined; - - /** - * Return a value. - * - * @param key A string. - * @param defaultValue A value that should be returned when there is no - * value (`undefined`) with the given key. - * @return The stored value or the defaultValue. - */ - get(key: string, defaultValue: T): T; - - /** - * Store a value. The value must be JSON-stringifyable. - * - * @param key A string. - * @param value A value. MUST not contain cyclic references. - */ - update(key: string, value: any): PromiseLike; - } - /** * Defines a port mapping used for localhost inside the webview. */ @@ -4734,7 +4589,7 @@ declare module '@theia/plugin' { /** * Icon for the button. */ - readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; + readonly iconPath: Uri | { light: string | Uri; dark: string | Uri } | monaco.theme.ThemeIcon; /** * An optional tooltip. diff --git a/packages/preview/src/browser/preview-widget.ts b/packages/preview/src/browser/preview-widget.ts index 13c4b3414738e..05a90cc5eff0f 100644 --- a/packages/preview/src/browser/preview-widget.ts +++ b/packages/preview/src/browser/preview-widget.ts @@ -99,7 +99,7 @@ export class PreviewWidget extends BaseWidget implements Navigatable { this.toDispose.push(this.workspace.onDidOpenTextDocument(document => updateIfAffected(document.uri))); this.toDispose.push(this.workspace.onDidChangeTextDocument(params => updateIfAffected(params.model.uri))); this.toDispose.push(this.workspace.onDidCloseTextDocument(document => updateIfAffected(document.uri))); - this.toDispose.push(this.themeService.onThemeChange(() => this.update())); + this.toDispose.push(this.themeService.onDidColorThemeChange(() => this.update())); this.firstUpdate = () => { this.revealFragment(this.uri); }; diff --git a/packages/scm/src/browser/scm-contribution.ts b/packages/scm/src/browser/scm-contribution.ts index 789492f5b6645..f9391daee6672 100644 --- a/packages/scm/src/browser/scm-contribution.ts +++ b/packages/scm/src/browser/scm-contribution.ts @@ -19,7 +19,6 @@ import { find } from '@theia/core/shared/@phosphor/algorithm'; import { AbstractViewContribution, FrontendApplicationContribution, LabelProvider, - QuickOpenService, StatusBar, StatusBarAlignment, StatusBarEntry, @@ -88,7 +87,6 @@ export class ScmContribution extends AbstractViewContribution impleme @inject(StatusBar) protected readonly statusBar: StatusBar; @inject(ScmService) protected readonly scmService: ScmService; - @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; @inject(ScmQuickOpenService) protected readonly scmQuickOpenService: ScmQuickOpenService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @inject(CommandService) protected readonly commands: CommandService; diff --git a/packages/scm/src/browser/scm-quick-open-service.ts b/packages/scm/src/browser/scm-quick-open-service.ts index 0de79a1223815..98f7bcf89e256 100644 --- a/packages/scm/src/browser/scm-quick-open-service.ts +++ b/packages/scm/src/browser/scm-quick-open-service.ts @@ -14,19 +14,17 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { QuickOpenItem, QuickOpenMode, QuickOpenModel } from '@theia/core/lib/browser/quick-open/quick-open-model'; -import { QuickOpenService, QuickOpenOptions } from '@theia/core/lib/browser/quick-open/quick-open-service'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { MessageService } from '@theia/core/lib/common/message-service'; import URI from '@theia/core/lib/common/uri'; import { ScmService } from './scm-service'; -import { ScmRepository } from './scm-repository'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; +import { QuickInputService } from '@theia/core/lib/browser'; @injectable() export class ScmQuickOpenService { - @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(MessageService) protected readonly messageService: MessageService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @inject(ScmService) protected readonly scmService: ScmService; @@ -36,64 +34,15 @@ export class ScmQuickOpenService { if (repositories.length > 1) { const items = await Promise.all(repositories.map(async repository => { const uri = new URI(repository.provider.rootUri); - const execute = () => { - this.scmService.selectedRepository = repository; + return { + label: this.labelProvider.getName(uri), + description: this.labelProvider.getLongName(uri), + execute: () => { + this.scmService.selectedRepository = repository; + } }; - const toLabel = () => this.labelProvider.getName(uri); - const toDescription = () => this.labelProvider.getLongName(uri); - return new ScmQuickOpenItem(repository, execute, toLabel, toDescription); })); - this.open(items, 'Select repository to work with:'); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select repository to work with:' }); } } - - private open(items: QuickOpenItem | QuickOpenItem[], placeholder: string): void { - this.quickOpenService.open(this.getModel(Array.isArray(items) ? items : [items]), this.getOptions(placeholder)); - } - - private getModel(items: QuickOpenItem | QuickOpenItem[]): QuickOpenModel { - return { - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(Array.isArray(items) ? items : [items]); - } - }; - } - - private getOptions(placeholder: string, fuzzyMatchLabel: boolean = true, onClose: (canceled: boolean) => void = () => { }): QuickOpenOptions { - return QuickOpenOptions.resolve({ - placeholder, - fuzzyMatchLabel, - fuzzySort: false, - onClose - }); - } -} - -class ScmQuickOpenItem extends QuickOpenItem { - - constructor( - public readonly ref: T, - protected readonly execute: (item: ScmQuickOpenItem) => void, - private readonly toLabel: (item: ScmQuickOpenItem) => string = () => `${ref}`, - private readonly toDescription: (item: ScmQuickOpenItem) => string | undefined = () => undefined) { - - super(); - } - - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.execute(this); - return true; - } - - getLabel(): string { - return this.toLabel(this); - } - - getDescription(): string | undefined { - return this.toDescription(this); - } - } diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts index 1f94e90b9fee6..7dd953173aae1 100644 --- a/packages/task/src/browser/quick-open-task.ts +++ b/packages/task/src/browser/quick-open-task.ts @@ -14,77 +14,36 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { TaskService } from './task-service'; import { TaskInfo, TaskConfiguration, TaskCustomization, TaskScope, TaskConfigurationScope } from '../common/task-protocol'; import { TaskDefinitionRegistry } from './task-definition-registry'; import URI from '@theia/core/lib/common/uri'; -import { QuickOpenHandler, QuickOpenService, QuickOpenOptions, QuickOpenBaseAction, LabelProvider } from '@theia/core/lib/browser'; +import { LabelProvider, QuickInputService } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; -import { - QuickOpenModel, QuickOpenItem, QuickOpenActionProvider, QuickOpenMode, QuickOpenGroupItem, QuickOpenGroupItemOptions, QuickOpenAction -} from '@theia/core/lib/common/quick-open-model'; import { PreferenceService } from '@theia/core/lib/browser'; import { TaskNameResolver } from './task-name-resolver'; import { TaskSourceResolver } from './task-source-resolver'; import { TaskConfigurationManager } from './task-configuration-manager'; +import { filterItems, QuickInputButton, QuickPickItem } from '@theia/core/lib/browser/quick-input/quick-input-service'; -@injectable() -export class ConfigureTaskAction extends QuickOpenBaseAction { - - @inject(TaskService) - protected readonly taskService: TaskService; - - constructor() { - super({ id: 'configure:task', label: 'Configure Task' }); - this.updateClass(); - } - - async run(item?: QuickOpenItem): Promise { - if (item instanceof TaskRunQuickOpenItem) { - this.taskService.configure(item.getToken(), item.getTask()); - } - } - - protected updateClass(): void { - this.class = 'codicon-gear quick-open-task-configure'; - } -} - -@injectable() -export class TaskActionProvider implements QuickOpenActionProvider { - - @inject(ConfigureTaskAction) - protected configureTaskAction: ConfigureTaskAction; - - hasActions(): boolean { - return true; - } - - getActions(): ReadonlyArray { - return [this.configureTaskAction]; - } +export namespace ConfigureTaskAction { + export const ID = 'workbench.action.tasks.configureTaskRunner'; + export const TEXT = 'Configure Task'; } @injectable() -export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { - - protected items: QuickOpenItem[]; - protected actionProvider: QuickOpenActionProvider | undefined; - +export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService { readonly prefix: string = 'task '; - readonly description: string = 'Run Task'; + protected items: Array = []; @inject(TaskService) protected readonly taskService: TaskService; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; - - @inject(TaskActionProvider) - protected readonly taskActionProvider: TaskActionProvider; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -111,7 +70,6 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { return this.doInit(this.taskService.startUserAction()); } - /** Initialize this quick open model with the tasks. */ protected async doInit(token: number): Promise { const recentTasks = this.taskService.recentTasks; const configuredTasks = await this.taskService.getConfiguredTasks(token); @@ -120,84 +78,42 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { const { filteredRecentTasks, filteredConfiguredTasks, filteredProvidedTasks } = this.getFilteredTasks(recentTasks, configuredTasks, providedTasks); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; this.items = []; + const configureButton = [{ + iconClass: 'codicon-gear', + tooltip: 'Configure Task', + }]; + + const filteredRecentTasksItems = this.getItems(filteredRecentTasks, 'recently used tasks', token, isMulti, configureButton); + const filteredConfiguredTasksItems = this.getItems(filteredConfiguredTasks, 'configured tasks', token, isMulti, configureButton); + const filteredProvidedTasksItems = this.getItems(filteredProvidedTasks, 'detected tasks', token, isMulti); + this.items.push( - ...filteredRecentTasks.map((task, index) => { - const item = new TaskRunQuickOpenItem(token, task, this.taskService, isMulti, { - groupLabel: index === 0 ? 'recently used tasks' : undefined, - showBorder: false - }, this.taskDefinitionRegistry, this.taskNameResolver, this.taskSourceResolver); - return item; - }), - ...filteredConfiguredTasks.map((task, index) => { - const item = new TaskRunQuickOpenItem(token, task, this.taskService, isMulti, { - groupLabel: index === 0 ? 'configured tasks' : undefined, - showBorder: ( - filteredRecentTasks.length <= 0 - ? false - : index === 0 ? true : false - ) - }, this.taskDefinitionRegistry, this.taskNameResolver, this.taskSourceResolver); - return item; - }), - ...filteredProvidedTasks.map((task, index) => { - const item = new TaskRunQuickOpenItem(token, task, this.taskService, isMulti, { - groupLabel: index === 0 ? 'detected tasks' : undefined, - showBorder: ( - filteredRecentTasks.length <= 0 && filteredConfiguredTasks.length <= 0 - ? false - : index === 0 ? true : false - ) - }, this.taskDefinitionRegistry, this.taskNameResolver, this.taskSourceResolver); - return item; - }) + ...filteredRecentTasksItems, + ...filteredConfiguredTasksItems, + ...filteredProvidedTasksItems ); - - this.actionProvider = this.items.length ? this.taskActionProvider : undefined; } async open(): Promise { const token: number = this.taskService.startUserAction(); await this.doInit(token); if (!this.items.length) { - this.items.push(new QuickOpenItem({ + this.items.push(({ label: 'No task to run found. Configure Tasks...', - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.configure(); - return true; - } + execute: () => this.configure() })); } - this.quickOpenService.open(this, { - placeholder: 'Select the task to run', - fuzzyMatchLabel: true, - fuzzySort: false - }); - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - fuzzyMatchLabel: true, - fuzzySort: false - }; + this.quickInputService?.open(this.prefix); } attach(): void { this.items = []; - this.actionProvider = undefined; const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; this.taskService.getRunningTasks().then(tasks => { if (!tasks.length) { - this.items.push(new QuickOpenItem({ + this.items.push({ label: 'No tasks found', - run: (_mode: QuickOpenMode): boolean => false - })); + }); } else { tasks.forEach((task: TaskInfo) => { // can only attach to terminal processes, so only list those @@ -210,134 +126,111 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { this.taskDefinitionRegistry, this.labelProvider, isMulti, - { - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.taskService.attach(task.terminalId!, task); - return true; - } - }, + () => this.taskService.attach(task.terminalId!, task) )); } }); } - this.quickOpenService.open(this, { - placeholder: 'Choose task to open', - fuzzyMatchLabel: true, - fuzzySort: true - }); + this.quickInputService?.open(this.prefix); }); } async configure(): Promise { this.items = []; - this.actionProvider = undefined; - + const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; const token: number = this.taskService.startUserAction(); const configuredTasks = await this.taskService.getConfiguredTasks(token); const providedTasks = await this.taskService.getProvidedTasks(token); + + // check if tasks.json exists. If not, display "Create tasks.json file from template" + // If tasks.json exists and empty, display 'Open tasks.json file' const { filteredConfiguredTasks, filteredProvidedTasks } = this.getFilteredTasks([], configuredTasks, providedTasks); const groupedTasks = this.getGroupedTasksByWorkspaceFolder([...filteredConfiguredTasks, ...filteredProvidedTasks]); - - const scopes: TaskConfigurationScope[] = [TaskScope.Global]; - if (this.workspaceService.opened) { - const roots = await this.workspaceService.roots; - scopes.push(...roots.map(rootStat => rootStat.resource.toString())); - if (this.workspaceService.saved || groupedTasks.get(TaskScope.Workspace.toString())?.length) { - scopes.push(TaskScope.Workspace); - } + if (groupedTasks.has(TaskScope.Global.toString())) { + const configs = groupedTasks.get(TaskScope.Global.toString())!; + this.items.push( + ...configs.map(taskConfig => { + const item = new TaskConfigureQuickOpenItem( + token, + taskConfig, + this.taskService, + this.taskNameResolver, + this.workspaceService, + isMulti + ); + item['taskDefinitionRegistry'] = this.taskDefinitionRegistry; + return item; + }) + ); } - let isFirstGroup = true; - let groupLabel = ''; - const optionsGenerator = (index: number): QuickOpenGroupItemOptions => ({ - showBorder: !isFirstGroup && index === 0, - groupLabel: index === 0 ? groupLabel : '', - description: groupLabel, - }); - - for (const scope of scopes) { - groupLabel = typeof scope === 'string' ? this.labelProvider.getName(new URI(scope)) : TaskScope[scope]; + const rootUris = (await this.workspaceService.roots).map(rootStat => rootStat.resource.toString()); + for (const rootFolder of rootUris) { + const folderName = new URI(rootFolder).displayName; + if (groupedTasks.has(rootFolder)) { + const configs = groupedTasks.get(rootFolder.toString())!; + this.items.push( + ...configs.map((taskConfig, index) => { + const item = new TaskConfigureQuickOpenItem( + token, + taskConfig, + this.taskService, + this.taskNameResolver, + this.workspaceService, + isMulti, - const configs = groupedTasks.get(scope.toString()); - if (configs?.length) { - this.items.push(...this.getTaskConfigureQuickOpenItems(configs, token, optionsGenerator)); + ); + item['taskDefinitionRegistry'] = this.taskDefinitionRegistry; + return item; + }) + ); } else { - this.items.push(this.getOpenFileItem(scope, optionsGenerator.bind(this, 0))); + const { configUri } = this.preferences.resolve('tasks', [], rootFolder); + const existTaskConfigFile = !!configUri; + this.items.push(({ + label: existTaskConfigFile ? 'Open tasks.json file' : 'Create tasks.json file from template', + execute: () => { + setTimeout(() => this.taskConfigurationManager.openConfiguration(rootFolder)); + } + })); + } + if (this.items.length > 0) { + this.items.unshift({ + type: 'separator', + label: isMulti ? folderName : '' + }); } - - isFirstGroup = false; } if (this.items.length === 0) { - this.items.push(new QuickOpenItem({ - label: 'No tasks found', - run: (_mode: QuickOpenMode): boolean => false + this.items.push(({ + label: 'No tasks found' })); } - this.quickOpenService.open(this, { - placeholder: 'Select a task to configure', - fuzzyMatchLabel: true, - fuzzySort: false - }); - } - - protected getTaskConfigureQuickOpenItems( - configs: TaskConfiguration[], - token: number, - optionsGenerator: (index: number) => QuickOpenGroupItemOptions - ): TaskConfigureQuickOpenItem[] { - return configs.map((taskConfig, index) => { - const item = new TaskConfigureQuickOpenItem( - token, - taskConfig, - this.taskService, - this.taskNameResolver, - this.workspaceService, - this.workspaceService.isMultiRootWorkspaceOpened, - optionsGenerator(index), - ); - item['taskDefinitionRegistry'] = this.taskDefinitionRegistry; - return item; - }); - } - - protected getOpenFileItem(scope: TaskConfigurationScope, optionsGenerator: () => QuickOpenGroupItemOptions): QuickOpenGroupItem { - return new QuickOpenGroupItem({ - label: 'Configure new task.', - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - setTimeout(() => this.taskConfigurationManager.openConfiguration(scope)); - return true; - }, - ...optionsGenerator(), - }); + this.quickInputService?.showQuickPick(this.items, { placeholder: 'Select a task to configure' }); } async runBuildOrTestTask(buildOrTestType: 'build' | 'test'): Promise { const shouldRunBuildTask = buildOrTestType === 'build'; const token: number = this.taskService.startUserAction(); + await this.doInit(token); - if (this.items.length > 1 || - this.items.length === 1 && (this.items[0] as TaskRunQuickOpenItem).getTask !== undefined) { // the item in `this.items` is not 'No tasks found' - const buildOrTestTasks = this.items.filter((t: TaskRunQuickOpenItem) => - shouldRunBuildTask ? TaskCustomization.isBuildTask(t.getTask()) : TaskCustomization.isTestTask(t.getTask()) + const taskItems = this.items.filter(item => item.type !== 'separator' && (item as TaskRunQuickOpenItem).task !== undefined); + + if (taskItems.length > 0) { // the item in `this.items` is not 'No tasks found' + const buildOrTestTasks = taskItems.filter((t: TaskRunQuickOpenItem) => + shouldRunBuildTask ? TaskCustomization.isBuildTask(t.task) : TaskCustomization.isTestTask(t.task) ); - this.actionProvider = undefined; if (buildOrTestTasks.length > 0) { // build / test tasks are defined in the workspace const defaultBuildOrTestTasks = buildOrTestTasks.filter((t: TaskRunQuickOpenItem) => - shouldRunBuildTask ? TaskCustomization.isDefaultBuildTask(t.getTask()) : TaskCustomization.isDefaultTestTask(t.getTask()) + shouldRunBuildTask ? TaskCustomization.isDefaultBuildTask(t.task) : TaskCustomization.isDefaultTestTask(t.task) ); if (defaultBuildOrTestTasks.length === 1) { // run the default build / test task const defaultBuildOrTestTask = defaultBuildOrTestTasks[0]; - const taskToRun = (defaultBuildOrTestTask as TaskRunQuickOpenItem).getTask(); + const taskToRun = (defaultBuildOrTestTask as TaskRunQuickOpenItem).task; const scope = taskToRun._scope; if (this.taskDefinitionRegistry && !!this.taskDefinitionRegistry.getDefinition(taskToRun)) { @@ -347,77 +240,75 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { } return; } - // if default build / test task is not found, or there are more than one default, // display the list of build /test tasks to let the user decide which to run this.items = buildOrTestTasks; - } else { // no build / test tasks, display an action item to configure the build / test task - this.items = [new QuickOpenItem({ + this.items = [({ label: `No ${buildOrTestType} task to run found. Configure ${buildOrTestType.charAt(0).toUpperCase() + buildOrTestType.slice(1)} Task...`, - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - + execute: () => { this.doInit(token).then(() => { // update the `tasks.json` file, instead of running the task itself - this.items = this.items.map((item: TaskRunQuickOpenItem) => { - const newItem = new ConfigureBuildOrTestTaskQuickOpenItem( - token, - item.getTask(), - this.taskService, - this.workspaceService.isMultiRootWorkspaceOpened, - item.options, - this.taskNameResolver, - shouldRunBuildTask, - this.taskConfigurationManager, - this.taskDefinitionRegistry, - this.taskSourceResolver - ); - return newItem; - }); - this.quickOpenService.open(this, { - placeholder: `Select the task to be used as the default ${buildOrTestType} task`, - fuzzyMatchLabel: true, - fuzzySort: false - }); + this.items = this.items.map((item: TaskRunQuickOpenItem) => new ConfigureBuildOrTestTaskQuickOpenItem( + token, + item.task, + this.taskService, + this.workspaceService.isMultiRootWorkspaceOpened, + this.taskNameResolver, + shouldRunBuildTask, + this.taskConfigurationManager, + this.taskDefinitionRegistry, + this.taskSourceResolver + )); + this.quickInputService?.showQuickPick(this.items, { placeholder: `Select the task to be used as the default ${buildOrTestType} task` }); }); - - return true; } })]; } } else { // no tasks are currently present, prompt users if they'd like to configure a task. - this.items = [ - new QuickOpenItem({ - label: `No ${buildOrTestType} task to run found. Configure ${buildOrTestType.charAt(0).toUpperCase() + buildOrTestType.slice(1)} Task...`, - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.configure(); - return true; - } - }) - ]; + this.items = [{ + label: `No ${buildOrTestType} task to run found. Configure ${buildOrTestType.charAt(0).toUpperCase() + buildOrTestType.slice(1)} Task...`, + execute: () => this.configure() + }]; } - this.quickOpenService.open(this, { - placeholder: `Select the ${buildOrTestType} task to run`, - fuzzyMatchLabel: true, - fuzzySort: false - }); + this.quickInputService?.showQuickPick(this.items, { placeholder: `Select the ${buildOrTestType} task to run` }); + } + + async getPicks(filter: string, token: monaco.CancellationToken): Promise> { + if (this.items.length === 0) { + await this.init(); + } + return filterItems(this.items, filter); } - onType(lookFor: string, acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider) => void): void { - acceptor(this.items, this.actionProvider); + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: TaskQuickAccessProvider, + prefix: TaskQuickAccessProvider.PREFIX, + placeholder: 'Select the task to run', + helpEntries: [{ description: 'Run Task', needsEditor: false }] + }); + TaskQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } protected getRunningTaskLabel(task: TaskInfo): string { return `Task id: ${task.taskId}, label: ${task.config.label}`; } + private getItems(tasks: TaskConfiguration[], groupLabel: string, token: number, isMulti: boolean, buttons?: Array): + Array { + const items: Array = tasks.map(task => + new TaskRunQuickOpenItem(token, task, this.taskService, isMulti, this.taskDefinitionRegistry, this.taskNameResolver, + this.taskSourceResolver, this.taskConfigurationManager, buttons) + ); + + if (items.length > 0) { + items.unshift({ type: 'separator', label: groupLabel }); + } + return items; + } + private getFilteredTasks(recentTasks: TaskConfiguration[], configuredTasks: TaskConfiguration[], providedTasks: TaskConfiguration[]): { filteredRecentTasks: TaskConfiguration[], filteredConfiguredTasks: TaskConfiguration[], filteredProvidedTasks: TaskConfiguration[] } { @@ -468,87 +359,77 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { } } -export class TaskRunQuickOpenItem extends QuickOpenGroupItem { - +export class TaskRunQuickOpenItem implements monaco.quickInput.IAnythingQuickPickItem { constructor( - protected readonly token: number, - protected readonly task: TaskConfiguration, + public readonly token: number, + public readonly task: TaskConfiguration, protected taskService: TaskService, protected isMulti: boolean, - public readonly options: QuickOpenGroupItemOptions, protected readonly taskDefinitionRegistry: TaskDefinitionRegistry, protected readonly taskNameResolver: TaskNameResolver, - protected readonly taskSourceResolver: TaskSourceResolver - ) { - super(options); - } - - getTask(): TaskConfiguration { - return this.task; - } + protected readonly taskSourceResolver: TaskSourceResolver, + protected taskConfigurationManager: TaskConfigurationManager, + public readonly buttons?: Array + ) { } - getLabel(): string { + get label(): string { return this.taskNameResolver.resolve(this.task); } - getToken(): number { - return this.token; + get description(): string { + return renderScope(this.task._scope, this.isMulti); } - getGroupLabel(): string { - return this.options.groupLabel || ''; + get detail(): string | undefined { + return this.task.detail; } - getDescription(): string { - return this.options.description || renderScope(this.task._scope, this.isMulti); + accept(): void { + this.execute(); } - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - + execute(): void { const scope = this.task._scope; if (this.taskDefinitionRegistry && !!this.taskDefinitionRegistry.getDefinition(this.task)) { this.taskService.run(this.token, this.task.source || this.task._source, this.task.label, scope); } else { this.taskService.run(this.token, this.task._source, this.task.label, scope); } - return true; } - getDetail(): string | undefined { - return this.task.detail; + trigger(): monaco.quickInput.TriggerAction { + if (this.task._scope) { + this.taskConfigurationManager.openConfiguration(this.task._scope); + } + return monaco.quickInput.TriggerAction.CLOSE_PICKER; } } export class ConfigureBuildOrTestTaskQuickOpenItem extends TaskRunQuickOpenItem { constructor( - protected readonly token: number, - protected readonly task: TaskConfiguration, + public readonly token: number, + public readonly task: TaskConfiguration, protected taskService: TaskService, protected isMulti: boolean, - public readonly options: QuickOpenGroupItemOptions, protected readonly taskNameResolver: TaskNameResolver, protected readonly isBuildTask: boolean, protected taskConfigurationManager: TaskConfigurationManager, protected readonly taskDefinitionRegistry: TaskDefinitionRegistry, protected readonly taskSourceResolver: TaskSourceResolver ) { - super(token, task, taskService, isMulti, options, taskDefinitionRegistry, taskNameResolver, taskSourceResolver); + super(token, task, taskService, isMulti, taskDefinitionRegistry, taskNameResolver, taskSourceResolver, taskConfigurationManager); + } + accept(): void { + this.execute(); } - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } + execute(): void { this.taskService.updateTaskConfiguration(this.token, this.task, { group: { kind: this.isBuildTask ? 'build' : 'test', isDefault: true } }) .then(() => { if (this.task._scope) { this.taskConfigurationManager.openConfiguration(this.task._scope); } }); - return true; } } @@ -562,10 +443,9 @@ function renderScope(scope: TaskConfigurationScope, isMulti: boolean): string { } else { return TaskScope[scope]; } - } -export class TaskConfigureQuickOpenItem extends QuickOpenGroupItem { +export class TaskConfigureQuickOpenItem implements monaco.quickInput.IAnythingQuickPickItem { protected taskDefinitionRegistry: TaskDefinitionRegistry; @@ -575,44 +455,37 @@ export class TaskConfigureQuickOpenItem extends QuickOpenGroupItem { protected readonly taskService: TaskService, protected readonly taskNameResolver: TaskNameResolver, protected readonly workspaceService: WorkspaceService, - protected readonly isMulti: boolean, - protected readonly options: QuickOpenGroupItemOptions + protected readonly isMulti: boolean ) { - super(options); const stat = this.workspaceService.workspace; this.isMulti = stat ? !stat.isDirectory : false; } - getLabel(): string { + get label(): string { return this.taskNameResolver.resolve(this.task); } - getGroupLabel(): string { - return this.options.groupLabel || ''; + get description(): string { + return renderScope(this.task._scope, this.isMulti); } - getDescription(): string { - return renderScope(this.task._scope, this.isMulti); + accept(): void { + this.execute(); } - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN) { - return false; - } + execute(): void { this.taskService.configure(this.token, this.task); - - return true; } } @injectable() -export class TaskTerminateQuickOpen implements QuickOpenModel { +export class TaskTerminateQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @@ -629,14 +502,13 @@ export class TaskTerminateQuickOpen implements QuickOpenModel { @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - async onType(_lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - const items: QuickOpenItem[] = []; + async getItems(): Promise> { + const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { - items.push(new QuickOpenItem({ + items.push(({ label: 'No task is currently running', - run: (): boolean => false, })); } else { runningTasks.forEach((task: TaskInfo) => { @@ -648,53 +520,36 @@ export class TaskTerminateQuickOpen implements QuickOpenModel { this.taskDefinitionRegistry, this.labelProvider, isMulti, - { - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.taskService.kill(task.taskId); - return true; - } - }, + () => this.taskService.kill(task.taskId) )); }); if (runningTasks.length > 1) { - items.push(new QuickOpenItem({ + items.push(({ label: 'All running tasks', - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } + execute: () => { runningTasks.forEach((t: TaskInfo) => { this.taskService.kill(t.taskId); }); - return true; } })); } } - acceptor(items); + return items; } async open(): Promise { - this.quickOpenService.open(this, { - placeholder: 'Select task to terminate', - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, - }); + const items = await this.getItems(); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select task to terminate' }); } - } @injectable() -export class TaskRunningQuickOpen implements QuickOpenModel { - +export class TaskRunningQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @@ -714,14 +569,13 @@ export class TaskRunningQuickOpen implements QuickOpenModel { @inject(TerminalService) protected readonly terminalService: TerminalService; - async onType(_lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - const items: QuickOpenItem[] = []; + async getItems(): Promise> { + const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { - items.push(new QuickOpenItem({ + items.push(({ label: 'No task is currently running', - run: (): boolean => false, })); } else { runningTasks.forEach((task: TaskInfo) => { @@ -733,37 +587,27 @@ export class TaskRunningQuickOpen implements QuickOpenModel { this.taskDefinitionRegistry, this.labelProvider, isMulti, - { - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; + () => { + if (task.terminalId) { + const terminal = this.terminalService.getByTerminalId(task.terminalId); + if (terminal) { + this.terminalService.open(terminal); } - if (task.terminalId) { - const terminal = this.terminalService.getByTerminalId(task.terminalId); - if (terminal) { - this.terminalService.open(terminal); - } - } - return true; } - }, + } )); }); } - acceptor(items); + return items; } async open(): Promise { - this.quickOpenService.open(this, { - placeholder: 'Select the task to show its output', - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, - }); + const items = await this.getItems(); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select the task to show its output' }); } } -export class RunningTaskQuickOpenItem extends QuickOpenItem { - +export class RunningTaskQuickOpenItem implements QuickPickItem { constructor( protected readonly taskInfo: TaskInfo, protected readonly taskService: TaskService, @@ -772,39 +616,29 @@ export class RunningTaskQuickOpenItem extends QuickOpenItem { protected readonly taskDefinitionRegistry: TaskDefinitionRegistry, protected readonly labelProvider: LabelProvider, protected readonly isMulti: boolean, - public readonly options: QuickOpenGroupItemOptions, - ) { - super(options); - } + public readonly execute: () => void, + ) { } - getLabel(): string { + get label(): string { return this.taskNameResolver.resolve(this.taskInfo.config); } - getDescription(): string { + get description(): string { return renderScope(this.taskInfo.config._scope, this.isMulti); } - run(mode: QuickOpenMode): boolean { - if (mode !== QuickOpenMode.OPEN || !this.options.run) { - return false; - } - return this.options.run(mode); - } - - getDetail(): string | undefined { + get detail(): string | undefined { return this.taskInfo.config.detail; } } @injectable() -export class TaskRestartRunningQuickOpen implements QuickOpenModel { - +export class TaskRestartRunningQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - @inject(QuickOpenService) - protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @@ -821,15 +655,14 @@ export class TaskRestartRunningQuickOpen implements QuickOpenModel { @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - async onType(_lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { - const items = []; + async getItems(): Promise> { + const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { - items.push(new QuickOpenItem({ - label: 'No task to restart', - run: (): boolean => false, - })); + items.push({ + label: 'No task to restart' + }); } else { runningTasks.forEach((task: TaskInfo) => { items.push(new RunningTaskQuickOpenItem( @@ -840,26 +673,39 @@ export class TaskRestartRunningQuickOpen implements QuickOpenModel { this.taskDefinitionRegistry, this.labelProvider, isMulti, - { - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.taskService.restartTask(task); - return true; - } - }, + () => this.taskService.restartTask(task) )); }); } - acceptor(items); + return items; } async open(): Promise { - this.quickOpenService.open(this, { - placeholder: 'Select task to restart', - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, + const items = await this.getItems(); + this.quickInputService?.showQuickPick(items, { placeholder: 'Select task to restart' }); + } +} + +export class TaskQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = 'task '; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching tasks' + }; + + constructor() { + super(TaskQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: TaskQuickAccessProvider.NO_RESULTS_PICK }); } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return TaskQuickAccessProvider.dataService?.getPicks(filter, token); + } } diff --git a/packages/task/src/browser/task-configuration-manager.ts b/packages/task/src/browser/task-configuration-manager.ts index cb14480572006..fadf18f5c3214 100644 --- a/packages/task/src/browser/task-configuration-manager.ts +++ b/packages/task/src/browser/task-configuration-manager.ts @@ -51,7 +51,7 @@ export class TaskConfigurationManager { protected readonly editorManager: EditorManager; @inject(QuickPickService) - protected readonly quickPick: QuickPickService; + protected readonly quickPickService: QuickPickService; @inject(FileService) protected readonly fileService: FileService; @@ -215,11 +215,11 @@ export class TaskConfigurationManager { } protected async getInitialConfigurationContent(): Promise { - const selected = await this.quickPick.show(this.taskTemplateSelector.selectTemplates(), { + const selected = await this.quickPickService.show(this.taskTemplateSelector.selectTemplates(), { placeholder: 'Select a Task Template' }); if (selected) { - return selected.content; + return selected.value?.content; } } diff --git a/packages/task/src/browser/task-frontend-contribution.ts b/packages/task/src/browser/task-frontend-contribution.ts index a885e27aa4aca..e5bac81082bf2 100644 --- a/packages/task/src/browser/task-frontend-contribution.ts +++ b/packages/task/src/browser/task-frontend-contribution.ts @@ -19,8 +19,8 @@ import { ILogger, ContributionProvider } from '@theia/core/lib/common'; import { QuickOpenTask, TaskTerminateQuickOpen, TaskRunningQuickOpen, TaskRestartRunningQuickOpen } from './quick-open-task'; import { CommandContribution, Command, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core/lib/common'; import { - FrontendApplication, FrontendApplicationContribution, QuickOpenContribution, - QuickOpenHandlerRegistry, KeybindingRegistry, KeybindingContribution, StorageService, StatusBar, StatusBarAlignment + FrontendApplication, FrontendApplicationContribution, QuickAccessContribution, + KeybindingRegistry, KeybindingContribution, StorageService, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { TaskContribution, TaskResolverRegistry, TaskProviderRegistry } from './task-contribution'; @@ -114,7 +114,7 @@ export namespace TaskCommands { const TASKS_STORAGE_KEY = 'tasks'; @injectable() -export class TaskFrontendContribution implements CommandContribution, MenuContribution, KeybindingContribution, FrontendApplicationContribution, QuickOpenContribution { +export class TaskFrontendContribution implements CommandContribution, MenuContribution, KeybindingContribution, FrontendApplicationContribution, QuickAccessContribution { @inject(QuickOpenTask) protected readonly quickOpenTask: QuickOpenTask; @@ -384,8 +384,8 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri }); } - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this.quickOpenTask); + registerQuickAccessProvider(): void { + this.quickOpenTask.registerQuickAccessProvider(); } registerKeybindings(keybindings: KeybindingRegistry): void { @@ -395,5 +395,4 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri when: '!textInputFocus || editorReadonly' }); } - } diff --git a/packages/task/src/browser/task-frontend-module.ts b/packages/task/src/browser/task-frontend-module.ts index 879da6d0a27b3..56009843b4431 100644 --- a/packages/task/src/browser/task-frontend-module.ts +++ b/packages/task/src/browser/task-frontend-module.ts @@ -15,10 +15,10 @@ ********************************************************************************/ import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, QuickOpenContribution, KeybindingContribution } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, KeybindingContribution } from '@theia/core/lib/browser'; import { CommandContribution, MenuContribution, bindContributionProvider } from '@theia/core/lib/common'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging'; -import { QuickOpenTask, TaskTerminateQuickOpen, TaskRestartRunningQuickOpen, TaskRunningQuickOpen, TaskActionProvider, ConfigureTaskAction } from './quick-open-task'; +import { QuickOpenTask, TaskTerminateQuickOpen, TaskRestartRunningQuickOpen, TaskRunningQuickOpen } from './quick-open-task'; import { TaskContribution, TaskProviderRegistry, TaskResolverRegistry } from './task-contribution'; import { TaskService } from './task-service'; import { TaskConfigurations } from './task-configurations'; @@ -41,14 +41,13 @@ import { TaskSourceResolver } from './task-source-resolver'; import { TaskTemplateSelector } from './task-templates'; import { TaskTerminalWidgetManager } from './task-terminal-widget-manager'; import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; export default new ContainerModule(bind => { bind(TaskFrontendContribution).toSelf().inSingletonScope(); bind(TaskService).toSelf().inSingletonScope(); - bind(TaskActionProvider).toSelf().inSingletonScope(); - bind(ConfigureTaskAction).toSelf().inSingletonScope(); - for (const identifier of [FrontendApplicationContribution, CommandContribution, KeybindingContribution, MenuContribution, QuickOpenContribution]) { + for (const identifier of [FrontendApplicationContribution, CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution]) { bind(identifier).toService(TaskFrontendContribution); } diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts index 07cd32438f572..bec3e0b10b1e8 100644 --- a/packages/task/src/browser/task-service.ts +++ b/packages/task/src/browser/task-service.ts @@ -14,12 +14,12 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { ApplicationShell, FrontendApplication, WidgetManager, WidgetOpenMode } from '@theia/core/lib/browser'; +import { ApplicationShell, FrontendApplication, QuickPickItem, QuickPickValue, WidgetManager, WidgetOpenMode } from '@theia/core/lib/browser'; import { open, OpenerService } from '@theia/core/lib/browser/opener-service'; import { CommandService, ILogger } from '@theia/core/lib/common'; import { MessageService } from '@theia/core/lib/common/message-service'; import { Deferred } from '@theia/core/lib/common/promise-util'; -import { QuickPickItem, QuickPickService } from '@theia/core/lib/common/quick-pick-service'; +import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import URI from '@theia/core/lib/common/uri'; import { EditorManager } from '@theia/editor/lib/browser'; @@ -152,7 +152,7 @@ export class TaskService implements TaskConfigurationClient { protected readonly problemMatcherRegistry: ProblemMatcherRegistry; @inject(QuickPickService) - protected readonly quickPick: QuickPickService; + protected readonly quickPickService: QuickPickService; @inject(OpenerService) protected readonly openerService: OpenerService; @@ -526,22 +526,22 @@ export class TaskService implements TaskConfigurationClient { if (!customizationObject.problemMatcher) { // ask the user what s/he wants to use to parse the task output const items = this.getCustomizeProblemMatcherItems(); - const selected = await this.quickPick.show(items, { + const selected = await this.quickPickService.show(items, { placeholder: 'Select for which kind of errors and warnings to scan the task output' }); - if (selected) { - if (selected.problemMatchers) { + if (selected && ('value' in selected)) { + if (selected.value?.problemMatchers) { let matcherNames: string[] = []; - if (selected.problemMatchers && selected.problemMatchers.length === 0) { // never parse output for this task + if (selected.value.problemMatchers && selected.value.problemMatchers.length === 0) { // never parse output for this task matcherNames = []; - } else if (selected.problemMatchers && selected.problemMatchers.length > 0) { // continue with user-selected parser - matcherNames = selected.problemMatchers.map(matcher => matcher.name); + } else if (selected.value.problemMatchers && selected.value.problemMatchers.length > 0) { // continue with user-selected parser + matcherNames = selected.value.problemMatchers.map(matcher => matcher.name); } customizationObject.problemMatcher = matcherNames; // write the selected matcher (or the decision of "never parse") into the `tasks.json` this.updateTaskConfiguration(token, task, { problemMatcher: matcherNames }); - } else if (selected.learnMore) { // user wants to learn more about parsing task output + } else if (selected.value?.learnMore) { // user wants to learn more about parsing task output open(this.openerService, new URI('https://code.visualstudio.com/docs/editor/tasks#_processing-task-output-with-problem-matchers')); } // else, continue the task with no parser @@ -982,8 +982,8 @@ export class TaskService implements TaskConfigurationClient { } } - protected getCustomizeProblemMatcherItems(): QuickPickItem[] { - const items: QuickPickItem[] = []; + protected getCustomizeProblemMatcherItems(): Array | QuickPickItem> { + const items: Array | QuickPickItem> = []; items.push({ label: 'Continue without scanning the task output', value: { problemMatchers: undefined } diff --git a/packages/task/src/browser/task-templates.ts b/packages/task/src/browser/task-templates.ts index 44eb92699d0bd..9e7ff7ca4974c 100644 --- a/packages/task/src/browser/task-templates.ts +++ b/packages/task/src/browser/task-templates.ts @@ -19,7 +19,7 @@ *--------------------------------------------------------------------------------------------*/ import { injectable } from '@theia/core/shared/inversify'; -import { QuickPickItem } from '@theia/core/lib/common/quick-pick-service'; +import { QuickPickValue } from '@theia/core/lib/browser'; /** The representation of a task template used in the auto-generation of `tasks.json` */ export interface TaskTemplateEntry { @@ -152,7 +152,7 @@ const command: TaskTemplateEntry = { @injectable() export class TaskTemplateSelector { - selectTemplates(): QuickPickItem[] { + selectTemplates(): QuickPickValue[] { const templates: TaskTemplateEntry[] = [ dotnetBuild, msbuild, maven ].sort((a, b) => diff --git a/packages/terminal/compile.tsconfig.json b/packages/terminal/compile.tsconfig.json index 208e509b8dc1d..76072a3950991 100644 --- a/packages/terminal/compile.tsconfig.json +++ b/packages/terminal/compile.tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../workspace/compile.tsconfig.json" + }, + { + "path": "../monaco/compile.tsconfig.json" } ] } diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 7d2b34ad7489f..0ffd7ca94cbf7 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -6,6 +6,7 @@ "@theia/core": "1.13.0", "@theia/editor": "1.13.0", "@theia/filesystem": "1.13.0", + "@theia/monaco": "1.13.0", "@theia/process": "1.13.0", "@theia/workspace": "1.13.0", "xterm": "~4.11.0", diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index 0de9bcecfb331..30a0c11382aef 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; import { CommandContribution, Command, @@ -27,10 +27,9 @@ import { Emitter, Event } from '@theia/core/lib/common'; -import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { ApplicationShell, KeybindingContribution, KeyCode, Key, - KeybindingRegistry, Widget, LabelProvider, WidgetOpenerOptions, StorageService + KeybindingRegistry, Widget, LabelProvider, WidgetOpenerOptions, StorageService, QuickInputService } from '@theia/core/lib/browser'; import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { WidgetManager } from '@theia/core/lib/browser'; @@ -150,8 +149,8 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - @inject(QuickPickService) - protected readonly quickPick: QuickPickService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -298,17 +297,14 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon isEnabled: widget => !!this.getTerminalRef(widget), isVisible: widget => !!this.getTerminalRef(widget) }); - commands.registerCommand(TerminalCommands.TERMINAL_CLEAR); commands.registerHandler(TerminalCommands.TERMINAL_CLEAR.id, { isEnabled: () => this.shell.activeWidget instanceof TerminalWidget, execute: () => (this.shell.activeWidget as TerminalWidget).clearOutput() }); - commands.registerCommand(TerminalCommands.TERMINAL_CONTEXT, UriAwareCommandHandler.MonoSelect(this.selectionService, { execute: uri => this.openInTerminal(uri) })); - commands.registerCommand(TerminalCommands.TERMINAL_FIND_TEXT); commands.registerHandler(TerminalCommands.TERMINAL_FIND_TEXT.id, { isEnabled: () => { @@ -337,7 +333,6 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon terminalSearchBox.hide(); } }); - commands.registerCommand(TerminalCommands.SCROLL_LINE_UP, { isEnabled: () => this.shell.activeWidget instanceof TerminalWidget, isVisible: () => false, @@ -422,7 +417,6 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon registerKeybindings(keybindings: KeybindingRegistry): void { /* Register passthrough keybindings for combinations recognized by xterm.js and converted to control characters. - See: https://github.com/xtermjs/xterm.js/blob/v3/src/Terminal.ts#L1684 */ /* Register ctrl + k (the passed Key) as a passthrough command in the @@ -587,10 +581,24 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon } protected async selectTerminalCwd(): Promise { - const roots = this.workspaceService.tryGetRoots(); - return this.quickPick.show(roots.map( - ({ resource }) => ({ label: this.labelProvider.getName(resource), description: this.labelProvider.getLongName(resource), value: resource.toString() }) - ), { placeholder: 'Select current working directory for new terminal' }); + return new Promise(async resolve => { + const roots = this.workspaceService.tryGetRoots(); + if (roots.length === 0) { + resolve(undefined); + } else if (roots.length === 1) { + resolve(roots[0].resource.toString()); + } else { + const items = roots.map(({ resource }) => ({ + label: this.labelProvider.getName(resource), + description: this.labelProvider.getLongName(resource), + resource + })); + const selectedItem = await this.quickInputService?.showQuickPick(items, { + placeholder: 'Select current working directory for new terminal' + }); + resolve(selectedItem?.resource?.toString()); + } + }); } protected async splitTerminal(widget?: Widget): Promise { @@ -677,5 +685,4 @@ export class TerminalFrontendContribution implements TerminalService, CommandCon }); } } - } diff --git a/packages/terminal/src/browser/terminal-frontend-module.ts b/packages/terminal/src/browser/terminal-frontend-module.ts index c8a5d328ea162..bd2ef52b6c720 100644 --- a/packages/terminal/src/browser/terminal-frontend-module.ts +++ b/packages/terminal/src/browser/terminal-frontend-module.ts @@ -20,7 +20,7 @@ import 'xterm/css/xterm.css'; import { ContainerModule, Container } from '@theia/core/shared/inversify'; import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; import { bindContributionProvider } from '@theia/core'; -import { KeybindingContribution, WebSocketConnectionProvider, WidgetFactory, KeybindingContext, QuickOpenContribution } from '@theia/core/lib/browser'; +import { KeybindingContribution, WebSocketConnectionProvider, WidgetFactory, KeybindingContext } from '@theia/core/lib/browser'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { TerminalFrontendContribution } from './terminal-frontend-contribution'; import { TerminalWidgetImpl, TERMINAL_WIDGET_FACTORY_ID } from './terminal-widget-impl'; @@ -42,6 +42,7 @@ import { createTerminalSearchFactory } from './search/terminal-search-container' import { TerminalCopyOnSelectionHandler } from './terminal-copy-on-selection-handler'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { TerminalThemeService } from './terminal-theme-service'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; export default new ContainerModule(bind => { bindTerminalPreferences(bind); @@ -78,7 +79,7 @@ export default new ContainerModule(bind => { bind(TerminalCopyOnSelectionHandler).toSelf().inSingletonScope(); bind(TerminalQuickOpenContribution).toSelf().inSingletonScope(); - for (const identifier of [CommandContribution, QuickOpenContribution]) { + for (const identifier of [CommandContribution, QuickAccessContribution]) { bind(identifier).toService(TerminalQuickOpenContribution); } diff --git a/packages/terminal/src/browser/terminal-quick-open-service.ts b/packages/terminal/src/browser/terminal-quick-open-service.ts index 2d683ff9d4920..e5d63cf508291 100644 --- a/packages/terminal/src/browser/terminal-quick-open-service.ts +++ b/packages/terminal/src/browser/terminal-quick-open-service.ts @@ -14,23 +14,22 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { - QuickOpenModel, QuickOpenGroupItem, QuickOpenHandler, - QuickOpenOptions, QuickOpenItemOptions, QuickOpenMode, - PrefixQuickOpenService, - QuickOpenContribution, QuickOpenHandlerRegistry, QuickOpenGroupItemOptions + QuickAccessContribution, + QuickInputService } from '@theia/core/lib/browser'; import { CommandContribution, CommandRegistry, CommandService } from '@theia/core/lib/common'; import { TerminalWidget } from './base/terminal-widget'; import { TerminalService } from './base/terminal-service'; import { TerminalCommands } from './terminal-frontend-contribution'; +import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; @injectable() -export class TerminalQuickOpenService implements QuickOpenModel, QuickOpenHandler { +export class TerminalQuickOpenService implements monaco.quickInput.IQuickAccessDataService { - @inject(PrefixQuickOpenService) - protected readonly prefixQuickOpenService: PrefixQuickOpenService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; @inject(CommandService) protected readonly commandService: CommandService; @@ -38,54 +37,38 @@ export class TerminalQuickOpenService implements QuickOpenModel, QuickOpenHandle @inject(TerminalService) protected readonly terminalService: TerminalService; - readonly prefix: string = 'term '; - - get description(): string { - return 'Show All Opened Terminals'; - } - - getModel(): QuickOpenModel { - return this; - } - - getOptions(): QuickOpenOptions { - return { - fuzzyMatchLabel: { - enableSeparateSubstringMatching: true - }, - fuzzyMatchDescription: { - enableSeparateSubstringMatching: true - } - }; - } - open(): void { - this.prefixQuickOpenService.open(this.prefix); + this.quickInputService?.open(TerminalQuickAccessProvider.PREFIX); } - async onType(lookFor: string, acceptor: (items: QuickOpenGroupItem[]) => void): Promise { - const terminalItems: QuickOpenGroupItem[] = []; + async getPicks(filter: string, token: monaco.CancellationToken): Promise> { + const items: Array = []; // Get the sorted list of currently opened terminal widgets const widgets: TerminalWidget[] = this.terminalService.all .sort((a: TerminalWidget, b: TerminalWidget) => this.compareItems(a, b)); for (const widget of widgets) { - const item = await this.toItem(widget); - terminalItems.push(item); + items.push(this.toItem(widget)); } // Append a quick open item to create a new terminal. - const createNewTerminalItem = new QuickOpenGroupItem({ + items.push({ label: 'Open New Terminal', - iconClass: 'fa fa-plus', - run: this.doCreateNewTerminal(), - groupLabel: undefined, - showBorder: !!terminalItems.length + iconClasses: ['fa fa-plus'], + accept: () => this.doCreateNewTerminal() }); - terminalItems.push(createNewTerminalItem); - acceptor(terminalItems); - return; + return filterItems(items, filter); + } + + registerQuickAccessProvider(): void { + monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ + ctor: TerminalQuickAccessProvider, + prefix: TerminalQuickAccessProvider.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Show All Opened Terminals', needsEditor: false }] + }); + TerminalQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } /** @@ -103,51 +86,21 @@ export class TerminalQuickOpenService implements QuickOpenModel, QuickOpenHandle } } - /** - * Get the function that can create a new terminal. - * @param {TerminalWidget} widget - the terminal widget to be opened. - * @returns Function that would create a new terminal if mode === QuickOpenMode.OPEN. - */ - protected doCreateNewTerminal(): (mode: QuickOpenMode) => boolean { - return (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.commandService.executeCommand(TerminalCommands.NEW.id); - return true; - }; + protected doCreateNewTerminal(): void { + this.commandService.executeCommand(TerminalCommands.NEW.id); } /** - * Convert the terminal widget to the quick open item. + * Convert the terminal widget to the quick pick item. * @param {TerminalWidget} widget - the terminal widget. - * @returns The quick open group item. + * @returns quick pick item. */ - protected async toItem(widget: TerminalWidget): Promise> { - const options: QuickOpenGroupItemOptions = { + protected toItem(widget: TerminalWidget): monaco.quickInput.IAnythingQuickPickItem { + return { label: widget.title.label, description: widget.id, - tooltip: widget.title.label, - hidden: false, - run: this.getRunFunction(widget), - groupLabel: undefined, - showBorder: false - }; - return new QuickOpenGroupItem(options); - } - - /** - * Get the function that can open the editor file. - * @param {TerminalWidget} widget - the terminal widget to be opened. - * @returns Function that would open the terminal if mode === QuickOpenMode.OPEN. - */ - protected getRunFunction(widget: TerminalWidget): (mode: QuickOpenMode) => boolean { - return (mode: QuickOpenMode) => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } - this.terminalService.open(widget); - return true; + ariaLabel: widget.title.label, + accept: () => this.terminalService.open(widget) }; } } @@ -156,13 +109,13 @@ export class TerminalQuickOpenService implements QuickOpenModel, QuickOpenHandle * TODO: merge it to TerminalFrontendContribution. */ @injectable() -export class TerminalQuickOpenContribution implements CommandContribution, QuickOpenContribution { +export class TerminalQuickOpenContribution implements CommandContribution, QuickAccessContribution { @inject(TerminalQuickOpenService) protected readonly terminalQuickOpenService: TerminalQuickOpenService; - registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void { - handlers.registerHandler(this.terminalQuickOpenService); + registerQuickAccessProvider(): void { + this.terminalQuickOpenService.registerQuickAccessProvider(); } registerCommands(commands: CommandRegistry): void { @@ -171,3 +124,27 @@ export class TerminalQuickOpenContribution implements CommandContribution, Quick }); } } + +export class TerminalQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + static PREFIX = 'term '; + static dataService: monaco.quickInput.IQuickAccessDataService; + + private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { + label: 'No matching results' + }; + + constructor() { + super(TerminalQuickAccessProvider.PREFIX, { + canAcceptInBackground: true, + noResultsPick: TerminalQuickAccessProvider.NO_RESULTS_PICK + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks + | Promise> + | monaco.quickInput.FastAndSlowPicks + | null { + return TerminalQuickAccessProvider.dataService?.getPicks(filter, token); + } +} diff --git a/packages/terminal/src/browser/terminal-theme-service.ts b/packages/terminal/src/browser/terminal-theme-service.ts index a14ab55ebc85f..4eca584cbf266 100644 --- a/packages/terminal/src/browser/terminal-theme-service.ts +++ b/packages/terminal/src/browser/terminal-theme-service.ts @@ -159,7 +159,7 @@ export class TerminalThemeService { @inject(ColorRegistry) protected readonly colorRegistry: ColorRegistry; - readonly onDidChange = ThemeService.get().onThemeChange; + readonly onDidChange = ThemeService.get().onDidColorThemeChange; get theme(): ITheme { const foregroundColor = this.colorRegistry.getCurrentColor('terminal.foreground'); diff --git a/packages/task/src/browser/task-action-provider.ts b/packages/terminal/src/common/monaco.d.ts similarity index 84% rename from packages/task/src/browser/task-action-provider.ts rename to packages/terminal/src/common/monaco.d.ts index 8882a0f889259..e83d9aa19bf14 100644 --- a/packages/task/src/browser/task-action-provider.ts +++ b/packages/terminal/src/common/monaco.d.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2019 Red Hat, Inc. and others. + * Copyright (C) 2018 TypeFox 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 @@ -14,4 +14,5 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -export { ConfigureTaskAction, TaskActionProvider } from './quick-open-task'; +// eslint-disable-next-line spaced-comment +/// diff --git a/packages/variable-resolver/src/browser/common-variable-contribution.ts b/packages/variable-resolver/src/browser/common-variable-contribution.ts index 3d90c28fd2dcd..11a3cabb53624 100644 --- a/packages/variable-resolver/src/browser/common-variable-contribution.ts +++ b/packages/variable-resolver/src/browser/common-variable-contribution.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { VariableContribution, VariableRegistry } from './variable'; import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; @@ -23,8 +23,7 @@ import { OS } from '@theia/core/lib/common/os'; import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { ResourceContextKey } from '@theia/core/lib/browser/resource-context-key'; import { VariableInput } from './variable-input'; -import { QuickInputService } from '@theia/core/lib/browser/quick-open/quick-input-service'; -import { QuickPickService, QuickPickItem } from '@theia/core/lib/common/quick-pick-service'; +import { QuickInputService, QuickPickValue } from '@theia/core/lib/browser'; import { MaybeArray, RecursivePartial } from '@theia/core/lib/common/types'; @injectable() @@ -42,12 +41,9 @@ export class CommonVariableContribution implements VariableContribution { @inject(ResourceContextKey) protected readonly resourceContextKey: ResourceContextKey; - @inject(QuickInputService) + @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; - @inject(QuickPickService) - protected readonly quickPickService: QuickPickService; - @inject(ApplicationServer) protected readonly appServer: ApplicationServer; @@ -103,7 +99,7 @@ export class CommonVariableContribution implements VariableContribution { if (typeof input.description !== 'string') { return undefined; } - return this.quickInputService.open({ + return this.quickInputService?.input({ prompt: input.description, value: input.default }); @@ -112,7 +108,7 @@ export class CommonVariableContribution implements VariableContribution { if (typeof input.description !== 'string' || !Array.isArray(input.options)) { return undefined; } - const elements: QuickPickItem[] = []; + const elements: Array> = []; for (const option of input.options) { if (typeof option !== 'string') { return undefined; @@ -130,7 +126,7 @@ export class CommonVariableContribution implements VariableContribution { }); } } - return this.quickPickService.show(elements, { placeholder: input.description }); + return this.quickInputService?.showQuickPick(elements, { placeholder: input.description }); } if (input.type === 'command') { if (typeof input.command !== 'string') { @@ -142,5 +138,4 @@ export class CommonVariableContribution implements VariableContribution { } }); } - } diff --git a/packages/variable-resolver/src/browser/variable-quick-open-service.ts b/packages/variable-resolver/src/browser/variable-quick-open-service.ts index 143948c14ac0b..74790bff3e9c5 100644 --- a/packages/variable-resolver/src/browser/variable-quick-open-service.ts +++ b/packages/variable-resolver/src/browser/variable-quick-open-service.ts @@ -14,60 +14,44 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { MessageService } from '@theia/core/lib/common/message-service'; -import { QuickOpenModel, QuickOpenItem, QuickOpenMode } from '@theia/core/lib/common/quick-open-model'; -import { QuickOpenService } from '@theia/core/lib/browser/quick-open/quick-open-service'; -import { QuickInputService } from '@theia/core/lib/browser/quick-open/quick-input-service'; import { VariableRegistry, Variable } from './variable'; import { VariableResolverService } from './variable-resolver-service'; +import { QuickPickItem, QuickInputService } from '@theia/core/lib/browser'; @injectable() -export class VariableQuickOpenService implements QuickOpenModel { +export class VariableQuickOpenService { - protected items: QuickOpenItem[]; + protected items: Array; @inject(MessageService) protected readonly messages: MessageService; - @inject(QuickInputService) + @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(VariableResolverService) protected readonly variableResolver: VariableResolverService; constructor( - @inject(VariableRegistry) protected readonly variableRegistry: VariableRegistry, - @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService + @inject(VariableRegistry) protected readonly variableRegistry: VariableRegistry ) { } open(): void { - this.items = this.variableRegistry.getVariables().map(v => new QuickOpenItem({ + this.items = this.variableRegistry.getVariables().map(v => ({ label: '${' + v.name + '}', detail: v.description, - run: mode => { - if (mode === QuickOpenMode.OPEN) { - setTimeout(() => this.showValue(v)); - return true; - } - return false; + execute: () => { + setTimeout(() => this.showValue(v)); } })); - this.quickOpenService.open(this, { - placeholder: 'Registered variables', - fuzzyMatchLabel: true, - fuzzyMatchDescription: true, - fuzzySort: true - }); - } - - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(this.items); + this.quickInputService?.showQuickPick(this.items, { placeholder: 'Registered variables' }); } protected async showValue(variable: Variable): Promise { - const argument = await this.quickInputService.open({ + const argument = await this.quickInputService?.input({ placeHolder: 'Type a variable argument' }); const value = await this.variableResolver.resolve('${' + variable.name + ':' + argument + '}'); @@ -75,5 +59,4 @@ export class VariableQuickOpenService implements QuickOpenModel { this.messages.info(value); } } - } diff --git a/packages/variable-resolver/src/browser/variable-resolver-frontend-contribution.spec.ts b/packages/variable-resolver/src/browser/variable-resolver-frontend-contribution.spec.ts index b30348ae7818f..75ecc63a7c33e 100644 --- a/packages/variable-resolver/src/browser/variable-resolver-frontend-contribution.spec.ts +++ b/packages/variable-resolver/src/browser/variable-resolver-frontend-contribution.spec.ts @@ -18,6 +18,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; + +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config, +}); import * as chai from 'chai'; import { Container, ContainerModule } from '@theia/core/shared/inversify'; import { ILogger, bindContributionProvider } from '@theia/core/lib/common'; diff --git a/packages/workspace/src/browser/quick-open-workspace.ts b/packages/workspace/src/browser/quick-open-workspace.ts index 31357a6da9dde..a50bb0e84e01e 100644 --- a/packages/workspace/src/browser/quick-open-workspace.ts +++ b/packages/workspace/src/browser/quick-open-workspace.ts @@ -14,8 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { QuickOpenService, QuickOpenModel, QuickOpenItem, QuickOpenGroupItem, QuickOpenMode, LabelProvider } from '@theia/core/lib/browser'; +import { injectable, inject, optional } from '@theia/core/shared/inversify'; +import { QuickPickItem, LabelProvider, QuickInputService } from '@theia/core/lib/browser'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { WorkspaceService } from './workspace-service'; import { WorkspacePreferences } from './workspace-preferences'; @@ -26,12 +26,11 @@ import { FileStat } from '@theia/filesystem/lib/common/files'; import { Path } from '@theia/core/lib/common'; @injectable() -export class QuickOpenWorkspace implements QuickOpenModel { - - protected items: QuickOpenGroupItem[]; +export class QuickOpenWorkspace { + protected items: Array; protected opened: boolean; - @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; + @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(FileService) protected readonly fileService: FileService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @@ -47,10 +46,9 @@ export class QuickOpenWorkspace implements QuickOpenModel { const home = new URI(homeDirUri).path.toString(); await this.preferences.ready; if (!workspaces.length) { - this.items.push(new QuickOpenGroupItem({ - label: 'No Recent Workspaces', - run: (mode: QuickOpenMode): boolean => false - })); + this.items.push({ + label: 'No Recent Workspaces' + }); } for (const workspace of workspaces) { const uri = new URI(workspace); @@ -66,35 +64,24 @@ export class QuickOpenWorkspace implements QuickOpenModel { continue; // skip the temporary workspace files } const icon = this.labelProvider.getIcon(stat); - const iconClass = icon === '' ? undefined : icon + ' file-icon'; - this.items.push(new QuickOpenGroupItem({ + const iconClasses = icon === '' ? undefined : [icon + ' file-icon']; + + this.items.push({ + type: 'separator', label: `last modified ${moment(stat.mtime).fromNow()}` + }, { label: uri.path.base, description: Path.tildify(uri.path.toString(), home), - groupLabel: `last modified ${moment(stat.mtime).fromNow()}`, - iconClass, - run: (mode: QuickOpenMode): boolean => { - if (mode !== QuickOpenMode.OPEN) { - return false; - } + iconClasses, + execute: () => { const current = this.workspaceService.workspace; const uriToOpen = new URI(workspace); if ((current && current.resource.toString() !== workspace) || !current) { this.workspaceService.open(uriToOpen); } - return true; }, - })); + }); } - - this.quickOpenService.open(this, { - placeholder: 'Type the name of the workspace you want to open', - fuzzyMatchLabel: true, - fuzzySort: false - }); - } - - onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void { - acceptor(this.items); + this.quickInputService?.showQuickPick(this.items, { placeholder: 'Type the name of the workspace you want to open' }); } select(): void { diff --git a/yarn.lock b/yarn.lock index 79435dabd2680..20040ff10e525 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1149,10 +1149,10 @@ serialize-javascript "^1.4.0" webpack-sources "^1.0.1" -"@theia/monaco-editor-core@^0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@theia/monaco-editor-core/-/monaco-editor-core-0.20.0.tgz#0f3cdfd6d1278bbcc3df0224471fc967a4d901c5" - integrity sha512-6QDOrZRW3dE0RgyD/hXMlVla49ACNjwIX+u9+i/qY+OqaZ1u/QdgdnHy4QO6g4J0lQCyr7nXgqF1BAc+Xbxx2g== +"@theia/monaco-editor-core@^0.23.0-next-82e8ea39fc101d63.2": + version "0.23.0-next-82e8ea39fc101d63.2" + resolved "https://registry.yarnpkg.com/@theia/monaco-editor-core/-/monaco-editor-core-0.23.0-next-82e8ea39fc101d63.2.tgz#776b9476cfd8979e90c1a4839ef184b0022b89f5" + integrity sha512-f/PKEjsOxalwtyKGr+wEovX5I/tvWUxZkTfqh5xjtfjHOIOfy9cXBwcoOjBYQS14RTWowKLmEPVGW1GNkCgrzg== "@theia/node-pty@0.9.0-theia.6": version "0.9.0-theia.6" From 364b8d13eaceee0cbc4685168979aec7d698c01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 27 May 2021 18:17:35 +0200 Subject: [PATCH 2/2] Refactor to hide VS Code interfaces behind core services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder --- .../browser/common-frontend-contribution.ts | 5 +- .../browser/frontend-application-module.ts | 14 +- packages/core/src/browser/index.ts | 2 +- .../core/src/browser/quick-input/index.ts | 4 +- ...access-contribution.ts => quick-access.ts} | 32 ++++ .../quick-command-frontend-contribution.ts | 9 +- .../quick-input/quick-command-service.ts | 94 ++++++++++- .../quick-input/quick-editor-service.ts | 33 ---- .../quick-help-frontend-contribution.ts | 29 ---- .../browser/quick-input/quick-help-service.ts | 77 ++++++++- .../quick-input-frontend-contribution.ts | 2 +- .../quick-input/quick-input-service.ts | 78 ++++----- .../browser/quick-input/quick-view-service.ts | 67 +++++++- .../src/browser/debug-frontend-module.ts | 2 +- .../src/browser/debug-prefix-configuration.ts | 49 ++---- .../editor/src/browser/editor-contribution.ts | 16 +- .../src/browser/editor-frontend-module.ts | 7 +- .../src/browser/quick-editor-service.ts} | 63 +++---- .../browser/file-search-frontend-module.ts | 2 +- .../src/browser/quick-file-open.ts | 154 ++++++------------ .../git/src/browser/git-quick-open-service.ts | 27 +-- packages/monaco/src/browser/monaco-command.ts | 2 +- .../src/browser/monaco-frontend-module.ts | 22 +-- .../browser/monaco-quick-access-registry.ts | 132 +++++++++++++++ .../browser/monaco-quick-command-service.ts | 132 --------------- .../src/browser/monaco-quick-help-service.ts | 106 ------------ .../src/browser/monaco-quick-input-service.ts | 61 +++++-- .../src/browser/monaco-quick-view-service.ts | 97 ----------- .../src/browser/workspace-symbol-command.ts | 55 ++----- packages/monaco/src/typings/monaco/index.d.ts | 25 ++- .../plugin-ext/src/common/plugin-api-rpc.ts | 4 +- .../src/main/browser/quick-open-main.ts | 8 +- packages/task/src/browser/quick-open-task.ts | 55 ++----- .../task/src/browser/task-frontend-module.ts | 2 +- .../src/browser/terminal-frontend-module.ts | 2 +- .../browser/terminal-quick-open-service.ts | 55 ++----- 36 files changed, 688 insertions(+), 836 deletions(-) rename packages/core/src/browser/quick-input/{quick-access-contribution.ts => quick-access.ts} (52%) delete mode 100644 packages/core/src/browser/quick-input/quick-editor-service.ts delete mode 100644 packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts rename packages/{monaco/src/browser/monaco-quick-editor-service.ts => editor/src/browser/quick-editor-service.ts} (53%) create mode 100644 packages/monaco/src/browser/monaco-quick-access-registry.ts delete mode 100644 packages/monaco/src/browser/monaco-quick-command-service.ts delete mode 100644 packages/monaco/src/browser/monaco-quick-help-service.ts delete mode 100644 packages/monaco/src/browser/monaco-quick-view-service.ts diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 3abcb7012c0ab..a800bb0b202a6 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -304,9 +304,6 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(StorageService) protected readonly storageService: StorageService; - @inject(QuickViewService) @optional() - protected readonly quickView: QuickViewService; - @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @@ -762,7 +759,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }); commandRegistry.registerCommand(CommonCommands.OPEN_VIEW, { - execute: () => this.quickInputService?.open(this.quickView?.prefix) + execute: () => this.quickInputService?.open(QuickViewService.PREFIX) }); commandRegistry.registerCommand(CommonCommands.SELECT_COLOR_THEME, { diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 9f0d24f1b6ce7..939a2ac8c92ba 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -91,13 +91,14 @@ import { EncodingService } from '../common/encoding-service'; import { AuthenticationService, AuthenticationServiceImpl } from '../browser/authentication-service'; import { DecorationsService, DecorationsServiceImpl } from './decorations-service'; import { QuickCommandFrontendContribution } from './quick-input/quick-command-frontend-contribution'; -import { QuickHelpFrontendContribution } from './quick-input/quick-help-frontend-contribution'; +import { QuickHelpService } from './quick-input/quick-help-service'; import { QuickPickService, quickPickServicePath } from '../common/quick-pick-service'; import { QuickPickServiceImpl, QuickInputFrontendContribution } from './quick-input'; -import { QuickAccessContribution } from './quick-input/quick-access-contribution'; +import { QuickAccessContribution } from './quick-input/quick-access'; +import { QuickCommandService } from './quick-input/quick-command-service'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -225,12 +226,14 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo ); bind(QuickCommandFrontendContribution).toSelf().inSingletonScope(); - [CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution].forEach(serviceIdentifier => + [CommandContribution, KeybindingContribution, MenuContribution].forEach(serviceIdentifier => bind(serviceIdentifier).toService(QuickCommandFrontendContribution) ); + bind(QuickCommandService).toSelf().inSingletonScope(); + bind(QuickAccessContribution).toService(QuickCommandService); - bind(QuickHelpFrontendContribution).toSelf().inSingletonScope(); - bind(QuickAccessContribution).toService(QuickHelpFrontendContribution); + bind(QuickHelpService).toSelf().inSingletonScope(); + bind(QuickAccessContribution).toService(QuickHelpService); bind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope().onActivation(({ container }, quickPickService: QuickPickService) => { WebSocketConnectionProvider.createProxy(container, quickPickServicePath, quickPickService); @@ -311,6 +314,7 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo return container.get(ViewContainer); }); + bind(QuickViewService).toSelf().inSingletonScope(); bind(QuickAccessContribution).toService(QuickViewService); bind(DialogOverlayService).toSelf().inSingletonScope(); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index b6ef3247daea0..937f2e04d4d18 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -26,12 +26,12 @@ export * from './tree'; export * from './messaging'; export * from './endpoint'; export * from './common-frontend-contribution'; -export * from './quick-input'; export * from './widget-manager'; export * from './saveable'; export * from './storage-service'; export * from './preferences'; export * from './keybinding'; +export * from './quick-input'; export * from './status-bar'; export * from './label-provider'; export * from './widget-open-handler'; diff --git a/packages/core/src/browser/quick-input/index.ts b/packages/core/src/browser/quick-input/index.ts index e720b9e53447f..62586db3a949d 100644 --- a/packages/core/src/browser/quick-input/index.ts +++ b/packages/core/src/browser/quick-input/index.ts @@ -15,10 +15,8 @@ ********************************************************************************/ export * from './quick-command-frontend-contribution'; export * from './quick-command-service'; -export * from './quick-editor-service'; -export * from './quick-help-frontend-contribution'; export * from './quick-help-service'; -export * from './quick-access-contribution'; +export * from './quick-access'; export * from './quick-input-frontend-contribution'; export * from './quick-input-service'; export * from './quick-view-service'; diff --git a/packages/core/src/browser/quick-input/quick-access-contribution.ts b/packages/core/src/browser/quick-input/quick-access.ts similarity index 52% rename from packages/core/src/browser/quick-input/quick-access-contribution.ts rename to packages/core/src/browser/quick-input/quick-access.ts index a810f22e6ea10..b4a3645f71db9 100644 --- a/packages/core/src/browser/quick-input/quick-access-contribution.ts +++ b/packages/core/src/browser/quick-input/quick-access.ts @@ -14,6 +14,9 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import { CancellationToken, Disposable } from '../../common'; +import { QuickPicks } from './quick-input-service'; + export const QuickAccessContribution = Symbol('QuickAccessContribution'); /** * The quick access contribution should be implemented to register custom quick access provider. @@ -21,3 +24,32 @@ export const QuickAccessContribution = Symbol('QuickAccessContribution'); export interface QuickAccessContribution { registerQuickAccessProvider(): void; } + +export interface QuickAccessProvider { + getPicks(filter: string, token: CancellationToken): QuickPicks | Promise; + reset?(): void; +} + +export interface QuickAccessProviderHelp { + prefix?: string; + description: string; + needsEditor: boolean; +} + +export interface QuickAccessProviderDescriptor { + readonly getInstance: () => QuickAccessProvider; + readonly prefix: string; + readonly placeholder?: string; + readonly helpEntries: QuickAccessProviderHelp[]; + readonly contextKey?: string; +} + +export const QuickAccessRegistry = Symbol('QuickAccessRegistry'); + +export interface QuickAccessRegistry { + registerQuickAccessProvider(provider: QuickAccessProviderDescriptor): Disposable; + getQuickAccessProviders(): QuickAccessProviderDescriptor[]; + getQuickAccessProvider(prefix: string): QuickAccessProviderDescriptor | undefined; + clear(): void; +} + diff --git a/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts b/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts index 02131987fbfac..9d68bef051472 100644 --- a/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts +++ b/packages/core/src/browser/quick-input/quick-command-frontend-contribution.ts @@ -17,12 +17,11 @@ import { injectable, inject, optional } from 'inversify'; import { CommandRegistry, CommandContribution, MenuContribution, MenuModelRegistry } from '../../common'; import { KeybindingRegistry, KeybindingContribution } from '../keybinding'; import { CommonMenus } from '../common-frontend-contribution'; -import { QuickInputService } from './quick-input-service'; import { CLEAR_COMMAND_HISTORY, quickCommand, QuickCommandService } from './quick-command-service'; -import { QuickAccessContribution } from './quick-access-contribution'; +import { QuickInputService } from './quick-input-service'; @injectable() -export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution { +export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution { @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @@ -59,8 +58,4 @@ export class QuickCommandFrontendContribution implements CommandContribution, Ke keybinding: 'ctrlcmd+shift+p' }); } - - registerQuickAccessProvider(): void { - this.quickCommandService?.registerQuickAccessProvider(); - } } diff --git a/packages/core/src/browser/quick-input/quick-command-service.ts b/packages/core/src/browser/quick-input/quick-command-service.ts index 8b0d468fb04c1..464f37d48be1a 100644 --- a/packages/core/src/browser/quick-input/quick-command-service.ts +++ b/packages/core/src/browser/quick-input/quick-command-service.ts @@ -15,11 +15,13 @@ ********************************************************************************/ import { inject, injectable } from 'inversify'; -import { Disposable, Command, CommandRegistry } from '../../common'; +import { KeybindingRegistry } from '../keybinding'; +import { Disposable, Command, CommandRegistry, CancellationToken } from '../../common'; import { ContextKeyService } from '../context-key-service'; import { CorePreferences } from '../core-preferences'; -import { QuickAccessContribution } from './quick-access-contribution'; -import { QuickInputService } from './quick-input-service'; +import { QuickAccessContribution, QuickAccessProvider, QuickAccessRegistry } from './quick-access'; +import { filterItems, QuickPickItem, QuickPicks } from './quick-input-service'; +import { KeySequence } from '../keys'; export const quickCommand: Command = { id: 'workbench.action.showCommands' @@ -31,7 +33,8 @@ export const CLEAR_COMMAND_HISTORY: Command = { }; @injectable() -export class QuickCommandService extends QuickInputService implements QuickAccessContribution { +export class QuickCommandService implements QuickAccessContribution, QuickAccessProvider { + static PREFIX = '>'; @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; @@ -42,12 +45,93 @@ export class QuickCommandService extends QuickInputService implements QuickAcces @inject(CorePreferences) protected readonly corePreferences: CorePreferences; + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; + + constructor(@inject(KeybindingRegistry) protected readonly keybindingRegistry: KeybindingRegistry) { + } + // The list of exempted commands not to be displayed in the recently used list. readonly exemptedCommands: Command[] = [ CLEAR_COMMAND_HISTORY, ]; - registerQuickAccessProvider(): void { } + private recentItems: QuickPickItem[] = []; + private otherItems: QuickPickItem[] = []; + + registerQuickAccessProvider(): void { + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: QuickCommandService.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Quick Command', needsEditor: false }] + }); + } + + reset(): void { + const { recent, other } = this.getCommands(); + this.recentItems = []; + this.otherItems = []; + this.recentItems.push(...recent.map(command => this.toItem(command))); + this.otherItems.push(...other.map(command => this.toItem(command))); + } + + getPicks(filter: string, token: CancellationToken): QuickPicks { + const items: QuickPicks = []; + if (this.recentItems.length === 0 && this.otherItems.length === 0) { + this.reset(); + } + const recentItems = filterItems(this.recentItems.slice(), filter); + const otherItems = filterItems(this.otherItems.slice(), filter); + + if (recentItems.length > 0) { + items.push({ type: 'separator', label: 'recently used' }, ...recentItems); + } + + if (otherItems.length > 0) { + items.push({ type: 'separator', label: 'other commands' }, ...otherItems); + } + return items; + } + + private toItem(command: Command): QuickPickItem { + const label = (command.category) ? `${command.category}: ` + command.label! : command.label!; + const iconClasses = this.getItemIconClasses(command); + const activeElement = window.document.activeElement as HTMLElement; + + return { + label, + iconClasses, + alwaysShow: !!this.commandRegistry.getActiveHandler(command.id), + keySequence: this.getKeybinding(command), + execute: () => { + activeElement.focus({ preventScroll: true }); + this.commandRegistry.executeCommand(command.id); + this.commandRegistry.addRecentCommand(command); + } + }; + } + + private getKeybinding(command: Command): KeySequence | undefined { + const keybindings = this.keybindingRegistry.getKeybindingsForCommand(command.id); + if (!keybindings || keybindings.length === 0) { + return undefined; + } + + try { + return this.keybindingRegistry.resolveKeybinding(keybindings[0]); + } catch (error) { + return undefined; + } + } + + private getItemIconClasses(command: Command): string[] | undefined { + const toggledHandler = this.commandRegistry.getToggledHandler(command.id); + if (toggledHandler) { + return ['fa fa-check']; + } + return undefined; + } protected readonly contexts = new Map(); pushCommandContext(commandId: string, when: string): Disposable { diff --git a/packages/core/src/browser/quick-input/quick-editor-service.ts b/packages/core/src/browser/quick-input/quick-editor-service.ts deleted file mode 100644 index 4067f3a9cef34..0000000000000 --- a/packages/core/src/browser/quick-input/quick-editor-service.ts +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 'inversify'; -import { LabelProvider } from '../label-provider'; -import { OpenerService } from '../opener-service'; -import { QuickAccessContribution } from './quick-access-contribution'; -import { QuickInputService } from './quick-input-service'; - -@injectable() -export class QuickEditorService extends QuickInputService implements QuickAccessContribution { - - @inject(OpenerService) - protected readonly openerService: OpenerService; - - @inject(LabelProvider) - protected readonly labelProvider: LabelProvider; - - registerQuickAccessProvider(): void { } -} diff --git a/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts b/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts deleted file mode 100644 index c9b55cd58e63c..0000000000000 --- a/packages/core/src/browser/quick-input/quick-help-frontend-contribution.ts +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 SAP SE or an SAP affiliate company 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, inject, optional } from 'inversify'; -import { QuickHelpService } from './quick-help-service'; -import { QuickAccessContribution } from './quick-access-contribution'; - -@injectable() -export class QuickHelpFrontendContribution implements QuickAccessContribution { - - @inject(QuickHelpService) @optional() - protected readonly quickHelpService: QuickHelpService; - - registerQuickAccessProvider(): void { - this.quickHelpService?.registerQuickAccessProvider(); - } -} diff --git a/packages/core/src/browser/quick-input/quick-help-service.ts b/packages/core/src/browser/quick-input/quick-help-service.ts index caaf22b77b9b5..15b20fbe4569e 100644 --- a/packages/core/src/browser/quick-input/quick-help-service.ts +++ b/packages/core/src/browser/quick-input/quick-help-service.ts @@ -14,11 +14,78 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable } from 'inversify'; -import { QuickAccessContribution } from './quick-access-contribution'; -import { QuickInputService } from './quick-input-service'; +import { inject, injectable } from 'inversify'; +import { CancellationToken } from '../../common'; +import { QuickPickService } from '../../common/quick-pick-service'; +import { QuickAccessContribution, QuickAccessProvider, QuickAccessRegistry } from './quick-access'; +import { QuickInputService, QuickPickItem, QuickPickSeparator } from './quick-input-service'; @injectable() -export class QuickHelpService extends QuickInputService implements QuickAccessContribution { - registerQuickAccessProvider(): void { } +export class QuickHelpService implements QuickAccessProvider, QuickAccessContribution { + static PREFIX = '?'; + + @inject(QuickAccessRegistry) + protected quickAccessRegistry: QuickAccessRegistry; + + @inject(QuickPickService) + protected quickPickService: QuickPickService; + + @inject(QuickInputService) + protected quickInputService: QuickInputService; + + getPicks(filter: string, token: CancellationToken): (QuickPickItem | QuickPickSeparator)[] { + const { editorProviders, globalProviders } = this.getQuickAccessProviders(); + const result: (QuickPickItem | QuickPickSeparator)[] = editorProviders.length === 0 || globalProviders.length === 0 ? + // Without groups + [ + ...(editorProviders.length === 0 ? globalProviders : editorProviders) + ] : + + // With groups + [ + { type: 'separator', label: 'global commands' }, + ...globalProviders, + { type: 'separator', label: 'editor commands' }, + ...editorProviders + ]; + return result; + } + + private getQuickAccessProviders(): { editorProviders: QuickPickItem[], globalProviders: QuickPickItem[] } { + const globalProviders: QuickPickItem[] = []; + const editorProviders: QuickPickItem[] = []; + + const providers = this.quickAccessRegistry.getQuickAccessProviders(); + + for (const provider of providers.sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) { + if (provider.prefix === QuickHelpService.PREFIX) { + continue; // exclude help which is already active + } + + for (const helpEntry of provider.helpEntries) { + const prefix = helpEntry.prefix || provider.prefix; + const label = prefix || '\u2026' /* ... */; + + (helpEntry.needsEditor ? editorProviders : globalProviders).push({ + label, + ariaLabel: `${label}, ${helpEntry.description}`, + description: helpEntry.description, + execute: () => this.quickInputService.open(prefix) + }); + } + } + + return { editorProviders, globalProviders }; + } + + registerQuickAccessProvider(): void { + this.quickAccessRegistry.registerQuickAccessProvider( + { + getInstance: () => this, + prefix: QuickHelpService.PREFIX, + placeholder: 'Type "?" to get help on the actions you can take from here.', + helpEntries: [{ description: 'Show all Quick Access Providers', needsEditor: false }] + } + ); + } } diff --git a/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts b/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts index 3d2e44feb55e4..8c61eca25d34f 100644 --- a/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts +++ b/packages/core/src/browser/quick-input/quick-input-frontend-contribution.ts @@ -17,7 +17,7 @@ import { injectable, inject, named } from 'inversify'; import { ContributionProvider } from '../../common'; import { FrontendApplicationContribution } from '../frontend-application'; -import { QuickAccessContribution } from './quick-access-contribution'; +import { QuickAccessContribution } from './quick-access'; @injectable() export class QuickInputFrontendContribution implements FrontendApplicationContribution { diff --git a/packages/core/src/browser/quick-input/quick-input-service.ts b/packages/core/src/browser/quick-input/quick-input-service.ts index 1869ed9fca043..218ef0195b28d 100644 --- a/packages/core/src/browser/quick-input/quick-input-service.ts +++ b/packages/core/src/browser/quick-input/quick-input-service.ts @@ -14,9 +14,19 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable } from 'inversify'; import { CancellationToken, Event } from '../../common'; import URI from '../../common/uri'; +import { KeySequence } from '../keyboard'; + +export interface Match { + start: number; + end: number; +} +export interface QuickPickItemHighlights { + label?: Match[]; + description?: Match[]; + detail?: Match[]; +} export interface QuickPickItem { type?: 'item' | 'separator'; @@ -26,16 +36,33 @@ export interface QuickPickItem { ariaLabel?: string; description?: string; detail?: string; - resource?: URI; + keySequence?: KeySequence; iconClasses?: string[]; - execute?: (item: QuickPickItem, lookFor: string) => void; + alwaysShow?: boolean; + highlights?: QuickPickItemHighlights; + execute?: () => void; +} + +export namespace QuickPickItem { + export function is(item: QuickPickSeparator | QuickPickItem): item is QuickPickItem { + // if it's not a separator, it's an item + return item.type !== 'separator'; + } } export interface QuickPickSeparator { - type: 'separator'; + type?: 'separator'; label?: string; } +export namespace QuickPickSeparator { + export function is(item: QuickPickSeparator | QuickPickItem): item is QuickPickSeparator { + return item.type === 'separator'; + } +} + +export type QuickPicks = (QuickPickSeparator | QuickPickItem)[]; + export interface QuickPickValue extends QuickPickItem { value: V } @@ -66,7 +93,7 @@ export interface QuickInput { dispose(): void; } -export interface IInputBox extends QuickInput { +export interface InputBox extends QuickInput { value: string | undefined; valueSelection: Readonly<[number, number]> | undefined; placeholder: string | undefined; @@ -96,7 +123,7 @@ export interface QuickPick extends QuickInput { readonly onDidChangeSelection: Event; } -export interface IPickOptions { +export interface PickOptions { placeHolder?: string; matchOnDescription?: boolean; matchOnDetail?: boolean; @@ -109,7 +136,7 @@ export interface IPickOptions { onDidFocus?: (entry: T) => void; } -export interface IInputOptions { +export interface InputOptions { value?: string; valueSelection?: [number, number]; prompt?: string; @@ -165,20 +192,19 @@ export interface QuickPickOptions { onDidTriggerItemButton?: Event>; } -export interface IQuickInputService { +export const QuickInputService = Symbol('QuickInputService'); +export interface QuickInputService { readonly backButton: QuickInputButton; open(filter: string): void; - reset(): void; - createInputBox(): IInputBox; - createQuickPick(): QuickPick; - input(options?: IInputOptions, token?: CancellationToken): Promise; - pick>(picks: Promise | T[], options?: O, token?: CancellationToken): + createInputBox(): InputBox; + input(options?: InputOptions, token?: CancellationToken): Promise; + pick>(picks: Promise | T[], options?: O, token?: CancellationToken): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined>; showQuickPick(items: Array, options?: QuickPickOptions): Promise; hide(): void; } -export function filterItems(items: Array, filter: string): Array { +export function filterItems(items: QuickPickItem[], filter: string): QuickPickItem[] { return filter.trim().length === 0 ? items : items .filter(item => item.label.toLowerCase().indexOf(filter.toLowerCase()) > -1) .map(item => Object.assign(item, { highlights: { label: findMatches(item.label.toLowerCase(), filter.toLowerCase()) } })); @@ -187,27 +213,3 @@ export function filterItems(items: Array, filter: string): Array< export function findMatches(label: string, lookFor: string): Array<{ start: number, end: number }> | undefined { return label.indexOf(lookFor) > -1 ? [{ start: label.indexOf(lookFor), end: label.indexOf(lookFor) + lookFor.length }] : undefined; } - -@injectable() -export class QuickInputService implements IQuickInputService { - createInputBox(): IInputBox { return {} as IInputBox; } - - createQuickPick(): QuickPick { return {} as QuickPick; } - - input(options?: IInputOptions, token?: CancellationToken): Promise { return Promise.resolve(undefined); } - - pick>(picks: Promise | T[], options: O = {}, token?: CancellationToken): - Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { - return Promise.resolve(undefined); - } - - showQuickPick(items: Array, options?: QuickPickOptions): Promise { return Promise.resolve({} as T); } - - get backButton(): QuickInputButton { return {} as QuickInputButton; } - - open(filter: string): void { } - - reset(): void { } - - hide(): void { } -} diff --git a/packages/core/src/browser/quick-input/quick-view-service.ts b/packages/core/src/browser/quick-input/quick-view-service.ts index 5ffd727f27f41..51afcd1c4afee 100644 --- a/packages/core/src/browser/quick-input/quick-view-service.ts +++ b/packages/core/src/browser/quick-input/quick-view-service.ts @@ -14,9 +14,12 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable } from 'inversify'; -import { Disposable } from '../../common'; -import { QuickInputService } from './quick-input-service'; +import { inject, injectable } from 'inversify'; +import { filterItems, QuickPickItem, QuickPicks } from '..'; +import { CancellationToken, Disposable } from '../../common'; +import { ContextKeyService } from '../context-key-service'; +import { QuickAccessProvider, QuickAccessRegistry } from './quick-access'; +import { QuickAccessContribution } from './quick-access'; export interface QuickViewItem { readonly label: string; @@ -26,12 +29,60 @@ export interface QuickViewItem { } @injectable() -export class QuickViewService extends QuickInputService { - readonly prefix = 'view '; +export class QuickViewService implements QuickAccessContribution, QuickAccessProvider { + static PREFIX = 'view '; + + protected readonly items: (QuickPickItem & { when?: string })[] = []; + private hiddenItemLabels = new Set(); + + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; + + @inject(ContextKeyService) + protected readonly contextKexService: ContextKeyService; registerItem(item: QuickViewItem): Disposable { - return Disposable.create(() => { }); + const quickOpenItem = { + label: item.label, + accept: () => item.open(), + when: item.when + }; + this.items.push(quickOpenItem); + this.items.sort((a, b) => a.label!.localeCompare(b.label!)); + + return Disposable.create(() => { + const index = this.items.indexOf(quickOpenItem); + if (index !== -1) { + this.items.splice(index, 1); + } + }); + } + + hideItem(label: string): void { + this.hiddenItemLabels.add(label); + } + + showItem(label: string): void { + this.hiddenItemLabels.delete(label); + } + + registerQuickAccessProvider(): void { + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: QuickViewService.PREFIX, + placeholder: '', + helpEntries: [{ description: 'Open View', needsEditor: false }] + }); + } + + getPicks(filter: string, token: CancellationToken): QuickPicks { + const items = this.items.filter(item => + (item.when === undefined || this.contextKexService.match(item.when)) && + (!this.hiddenItemLabels.has(item.label)) + ); + return filterItems(items, filter); } - hideItem(label: string): void { } - showItem(label: string): void { } + + readonly prefix = 'view '; + } diff --git a/packages/debug/src/browser/debug-frontend-module.ts b/packages/debug/src/browser/debug-frontend-module.ts index e94f9340d7e88..e521cabf6a31e 100644 --- a/packages/debug/src/browser/debug-frontend-module.ts +++ b/packages/debug/src/browser/debug-frontend-module.ts @@ -59,7 +59,7 @@ import { DebugInlineValueDecorator } from './editor/debug-inline-value-decorator import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; import { TabBarDecorator } from '@theia/core/lib/browser/shell/tab-bar-decorator'; import { DebugTabBarDecorator } from './debug-tab-bar-decorator'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; export default new ContainerModule((bind: interfaces.Bind) => { bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) => diff --git a/packages/debug/src/browser/debug-prefix-configuration.ts b/packages/debug/src/browser/debug-prefix-configuration.ts index fbb9aca3fc5ca..45b05299c4e2a 100644 --- a/packages/debug/src/browser/debug-prefix-configuration.ts +++ b/packages/debug/src/browser/debug-prefix-configuration.ts @@ -23,12 +23,14 @@ import { DebugSessionOptions } from './debug-session-options'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import URI from '@theia/core/lib/common/uri'; -import { QuickAccessContribution, QuickInputService, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; +import { QuickAccessContribution, QuickAccessProvider, QuickAccessRegistry, QuickInputService, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; import { DebugPreferences } from './debug-preferences'; -import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { filterItems, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { CancellationToken } from '@theia/core/lib/common'; @injectable() -export class DebugPrefixConfiguration implements CommandContribution, CommandHandler, QuickAccessContribution, monaco.quickInput.IQuickAccessDataService { +export class DebugPrefixConfiguration implements CommandContribution, CommandHandler, QuickAccessContribution, QuickAccessProvider { + static readonly PREFIX = 'debug '; @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -45,6 +47,9 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; + @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -78,7 +83,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan } execute(): void { - this.quickInputService?.open(DebugQuickAccessProvider.PREFIX); + this.quickInputService?.open(DebugPrefixConfiguration.PREFIX); } isEnabled(): boolean { @@ -94,17 +99,16 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan } registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: DebugQuickAccessProvider, - prefix: DebugQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: DebugPrefixConfiguration.PREFIX, placeholder: '', helpEntries: [{ description: 'Debug Configuration', needsEditor: false }] }); - DebugQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } - async getPicks(filter: string, token: monaco.CancellationToken): Promise> { - const items: Array = []; + getPicks(filter: string, token: CancellationToken): QuickPicks { + const items: QuickPickItem[] = []; const configurations = this.debugConfigurationManager.all; Array.from(configurations).forEach(config => { items.push({ @@ -112,7 +116,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan description: this.workspaceService.isMultiRootWorkspaceOpened ? this.labelProvider.getName(new URI(config.workspaceFolderUri)) : '', - accept: () => this.runConfiguration(config) + execute: () => this.runConfiguration(config) }); }); return filterItems(items, filter); @@ -164,26 +168,3 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan this.statusBar.removeElement(this.statusBarId); } } -export class DebugQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = 'debug '; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching launch configurations' - }; - - constructor() { - super(DebugQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: DebugQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return DebugQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/editor/src/browser/editor-contribution.ts b/packages/editor/src/browser/editor-contribution.ts index 2415c8bf5b996..f24c8a5f27bc1 100644 --- a/packages/editor/src/browser/editor-contribution.ts +++ b/packages/editor/src/browser/editor-contribution.ts @@ -18,7 +18,7 @@ import { EditorManager } from './editor-manager'; import { TextEditor } from './editor'; import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { StatusBarAlignment, StatusBar } from '@theia/core/lib/browser/status-bar/status-bar'; -import { FrontendApplicationContribution, DiffUris, DockLayout } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, DiffUris, DockLayout, QuickInputService } from '@theia/core/lib/browser'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { CommandHandler, DisposableCollection } from '@theia/core'; import { EditorCommands } from './editor-command'; @@ -26,11 +26,9 @@ import { CommandRegistry, CommandContribution } from '@theia/core/lib/common'; import { KeybindingRegistry, KeybindingContribution } from '@theia/core/lib/browser'; import { LanguageService } from '@theia/core/lib/browser/language-service'; import { SUPPORTED_ENCODINGS } from '@theia/core/lib/browser/supported-encodings'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; -import { QuickEditorService } from '@theia/core/lib/browser/quick-input/quick-editor-service'; @injectable() -export class EditorContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution, QuickAccessContribution { +export class EditorContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution { @inject(StatusBar) protected readonly statusBar: StatusBar; @inject(EditorManager) protected readonly editorManager: EditorManager; @@ -39,8 +37,8 @@ export class EditorContribution implements FrontendApplicationContribution, Comm @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; - @inject(QuickEditorService) @optional() - protected readonly quickEditorService: QuickEditorService; + @inject(QuickInputService) @optional() + protected readonly quickInputService: QuickInputService; onStart(): void { this.initEditorContextKeys(); @@ -132,7 +130,7 @@ export class EditorContribution implements FrontendApplicationContribution, Comm registerCommands(commands: CommandRegistry): void { commands.registerCommand(EditorCommands.SHOW_ALL_OPENED_EDITORS, { - execute: () => this.quickEditorService?.open('edt ') + execute: () => this.quickInputService?.open('edt ') }); const splitHandlerFactory = (splitMode: DockLayout.InsertMode): CommandHandler => ({ isEnabled: () => !!this.editorManager.currentEditor, @@ -169,8 +167,4 @@ export class EditorContribution implements FrontendApplicationContribution, Comm keybinding: 'ctrlcmd+k ctrlcmd+\\', }); } - - registerQuickAccessProvider(): void { - this.quickEditorService?.registerQuickAccessProvider(); - } } diff --git a/packages/editor/src/browser/editor-frontend-module.ts b/packages/editor/src/browser/editor-frontend-module.ts index e8b6f71be4df5..330b5f8bdb67b 100644 --- a/packages/editor/src/browser/editor-frontend-module.ts +++ b/packages/editor/src/browser/editor-frontend-module.ts @@ -33,7 +33,8 @@ import { NavigationLocationUpdater } from './navigation/navigation-location-upda import { NavigationLocationService } from './navigation/navigation-location-service'; import { NavigationLocationSimilarity } from './navigation/navigation-location-similarity'; import { EditorVariableContribution } from './editor-variable-contribution'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; +import { QuickEditorService } from './quick-editor-service'; export default new ContainerModule(bind => { bindEditorPreferences(bind); @@ -68,9 +69,11 @@ export default new ContainerModule(bind => { bind(VariableContribution).to(EditorVariableContribution).inSingletonScope(); - [CommandContribution, KeybindingContribution, QuickAccessContribution].forEach(serviceIdentifier => { + [CommandContribution, KeybindingContribution].forEach(serviceIdentifier => { bind(serviceIdentifier).toService(EditorContribution); }); + bind(QuickEditorService).toSelf().inSingletonScope(); + bind(QuickAccessContribution).to(QuickEditorService); bind(CurrentEditorAccess).toSelf().inSingletonScope(); bind(ActiveEditorAccess).toSelf().inSingletonScope(); diff --git a/packages/monaco/src/browser/monaco-quick-editor-service.ts b/packages/editor/src/browser/quick-editor-service.ts similarity index 53% rename from packages/monaco/src/browser/monaco-quick-editor-service.ts rename to packages/editor/src/browser/quick-editor-service.ts index 03effbda8d380..157439cef9ea1 100644 --- a/packages/monaco/src/browser/monaco-quick-editor-service.ts +++ b/packages/editor/src/browser/quick-editor-service.ts @@ -14,29 +14,43 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from '@theia/core/shared/inversify'; -import { QuickEditorService, filterItems } from '@theia/core/lib/browser'; -import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; +import { injectable, inject } from '@theia/core/shared/inversify'; +import { CancellationToken } from '@theia/core/lib/common'; import URI from '@theia/core/lib/common/uri'; +import { LabelProvider } from '@theia/core/lib/browser/label-provider'; +import { OpenerService } from '@theia/core/lib/browser/opener-service'; +import { QuickAccessProvider, QuickAccessRegistry } from '@theia/core/lib/browser/quick-input/quick-access'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; +import { filterItems, QuickPickItem, QuickPickSeparator } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { EditorManager, EditorWidget } from '.'; @injectable() -export class MonacoQuickEditorService extends QuickEditorService implements monaco.quickInput.IQuickAccessDataService { +export class QuickEditorService implements QuickAccessContribution, QuickAccessProvider { + static PREFIX = 'edt '; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; + + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; @inject(EditorManager) protected readonly editorManager: EditorManager; registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: EditorQuickAccessProvider, - prefix: EditorQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: QuickEditorService.PREFIX, placeholder: '', helpEntries: [{ description: 'Show All Opened Editors', needsEditor: false }] }); - EditorQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } - getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { - const editorItems: Array = []; + getPicks(filter: string, token: CancellationToken): (QuickPickItem | QuickPickSeparator)[] { + const editorItems: QuickPickItem[] = []; // Get the alphabetically sorted list of URIs of all currently opened editor widgets. const widgets: URI[] = this.editorManager.all @@ -57,7 +71,7 @@ export class MonacoQuickEditorService extends QuickEditorService implements mona return filterItems(editorItems.slice(), filter); } - protected toItem(uri: URI): monaco.quickInput.IAnythingQuickPickItem { + protected toItem(uri: URI): QuickPickItem { const description = this.labelProvider.getLongName(uri.parent); const icon = this.labelProvider.getIcon(uri); const iconClasses = icon === '' ? undefined : [icon + ' file-icon']; @@ -67,9 +81,8 @@ export class MonacoQuickEditorService extends QuickEditorService implements mona description: description, iconClasses, ariaLabel: uri.path.toString(), - resource: uri, alwaysShow: true, - accept: () => this.openFile(uri) + execute: () => this.openFile(uri) }; } @@ -78,27 +91,3 @@ export class MonacoQuickEditorService extends QuickEditorService implements mona .then(opener => opener.open(uri)); } } - -export class EditorQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = 'edt '; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; - - constructor() { - super(EditorQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: EditorQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return EditorQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/file-search/src/browser/file-search-frontend-module.ts b/packages/file-search/src/browser/file-search-frontend-module.ts index 829fae7d3271d..9cf6ba3dbe063 100644 --- a/packages/file-search/src/browser/file-search-frontend-module.ts +++ b/packages/file-search/src/browser/file-search-frontend-module.ts @@ -20,7 +20,7 @@ import { WebSocketConnectionProvider, KeybindingContribution } from '@theia/core import { QuickFileOpenFrontendContribution } from './quick-file-open-contribution'; import { QuickFileOpenService } from './quick-file-open'; import { fileSearchServicePath, FileSearchService } from '../common/file-search-service'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; export default new ContainerModule((bind: interfaces.Bind) => { bind(FileSearchService).toDynamicValue(ctx => { diff --git a/packages/file-search/src/browser/quick-file-open.ts b/packages/file-search/src/browser/quick-file-open.ts index 714b1bf010673..10b43afc6bef1 100644 --- a/packages/file-search/src/browser/quick-file-open.ts +++ b/packages/file-search/src/browser/quick-file-open.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import { inject, injectable, optional } from '@theia/core/shared/inversify'; -import { OpenerService, KeybindingRegistry } from '@theia/core/lib/browser'; +import { OpenerService, KeybindingRegistry, QuickAccessRegistry, QuickAccessProvider } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import URI from '@theia/core/lib/common/uri'; import { FileSearchService, WHITESPACE_QUERY_SEPARATOR } from '../common/file-search-service'; @@ -26,8 +26,8 @@ import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/ import * as fuzzy from '@theia/core/shared/fuzzy'; import { MessageService } from '@theia/core/lib/common/message-service'; import { FileSystemPreferences } from '@theia/filesystem/lib/browser'; -import { findMatches, QuickInputService } from '@theia/core/lib/browser/quick-input/quick-input-service'; import { EditorOpenerOptions, Position, Range } from '@theia/editor/lib/browser'; +import { findMatches, QuickInputService, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service'; export const quickFileOpen: Command = { id: 'file-search.openFile', @@ -43,8 +43,12 @@ export interface FilterAndRange { // Supports patterns of <#|:><#|:|,> const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?\s*$/; +type FileQuickPickItem = QuickPickItem & { uri: URI }; + @injectable() -export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataService { +export class QuickFileOpenService implements QuickAccessProvider { + static readonly PREFIX = ''; + @inject(KeybindingRegistry) protected readonly keybindingRegistry: KeybindingRegistry; @inject(WorkspaceService) @@ -53,6 +57,8 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS protected readonly openerService: OpenerService; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; @inject(FileSearchService) protected readonly fileSearchService: FileSearchService; @inject(LabelProvider) @@ -65,13 +71,12 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS protected readonly fsPreferences: FileSystemPreferences; registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: AnythingQuickAccessProvider, - prefix: AnythingQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: QuickFileOpenService.PREFIX, placeholder: this.getPlaceHolder(), helpEntries: [{ description: 'Open File', needsEditor: false }] }); - AnythingQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } /** @@ -133,14 +138,14 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS return undefined; } - async getPicks(filter: string, token: CancellationToken): Promise> { + async getPicks(filter: string, token: CancellationToken): Promise { const roots = this.workspaceService.tryGetRoots(); this.filterAndRange = this.splitFilterAndRange(filter); const fileFilter = this.filterAndRange.filter; const alreadyCollected = new Set(); - const recentlyUsedItems: Array = []; + const recentlyUsedItems: QuickPicks = []; const locations = [...this.navigationLocationService.locations()].reverse(); for (const location of locations) { @@ -161,7 +166,9 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS if (token.isCancellationRequested || results.length <= 0) { return []; } - const fileSearchResultItems: Array = []; + + const result = [...recentlyUsedItems]; + const fileSearchResultItems: FileQuickPickItem[] = []; for (const fileUri of results) { if (!alreadyCollected.has(fileUri)) { @@ -176,11 +183,12 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS sortedResults.sort((a, b) => this.compareItems(a, b)); if (sortedResults.length > 0) { - sortedResults.unshift({ type: 'separator', label: 'file results' }); + result.unshift({ type: 'separator', label: 'file results' }); + result.push(...sortedResults); } // Return the recently used items, followed by the search results. - return ([...recentlyUsedItems, ...sortedResults]); + return result; }; return this.fileSearchService.find(fileFilter, { @@ -196,31 +204,9 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS return roots.length !== 0 ? recentlyUsedItems : []; } } - - /** - * Compare two `IAnythingQuickPickItem`. - * - * @param a `IAnythingQuickPickItem` for comparison. - * @param b `IAnythingQuickPickItem` for comparison. - * @param member the `IAnythingQuickPickItem` object member for comparison. - */ protected compareItems( - a: monaco.quickInput.IAnythingQuickPickItem, - b: monaco.quickInput.IAnythingQuickPickItem, - member: 'label' | 'resource' = 'label'): number { - - /** - * Normalize a given string. - * - * @param str the raw string value. - * @returns the normalized string value. - */ - function normalize(str: string): string { - return str.trim().toLowerCase(); - } - - // Normalize the user query. - const query: string = normalize(this.filterAndRange.filter); + left: FileQuickPickItem, + right: FileQuickPickItem): number { /** * Score a given string. @@ -261,55 +247,17 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS } } - // Get the item's member values for comparison. - let itemA = a[member]!; - let itemB = b[member]!; - - // If the `URI` is used as a comparison member, perform the necessary string conversions. - if (typeof itemA !== 'string') { - itemA = itemA.path.toString(); - } - if (typeof itemB !== 'string') { - itemB = itemB.path.toString(); - } - - // Normalize the item labels. - itemA = normalize(itemA); - itemB = normalize(itemB); - - // Score the item labels. - const scoreA: number = score(itemA); - const scoreB: number = score(itemB); - - // If both label scores are identical, perform additional computation. - if (scoreA === scoreB) { - - // Favor the label which have the smallest substring index. - const indexA: number = itemA.indexOf(query); - const indexB: number = itemB.indexOf(query); - - if (indexA === indexB) { - - // Favor the result with the shortest label length. - if (itemA.length !== itemB.length) { - return (itemA.length < itemB.length) ? -1 : 1; - } - - // Fallback to the alphabetical order. - const comparison = itemB.localeCompare(itemA); - - // If the alphabetical comparison is equal, call `compareItems` recursively using the `URI` member instead. - if (comparison === 0) { - return this.compareItems(a, b, 'resource'); - } + const query: string = normalize(this.filterAndRange.filter); - return itemB.localeCompare(itemA); - } + const compareByLabelScore = (l: FileQuickPickItem, r: FileQuickPickItem) => score(r.label) - score(l.label); + const compareByLabelIndex = (l: FileQuickPickItem, r: FileQuickPickItem) => r.label.indexOf(query) - l.label.indexOf(query); + const compareByLabel = (l: FileQuickPickItem, r: FileQuickPickItem) => r.label.localeCompare(l.label); - return indexA - indexB; - } + const compareByPathLabelScore = (l: FileQuickPickItem, r: FileQuickPickItem) => score(r.uri.path.toString()) - score(l.uri.path.toString()); + const compareByPathIndex = (l: FileQuickPickItem, r: FileQuickPickItem) => r.uri.path.toString().indexOf(query) - l.uri.path.toString().indexOf(query); + const compareByPathLabel = (l: FileQuickPickItem, r: FileQuickPickItem) => r.uri.path.toString().localeCompare(l.uri.path.toString()); - return scoreB - scoreA; + return compareWithDiscriminators(left, right, compareByLabelScore, compareByLabelIndex, compareByLabel, compareByPathLabelScore, compareByPathIndex, compareByPathLabel); } openFile(uri: URI): void { @@ -324,14 +272,13 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS return { selection: this.filterAndRange.range }; } - private toItem(lookFor: string, uriOrString: URI | string): monaco.quickInput.IAnythingQuickPickItem { + private toItem(lookFor: string, uriOrString: URI | string): FileQuickPickItem { const uri = uriOrString instanceof URI ? uriOrString : new URI(uriOrString); const label = this.labelProvider.getName(uri); const description = this.getItemDescription(uri); const iconClasses = this.getItemIconClasses(uri); return { - resource: uri, label, description, highlights: { @@ -339,7 +286,8 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS description: findMatches(description, lookFor) }, iconClasses, - accept: () => this.openFile(uri) + uri, + execute: () => this.openFile(uri) }; } @@ -401,27 +349,23 @@ export class QuickFileOpenService implements monaco.quickInput.IQuickAccessDataS } } -export class AnythingQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = ''; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; +/** + * Normalize a given string. + * + * @param str the raw string value. + * @returns the normalized string value. + */ +function normalize(str: string): string { + return str.trim().toLowerCase(); +} - constructor() { - super(AnythingQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: AnythingQuickAccessProvider.NO_RESULTS_PICK - }); - } +function compareWithDiscriminators(left: T, right: T, ...discriminators: ((left: T, right: T) => number)[]): number { + let comparisonValue = 0; + let i = 0; - // TODO: disposabled - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return AnythingQuickAccessProvider.dataService?.getPicks(filter, token); + while (comparisonValue === 0 && i < discriminators.length) { + comparisonValue = discriminators[i](left, right); + i++; } + return comparisonValue; } diff --git a/packages/git/src/browser/git-quick-open-service.ts b/packages/git/src/browser/git-quick-open-service.ts index a0817ce80aaf9..11ef547f80e1b 100644 --- a/packages/git/src/browser/git-quick-open-service.ts +++ b/packages/git/src/browser/git-quick-open-service.ts @@ -124,7 +124,7 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const remotes = await this.getRemotes(); - const execute = async (item: GitQuickPickItem, lookFor: string) => { + const execute = async (item: GitQuickPickItem) => { try { await this.git.fetch(repository, { remote: item.ref!.name }); } catch (error) { @@ -165,7 +165,7 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [remotes, currentBranch] = await Promise.all([this.getRemotes(), this.getCurrentBranch()]); - const execute = async (item: GitQuickPickItem, lookFor: string) => { + const execute = async (item: GitQuickPickItem) => { try { await this.git.push(repository, { remote: item.label, setUpstream: true }); } catch (error) { @@ -186,7 +186,7 @@ export class GitQuickOpenService { return this.withProgress(async () => { const remotes = await this.getRemotes(); const defaultRemote = remotes[0].name; // I wish I could use assignment destructuring here. (GH-413) - const executeRemote = async (remoteItem: GitQuickPickItem, lookFor: string) => { + const executeRemote = async (remoteItem: GitQuickPickItem) => { // The first remote is the default. if (remoteItem.ref!.name === defaultRemote) { try { @@ -197,7 +197,7 @@ export class GitQuickOpenService { } else { // Otherwise we need to propose the branches from const branches = await this.getBranches(); - const executeBranch = async (branchItem: GitQuickPickItem, lookForBranch: string) => { + const executeBranch = async (branchItem: GitQuickPickItem) => { try { await this.git.pull(repository, { remote: remoteItem.ref!.name, branch: branchItem.ref!.nameWithoutRemote }); } catch (error) { @@ -224,7 +224,7 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [branches, currentBranch] = await Promise.all([this.getBranches(), this.getCurrentBranch()]); - const execute = async (item: GitQuickPickItem, lookFor: string) => { + const execute = async (item: GitQuickPickItem) => { try { await this.git.merge(repository, { branch: item.label }); } catch (error) { @@ -249,7 +249,7 @@ export class GitQuickOpenService { const index = branches.findIndex(branch => branch && branch.name === currentBranch.name); branches.splice(index, 1); } - const switchBranch = async (item: GitQuickPickItem, lookFor: string) => { + const switchBranch = async (item: GitQuickPickItem) => { try { await this.git.checkout(repository, { branch: item.ref!.nameWithoutRemote }); } catch (error) { @@ -303,7 +303,7 @@ export class GitQuickOpenService { } return this.withProgress(async () => { const [branches, tags, currentBranch] = await Promise.all([this.getBranches(repository), this.getTags(repository), this.getCurrentBranch(repository)]); - const execute = async (item: GitQuickPickItem, lookFor: string) => { + const execute = async (item: GitQuickPickItem) => { execFunc(item.ref!.name, currentBranch ? currentBranch.name : ''); }; const branchItems = branches.map(branch => new GitQuickPickItem(branch.name, execute, branch)); @@ -471,7 +471,7 @@ export class GitQuickOpenService { private toRepositoryPathQuickOpenItem(root: FileStat): GitQuickPickItem { const rootUri = root.resource; - const execute = async (item: GitQuickPickItem, lookFor: string) => { + const execute = async (item: GitQuickPickItem) => { const wsRoot = item.ref!.toString(); this.doInitRepository(wsRoot); }; @@ -550,11 +550,18 @@ export class GitQuickOpenService { } class GitQuickPickItem implements QuickPickItem { + readonly execute?: () => void; constructor( public label: string, - public readonly execute?: (item: QuickPickItem, lookFor: string) => void, + execute?: (item: QuickPickItem) => void, public readonly ref?: T, public description?: string, public alwaysShow = true, - public sortByLabel = false) { } + public sortByLabel = false) { + this.execute = execute ? createExecFunction(execute, this) : undefined; + } +} + +function createExecFunction(f: (item: QuickPickItem) => void, item: QuickPickItem): () => void { + return () => { f(item); }; } diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index 52d1530cc56bd..d175280006c23 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -254,7 +254,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { ({ label: size === tabSize ? `${size} Configured Tab Size` : size.toString(), // eslint-disable-next-line @typescript-eslint/no-explicit-any - execute: (quickPick: any, lookFor: string) => model.updateOptions({ + execute: () => model.updateOptions({ tabSize: size || tabSize, insertSpaces: useSpaces }) diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 8af457d644a53..74bbc2dbaadc6 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -22,7 +22,7 @@ import { MenuContribution, CommandContribution } from '@theia/core/lib/common'; import { FrontendApplicationContribution, KeybindingContribution, PreferenceService, PreferenceSchemaProvider, createPreferenceProxy, - PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService, QuickCommandService, QuickHelpService, QuickViewService, QuickEditorService + PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService } from '@theia/core/lib/browser'; import { TextEditorProvider, DiffNavigatorProvider } from '@theia/editor/lib/browser'; import { StrictEditorTextFocusContext } from '@theia/editor/lib/browser/editor-keybinding-contexts'; @@ -62,11 +62,8 @@ import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter'; import { MonacoFormattingConflictsContribution } from './monaco-formatting-conflicts'; import { MonacoQuickInputService } from './monaco-quick-input-service'; -import { MonacoQuickCommandService } from './monaco-quick-command-service'; -import { MonacoQuickHelpService } from './monaco-quick-help-service'; -import { MonacoQuickViewService } from './monaco-quick-view-service'; -import { MonacoQuickEditorService } from './monaco-quick-editor-service'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution, QuickAccessRegistry } from '@theia/core/lib/browser/quick-input/quick-access'; +import { MonacoQuickAccessRegistry } from './monaco-quick-access-registry'; decorate(injectable(), monaco.contextKeyService.ContextKeyService); @@ -140,17 +137,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoQuickInputService).toSelf().inSingletonScope(); bind(QuickInputService).toService(MonacoQuickInputService); - bind(MonacoQuickCommandService).toSelf().inSingletonScope(); - bind(QuickCommandService).toService(MonacoQuickCommandService); - - bind(MonacoQuickHelpService).toSelf().inSingletonScope(); - bind(QuickHelpService).toService(MonacoQuickHelpService); - - bind(MonacoQuickViewService).toSelf().inSingletonScope(); - bind(QuickViewService).toService(MonacoQuickViewService); - - bind(MonacoQuickEditorService).toSelf().inSingletonScope(); - bind(QuickEditorService).toService(MonacoQuickEditorService); + bind(MonacoQuickAccessRegistry).toSelf().inSingletonScope(); + bind(QuickAccessRegistry).toService(MonacoQuickAccessRegistry); MonacoTextmateModuleBinder(bind, unbind, isBound, rebind); diff --git a/packages/monaco/src/browser/monaco-quick-access-registry.ts b/packages/monaco/src/browser/monaco-quick-access-registry.ts new file mode 100644 index 0000000000000..f7343dd7791a0 --- /dev/null +++ b/packages/monaco/src/browser/monaco-quick-access-registry.ts @@ -0,0 +1,132 @@ +/******************************************************************************** + * Copyright (c) 2021 Red Hat 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 { KeySequence, QuickPickItem, QuickPickItemHighlights, QuickPickSeparator } from '@theia/core/lib/browser'; +import { QuickAccessProviderDescriptor, QuickAccessRegistry } from '@theia/core/lib/browser/quick-input/quick-access'; +import { CancellationToken, Disposable } from '@theia/core/lib/common'; +import { injectable } from '@theia/core/shared/inversify'; + +abstract class MonacoPickerAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { + constructor(prefix: string, options?: monaco.quickInput.IPickerQuickAccessProviderOptions) { + super(prefix, options); + } + + abstract getDescriptor(): QuickAccessProviderDescriptor; +} + +class TheiaQuickAccessDescriptor implements monaco.quickInput.IQuickAccessProviderDescriptor { + constructor( + public readonly theiaDescriptor: QuickAccessProviderDescriptor, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readonly ctor: { new(...services: any /* TS BrandedService but no clue how to type this properly */[]): monaco.quickInput.IQuickAccessProvider }, + readonly prefix: string, + readonly helpEntries: monaco.quickInput.IQuickAccessProviderHelp[], + readonly placeholder?: string) { } +} + +@injectable() +export class MonacoQuickAccessRegistry implements QuickAccessRegistry { + private get monacoRegistry(): monaco.quickInput.IQuickAccessRegistry { + return monaco.platform.Registry.as('workbench.contributions.quickaccess'); + } + + registerQuickAccessProvider(descriptor: QuickAccessProviderDescriptor): Disposable { + const inner = + class extends MonacoPickerAccessProvider { + getDescriptor(): QuickAccessProviderDescriptor { + return descriptor; + } + constructor() { + super(descriptor.prefix); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getPicks(filter: string, disposables: any, token: CancellationToken): monaco.quickInput.Picks | Promise> { + const result = descriptor.getInstance().getPicks(filter, token); + if (result instanceof Promise) { + return result.then(picks => picks.map(toMonacoPick)); + } else { + return result.map(toMonacoPick); + } + } + }; + + return this.monacoRegistry.registerQuickAccessProvider(new TheiaQuickAccessDescriptor( + descriptor, + inner, + descriptor.prefix, + descriptor.helpEntries, + descriptor.placeholder + )); + } + + getQuickAccessProviders(): QuickAccessProviderDescriptor[] { + return this.monacoRegistry.getQuickAccessProviders() + .filter(provider => provider instanceof TheiaQuickAccessDescriptor) + .map(provider => (provider as TheiaQuickAccessDescriptor).theiaDescriptor); + } + getQuickAccessProvider(prefix: string): QuickAccessProviderDescriptor | undefined { + const monacoDescriptor = this.monacoRegistry.getQuickAccessProvider(prefix); + return monacoDescriptor ? (monacoDescriptor as TheiaQuickAccessDescriptor).theiaDescriptor : undefined; + } + clear(): void { + this.monacoRegistry.clear(); + } +} + +class MonacoQuickPickItem implements monaco.quickInput.IAnythingQuickPickItem { + readonly type?: 'item' | 'separator'; + readonly id?: string; + readonly label: string; + readonly meta?: string; + readonly ariaLabel?: string; + readonly description?: string; + readonly detail?: string; + readonly keySequence?: KeySequence; + readonly iconClasses?: string[]; + readonly alwaysShow?: boolean; + readonly highlights?: QuickPickItemHighlights; + + constructor(private readonly item: QuickPickItem) { + this.type = item.type; + this.id = item.id; + this.label = item.label; + this.meta = item.meta; + this.ariaLabel = item.ariaLabel; + this.description = item.description; + this.detail = item.type; + this.keySequence = item.keySequence; // todo fix keybinding + this.iconClasses = item.iconClasses; + this.alwaysShow = item.alwaysShow; + this.highlights = item.highlights; + } + + accept(): void { + if (this.item.execute) { + this.item.execute(); + } + } + +} + +function toMonacoPick(item: QuickPickItem): monaco.quickInput.Pick { + if (QuickPickSeparator.is(item)) { + return item; + } else { + return new MonacoQuickPickItem(item); + } +} + diff --git a/packages/monaco/src/browser/monaco-quick-command-service.ts b/packages/monaco/src/browser/monaco-quick-command-service.ts deleted file mode 100644 index 182ec816afae7..0000000000000 --- a/packages/monaco/src/browser/monaco-quick-command-service.ts +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { KeybindingRegistry, KeySequence, QuickCommandService } from '@theia/core/lib/browser'; -import { Command } from '@theia/core/lib/common/command'; -import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; -import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; - -@injectable() -export class MonacoQuickCommandService extends QuickCommandService implements monaco.quickInput.IQuickAccessDataService { - - private recentItems: Array = []; - private otherItems: Array = []; - - @inject(KeybindingRegistry) - protected readonly keybindingRegistry: KeybindingRegistry; - - registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: CommandsQuickAccessProvider, - prefix: CommandsQuickAccessProvider.PREFIX, - placeholder: '', - helpEntries: [{ description: 'Quick Command', needsEditor: false }] - }); - CommandsQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; - } - - reset(): void { - const { recent, other } = this.getCommands(); - this.recentItems = []; - this.otherItems = []; - this.recentItems.push(...recent.map(command => this.toItem(command))); - this.otherItems.push(...other.map(command => this.toItem(command))); - } - - getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { - const items: Array = []; - if (this.recentItems.length === 0 && this.otherItems.length === 0) { - this.reset(); - } - const recentItems = filterItems(this.recentItems.slice(), filter); - const otherItems = filterItems(this.otherItems.slice(), filter); - - if (recentItems.length > 0) { - items.push({ type: 'separator', label: 'recently used' }, ...recentItems); - } - - if (otherItems.length > 0) { - items.push({ type: 'separator', label: 'other commands' }, ...otherItems); - } - return items; - } - - private toItem(command: Command): monaco.quickInput.IAnythingQuickPickItem { - const label = (command.category) ? `${command.category}: ` + command.label! : command.label!; - const iconClasses = this.getItemIconClasses(command); - const activeElement = window.document.activeElement as HTMLElement; - - return { - label, - iconClasses, - alwaysShow: !!this.commandRegistry.getActiveHandler(command.id), - keybinding: this.getKeybinding(command), - accept: () => { - activeElement.focus({ preventScroll: true }); - this.commandRegistry.executeCommand(command.id); - this.commandRegistry.addRecentCommand(command); - } - }; - } - - private getKeybinding(command: Command): monaco.keybindings.ResolvedKeybinding | undefined { - const keybindings = this.keybindingRegistry.getKeybindingsForCommand(command.id); - if (!keybindings || keybindings.length === 0) { - return undefined; - } - - let keySequence: KeySequence; - try { - keySequence = this.keybindingRegistry.resolveKeybinding(keybindings[0]); - } catch (error) { - return undefined; - } - return new MonacoResolvedKeybinding(keySequence, this.keybindingRegistry); - } - - private getItemIconClasses(command: Command): string[] | undefined { - const toggledHandler = this.commandRegistry.getToggledHandler(command.id); - if (toggledHandler) { - return ['fa fa-check']; - } - return undefined; - } -} - -export class CommandsQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = '>'; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; - - constructor() { - super(CommandsQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: CommandsQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return CommandsQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/monaco/src/browser/monaco-quick-help-service.ts b/packages/monaco/src/browser/monaco-quick-help-service.ts deleted file mode 100644 index dcd8542f71abc..0000000000000 --- a/packages/monaco/src/browser/monaco-quick-help-service.ts +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { QuickHelpService } from '@theia/core/lib/browser'; -import { inject, injectable } from '@theia/core/shared/inversify'; -import { MonacoQuickInputService } from './monaco-quick-input-service'; - -@injectable() -export class MonacoQuickHelpService extends QuickHelpService implements monaco.quickInput.IQuickAccessDataService { - - @inject(MonacoQuickInputService) - protected readonly monacoQuickInputService: MonacoQuickInputService; - - registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: HelpQuickAccessProvider, - prefix: HelpQuickAccessProvider.PREFIX, - placeholder: 'Type "?" to get help on the actions you can take from here.', - helpEntries: [{ description: 'Show all Quick Access Providers', needsEditor: false }] - }); - HelpQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; - } - - getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { - const { editorProviders, globalProviders } = this.getQuickAccessProviders(); - const result = editorProviders.length === 0 || globalProviders.length === 0 ? - // Without groups - [ - ...(editorProviders.length === 0 ? globalProviders : editorProviders) - ] : - - // With groups - [ - { label: 'global commands', type: 'separator' }, - ...globalProviders, - { label: 'editor commands', type: 'separator' }, - ...editorProviders - ]; - return result as monaco.quickInput.Picks; - } - - private getQuickAccessProviders(): { editorProviders: monaco.quickInput.IHelpQuickAccessPickItem[], globalProviders: monaco.quickInput.IHelpQuickAccessPickItem[] } { - const globalProviders: monaco.quickInput.IHelpQuickAccessPickItem[] = []; - const editorProviders: monaco.quickInput.IHelpQuickAccessPickItem[] = []; - - const providers = monaco.platform.Registry.as('workbench.contributions.quickaccess').getQuickAccessProviders(); - - for (const provider of providers.sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) { - if (provider.prefix === HelpQuickAccessProvider.PREFIX) { - continue; // exclude help which is already active - } - - for (const helpEntry of provider.helpEntries) { - const prefix = helpEntry.prefix || provider.prefix; - const label = prefix || '\u2026' /* ... */; - - (helpEntry.needsEditor ? editorProviders : globalProviders).push({ - prefix, - label, - ariaLabel: `${label}, ${helpEntry.description}`, - description: helpEntry.description, - accept: () => this.monacoQuickInputService.open(prefix) - }); - } - } - - return { editorProviders, globalProviders }; - } -} - -export class HelpQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = '?'; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; - - constructor() { - super(HelpQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: HelpQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - protected getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return HelpQuickAccessProvider.dataService.getPicks(filter, token); - } -} diff --git a/packages/monaco/src/browser/monaco-quick-input-service.ts b/packages/monaco/src/browser/monaco-quick-input-service.ts index 9675f54d683ea..ac69cad5979d3 100644 --- a/packages/monaco/src/browser/monaco-quick-input-service.ts +++ b/packages/monaco/src/browser/monaco-quick-input-service.ts @@ -15,16 +15,56 @@ ********************************************************************************/ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { QuickInputService } from '@theia/core/lib/browser'; +import { InputBox, InputOptions, PickOptions, QuickInputButton, QuickInputService, QuickPickItem, QuickPickOptions } from '@theia/core/lib/browser'; import { CancellationToken, Event } from '@theia/core/lib/common'; -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject } from '@theia/core/shared/inversify'; @injectable() -export class MonacoQuickInputService extends QuickInputService implements monaco.quickInput.IQuickInputService { - +export class MonacoQuickInputService implements QuickInputService { + private monacoService: MonacoQuickImplementation; @inject(monaco.contextKeyService.ContextKeyService) protected readonly contextKeyService: monaco.contextKeyService.ContextKeyService; + constructor() { + this.monacoService = new MonacoQuickImplementation(this.contextKeyService); + this.monacoService.init(); + } + + get backButton(): QuickInputButton { + return this.monacoService.backButton; + } + + open(filter: string): void { + this.monacoService.open(filter); + } + + createInputBox(): InputBox { + return this.monacoService.createInputBox(); + } + + input(options?: InputOptions, token?: CancellationToken): Promise { + return this.monacoService.input(); + } + pick>(picks: T[] | Promise, options?: O, token?: CancellationToken): + Promise<(O extends { canPickMany: true; } ? T[] : T) | undefined> { + return this.monacoService.pick(picks, options, token); + } + showQuickPick(items: T[], options?: QuickPickOptions): Promise { + return this.monacoService.showQuickPick(items, options).then(item => { + if (item.execute) { + item.execute(); + } + return item; + }); + } + hide(): void { + return this.monacoService.hide(); + } +} + +@injectable() +class MonacoQuickImplementation implements monaco.quickInput.IQuickInputService { + controller: monaco.quickInput.QuickInputController; quickAccess: monaco.quickInput.IQuickAccessController; @@ -33,14 +73,12 @@ export class MonacoQuickInputService extends QuickInputService implements monaco get backButton(): monaco.quickInput.IQuickInputButton { return this.controller.backButton; } - constructor() { - super(); + constructor(protected readonly contextKeyService: monaco.contextKeyService.ContextKeyService) { this.initContainer(); this.initController(); } - @postConstruct() - protected async init(): Promise { + async init(): Promise { this.quickAccess = new monaco.quickInput.QuickAccessController(this, monaco.services.StaticServices.instantiationService.get()); } @@ -101,6 +139,8 @@ export class MonacoQuickInputService extends QuickInputService implements monaco if (options?.onDidAccept) { options.onDidAccept(); } + quickPick.hide(); + resolve(quickPick.selectedItems[0]); }); quickPick.onDidHide(() => { @@ -128,11 +168,6 @@ export class MonacoQuickInputService extends QuickInputService implements monaco if (options.onDidChangeSelection) { options.onDidChangeSelection(quickPick, selectedItems); } - if (selectedItems[0].execute) { - selectedItems[0].execute(selectedItems[0], quickPick.value); - } - quickPick.hide(); - resolve(selectedItems[0]); }); } diff --git a/packages/monaco/src/browser/monaco-quick-view-service.ts b/packages/monaco/src/browser/monaco-quick-view-service.ts deleted file mode 100644 index ababfaa6c5c9b..0000000000000 --- a/packages/monaco/src/browser/monaco-quick-view-service.ts +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { QuickViewItem, QuickViewService } from '@theia/core/lib/browser/quick-input/quick-view-service'; -import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; -import { Disposable } from '@theia/core/lib/common'; -import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; - -@injectable() -export class MonacoQuickViewService extends QuickViewService implements monaco.quickInput.IQuickAccessDataService { - protected readonly items: (monaco.quickInput.IAnythingQuickPickItem & { when?: string })[] = []; - private hiddenItemLabels = new Set(); - - @inject(ContextKeyService) - protected readonly contextKexService: ContextKeyService; - - registerItem(item: QuickViewItem): Disposable { - const quickOpenItem = { - label: item.label, - accept: () => item.open(), - when: item.when - }; - this.items.push(quickOpenItem); - this.items.sort((a, b) => a.label!.localeCompare(b.label!)); - - return Disposable.create(() => { - const index = this.items.indexOf(quickOpenItem); - if (index !== -1) { - this.items.splice(index, 1); - } - }); - } - - hideItem(label: string): void { - this.hiddenItemLabels.add(label); - } - - showItem(label: string): void { - this.hiddenItemLabels.delete(label); - } - - registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: ViewQuickAccessProvider, - prefix: ViewQuickAccessProvider.PREFIX, - placeholder: '', - helpEntries: [{ description: 'Open View', needsEditor: false }] - }); - ViewQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; - } - - getPicks(filter: string, token: monaco.CancellationToken): monaco.quickInput.Picks { - const items = this.items.filter(item => - (item.when === undefined || this.contextKexService.match(item.when)) && - (!this.hiddenItemLabels.has(item.label)) - ); - return filterItems(items, filter); - } -} - -export class ViewQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = 'view '; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching views' - }; - - constructor() { - super(ViewQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: ViewQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return ViewQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/monaco/src/browser/workspace-symbol-command.ts b/packages/monaco/src/browser/workspace-symbol-command.ts index 8131911ef7065..ce5b1bff9fd75 100644 --- a/packages/monaco/src/browser/workspace-symbol-command.ts +++ b/packages/monaco/src/browser/workspace-symbol-command.ts @@ -16,9 +16,9 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; -import { - KeybindingContribution, KeybindingRegistry, QuickAccessContribution, OpenerService, LabelProvider -} from '@theia/core/lib/browser'; +import { KeybindingContribution, KeybindingRegistry, OpenerService, LabelProvider } from '@theia/core/lib/browser'; + +import { QuickAccessContribution, QuickAccessProvider, QuickInputService, QuickAccessRegistry, QuickPicks, QuickPickItem } from '@theia/core/lib/browser/quick-input'; import { CommandRegistry, CommandHandler, Command, SelectionService, CancellationToken } from '@theia/core'; @@ -26,12 +26,11 @@ import { CommandContribution } from '@theia/core/lib/common'; import { Range, Position, SymbolInformation } from '@theia/core/shared/vscode-languageserver-types'; import { WorkspaceSymbolParams } from '@theia/core/shared/vscode-languageserver-protocol'; import { MonacoLanguages, WorkspaceSymbolProvider } from './monaco-languages'; -import { MonacoQuickInputService } from './monaco-quick-input-service'; import URI from '@theia/core/lib/common/uri'; @injectable() -export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDataService, CommandContribution, KeybindingContribution, CommandHandler, QuickAccessContribution { - readonly prefix = '#'; +export class WorkspaceSymbolCommand implements QuickAccessProvider, CommandContribution, KeybindingContribution, CommandHandler, QuickAccessContribution { + public static readonly PREFIX = '#'; private command: Command = { id: 'languages.workspace.symbol', @@ -40,7 +39,8 @@ export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDat @inject(MonacoLanguages) protected readonly languages: MonacoLanguages; @inject(OpenerService) protected readonly openerService: OpenerService; - @inject(MonacoQuickInputService) protected monacoQuickInputService: MonacoQuickInputService; + @inject(QuickInputService) protected quickInputService: QuickInputService; + @inject(QuickAccessRegistry) protected quickAccessRegistry: QuickAccessRegistry; @inject(SelectionService) protected selectionService: SelectionService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @@ -49,7 +49,7 @@ export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDat } execute(): void { - this.monacoQuickInputService.open(this.prefix); + this.quickInputService.open(WorkspaceSymbolCommand.PREFIX); } registerCommands(commands: CommandRegistry): void { @@ -68,17 +68,16 @@ export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDat } registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: SymbolsQuickAccessProvider, - prefix: SymbolsQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: WorkspaceSymbolCommand.PREFIX, placeholder: '', helpEntries: [{ description: 'Go to Symbol in Workspace', needsEditor: false }] }); - SymbolsQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } - async getPicks(filter: string, token: CancellationToken): Promise> { - const items: Array = []; + async getPicks(filter: string, token: CancellationToken): Promise { + const items: QuickPicks = []; if (this.languages.workspaceSymbolProviders) { const param: WorkspaceSymbolParams = { query: filter @@ -109,7 +108,7 @@ export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDat return items; } - protected createItem(sym: SymbolInformation, provider: WorkspaceSymbolProvider, token: CancellationToken): monaco.quickInput.IAnythingQuickPickItem { + protected createItem(sym: SymbolInformation, provider: WorkspaceSymbolProvider, token: CancellationToken): QuickPickItem { const uri = new URI(sym.location.uri); const iconClasses = this.toCssClassName(sym.kind); let parent = sym.containerName; @@ -122,7 +121,7 @@ export class WorkspaceSymbolCommand implements monaco.quickInput.IQuickAccessDat description: parent, ariaLabel: uri.toString(), iconClasses, - accept: () => { + execute: () => { if (provider.resolveWorkspaceSymbol) { provider.resolveWorkspaceSymbol(sym, token).then(resolvedSymbol => { if (resolvedSymbol) { @@ -183,27 +182,3 @@ enum SymbolKind { Operator = 25, TypeParameter = 26 } - -export class SymbolsQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = '#'; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; - - constructor() { - super(SymbolsQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: SymbolsQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return SymbolsQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts index 4071ee3581802..adf59cc7becc8 100644 --- a/packages/monaco/src/typings/monaco/index.d.ts +++ b/packages/monaco/src/typings/monaco/index.d.ts @@ -1425,7 +1425,6 @@ declare module monaco.quickInput { buttons?: IQuickInputButton[]; picked?: boolean; alwaysShow?: boolean; - execute?: (item: IQuickPickItem, lookFor: string) => void } // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/base/parts/quickinput/common/quickInput.ts#L306 @@ -1707,9 +1706,8 @@ declare module monaco.quickInput { // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/browser/pickerQuickAccess.ts#L92 export class PickerQuickAccessProvider extends Disposable implements IQuickAccessProvider { - constructor(private prefix: string, protected options?: IPickerQuickAccessProviderOptions) { - super(); - } + constructor(prefix: string, options?: IPickerQuickAccessProviderOptions); + provide(picker: IQuickPick, token: CancellationToken): IDisposable; protected getPicks(filter: string, disposables: any, token: CancellationToken): Picks | Promise> | FastAndSlowPicks | null; } @@ -1825,7 +1823,24 @@ declare module monaco.quickInput { provide(picker: monaco.quickInput.IQuickPick, token: CancellationToken): IDisposable; } - // https://github.com/theia-ide/vscode/blob/standalone/0.23.x/src/vs/platform/quickinput/common/quickAccess.ts#L101 + export interface IQuickAccessProviderHelp { + + /** + * The prefix to show for the help entry. If not provided, + * the prefix used for registration will be taken. + */ + prefix?: string; + + /** + * A description text to help understand the intent of the provider. + */ + description: string; + + /** + * Separation between provider for editors and global ones. + */ + needsEditor: boolean; + } export interface IQuickAccessProviderDescriptor { /** * The actual provider that will be instantiated as needed. diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 68d12c4335543..00c30ce9dbb3c 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -93,7 +93,7 @@ import type { import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/base-terminal-protocol'; import { ThemeType } from '@theia/core/lib/browser/theming'; import { Disposable } from '@theia/core/lib/common/disposable'; -import { IPickOptions, QuickInputButtonHandle, QuickPickItem } from '@theia/core/lib/browser'; +import { PickOptions, QuickInputButtonHandle, QuickPickItem } from '@theia/core/lib/browser'; export interface PreferenceData { [scope: number]: any; @@ -600,7 +600,7 @@ export interface IInputBoxOptions { } export interface QuickOpenMain { - $show(instance: number, options: IPickOptions, token: CancellationToken): Promise; + $show(instance: number, options: PickOptions, token: CancellationToken): Promise; $setItems(instance: number, items: TransferQuickPickItems[]): Promise; $setError(instance: number, error: Error): Promise; $input(options: theia.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise; diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index 4737a6088a318..2e6482e11eec6 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -28,7 +28,9 @@ import { TransferQuickInput, TransferQuickInputButton } from '../../common/plugin-api-rpc'; -import { IInputOptions, IPickOptions, QuickInputButton, QuickInputService, QuickPickItem, QuickPickValue } from '@theia/core/lib/browser'; +import { + InputOptions, PickOptions, QuickInputButton, QuickInputService, QuickPickItem, QuickPickValue +} from '@theia/core/lib/browser'; import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; @@ -66,7 +68,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { this.toDispose.dispose(); } - async $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { + async $show(instance: number, options: PickOptions, token: CancellationToken): Promise { const contents = new Promise((resolve, reject) => { this.items[instance] = { resolve, reject }; }); @@ -114,7 +116,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } $input(options: InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise { - const inputOptions: IInputOptions = Object.create(null); + const inputOptions: InputOptions = Object.create(null); if (options) { inputOptions.password = options.password; diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts index 7dd953173aae1..3a640a85071db 100644 --- a/packages/task/src/browser/quick-open-task.ts +++ b/packages/task/src/browser/quick-open-task.ts @@ -19,14 +19,15 @@ import { TaskService } from './task-service'; import { TaskInfo, TaskConfiguration, TaskCustomization, TaskScope, TaskConfigurationScope } from '../common/task-protocol'; import { TaskDefinitionRegistry } from './task-definition-registry'; import URI from '@theia/core/lib/common/uri'; -import { LabelProvider, QuickInputService } from '@theia/core/lib/browser'; +import { LabelProvider, QuickAccessProvider, QuickAccessRegistry, QuickInputService } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; import { PreferenceService } from '@theia/core/lib/browser'; import { TaskNameResolver } from './task-name-resolver'; import { TaskSourceResolver } from './task-source-resolver'; import { TaskConfigurationManager } from './task-configuration-manager'; -import { filterItems, QuickInputButton, QuickPickItem } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { filterItems, QuickInputButton, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { CancellationToken } from '@theia/core/lib/common'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; @@ -34,8 +35,8 @@ export namespace ConfigureTaskAction { } @injectable() -export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService { - readonly prefix: string = 'task '; +export class QuickOpenTask implements QuickAccessProvider { + static readonly PREFIX = 'task '; readonly description: string = 'Run Task'; protected items: Array = []; @@ -45,6 +46,9 @@ export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; + @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -103,7 +107,7 @@ export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService execute: () => this.configure() })); } - this.quickInputService?.open(this.prefix); + this.quickInputService?.open(QuickOpenTask.PREFIX); } attach(): void { @@ -131,7 +135,7 @@ export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService } }); } - this.quickInputService?.open(this.prefix); + this.quickInputService?.open(QuickOpenTask.PREFIX); }); } @@ -275,7 +279,7 @@ export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService this.quickInputService?.showQuickPick(this.items, { placeholder: `Select the ${buildOrTestType} task to run` }); } - async getPicks(filter: string, token: monaco.CancellationToken): Promise> { + async getPicks(filter: string, token: CancellationToken): Promise { if (this.items.length === 0) { await this.init(); } @@ -283,13 +287,12 @@ export class QuickOpenTask implements monaco.quickInput.IQuickAccessDataService } registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: TaskQuickAccessProvider, - prefix: TaskQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: QuickOpenTask.PREFIX, placeholder: 'Select the task to run', helpEntries: [{ description: 'Run Task', needsEditor: false }] }); - TaskQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } protected getRunningTaskLabel(task: TaskInfo): string { @@ -384,10 +387,6 @@ export class TaskRunQuickOpenItem implements monaco.quickInput.IAnythingQuickPic return this.task.detail; } - accept(): void { - this.execute(); - } - execute(): void { const scope = this.task._scope; if (this.taskDefinitionRegistry && !!this.taskDefinitionRegistry.getDefinition(this.task)) { @@ -419,9 +418,6 @@ export class ConfigureBuildOrTestTaskQuickOpenItem extends TaskRunQuickOpenItem ) { super(token, task, taskService, isMulti, taskDefinitionRegistry, taskNameResolver, taskSourceResolver, taskConfigurationManager); } - accept(): void { - this.execute(); - } execute(): void { this.taskService.updateTaskConfiguration(this.token, this.task, { group: { kind: this.isBuildTask ? 'build' : 'test', isDefault: true } }) @@ -686,26 +682,3 @@ export class TaskRestartRunningQuickOpen { } } -export class TaskQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = 'task '; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching tasks' - }; - - constructor() { - super(TaskQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: TaskQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return TaskQuickAccessProvider.dataService?.getPicks(filter, token); - } -} diff --git a/packages/task/src/browser/task-frontend-module.ts b/packages/task/src/browser/task-frontend-module.ts index 56009843b4431..2bb52b4f81de4 100644 --- a/packages/task/src/browser/task-frontend-module.ts +++ b/packages/task/src/browser/task-frontend-module.ts @@ -41,7 +41,7 @@ import { TaskSourceResolver } from './task-source-resolver'; import { TaskTemplateSelector } from './task-templates'; import { TaskTerminalWidgetManager } from './task-terminal-widget-manager'; import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; export default new ContainerModule(bind => { bind(TaskFrontendContribution).toSelf().inSingletonScope(); diff --git a/packages/terminal/src/browser/terminal-frontend-module.ts b/packages/terminal/src/browser/terminal-frontend-module.ts index bd2ef52b6c720..677d3d158aa18 100644 --- a/packages/terminal/src/browser/terminal-frontend-module.ts +++ b/packages/terminal/src/browser/terminal-frontend-module.ts @@ -42,7 +42,7 @@ import { createTerminalSearchFactory } from './search/terminal-search-container' import { TerminalCopyOnSelectionHandler } from './terminal-copy-on-selection-handler'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { TerminalThemeService } from './terminal-theme-service'; -import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access-contribution'; +import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; export default new ContainerModule(bind => { bindTerminalPreferences(bind); diff --git a/packages/terminal/src/browser/terminal-quick-open-service.ts b/packages/terminal/src/browser/terminal-quick-open-service.ts index e5d63cf508291..b16d494f0fdba 100644 --- a/packages/terminal/src/browser/terminal-quick-open-service.ts +++ b/packages/terminal/src/browser/terminal-quick-open-service.ts @@ -17,20 +17,26 @@ import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { QuickAccessContribution, + QuickAccessProvider, + QuickAccessRegistry, QuickInputService } from '@theia/core/lib/browser'; -import { CommandContribution, CommandRegistry, CommandService } from '@theia/core/lib/common'; +import { CancellationToken, CommandContribution, CommandRegistry, CommandService } from '@theia/core/lib/common'; import { TerminalWidget } from './base/terminal-widget'; import { TerminalService } from './base/terminal-service'; import { TerminalCommands } from './terminal-frontend-contribution'; -import { filterItems } from '@theia/core/lib/browser/quick-input/quick-input-service'; +import { filterItems, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service'; @injectable() -export class TerminalQuickOpenService implements monaco.quickInput.IQuickAccessDataService { +export class TerminalQuickOpenService implements QuickAccessProvider { + static readonly PREFIX = 'term '; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; + @inject(QuickAccessRegistry) + protected readonly quickAccessRegistry: QuickAccessRegistry; + @inject(CommandService) protected readonly commandService: CommandService; @@ -38,11 +44,11 @@ export class TerminalQuickOpenService implements monaco.quickInput.IQuickAccessD protected readonly terminalService: TerminalService; open(): void { - this.quickInputService?.open(TerminalQuickAccessProvider.PREFIX); + this.quickInputService?.open(TerminalQuickOpenService.PREFIX); } - async getPicks(filter: string, token: monaco.CancellationToken): Promise> { - const items: Array = []; + async getPicks(filter: string, token: CancellationToken): Promise { + const items: QuickPickItem[] = []; // Get the sorted list of currently opened terminal widgets const widgets: TerminalWidget[] = this.terminalService.all @@ -55,20 +61,19 @@ export class TerminalQuickOpenService implements monaco.quickInput.IQuickAccessD items.push({ label: 'Open New Terminal', iconClasses: ['fa fa-plus'], - accept: () => this.doCreateNewTerminal() + execute: () => this.doCreateNewTerminal() }); return filterItems(items, filter); } registerQuickAccessProvider(): void { - monaco.platform.Registry.as('workbench.contributions.quickaccess').registerQuickAccessProvider({ - ctor: TerminalQuickAccessProvider, - prefix: TerminalQuickAccessProvider.PREFIX, + this.quickAccessRegistry.registerQuickAccessProvider({ + getInstance: () => this, + prefix: TerminalQuickOpenService.PREFIX, placeholder: '', helpEntries: [{ description: 'Show All Opened Terminals', needsEditor: false }] }); - TerminalQuickAccessProvider.dataService = this as monaco.quickInput.IQuickAccessDataService; } /** @@ -95,12 +100,12 @@ export class TerminalQuickOpenService implements monaco.quickInput.IQuickAccessD * @param {TerminalWidget} widget - the terminal widget. * @returns quick pick item. */ - protected toItem(widget: TerminalWidget): monaco.quickInput.IAnythingQuickPickItem { + protected toItem(widget: TerminalWidget): QuickPickItem { return { label: widget.title.label, description: widget.id, ariaLabel: widget.title.label, - accept: () => this.terminalService.open(widget) + execute: () => this.terminalService.open(widget) }; } } @@ -124,27 +129,3 @@ export class TerminalQuickOpenContribution implements CommandContribution, Quick }); } } - -export class TerminalQuickAccessProvider extends monaco.quickInput.PickerQuickAccessProvider { - static PREFIX = 'term '; - static dataService: monaco.quickInput.IQuickAccessDataService; - - private static readonly NO_RESULTS_PICK: monaco.quickInput.IAnythingQuickPickItem = { - label: 'No matching results' - }; - - constructor() { - super(TerminalQuickAccessProvider.PREFIX, { - canAcceptInBackground: true, - noResultsPick: TerminalQuickAccessProvider.NO_RESULTS_PICK - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPicks(filter: string, disposables: any, token: monaco.CancellationToken): monaco.quickInput.Picks - | Promise> - | monaco.quickInput.FastAndSlowPicks - | null { - return TerminalQuickAccessProvider.dataService?.getPicks(filter, token); - } -}