diff --git a/src/vs/editor/contrib/find/browser/simpleFindWidget.contribution.ts b/src/vs/editor/contrib/find/browser/simpleFindWidget.contribution.ts new file mode 100644 index 0000000000000..416b13ebc4ae4 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/simpleFindWidget.contribution.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Command } from 'vs/editor/common/editorCommonExtensions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ISimpleFindWidgetService, KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; +import { SimpleFindWidget, KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED } from 'vs/editor/contrib/find/browser/simpleFindWidget'; + +export const SIMPLE_FIND_IDS = { + ShowWidgetSimpleFindCommand: 'simpleFind.show', + HideWidgetSimpleFindCommand: 'simpleFind.hide', + HistoryNextSimpleFindCommand: 'simpleFind.nextHistory', + HistoryPreviousSimpleFindCommand: 'simpleFind.previousHistory', + FindNextSimpleFindCommand: 'simpleFind.nextMatch', + FindPreviousSimpleFindCommand: 'simpleFind.previousMatch' +}; + + +// This probably should be converted to actions +export abstract class SimpleFindCommand extends Command { + + public abstract runCommand(accessor: ServicesAccessor, args: any): void; + + protected getSimpleFindWidget(accessor: ServicesAccessor): SimpleFindWidget { + const activeSimpleFindWidget = accessor.get(ISimpleFindWidgetService).getActiveSimpleFindWidget() as SimpleFindWidget; + if (activeSimpleFindWidget) { + return activeSimpleFindWidget; + } + return activeSimpleFindWidget; + } + + protected getFocusedSimpleFindWidgetInput(accessor: ServicesAccessor): SimpleFindWidget { + const activeSimpleFindWidgetInput = accessor.get(ISimpleFindWidgetService).getFocusedSimpleFindWidgetInput() as SimpleFindWidget; + if (activeSimpleFindWidgetInput) { + return activeSimpleFindWidgetInput; + } + return null; + } +} + +// These commands may be more appropriate as workbench ids , left them as editor for now + +export class ShowWidgetSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.ShowWidgetSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + accessor.get(ISimpleFindWidgetService).show(); + } +} +const showWidgetSimpleFindCommand = new ShowWidgetSimpleFindCommand({ + id: SIMPLE_FIND_IDS.ShowWidgetSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(showWidgetSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +export class HideWidgetSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.HideWidgetSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + accessor.get(ISimpleFindWidgetService).hide(); + } +} +const hideWidgetSimpleFindCommand = new HideWidgetSimpleFindCommand({ + id: SIMPLE_FIND_IDS.HideWidgetSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE, + kbOpts: { + primary: KeyCode.Escape, + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(hideWidgetSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class HistoryNextSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.HistoryNextSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const simpleFindWidget = this.getFocusedSimpleFindWidgetInput(accessor); + + if (simpleFindWidget) { + simpleFindWidget.showNextFindTerm(); + } + } +} +const historyNextSimpleFindCommand = new HistoryNextSimpleFindCommand({ + id: SIMPLE_FIND_IDS.HistoryNextSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.DownArrow, + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(historyNextSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class HistoryPreviousSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.HistoryPreviousSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const simpleFindWidget = this.getFocusedSimpleFindWidgetInput(accessor); + + if (simpleFindWidget) { + simpleFindWidget.showPreviousFindTerm(); + } + } +} +const historyPreviousSimpleFindCommand = new HistoryPreviousSimpleFindCommand({ + id: SIMPLE_FIND_IDS.HistoryPreviousSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.UpArrow, + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(historyPreviousSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +export class FindNextSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.FindNextSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + accessor.get(ISimpleFindWidgetService).find(false); + } +} +const findNextSimpleFindCommand = new FindNextSimpleFindCommand({ + id: SIMPLE_FIND_IDS.FindNextSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE, + kbOpts: { + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(findNextSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +export class FindPreviousSimpleFindCommand extends SimpleFindCommand { + public static ID = SIMPLE_FIND_IDS.FindPreviousSimpleFindCommand; + + public runCommand(accessor: ServicesAccessor, args: any): void { + accessor.get(ISimpleFindWidgetService).find(true); + } +} +const findPreviousSimpleFindCommand = new FindPreviousSimpleFindCommand({ + id: SIMPLE_FIND_IDS.FindPreviousSimpleFindCommand, + precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE, + kbOpts: { + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] } + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(findPreviousSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/editor/contrib/find/browser/simpleFindWidget.ts b/src/vs/editor/contrib/find/browser/simpleFindWidget.ts index dc564a4ca70e2..7b474ea0f2b40 100644 --- a/src/vs/editor/contrib/find/browser/simpleFindWidget.ts +++ b/src/vs/editor/contrib/find/browser/simpleFindWidget.ts @@ -10,9 +10,17 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as dom from 'vs/base/browser/dom'; import { FindInput } from 'vs/base/browser/ui/findinput/findInput'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService'; import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { HistoryNavigator } from 'vs/base/common/history'; import { SimpleButton } from './findWidget'; +import { Delayer } from 'vs/base/common/async'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; + +// We may need to add a ContextKey to track a focused widget (currently commands are executed within the action context) +// ContextKey to track widget with focused input +export const KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey('simpleFindWidgetInputFocused', undefined); const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -21,14 +29,21 @@ const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next mat const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); export abstract class SimpleFindWidget extends Widget { + protected _findInput: FindInput; protected _domNode: HTMLElement; protected _isVisible: boolean; protected _focusTracker: dom.IFocusTracker; + protected _findInputFocusTracker: dom.IFocusTracker; + protected _findInputFocused: IContextKey; + protected _findHistory: HistoryNavigator; + protected _updateHistoryDelayer: Delayer; constructor( @IContextViewService private _contextViewService: IContextViewService, - private animate: boolean = true + @IContextKeyService private _contextKeyService: IContextKeyService, + @ISimpleFindWidgetService private _simpleFindWidgetService: ISimpleFindWidgetService, + private _animate: boolean = true ) { super(); this._findInput = this._register(new FindInput(null, this._contextViewService, { @@ -36,8 +51,15 @@ export abstract class SimpleFindWidget extends Widget { placeholder: NLS_FIND_INPUT_PLACEHOLDER, })); + // Find History with update delayer + this._findHistory = new HistoryNavigator(); + this._updateHistoryDelayer = new Delayer(500); + + this._findInputFocused = KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED.bindTo(this._contextKeyService); + this.oninput(this._findInput.domNode, (e) => { this.onInputChanged(); + this._delayedUpdateHistory(); }); this._register(this._findInput.onKeyDown((e) => { @@ -100,20 +122,44 @@ export abstract class SimpleFindWidget extends Widget { this._register(this._focusTracker.addFocusListener(this.onFocusTrackerFocus.bind(this))); this._register(this._focusTracker.addBlurListener(this.onFocusTrackerBlur.bind(this))); + this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode)); + this._register(this._findInputFocusTracker.addFocusListener(this._onFindInputFocusTrackerFocus.bind(this))); + this._register(this._findInputFocusTracker.addBlurListener(this._onFindInputFocusTrackerBlur.bind(this))); + this._register(dom.addDisposableListener(this._domNode, 'click', (event) => { event.stopPropagation(); })); } + public abstract find(previous: boolean); protected abstract onInputChanged(); - protected abstract find(previous: boolean); protected abstract onFocusTrackerFocus(); protected abstract onFocusTrackerBlur(); + private _onFindInputFocusTrackerFocus() { + this._findInputFocused.set(true); + this._simpleFindWidgetService.setFocusedSimpleFindWidgetInput(this); + } + + private _onFindInputFocusTrackerBlur() { + this._findInputFocused.reset(); + this._simpleFindWidgetService.setFocusedSimpleFindWidgetInput(null); + } + protected get inputValue() { return this._findInput.getValue(); } + protected _delayedUpdateHistory() { + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + } + + protected _updateHistory() { + if (this.inputValue) { + this._findHistory.add(this._findInput.getValue()); + } + } + public updateTheme(theme?: ITheme): void { let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), @@ -134,12 +180,13 @@ export abstract class SimpleFindWidget extends Widget { return this._domNode; } - public reveal(initialInput?: string): void { + // Reveal is used to show widget and optionally focus on input + public reveal(focusInput: boolean, initialInput?: string): void { if (initialInput) { this._findInput.setValue(initialInput); } - if (this._isVisible) { + if (this._isVisible && focusInput) { this._findInput.select(); return; } @@ -149,12 +196,14 @@ export abstract class SimpleFindWidget extends Widget { setTimeout(() => { dom.addClass(this._domNode, 'visible'); this._domNode.setAttribute('aria-hidden', 'false'); - if (!this.animate) { + if (!this._animate) { dom.addClass(this._domNode, 'noanimation'); } setTimeout(() => { dom.removeClass(this._domNode, 'noanimation'); - this._findInput.select(); + if (focusInput) { + this._findInput.select(); + } }, 200); }, 0); } @@ -167,6 +216,21 @@ export abstract class SimpleFindWidget extends Widget { this._domNode.setAttribute('aria-hidden', 'true'); } } + + public showNextFindTerm() { + let next = this._findHistory.next(); + if (next) { + this._findInput.setValue(next); + } + } + + public showPreviousFindTerm() { + let previous = this._findHistory.previous(); + if (previous) { + this._findInput.setValue(previous); + } + } + } // theming diff --git a/src/vs/editor/contrib/find/browser/simpleFindWidgetService.ts b/src/vs/editor/contrib/find/browser/simpleFindWidgetService.ts new file mode 100644 index 0000000000000..b77ee6c26ddc3 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/simpleFindWidgetService.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SimpleFindWidget } from 'vs/editor/contrib/find/browser/simpleFindWidget'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +// This ContextKey is used to track if any simple find widgets have been instantiated and registered +// We then can use this as the primary precondition for all (Simple)Find commands +// The first key of contextKeys passed into registration is used to determine the current active client +export const KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE = new RawContextKey('simpleFindWidgetInputActive', undefined); + +export const ISimpleFindWidgetService = createDecorator('simpleFindWidgetService'); + +export interface ISimpleFindWidgetService { + + _serviceBrand: any; + + register(simpleFindWidget: SimpleFindWidget, contextKeys: (IContextKey)[]): IDisposable; + getSimpleFindWidgetCount(): number; + getActiveSimpleFindWidget(): SimpleFindWidget; + setFocusedSimpleFindWidgetInput(simpleFindWidget): void; + getFocusedSimpleFindWidgetInput(): SimpleFindWidget; + show(): void; + hide(): void; + find(previous: boolean): void; + nextMatch(): void; + previousMatch(): void; +} + +export const SimpleFindWidgetInputFocusContext = new RawContextKey('simpleFindWidgetInputFocus', false); + + +interface IRegisteredSimpleFindWidget { + widget: SimpleFindWidget; + contextKeys: (IContextKey)[]; +} + +export class SimpleFindWidgetService implements ISimpleFindWidgetService { + + public _serviceBrand: any; + private _activeContextKey: IContextKey; + private _focusedSimpleFindWidgetInput: SimpleFindWidget; + private _simpleFindWidgets: IRegisteredSimpleFindWidget[]; + + constructor( + @IContextKeyService private _contextKeyService: IContextKeyService + ) { + this._focusedSimpleFindWidgetInput = null; + // Maintain instantiated SimpleFindWidgets list + // When all widgets are disposed we reset the active context key + this._simpleFindWidgets = []; + this._activeContextKey = KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE.bindTo(this._contextKeyService); + } + + public register(widget: SimpleFindWidget, contextKeys: (IContextKey)[]): IDisposable { + + // Save registered simple widgets + // Use first context key to track parent owner of widget + const registeredSimpleFindWidget: IRegisteredSimpleFindWidget = { widget, contextKeys }; + this._simpleFindWidgets.push(registeredSimpleFindWidget); + + // Set ContextKey to track any active widget + this._activeContextKey.set(true); + + const toDispose = []; + + // Remove SimpleFindWidget from list once disposed + toDispose.push({ + dispose: () => { + this._simpleFindWidgets.splice(this._simpleFindWidgets.indexOf(registeredSimpleFindWidget), 1); + if (this._simpleFindWidgets.length === 0) { + // No more instantiated/active widgets, reset ContextKey + this._activeContextKey.reset(); + } + } + }); + + return { + dispose: () => dispose(toDispose) + }; + } + + // Track Widget with focused input + public setFocusedSimpleFindWidgetInput(simpleFindWidget): void { + this._focusedSimpleFindWidgetInput = simpleFindWidget; + } + + public getFocusedSimpleFindWidgetInput(): SimpleFindWidget { + if (this._focusedSimpleFindWidgetInput === null) { + // We may want to do something more here + } + return this._focusedSimpleFindWidgetInput; + } + + public getSimpleFindWidgetCount(): number { + return this._simpleFindWidgets.length; + } + + // Get active widget using first registered context key or + // if an input is focused, return that widget + public getActiveSimpleFindWidget(): SimpleFindWidget { + var activeSimpleFindWidget: SimpleFindWidget = null; + var contextMatch = false; + for (let i = 0; i < this._simpleFindWidgets.length; i++) { + const contextKeys = this._simpleFindWidgets[i].contextKeys; + for (let j = 0; j < contextKeys.length; j++) { + contextMatch = contextKeys[j].get(); + if (!contextMatch || contextMatch === undefined) { + break; + } + } + if (contextMatch) { + activeSimpleFindWidget = this._simpleFindWidgets[i].widget; + break; + } + } + return activeSimpleFindWidget; + } + + public hide(): void { + const activeSimpleFindWidget = this.getActiveSimpleFindWidget(); + if (!!activeSimpleFindWidget) { + activeSimpleFindWidget.hide(); + } + } + + public show(): void { + const activeSimpleFindWidget = this.getActiveSimpleFindWidget(); + if (!!activeSimpleFindWidget) { + activeSimpleFindWidget.reveal(true); + } + } + + public find(previous: boolean): void { + // We allow the subclass to use its find function + if (!!this._focusedSimpleFindWidgetInput) { + this._focusedSimpleFindWidgetInput.find(previous); + return; + } + const activeSimpleFindWidget = this.getActiveSimpleFindWidget(); + if (!!activeSimpleFindWidget) { + activeSimpleFindWidget.find(previous); + } + } + + public nextMatch(): void { + if (!!this._focusedSimpleFindWidgetInput) { + this._focusedSimpleFindWidgetInput.find(false); + } + } + + public previousMatch(): void { + if (!!this._focusedSimpleFindWidgetInput) { + this._focusedSimpleFindWidgetInput.find(true); + } + } +} \ No newline at end of file diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index bba9229491deb..9f99baa8f07e7 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -41,4 +41,5 @@ import 'vs/editor/contrib/suggest/browser/suggestController'; import 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; import 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; import 'vs/editor/contrib/wordOperations/common/wordOperations'; -import 'vs/editor/contrib/colorPicker/browser/colorDetector'; \ No newline at end of file +import 'vs/editor/contrib/colorPicker/browser/colorDetector'; +import 'vs/editor/contrib/find/browser/simpleFindWidget.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index e005dc16cddd9..86728f254e05e 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -103,7 +103,7 @@ import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/wo import URI from 'vs/base/common/uri'; import { isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceMigrationService } from 'vs/workbench/services/workspace/node/workspaceMigrationService'; - +import { ISimpleFindWidgetService, SimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; export const MessagesVisibleContext = new RawContextKey('globalMessageVisible', false); export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); export const InZenModeContext = new RawContextKey('inZenMode', false); @@ -639,6 +639,7 @@ export class Workbench implements IPartService { this.toDispose.push(this.quickOpen); this.toShutdown.push(this.quickOpen); serviceCollection.set(IQuickOpenService, this.quickOpen); + serviceCollection.set(ISimpleFindWidgetService, this.instantiationService.createInstance(SimpleFindWidgetService)); // Contributed services const contributedServices = getServices(); diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index fb9961d043e61..06f6e6214ca14 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -24,9 +24,9 @@ import { append, $, addClass, removeClass, finalHandler, join } from 'vs/base/br import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionGalleryService, IExtensionManifest, IKeyBinding, IView } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions'; import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer'; @@ -36,7 +36,6 @@ import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/brow import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; -import WebView from 'vs/workbench/parts/html/browser/webview'; import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -51,9 +50,8 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { Command } from 'vs/editor/common/editorCommonExtensions'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { default as WebView } from 'vs/workbench/parts/html/browser/webview'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; /** A context key that is set when an extension editor webview has focus. */ export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey('extensionEditorWebviewFocus', undefined); @@ -189,6 +187,7 @@ export class ExtensionEditor extends BaseEditor { @IPartService private partService: IPartService, @IContextViewService private contextViewService: IContextViewService, @IContextKeyService private contextKeyService: IContextKeyService, + @ISimpleFindWidgetService private simpleFindWidgetService: ISimpleFindWidgetService ) { super(ExtensionEditor.ID, telemetryService, themeService); this._highlight = null; @@ -334,18 +333,6 @@ export class ExtensionEditor extends BaseEditor { super.changePosition(position); } - showFind(): void { - if (this.activeWebview) { - this.activeWebview.showFind(); - } - } - - hideFind(): void { - if (this.activeWebview) { - this.activeWebview.hideFind(); - } - } - private onNavbarChange(extension: IExtension, id: string): void { this.contentDisposables = dispose(this.contentDisposables); this.content.innerHTML = ''; @@ -366,7 +353,7 @@ export class ExtensionEditor extends BaseEditor { .then(body => { const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : undefined; - this.activeWebview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.contextViewService, this.contextKey, webViewOptions); + this.activeWebview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.contextViewService, this.contextKeyService, this.simpleFindWidgetService, this.contextKey, webViewOptions); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); this.contentDisposables.push(toDisposable(removeLayoutParticipant)); @@ -798,52 +785,3 @@ export class ExtensionEditor extends BaseEditor { } } -class ShowExtensionEditorFindCommand extends Command { - public runCommand(accessor: ServicesAccessor, args: any): void { - const extensionEditor = this.getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.showFind(); - } - } - - private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor { - const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor; - if (activeEditor instanceof ExtensionEditor) { - return activeEditor; - } - return null; - } -} -const showCommand = new ShowExtensionEditorFindCommand({ - id: 'editor.action.extensioneditor.showfind', - precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS, - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F - } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(showCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); - -class HideExtensionEditorFindCommand extends Command { - public runCommand(accessor: ServicesAccessor, args: any): void { - const extensionEditor = this.getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.hideFind(); - } - } - - private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor { - const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor; - if (activeEditor instanceof ExtensionEditor) { - return activeEditor; - } - return null; - } -} -const hideCommand = new ShowExtensionEditorFindCommand({ - id: 'editor.action.extensioneditor.hidefind', - precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS, - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F - } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index bb487e392dc70..15426c30259ab 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -25,7 +25,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import Webview, { WebviewOptions } from './webview'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { WebviewEditor } from './webviewEditor'; - +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; /** * An implementation of editor for showing HTML content in an IFrame by leveraging the HTML input. @@ -52,9 +52,10 @@ export class HtmlPreviewPart extends WebviewEditor { @IPartService private partService: IPartService, @IStorageService storageService: IStorageService, @IContextViewService private _contextViewService: IContextViewService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService private _contextKeyService: IContextKeyService, + @ISimpleFindWidgetService private simpleFindWidgetService: ISimpleFindWidgetService ) { - super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, contextKeyService); + super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, _contextKeyService); } dispose(): void { @@ -84,7 +85,7 @@ export class HtmlPreviewPart extends WebviewEditor { webviewOptions = this.input.options; } - this._webview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this.contextKey, webviewOptions); + this._webview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this._contextKeyService, this.simpleFindWidgetService, this.contextKey, webviewOptions); if (this.input && this.input instanceof HtmlInput) { const state = this.loadViewState(this.input.getResource()); this.scrollYPercentage = state ? state.scrollYPercentage : 0; diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index bf0c18d7e51f2..bb4207d1014fc 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -14,7 +14,9 @@ import { editorBackground, editorForeground } from 'vs/platform/theme/common/col import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; import { WebviewFindWidget } from './webviewFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; + export declare interface WebviewElement extends HTMLElement { src: string; @@ -55,6 +57,7 @@ export interface WebviewOptions { svgWhiteList?: string[]; } + export default class Webview { private static index: number = 0; @@ -70,9 +73,11 @@ export default class Webview { private _findStarted: boolean = false; constructor( - private parent: HTMLElement, + private _parent: HTMLElement, private _styleElement: Element, @IContextViewService private _contextViewService: IContextViewService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @ISimpleFindWidgetService private _simpleFindWidgetService: ISimpleFindWidgetService, private _contextKey: IContextKey, private _options: WebviewOptions = {}, ) { @@ -192,12 +197,14 @@ export default class Webview { }) ); - this._webviewFindWidget = new WebviewFindWidget(this._contextViewService, this); + this._webviewFindWidget = new WebviewFindWidget(this._contextViewService, this._contextKeyService, this._simpleFindWidgetService, this); + // Register and add to disposables + this._disposables.push(this._simpleFindWidgetService.register(this._webviewFindWidget, [this._contextKey])); this._disposables.push(this._webviewFindWidget); - if (parent) { - parent.appendChild(this._webviewFindWidget.getDomNode()); - parent.appendChild(this._webview); + if (_parent) { + _parent.appendChild(this._webviewFindWidget.getDomNode()); + _parent.appendChild(this._webview); } } @@ -372,8 +379,8 @@ export default class Webview { contents.setZoomFactor(factor); - const width = this.parent.clientWidth; - const height = this.parent.clientHeight; + const width = this._parent.clientWidth; + const height = this._parent.clientHeight; contents.setSize({ normal: { width: Math.floor(width * factor), @@ -442,12 +449,4 @@ export default class Webview { this._findStarted = false; this._webview.stopFindInPage(keepSelection ? StopFindInPageActions.keepSelection : StopFindInPageActions.clearSelection); } - - public showFind() { - this._webviewFindWidget.reveal(); - } - - public hideFind() { - this._webviewFindWidget.hide(); - } } diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts index decb70ff67906..189d9e8c02a77 100644 --- a/src/vs/workbench/parts/html/browser/webviewEditor.ts +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -2,20 +2,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + 'use strict'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseWebviewEditor } from 'vs/workbench/browser/parts/editor/webviewEditor'; import { IStorageService } from 'vs/platform/storage/common/storage'; - -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { Command } from 'vs/editor/common/editorCommonExtensions'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ContextKeyExpr, IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; - -import WebView from './webview'; +import { default as WebView } from './webview'; import { Builder } from 'vs/base/browser/builder'; export interface HtmlPreviewEditorViewState { @@ -50,18 +44,6 @@ export abstract class WebviewEditor extends BaseWebviewEditor { } } - public showFind() { - if (this._webview) { - this._webview.showFind(); - } - } - - public hideFind() { - if (this._webview) { - this._webview.hideFind(); - } - } - public updateStyles() { super.updateStyles(); if (this._webview) { @@ -75,53 +57,3 @@ export abstract class WebviewEditor extends BaseWebviewEditor { protected abstract createEditor(parent: Builder); } - -class ShowWebViewEditorFindCommand extends Command { - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = this.getWebViewEditor(accessor); - if (webViewEditor) { - webViewEditor.showFind(); - } - } - - private getWebViewEditor(accessor: ServicesAccessor): WebviewEditor { - const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as WebviewEditor; - if (activeEditor.isWebviewEditor) { - return activeEditor; - } - return null; - } -} -const showFindCommand = new ShowWebViewEditorFindCommand({ - id: 'editor.action.webvieweditor.showFind', - precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F - } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(showFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); - -class HideWebViewEditorFindCommand extends Command { - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = this.getWebViewEditor(accessor); - if (webViewEditor) { - webViewEditor.hideFind(); - } - } - - private getWebViewEditor(accessor: ServicesAccessor): WebviewEditor { - const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as WebviewEditor; - if (activeEditor.isWebviewEditor) { - return activeEditor; - } - return null; - } -} -const hideCommand = new HideWebViewEditorFindCommand({ - id: 'editor.action.webvieweditor.hideFind', - precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, - kbOpts: { - primary: KeyCode.Escape - } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webviewFindWidget.ts b/src/vs/workbench/parts/html/browser/webviewFindWidget.ts index 83dca6ab47727..cb44e33faddea 100644 --- a/src/vs/workbench/parts/html/browser/webviewFindWidget.ts +++ b/src/vs/workbench/parts/html/browser/webviewFindWidget.ts @@ -5,16 +5,19 @@ import { SimpleFindWidget } from 'vs/editor/contrib/find/browser/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import Webview from './webview'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; +import WebView from './webview'; export class WebviewFindWidget extends SimpleFindWidget { constructor( @IContextViewService _contextViewService: IContextViewService, - private webview: Webview + @IContextKeyService _contextKeyService: IContextKeyService, + @ISimpleFindWidgetService _simpleFindWidgetService: ISimpleFindWidgetService, + private _webview: WebView, ) { - super(_contextViewService); - + super(_contextViewService, _contextKeyService, _simpleFindWidgetService); this.find = this.find.bind(this); this.hide = this.hide.bind(this); this.onInputChanged = this.onInputChanged.bind(this); @@ -22,36 +25,39 @@ export class WebviewFindWidget extends SimpleFindWidget { public find(previous) { let val = this.inputValue; - if (this.webview !== null && val) { - this.webview.find(val, { findNext: true, forward: !previous }); + if (this._webview !== null && val) { + if (!this._isVisible) { + this.reveal(false); + } + this._webview.find(val, { findNext: true, forward: !previous }); } }; public hide() { super.hide(); - this.webview.stopFind(true); - this.webview.focus(); + this._webview.stopFind(true); + this._webview.focus(); } public onInputChanged() { - if (!this.webview) { + if (!this._webview) { return; } let val = this.inputValue; if (val) { - this.webview.startFind(val); + this._webview.startFind(val); } else { - this.webview.stopFind(false); + this._webview.stopFind(false); } } protected onFocusTrackerFocus() { - this.webview.notifyFindWidgetFocusChanged(true); + this._webview.notifyFindWidgetFocusChanged(true); } protected onFocusTrackerBlur() { - this.webview.notifyFindWidgetFocusChanged(false); + this._webview.notifyFindWidgetFocusChanged(false); } } \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index b0c8eb3f6a812..c253865ec76ba 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -7,7 +7,7 @@ import Event from 'vs/base/common/event'; import platform = require('vs/base/common/platform'); import { IDisposable } from 'vs/base/common/lifecycle'; -import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -32,6 +32,11 @@ export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE = new RawContextKey /** A context key that is set when the find widget in integrated terminal is not visible. */ export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.toNegated(); +/** A context key that is set when the find widget find input in integrated terminal is focused. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey('terminalFindWidgetInputFocused', undefined); +/** A context key that is set when the find widget find input in integrated terminal is not focused. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED.toNegated(); + export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; export const NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY = 'terminal.integrated.neverSuggestSelectWindowsShell'; @@ -144,6 +149,7 @@ export interface ITerminalService { onInstancesChanged: Event; onInstanceTitleChanged: Event; terminalInstances: ITerminalInstance[]; + terminalFocusContextKey: IContextKey; createInstance(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; getInstanceFromId(terminalId: number): ITerminalInstance; @@ -158,8 +164,7 @@ export interface ITerminalService { showPanel(focus?: boolean): TPromise; hidePanel(): void; - focusFindWidget(): TPromise; - hideFindWidget(): void; + setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; updateConfig(): void; selectDefaultWindowsShell(): TPromise; diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index b556f5b296933..b240d6a9bce75 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -38,6 +38,7 @@ export abstract class TerminalService implements ITerminalService { public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } + public get terminalFocusContextKey(): IContextKey { return this._terminalFocusContextKey; } public abstract get configHelper(): ITerminalConfigHelper; @@ -63,6 +64,7 @@ export abstract class TerminalService implements ITerminalService { lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown())); this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); + this.onInstanceDisposed((terminalInstance) => { this._removeInstance(terminalInstance); }); } @@ -206,9 +208,6 @@ export abstract class TerminalService implements ITerminalService { } } - public abstract focusFindWidget(): TPromise; - public abstract hideFindWidget(): void; - private _getIndexFromId(terminalId: number): number { let terminalIndex = -1; this.terminalInstances.forEach((terminalInstance, i) => { @@ -229,4 +228,4 @@ export abstract class TerminalService implements ITerminalService { public setWorkspaceShellAllowed(isAllowed: boolean): void { this.configHelper.setWorkspaceShellAllowed(isAllowed); } -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index f94473c08b9e1..a398d7cb24ed5 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -12,12 +12,12 @@ import * as nls from 'vs/nls'; import * as panel from 'vs/workbench/browser/panel'; import * as platform from 'vs/base/common/platform'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE, TerminalCursorStyle } from 'vs/workbench/parts/terminal/common/terminal'; import { TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX, TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, FocusTerminalAtIndexAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, FocusTerminalAtIndexAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; @@ -33,6 +33,7 @@ import { QUICKOPEN_ACTION_ID, getQuickNavigateHandler } from 'vs/workbench/brows import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ShowWidgetSimpleFindCommand, HideWidgetSimpleFindCommand, FindNextSimpleFindCommand, FindPreviousSimpleFindCommand } from 'vs/editor/contrib/find/browser/simpleFindWidget.contribution'; const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); @@ -219,8 +220,10 @@ configurationRegistry.registerConfiguration({ FocusSecondGroupAction.ID, FocusThirdGroupAction.ID, SelectAllTerminalAction.ID, - FocusTerminalFindWidgetAction.ID, - HideTerminalFindWidgetAction.ID, + ShowWidgetSimpleFindCommand.ID, + HideWidgetSimpleFindCommand.ID, + FindNextSimpleFindCommand.ID, + FindPreviousSimpleFindCommand.ID, NavigateUpAction.ID, NavigateDownAction.ID, NavigateRightAction.ID, @@ -333,13 +336,7 @@ if (platform.isWindows) { actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { - primary: KeyCode.Escape, - secondary: [KeyCode.Shift | KeyCode.Escape] -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Focus Find Widget', category); + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Backspace, mac: { primary: KeyMod.Alt | KeyCode.Backspace } @@ -349,4 +346,3 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightT mac: { primary: KeyMod.Alt | KeyCode.Delete } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word After Cursor', category); registerColors(); - diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 87e36ec933113..05332fd0f782b 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -662,41 +662,6 @@ export class RenameTerminalAction extends Action { } } -export class FocusTerminalFindWidgetAction extends Action { - - public static ID = 'workbench.action.terminal.focusFindWidget'; - public static LABEL = nls.localize('workbench.action.terminal.focusFindWidget', "Focus Find Widget"); - - constructor( - id: string, label: string, - @ITerminalService private terminalService: ITerminalService - ) { - super(id, label); - } - - public run(): TPromise { - return this.terminalService.focusFindWidget(); - } -} - -export class HideTerminalFindWidgetAction extends Action { - - public static ID = 'workbench.action.terminal.hideFindWidget'; - public static LABEL = nls.localize('workbench.action.terminal.hideFindWidget', "Hide Find Widget"); - - constructor( - id: string, label: string, - @ITerminalService private terminalService: ITerminalService - ) { - super(id, label); - } - - public run(): TPromise { - return TPromise.as(this.terminalService.hideFindWidget()); - } -} - - export class QuickOpenActionTermContributor extends ActionBarContributor { constructor( @@ -759,4 +724,4 @@ export class RenameTerminalQuickOpenAction extends RenameTerminalAction { .then(result => this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null)); return TPromise.as(null); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts index 8c1a2f661d020..90987953fc9a7 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts @@ -5,21 +5,28 @@ import { SimpleFindWidget } from 'vs/editor/contrib/find/browser/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService, } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; export class TerminalFindWidget extends SimpleFindWidget { constructor( @IContextViewService _contextViewService: IContextViewService, - @ITerminalService private _terminalService: ITerminalService + @IContextKeyService _contextKeyService: IContextKeyService, + @ITerminalService private _terminalService: ITerminalService, + @ISimpleFindWidgetService _simpleFindWidgetService: ISimpleFindWidgetService ) { - super(_contextViewService); + super(_contextViewService, _contextKeyService, _simpleFindWidgetService); } public find(previous) { let val = this.inputValue; let instance = this._terminalService.getActiveInstance(); if (instance !== null) { + if (!this._isVisible) { + this.reveal(false); + } if (previous) { instance.findPrevious(val); } else { @@ -44,4 +51,5 @@ export class TerminalFindWidget extends SimpleFindWidget { protected onFocusTrackerBlur() { this._terminalService.getActiveInstance().notifyFindWidgetFocusChanged(false); } + } \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index fa7c86eee1711..f73219c9fa94b 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -26,6 +26,7 @@ import { Panel } from 'vs/workbench/browser/panel'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; export class TerminalPanel extends Panel { @@ -47,7 +48,8 @@ export class TerminalPanel extends Panel { @IInstantiationService private _instantiationService: IInstantiationService, @ITerminalService private _terminalService: ITerminalService, @IThemeService protected themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService + @ITelemetryService telemetryService: ITelemetryService, + @ISimpleFindWidgetService private _simpleFindWidgetService: ISimpleFindWidgetService ) { super(TERMINAL_PANEL_ID, telemetryService, themeService); } @@ -63,6 +65,8 @@ export class TerminalPanel extends Panel { dom.addClass(this._terminalContainer, 'terminal-outer-container'); this._findWidget = this._instantiationService.createInstance(TerminalFindWidget); + // Register SimpleFindWidget + this._register(this._simpleFindWidgetService.register(this._findWidget, [this._terminalService.terminalFocusContextKey])); this._parentDomElement.appendChild(this._themeStyleElement); this._parentDomElement.appendChild(this._fontStyleElement); @@ -161,19 +165,6 @@ export class TerminalPanel extends Panel { } } - public focusFindWidget() { - const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance && activeInstance.hasSelection() && activeInstance.selection.indexOf('\n') === -1) { - this._findWidget.reveal(activeInstance.selection); - } else { - this._findWidget.reveal(); - } - } - - public hideFindWidget() { - this._findWidget.hide(); - } - private _attachEventListeners(): void { this._register(dom.addDisposableListener(this._parentDomElement, 'mousedown', (event: MouseEvent) => { if (this._terminalService.terminalInstances.length === 0) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index 9b20cbbd3b81b..8d9223d15a0a0 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -15,7 +15,7 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IQuickOpenService, IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; -import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY } from 'vs/workbench/parts/terminal/common/terminal'; import { TerminalService as AbstractTerminalService } from 'vs/workbench/parts/terminal/common/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; @@ -24,7 +24,6 @@ import { IChoiceService } from 'vs/platform/message/common/message'; import Severity from 'vs/base/common/severity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal'; -import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; import { IWindowService } from 'vs/platform/windows/common/windows'; export class TerminalService extends AbstractTerminalService implements ITerminalService { @@ -69,23 +68,6 @@ export class TerminalService extends AbstractTerminalService implements ITermina return terminalInstance; } - public focusFindWidget(): TPromise { - return this.showPanel(false).then(() => { - let panel = this._panelService.getActivePanel() as TerminalPanel; - panel.focusFindWidget(); - this._findWidgetVisible.set(true); - }); - } - - public hideFindWidget(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.hideFindWidget(); - this._findWidgetVisible.reset(); - panel.focus(); - } - } - private _suggestShellChange(wasNewTerminalAction?: boolean): void { // Only suggest on Windows since $SHELL works great for macOS/Linux if (!platform.isWindows) { diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index cf0198f4b573f..98d3d351ab1a1 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -14,7 +14,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ReleaseNotesInput } from './releaseNotesInput'; import { EditorOptions } from 'vs/workbench/common/editor'; -import WebView from 'vs/workbench/parts/html/browser/webview'; +import Webview from 'vs/workbench/parts/html/browser/webview'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IModeService } from 'vs/editor/common/services/modeService'; import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; @@ -23,6 +23,7 @@ import { WebviewEditor } from 'vs/workbench/parts/html/browser/webviewEditor'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService'; function renderBody(body: string): string { return ` @@ -52,9 +53,10 @@ export class ReleaseNotesEditor extends WebviewEditor { @IPartService private partService: IPartService, @IStorageService storageService: IStorageService, @IContextViewService private _contextViewService: IContextViewService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService private _contextKeyService: IContextKeyService, + @ISimpleFindWidgetService private simpleFindWidgetService: ISimpleFindWidgetService ) { - super(ReleaseNotesEditor.ID, telemetryService, themeService, storageService, contextKeyService); + super(ReleaseNotesEditor.ID, telemetryService, themeService, storageService, _contextKeyService); } createEditor(parent: Builder): void { @@ -72,42 +74,48 @@ export class ReleaseNotesEditor extends WebviewEditor { this.contentDisposables = dispose(this.contentDisposables); this.content.innerHTML = ''; - await super.setInput(input, options); - - const result = []; - const renderer = new marked.Renderer(); - renderer.code = (code, lang) => { - const modeId = this.modeService.getModeIdForLanguageName(lang); - result.push(this.modeService.getOrCreateMode(modeId)); - return ''; - }; - - marked(text, { renderer }); - await TPromise.join(result); - - renderer.code = (code, lang) => { - const modeId = this.modeService.getModeIdForLanguageName(lang); - return `${tokenizeToString(code, modeId)}`; - }; - - const body = renderBody(marked(text, { renderer })); - this._webview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this.contextKey); - if (this.input && this.input instanceof ReleaseNotesInput) { - const state = this.loadViewState(this.input.version); - if (state) { - this._webview.initialScrollProgress = state.scrollYPercentage; - } - } - this.onThemeChange(this.themeService.getTheme()); - this._webview.contents = [body]; - - this._webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables); - this._webview.onDidScroll(event => { - this.scrollYPercentage = event.scrollYPercentage; - }, null, this.contentDisposables); - this.themeService.onThemeChange(this.onThemeChange, this, this.contentDisposables); - this.contentDisposables.push(this._webview); - this.contentDisposables.push(toDisposable(() => this._webview = null)); + return super.setInput(input, options) + .then(() => { + const result = []; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + result.push(this.modeService.getOrCreateMode(modeId)); + return ''; + }; + + marked(text, { renderer }); + return TPromise.join(result); + }).then(() => { + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + return `${tokenizeToString(code, modeId)}`; + }; + + return marked(text, { renderer }); + }) + .then(renderBody) + .then(body => { + this._webview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this._contextKeyService, this.simpleFindWidgetService, this.contextKey); + + if (this.input && this.input instanceof ReleaseNotesInput) { + const state = this.loadViewState(this.input.version); + if (state) { + this._webview.initialScrollProgress = state.scrollYPercentage; + } + } + this.onThemeChange(this.themeService.getTheme()); + this._webview.contents = [body]; + + this._webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables); + this._webview.onDidScroll(event => { + this.scrollYPercentage = event.scrollYPercentage; + }, null, this.contentDisposables); + this.themeService.onThemeChange(this.onThemeChange, null, this.contentDisposables); + this.contentDisposables.push(this._webview); + this.contentDisposables.push(toDisposable(() => this._webview = null)); + }); } layout(): void {