diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 889b3cac49356..536d41b8e217c 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -14,6 +14,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IDisposable } from 'vs/base/common/lifecycle'; export const ID = 'searchService'; +export const VIEW_ID = 'workbench.view.search'; export const ISearchService = createDecorator(ID); @@ -178,6 +179,7 @@ export interface ISearchConfigurationProperties { followSymlinks: boolean; smartCase: boolean; globalFindClipboard: boolean; + location: 'sidebar' | 'panel'; } export interface ISearchConfiguration extends IFilesConfiguration { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 122d34883f6d0..d0018ceac5cf0 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -29,6 +29,8 @@ import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ISearchConfiguration, VIEW_ID as SEARCH_VIEW_ID } from 'vs/platform/search/common/search'; export class ActivitybarPart extends Part { @@ -56,7 +58,8 @@ export class ActivitybarPart extends Part { @IContextMenuService private contextMenuService: IContextMenuService, @IInstantiationService private instantiationService: IInstantiationService, @IPartService private partService: IPartService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IConfigurationService private configurationService: IConfigurationService ) { super(id, { hasTitle: false }, themeService); @@ -86,6 +89,17 @@ export class ActivitybarPart extends Part { // Deactivate viewlet action on close this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e))); + + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('search.location')) { + const location = this.configurationService.getValue().search.location; + if (location === 'sidebar') { + this.compositeBar.addComposite(this.viewletService.getViewlet(SEARCH_VIEW_ID)); + } else { + this.compositeBar.removeComposite(SEARCH_VIEW_ID); + } + } + })); } public showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable { diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts index b08ad7ef36979..25234941b8d08 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts @@ -81,6 +81,24 @@ export class CompositeBar implements ICompositeBar { return this._onDidContextMenu.event; } + public addComposite(compositeData: { id: string; name: string }): void { + if (this.options.composites.filter(c => c.id === compositeData.id).length) { + return; + } + + this.options.composites.push(compositeData); + this.pin(compositeData.id); + } + + public removeComposite(id: string): void { + if (this.options.composites.filter(c => c.id === id).length === 0) { + return; + } + + this.options.composites = this.options.composites.filter(c => c.id !== id); + this.unpin(id); + } + public activateComposite(id: string): void { if (this.compositeIdToActions[id]) { if (this.compositeIdToActions[this.activeCompositeId]) { diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index daf7329b4301c..a8a017a4a1040 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -30,6 +30,8 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ISearchConfiguration, VIEW_ID as SEARCH_VIEW_ID } from 'vs/platform/search/common/search'; export class PanelPart extends CompositePart implements IPanelService { @@ -54,6 +56,7 @@ export class PanelPart extends CompositePart implements IPanelService { @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, + @IConfigurationService private configurationService: IConfigurationService ) { super( notificationService, @@ -106,10 +109,20 @@ export class PanelPart extends CompositePart implements IPanelService { // Need to relayout composite bar since different panels have different action bar width this.layoutCompositeBar(); })); + this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e))); // Deactivate panel action on close this.toUnbind.push(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId()))); - this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('search.location')) { + const location = this.configurationService.getValue().search.location; + if (location === 'panel') { + this.compositeBar.addComposite(this.getPanel(SEARCH_VIEW_ID)); + } else { + this.compositeBar.removeComposite(SEARCH_VIEW_ID); + } + } + })); } public get onDidPanelOpen(): Event { @@ -155,7 +168,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private getPanel(panelId: string): IPanelIdentifier { - return Registry.as(PanelExtensions.Panels).getPanels().filter(p => p.id === panelId).pop(); + return this.getPanels().filter(p => p.id === panelId).pop(); } private showContextMenu(e: MouseEvent): void { @@ -170,7 +183,10 @@ export class PanelPart extends CompositePart implements IPanelService { } public getPanels(): IPanelIdentifier[] { + const searchConfig = this.configurationService.getValue(); + const excludeSearch = searchConfig.search.location !== 'panel'; return Registry.as(PanelExtensions.Panels).getPanels() + .filter(p => !(p.id === SEARCH_VIEW_ID && excludeSearch)) .sort((v1, v2) => v1.order - v2.order); } diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css index d866df8c8091d..fd40457833c72 100644 --- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -3,12 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.search-viewlet .search-widgets-container { +.vs .panel .search-view .monaco-inputbox { + border: 1px solid #ddd; +} + +.search-view .search-widgets-container { margin: 0px 9px 0 2px; padding-top: 6px; } -.search-viewlet .search-widget .toggle-replace-button { +.search-view .search-widget .toggle-replace-button { position: absolute; top: 0; left: 0; @@ -26,42 +30,42 @@ cursor: pointer; } -.search-viewlet .search-widget .search-container, -.search-viewlet .search-widget .replace-container { +.search-view .search-widget .search-container, +.search-view .search-widget .replace-container { margin-left: 17px; } -.search-viewlet .search-widget .input-box, -.search-viewlet .search-widget .input-box .monaco-inputbox { +.search-view .search-widget .input-box, +.search-view .search-widget .input-box .monaco-inputbox { height: 25px; } -.search-viewlet .search-widget .monaco-findInput { +.search-view .search-widget .monaco-findInput { display: inline-block; vertical-align: middle; } -.search-viewlet .search-widget .replace-container { +.search-view .search-widget .replace-container { margin-top: 6px; position: relative; display: inline-flex; } -.search-viewlet .search-widget .replace-container.disabled { +.search-view .search-widget .replace-container.disabled { display: none; } -.search-viewlet .search-widget .replace-container .monaco-action-bar { +.search-view .search-widget .replace-container .monaco-action-bar { margin-left: 3px; } -.search-viewlet .search-widget .replace-container .monaco-action-bar .action-item .icon { +.search-view .search-widget .replace-container .monaco-action-bar .action-item .icon { background-repeat: no-repeat; width: 20px; height: 25px; } -.search-viewlet .query-clear { +.search-view .query-clear { width: 20px; height: 20px; position: absolute; @@ -70,13 +74,13 @@ cursor: pointer; } -.search-viewlet .query-details { +.search-view .query-details { min-height: 1em; position: relative; margin: 0 0 0 17px; } -.search-viewlet .query-details .more { +.search-view .query-details .more { position: absolute; margin-right: 0.3em; right: 0; @@ -86,38 +90,38 @@ z-index: 2; /* Force it above the search results message, which has a negative top margin */ } -.hc-black .monaco-workbench .search-viewlet .query-details .more, -.vs-dark .monaco-workbench .search-viewlet .query-details .more { +.hc-black .monaco-workbench .search-view .query-details .more, +.vs-dark .monaco-workbench .search-view .query-details .more { background: url('ellipsis-inverse.svg') top center no-repeat; } -.vs .monaco-workbench .search-viewlet .query-details .more { +.vs .monaco-workbench .search-view .query-details .more { background: url('ellipsis.svg') top center no-repeat; } -.search-viewlet .query-details .file-types { +.search-view .query-details .file-types { display: none; } -.search-viewlet .query-details .file-types > .monaco-inputbox { +.search-view .query-details .file-types > .monaco-inputbox { width: 100%; height: 25px; } -.search-viewlet .query-details.more .file-types { +.search-view .query-details.more .file-types { display: inherit; } -.search-viewlet .query-details.more .file-types:last-child { +.search-view .query-details.more .file-types:last-child { padding-bottom: 10px; } -.search-viewlet .query-details .search-pattern-info { +.search-view .query-details .search-pattern-info { width: 16px; height: 16px; } -.search-viewlet .query-details .search-configure-exclusions { +.search-view .query-details .search-configure-exclusions { width: 16px; height: 16px; } @@ -137,7 +141,7 @@ opacity: 0.7; } -.search-viewlet .query-details.more h4 { +.search-view .query-details.more h4 { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -147,18 +151,18 @@ font-weight: normal; } -.search-viewlet .messages { +.search-view .messages { margin-top: -5px; cursor: default; } -.search-viewlet .message { +.search-view .message { padding-left: 22px; padding-right: 22px; padding-top: 0px; } -.search-viewlet .message p:first-child { +.search-view .message p:first-child { margin-top: 0px; margin-bottom: 0px; padding-bottom: 4px; @@ -166,72 +170,72 @@ user-select: text; } -.search-viewlet > .results > .monaco-tree .sub-content { +.search-view > .results > .monaco-tree .sub-content { overflow: hidden; } -.search-viewlet .foldermatch, -.search-viewlet .filematch { +.search-view .foldermatch, +.search-view .filematch { display: flex; position: relative; line-height: 22px; padding: 0; } -.search-viewlet .foldermatch .monaco-icon-label, -.search-viewlet .filematch .monaco-icon-label { +.search-view .foldermatch .monaco-icon-label, +.search-view .filematch .monaco-icon-label { flex: 1; } -.search-viewlet .foldermatch .directory, -.search-viewlet .filematch .directory { +.search-view .foldermatch .directory, +.search-view .filematch .directory { opacity: 0.7; font-size: 0.9em; margin-left: 0.8em; } -.search-viewlet .linematch { +.search-view .linematch { position: relative; line-height: 22px; display: flex; } -.search-viewlet .linematch > .match { +.search-view .linematch > .match { flex: 1; overflow: hidden; text-overflow: ellipsis; } -.search-viewlet .linematch.changedOrRemoved { +.search-view .linematch.changedOrRemoved { font-style: italic; } -.search-viewlet .query-clear { +.search-view .query-clear { background: url("action-query-clear.svg") center center no-repeat; } -.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar { +.search-view .monaco-tree .monaco-tree-row .monaco-action-bar { line-height: 1em; display: none; padding: 0 0.8em 0 0.4em; } -.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar .action-item { +.search-view .monaco-tree .monaco-tree-row .monaco-action-bar .action-item { margin: 0; } -.search-viewlet .monaco-tree .monaco-tree-row.focused .monaco-action-bar { +.search-view .monaco-tree .monaco-tree-row.focused .monaco-action-bar { width: 0; /* in order to support a11y with keyboard, we use width: 0 to hide the actions, which still allows to "Tab" into the actions */ display: block; } -.search-viewlet .monaco-tree .monaco-tree-row:hover:not(.highlighted) .monaco-action-bar, -.search-viewlet .monaco-tree .monaco-tree-row.focused .monaco-action-bar { +.search-view .monaco-tree .monaco-tree-row:hover:not(.highlighted) .monaco-action-bar, +.search-view .monaco-tree .monaco-tree-row.focused .monaco-action-bar { width: inherit; display: block; } -.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar .action-label { +.search-view .monaco-tree .monaco-tree-row .monaco-action-bar .action-label { margin-right: 0.2em; margin-top: 4px; background-repeat: no-repeat; @@ -239,47 +243,47 @@ height: 16px; } -.search-viewlet .action-remove { +.search-view .action-remove { background: url("action-remove.svg") center center no-repeat; } -.search-viewlet .action-replace { +.search-view .action-replace { background-image: url('replace.svg'); } -.search-viewlet .action-replace-all { +.search-view .action-replace-all { background: url('replace-all.svg') center center no-repeat; } -.hc-black .search-viewlet .action-replace, -.vs-dark .search-viewlet .action-replace { +.hc-black .search-view .action-replace, +.vs-dark .search-view .action-replace { background-image: url('replace-inverse.svg'); } -.hc-black .search-viewlet .action-replace-all, -.vs-dark .search-viewlet .action-replace-all { +.hc-black .search-view .action-replace-all, +.vs-dark .search-view .action-replace-all { background: url('replace-all-inverse.svg') center center no-repeat; } -.search-viewlet .label { +.search-view .label { font-style: italic; } -.search-viewlet .monaco-count-badge { +.search-view .monaco-count-badge { margin-right: 12px; } -.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .filematch .monaco-count-badge, -.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .foldermatch .monaco-count-badge, -.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .linematch .monaco-count-badge, -.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .filematch .monaco-count-badge, -.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .foldermatch .monaco-count-badge, -.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .linematch .monaco-count-badge { +.search-view > .results > .monaco-tree .monaco-tree-row:hover .content .filematch .monaco-count-badge, +.search-view > .results > .monaco-tree .monaco-tree-row:hover .content .foldermatch .monaco-count-badge, +.search-view > .results > .monaco-tree .monaco-tree-row:hover .content .linematch .monaco-count-badge, +.search-view > .results > .monaco-tree.focused .monaco-tree-row.focused .content .filematch .monaco-count-badge, +.search-view > .results > .monaco-tree.focused .monaco-tree-row.focused .content .foldermatch .monaco-count-badge, +.search-view > .results > .monaco-tree.focused .monaco-tree-row.focused .content .linematch .monaco-count-badge { display: none; } -.search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove, -.vs-dark .monaco-workbench .search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove { +.search-view .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove, +.vs-dark .monaco-workbench .search-view .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove { background: url("action-remove-focus.svg") center center no-repeat; } @@ -319,93 +323,93 @@ background: url('stop-inverse.svg') center center no-repeat; } -.vs .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern { +.vs .monaco-workbench .search-view .query-details .file-types .controls > .custom-checkbox.pattern { background: url('pattern.svg') center center no-repeat; } -.vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern, -.hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern { +.vs-dark .monaco-workbench .search-view .query-details .file-types .controls > .custom-checkbox.pattern, +.hc-black .monaco-workbench .search-view .query-details .file-types .controls > .custom-checkbox.pattern { background: url('pattern-dark.svg') center center no-repeat; } -.vs .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles { +.vs .monaco-workbench .search-view .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles { background: url('excludeSettings.svg') center center no-repeat; } -.vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles, -.hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles { +.vs-dark .monaco-workbench .search-view .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles, +.hc-black .monaco-workbench .search-view .query-details .file-types .controls>.custom-checkbox.useExcludesAndIgnoreFiles { background: url('excludeSettings-dark.svg') center center no-repeat; } -.search-viewlet .replace.findInFileMatch { +.search-view .replace.findInFileMatch { text-decoration: line-through; } -.search-viewlet .findInFileMatch, -.search-viewlet .replaceMatch { +.search-view .findInFileMatch, +.search-view .replaceMatch { white-space: pre; } -.hc-black .monaco-workbench .search-viewlet .replaceMatch, -.hc-black .monaco-workbench .search-viewlet .findInFileMatch { +.hc-black .monaco-workbench .search-view .replaceMatch, +.hc-black .monaco-workbench .search-view .findInFileMatch { background: none !important; box-sizing: border-box; } -.monaco-workbench .search-viewlet a.prominent { +.monaco-workbench .search-view a.prominent { text-decoration: underline; } /* Theming */ -.vs .search-viewlet .search-widget .toggle-replace-button:hover { +.vs .viewlet .search-view .search-widget .toggle-replace-button:hover { background-color: rgba(0, 0, 0, 0.1) !important; } -.vs-dark .search-viewlet .search-widget .toggle-replace-button:hover { +.vs-dark .viewlet .search-view .search-widget .toggle-replace-button:hover { background-color: rgba(255, 255, 255, 0.1) !important; } -.vs .search-viewlet .search-widget .toggle-replace-button.collapse { +.vs .search-view .search-widget .toggle-replace-button.collapse { background-image: url('expando-collapsed.svg'); } -.vs .search-viewlet .search-widget .toggle-replace-button.expand { +.vs .search-view .search-widget .toggle-replace-button.expand { background-image: url('expando-expanded.svg'); } -.vs-dark .search-viewlet .query-clear { +.vs-dark .search-view .query-clear { background: url("action-query-clear-dark.svg") center center no-repeat; } -.vs-dark .search-viewlet .action-remove, -.hc-black .search-viewlet .action-remove { +.vs-dark .search-view .action-remove, +.hc-black .search-view .action-remove { background: url("action-remove-dark.svg") center center no-repeat; } -.vs-dark .search-viewlet .message { +.vs-dark .search-view .message { opacity: .5; } -.vs-dark .search-viewlet .foldermatch, -.vs-dark .search-viewlet .filematch { +.vs-dark .search-view .foldermatch, +.vs-dark .search-view .filematch { padding: 0; } -.vs-dark .search-viewlet .search-widget .toggle-replace-button.expand, -.hc-black .search-viewlet .search-widget .toggle-replace-button.expand { +.vs-dark .search-view .search-widget .toggle-replace-button.expand, +.hc-black .search-view .search-widget .toggle-replace-button.expand { background-image: url('expando-expanded-dark.svg'); } -.vs-dark .search-viewlet .search-widget .toggle-replace-button.collapse, -.hc-black .search-viewlet .search-widget .toggle-replace-button.collapse { +.vs-dark .search-view .search-widget .toggle-replace-button.collapse, +.hc-black .search-view .search-widget .toggle-replace-button.collapse { background-image: url('expando-collapsed-dark.svg'); } /* High Contrast Theming */ -.hc-black .monaco-workbench .search-viewlet .foldermatch, -.hc-black .monaco-workbench .search-viewlet .filematch, -.hc-black .monaco-workbench .search-viewlet .linematch { +.hc-black .monaco-workbench .search-view .foldermatch, +.hc-black .monaco-workbench .search-view .filematch, +.hc-black .monaco-workbench .search-view .linematch { line-height: 20px; -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index d1695740015ae..99a6fc05b1463 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -10,7 +10,7 @@ import { Action } from 'vs/base/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { INavigator } from 'vs/base/common/iterator'; -import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { SearchView } from 'vs/workbench/parts/search/browser/searchViewlet'; import { Match, FileMatch, FileMatchOrMatch, FolderMatch, RenderableMatch } from 'vs/workbench/parts/search/common/searchModel'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import * as Constants from 'vs/workbench/parts/search/common/constants'; @@ -20,11 +20,13 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OS } from 'vs/base/common/platform'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { VIEW_ID } from 'vs/platform/search/common/search'; -export function isSearchViewletFocused(viewletService: IViewletService): boolean { - let activeViewlet = viewletService.getActiveViewlet(); +export function isSearchViewletFocused(viewletService: IViewletService, panelService: IPanelService): boolean { + let searchView = getSearchView(viewletService, panelService); let activeElement = document.activeElement; - return activeViewlet && activeViewlet.getId() === Constants.VIEWLET_ID && activeElement && DOM.isAncestor(activeElement, (activeViewlet).getContainer().getHTMLElement()); + return searchView && activeElement && DOM.isAncestor(activeElement, searchView.getContainer().getHTMLElement()); } export function appendKeyBindingLabel(label: string, keyBinding: number | ResolvedKeybinding, keyBindingService2: IKeybindingService): string { @@ -36,26 +38,45 @@ export function appendKeyBindingLabel(label: string, keyBinding: number | Resolv } } +export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): TPromise { + if (viewletService.getViewlets().filter(v => v.id === VIEW_ID).length) { + return viewletService.openViewlet(VIEW_ID, focus).then(viewlet => viewlet); + } + + return panelService.openPanel(VIEW_ID, focus).then(panel => panel); +} + +export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView { + const activeViewlet = viewletService.getActiveViewlet(); + if (activeViewlet && activeViewlet.getId() === VIEW_ID) { + return activeViewlet; + } + + const activePanel = panelService.getActivePanel(); + if (activePanel && activePanel.getId() === VIEW_ID) { + return activePanel; + } + + return undefined; +} + function doAppendKeyBindingLabel(label: string, keyBinding: ResolvedKeybinding): string { return keyBinding ? label + ' (' + keyBinding.getLabel() + ')' : label; } export const toggleCaseSensitiveCommand = (accessor: ServicesAccessor) => { - const viewletService = accessor.get(IViewletService); - let searchViewlet = viewletService.getActiveViewlet(); - searchViewlet.toggleCaseSensitive(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + searchView.toggleCaseSensitive(); }; export const toggleWholeWordCommand = (accessor: ServicesAccessor) => { - const viewletService = accessor.get(IViewletService); - let searchViewlet = viewletService.getActiveViewlet(); - searchViewlet.toggleWholeWords(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + searchView.toggleWholeWords(); }; export const toggleRegexCommand = (accessor: ServicesAccessor) => { - const viewletService = accessor.get(IViewletService); - let searchViewlet = viewletService.getActiveViewlet(); - searchViewlet.toggleRegex(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + searchView.toggleRegex(); }; export class ShowNextSearchIncludeAction extends Action { @@ -66,6 +87,7 @@ export class ShowNextSearchIncludeAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, @IContextKeyService private contextKeyService: IContextKeyService ) { super(id, label); @@ -73,8 +95,8 @@ export class ShowNextSearchIncludeAction extends Action { } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; - searchAndReplaceWidget.showNextTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchIncludePattern.showNextTerm(); return TPromise.as(null); } } @@ -87,6 +109,7 @@ export class ShowPreviousSearchIncludeAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, @IContextKeyService private contextKeyService: IContextKeyService ) { super(id, label); @@ -94,8 +117,8 @@ export class ShowPreviousSearchIncludeAction extends Action { } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; - searchAndReplaceWidget.showPreviousTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchIncludePattern.showPreviousTerm(); return TPromise.as(null); } } @@ -108,14 +131,16 @@ export class ShowNextSearchExcludeAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, @IContextKeyService private contextKeyService: IContextKeyService ) { super(id, label); this.enabled = this.contextKeyService.contextMatchesRules(ShowNextSearchExcludeAction.CONTEXT_KEY_EXPRESSION); } + public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; - searchAndReplaceWidget.showNextTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchExcludePattern.showNextTerm(); return TPromise.as(null); } } @@ -128,15 +153,16 @@ export class ShowPreviousSearchExcludeAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, - @IContextKeyService private contextKeyService: IContextKeyService + @IContextKeyService private contextKeyService: IContextKeyService, + @IPanelService private panelService: IPanelService ) { super(id, label); this.enabled = this.contextKeyService.contextMatchesRules(ShowPreviousSearchExcludeAction.CONTEXT_KEY_EXPRESSION); } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; - searchAndReplaceWidget.showPreviousTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchExcludePattern.showPreviousTerm(); return TPromise.as(null); } } @@ -149,16 +175,16 @@ export class ShowNextSearchTermAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, - @IContextKeyService private contextKeyService: IContextKeyService + @IContextKeyService private contextKeyService: IContextKeyService, + @IPanelService private panelService: IPanelService ) { super(id, label); this.enabled = this.contextKeyService.contextMatchesRules(ShowNextSearchTermAction.CONTEXT_KEY_EXPRESSION); - } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; - searchAndReplaceWidget.showNextSearchTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchAndReplaceWidget.showNextSearchTerm(); return TPromise.as(null); } } @@ -171,15 +197,16 @@ export class ShowPreviousSearchTermAction extends Action { constructor(id: string, label: string, @IViewletService private viewletService: IViewletService, - @IContextKeyService private contextKeyService: IContextKeyService + @IContextKeyService private contextKeyService: IContextKeyService, + @IPanelService private panelService: IPanelService ) { super(id, label); this.enabled = this.contextKeyService.contextMatchesRules(ShowPreviousSearchTermAction.CONTEXT_KEY_EXPRESSION); } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; - searchAndReplaceWidget.showPreviousSearchTerm(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchAndReplaceWidget.showPreviousSearchTerm(); return TPromise.as(null); } } @@ -188,12 +215,16 @@ export class FocusNextInputAction extends Action { public static readonly ID = 'search.focus.nextInputBox'; - constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { super(id, label); } public run(): TPromise { - (this.viewletService.getActiveViewlet()).focusNextInputBox(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.focusNextInputBox(); return TPromise.as(null); } } @@ -202,12 +233,16 @@ export class FocusPreviousInputAction extends Action { public static readonly ID = 'search.focus.previousInputBox'; - constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { super(id, label); } public run(): TPromise { - (this.viewletService.getActiveViewlet()).focusPreviousInputBox(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.focusPreviousInputBox(); return TPromise.as(null); } } @@ -223,17 +258,16 @@ export const FocusActiveEditorCommand = (accessor: ServicesAccessor) => { export abstract class FindOrReplaceInFilesAction extends Action { - constructor(id: string, label: string, private viewletService: IViewletService, + constructor(id: string, label: string, private viewletService: IViewletService, private panelService: IPanelService, private expandSearchReplaceWidget: boolean, private selectWidgetText: boolean, private focusReplace: boolean) { super(id, label); } public run(): TPromise { - const viewlet = this.viewletService.getActiveViewlet(); - const searchViewletWasOpen = viewlet && viewlet.getId() === Constants.VIEWLET_ID; - return this.viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet) => { - if (!searchViewletWasOpen || this.expandSearchReplaceWidget) { - const searchAndReplaceWidget = (viewlet).searchAndReplaceWidget; + const searchView = getSearchView(this.viewletService, this.panelService); + return openSearchView(this.viewletService, this.panelService, true).then(openedView => { + if (!searchView || this.expandSearchReplaceWidget) { + const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); // Focus replace only when there is text in the searchInput box const focusReplace = this.focusReplace && searchAndReplaceWidget.searchInput.getValue(); @@ -249,8 +283,11 @@ export class FindInFilesAction extends FindOrReplaceInFilesAction { public static readonly LABEL = nls.localize('findInFiles', "Find in Files"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService, /*expandSearchReplaceWidget=*/false, /*selectWidgetText=*/true, /*focusReplace=*/false); + constructor(id: string, label: string, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService + ) { + super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/false, /*selectWidgetText=*/true, /*focusReplace=*/false); } } @@ -259,87 +296,80 @@ export class ReplaceInFilesAction extends FindOrReplaceInFilesAction { public static readonly ID = 'workbench.action.replaceInFiles'; public static readonly LABEL = nls.localize('replaceInFiles', "Replace in Files"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService, /*expandSearchReplaceWidget=*/true, /*selectWidgetText=*/false, /*focusReplace=*/true); + constructor(id: string, label: string, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService + ) { + super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/true, /*selectWidgetText=*/false, /*focusReplace=*/true); } } export class CloseReplaceAction extends Action { - constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { super(id, label); } public run(): TPromise { - let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; - searchAndReplaceWidget.toggleReplace(false); - searchAndReplaceWidget.focus(); + const searchView = getSearchView(this.viewletService, this.panelService); + searchView.searchAndReplaceWidget.toggleReplace(false); + searchView.searchAndReplaceWidget.focus(); return TPromise.as(null); } } -export abstract class SearchAction extends Action { - - constructor(id: string, label: string, @IViewletService protected viewletService: IViewletService) { - super(id, label); - } - - abstract update(): void; - - protected getSearchViewlet(): SearchViewlet { - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet && activeViewlet.getId() === Constants.VIEWLET_ID) { - return activeViewlet as SearchViewlet; - } - return null; - } -} - -export class RefreshAction extends SearchAction { +export class RefreshAction extends Action { static readonly ID: string = 'search.action.refreshSearchResults'; static LABEL: string = nls.localize('RefreshAction.label', "Refresh"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService); - this.class = 'search-action refresh'; + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'search-action refresh'); this.update(); } update(): void { - const searchViewlet = this.getSearchViewlet(); - this.enabled = searchViewlet && searchViewlet.isSearchSubmitted(); + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = searchView && searchView.isSearchSubmitted(); } public run(): TPromise { - const searchViewlet = this.getSearchViewlet(); - if (searchViewlet) { - searchViewlet.onQueryChanged(true); + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + searchView.onQueryChanged(true); } return TPromise.as(null); } } -export class CollapseDeepestExpandedLevelAction extends SearchAction { +export class CollapseDeepestExpandedLevelAction extends Action { static readonly ID: string = 'search.action.collapseSearchResults'; static LABEL: string = nls.localize('CollapseDeepestExpandedLevelAction.label', "Collapse All"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService); - this.class = 'search-action collapse'; + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'search-action collapse'); this.update(); } update(): void { - const searchViewlet = this.getSearchViewlet(); - this.enabled = searchViewlet && searchViewlet.hasSearchResults(); + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = searchView && searchView.hasSearchResults(); } public run(): TPromise { - const searchViewlet = this.getSearchViewlet(); - if (searchViewlet) { - const viewer = searchViewlet.getControl(); + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + const viewer = searchView.getControl(); if (viewer.getHighlight()) { return TPromise.as(null); // Global action disabled if user is in edit mode from another action } @@ -354,51 +384,55 @@ export class CollapseDeepestExpandedLevelAction extends SearchAction { } } -export class ClearSearchResultsAction extends SearchAction { +export class ClearSearchResultsAction extends Action { static readonly ID: string = 'search.action.clearSearchResults'; static LABEL: string = nls.localize('ClearSearchResultsAction.label', "Clear"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService); - this.class = 'search-action clear-search-results'; + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'search-action clear-search-results'); this.update(); } update(): void { - const searchViewlet = this.getSearchViewlet(); - this.enabled = searchViewlet && searchViewlet.hasSearchResults(); + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = searchView && searchView.hasSearchResults(); } public run(): TPromise { - const searchViewlet = this.getSearchViewlet(); - if (searchViewlet) { - searchViewlet.clearSearchResults(); + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + searchView.clearSearchResults(); } return TPromise.as(null); } } -export class CancelSearchAction extends SearchAction { +export class CancelSearchAction extends Action { static readonly ID: string = 'search.action.cancelSearch'; static LABEL: string = nls.localize('CancelSearchAction.label', "Cancel Search"); - constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { - super(id, label, viewletService); - this.class = 'search-action cancel-search'; + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'search-action cancel-search'); this.update(); } update(): void { - const searchViewlet = this.getSearchViewlet(); - this.enabled = searchViewlet && searchViewlet.isSearching(); + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = searchView && searchView.isSearching(); } public run(): TPromise { - const searchViewlet = this.getSearchViewlet(); - if (searchViewlet) { - searchViewlet.cancelSearch(); + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + searchView.cancelSearch(); } return TPromise.as(null); @@ -409,13 +443,16 @@ export class FocusNextSearchResultAction extends Action { public static readonly ID = 'search.action.focusNextSearchResult'; public static readonly LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); - constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { super(id, label); } public run(): TPromise { - return this.viewletService.openViewlet(Constants.VIEWLET_ID).then(searchViewlet => { - (searchViewlet as SearchViewlet).selectNextMatch(); + return openSearchView(this.viewletService, this.panelService).then(searchView => { + searchView.selectNextMatch(); }); } } @@ -424,13 +461,16 @@ export class FocusPreviousSearchResultAction extends Action { public static readonly ID = 'search.action.focusPreviousSearchResult'; public static readonly LABEL = nls.localize('FocusPreviousSearchResult.label', "Focus Previous Search Result"); - constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService + ) { super(id, label); } public run(): TPromise { - return this.viewletService.openViewlet(Constants.VIEWLET_ID).then(searchViewlet => { - (searchViewlet as SearchViewlet).selectPreviousMatch(); + return openSearchView(this.viewletService, this.panelService).then(searchView => { + searchView.selectPreviousMatch(); }); } } @@ -516,7 +556,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { export class ReplaceAllAction extends AbstractSearchAndReplaceAction { - constructor(private viewer: ITree, private fileMatch: FileMatch, private viewlet: SearchViewlet, + constructor(private viewer: ITree, private fileMatch: FileMatch, private viewlet: SearchView, @IKeybindingService keyBindingService: IKeybindingService) { super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(nls.localize('file.replaceAll.label', "Replace All"), keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), 'action-replace-all'); } @@ -554,7 +594,7 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { export class ReplaceAction extends AbstractSearchAndReplaceAction { - constructor(private viewer: ITree, private element: Match, private viewlet: SearchViewlet, + constructor(private viewer: ITree, private element: Match, private viewlet: SearchView, @IReplaceService private replaceService: IReplaceService, @IKeybindingService keyBindingService: IKeybindingService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 95a1fec354b36..acdb0b26f85f0 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -16,7 +16,7 @@ import { ITree, IDataSource, ISorter, IAccessibilityProvider, IFilter, IRenderer import { Match, SearchResult, FileMatch, FileMatchOrMatch, SearchModel, FolderMatch } from 'vs/workbench/parts/search/common/searchModel'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Range } from 'vs/editor/common/core/range'; -import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { SearchView } from 'vs/workbench/parts/search/browser/searchViewlet'; import { RemoveAction, ReplaceAllAction, ReplaceAction, ReplaceAllInFolderAction } from 'vs/workbench/parts/search/browser/searchActions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; @@ -154,7 +154,7 @@ export class SearchRenderer extends Disposable implements IRenderer { constructor( actionRunner: IActionRunner, - private viewlet: SearchViewlet, + private searchView: SearchView, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService ) { @@ -275,7 +275,7 @@ export class SearchRenderer extends Disposable implements IRenderer { const actions: IAction[] = []; if (input.searchModel.isReplaceActive() && count > 0) { - actions.push(this.instantiationService.createInstance(ReplaceAllAction, tree, fileMatch, this.viewlet)); + actions.push(this.instantiationService.createInstance(ReplaceAllAction, tree, fileMatch, this.searchView)); } actions.push(new RemoveAction(tree, fileMatch)); templateData.actions.push(actions, { icon: true, label: false }); @@ -295,7 +295,7 @@ export class SearchRenderer extends Disposable implements IRenderer { templateData.actions.clear(); if (searchModel.isReplaceActive()) { - templateData.actions.push([this.instantiationService.createInstance(ReplaceAction, tree, match, this.viewlet), new RemoveAction(tree, match)], { icon: true, label: false }); + templateData.actions.push([this.instantiationService.createInstance(ReplaceAction, tree, match, this.searchView), new RemoveAction(tree, match)], { icon: true, label: false }); } else { templateData.actions.push([new RemoveAction(tree, match)], { icon: true, label: false }); } diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 24e553b9c149e..6a9e0b1193916 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -26,11 +26,10 @@ import { Scope } from 'vs/workbench/common/memento'; import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; -import { Viewlet } from 'vs/workbench/browser/viewlet'; import { Match, FileMatch, SearchModel, FileMatchOrMatch, IChangeEvent, ISearchWorkbenchService, FolderMatch } from 'vs/workbench/parts/search/common/searchModel'; import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; -import { ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration, IPatternInfo } from 'vs/platform/search/common/search'; +import { ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration, IPatternInfo, VIEW_ID } from 'vs/platform/search/common/search'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -44,7 +43,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { PatternInputWidget, ExcludePatternInputWidget } from 'vs/workbench/parts/search/browser/patternInputWidget'; import { SearchRenderer, SearchDataSource, SearchSorter, SearchAccessibilityProvider, SearchFilter } from 'vs/workbench/parts/search/browser/searchResultsView'; import { SearchWidget, ISearchWidgetOptions } from 'vs/workbench/parts/search/browser/searchWidget'; -import { RefreshAction, CollapseDeepestExpandedLevelAction, ClearSearchResultsAction, SearchAction, CancelSearchAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { RefreshAction, CollapseDeepestExpandedLevelAction, ClearSearchResultsAction, CancelSearchAction } from 'vs/workbench/parts/search/browser/searchActions'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; @@ -59,9 +58,12 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { SimpleFileResourceDragAndDrop } from 'vs/workbench/browser/dnd'; import { IConfirmation, IConfirmationService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IPanel } from 'vs/workbench/common/panel'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -export class SearchViewlet extends Viewlet { +export class SearchView extends Viewlet implements IViewlet, IPanel { private static readonly MAX_TEXT_RESULTS = 10000; private static readonly SHOW_REPLACE_STORAGE_KEY = 'vs.search.show.replace'; @@ -83,7 +85,7 @@ export class SearchViewlet extends Viewlet { private searchSubmitted: boolean; private searching: boolean; - private actions: SearchAction[] = []; + private actions: (RefreshAction | CollapseDeepestExpandedLevelAction | ClearSearchResultsAction | CancelSearchAction)[] = []; private tree: WorkbenchTree; private viewletSettings: any; private messages: Builder; @@ -124,7 +126,7 @@ export class SearchViewlet extends Viewlet { @IPreferencesService private preferencesService: IPreferencesService, @IThemeService protected themeService: IThemeService ) { - super(Constants.VIEWLET_ID, partService, telemetryService, themeService); + super(VIEW_ID, partService, telemetryService, themeService); this.viewletVisible = Constants.SearchViewletVisibleKey.bindTo(contextKeyService); this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService); @@ -162,7 +164,7 @@ export class SearchViewlet extends Viewlet { this.viewModel = this.searchWorkbenchService.searchModel; let builder: Builder; parent.div({ - 'class': 'search-viewlet' + 'class': 'search-view' }, (div) => { builder = div; }); @@ -297,7 +299,7 @@ export class SearchViewlet extends Viewlet { history: searchHistory }); - if (this.storageService.getBoolean(SearchViewlet.SHOW_REPLACE_STORAGE_KEY, StorageScope.WORKSPACE, true)) { + if (this.storageService.getBoolean(SearchView.SHOW_REPLACE_STORAGE_KEY, StorageScope.WORKSPACE, true)) { this.searchWidget.toggleReplace(true); } @@ -345,9 +347,9 @@ export class SearchViewlet extends Viewlet { const isReplaceShown = this.searchAndReplaceWidget.isReplaceShown(); if (!isReplaceShown) { - this.storageService.store(SearchViewlet.SHOW_REPLACE_STORAGE_KEY, false, StorageScope.WORKSPACE); + this.storageService.store(SearchView.SHOW_REPLACE_STORAGE_KEY, false, StorageScope.WORKSPACE); } else { - this.storageService.remove(SearchViewlet.SHOW_REPLACE_STORAGE_KEY); + this.storageService.remove(SearchView.SHOW_REPLACE_STORAGE_KEY); } } @@ -1015,7 +1017,7 @@ export class SearchViewlet extends Viewlet { const options: IQueryOptions = { extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService), - maxResults: SearchViewlet.MAX_TEXT_RESULTS, + maxResults: SearchView.MAX_TEXT_RESULTS, disregardIgnoreFiles: !useExcludesAndIgnoreFiles, disregardExcludeSettings: !useExcludesAndIgnoreFiles, excludePattern, @@ -1447,7 +1449,7 @@ export class SearchViewlet extends Viewlet { return this.actions; } - private changeActionAtPosition(index: number, newAction: SearchAction): void { + private changeActionAtPosition(index: number, newAction: ClearSearchResultsAction | CancelSearchAction | RefreshAction | CollapseDeepestExpandedLevelAction): void { this.actions.splice(index, 1, newAction); this.updateTitleArea(); } @@ -1499,31 +1501,31 @@ export class SearchViewlet extends Viewlet { registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const matchHighlightColor = theme.getColor(editorFindMatchHighlight); if (matchHighlightColor) { - collector.addRule(`.monaco-workbench .search-viewlet .findInFileMatch { background-color: ${matchHighlightColor}; }`); + collector.addRule(`.monaco-workbench .search-view .findInFileMatch { background-color: ${matchHighlightColor}; }`); } const diffInsertedColor = theme.getColor(diffInserted); if (diffInsertedColor) { - collector.addRule(`.monaco-workbench .search-viewlet .replaceMatch { background-color: ${diffInsertedColor}; }`); + collector.addRule(`.monaco-workbench .search-view .replaceMatch { background-color: ${diffInsertedColor}; }`); } const diffRemovedColor = theme.getColor(diffRemoved); if (diffRemovedColor) { - collector.addRule(`.monaco-workbench .search-viewlet .replace.findInFileMatch { background-color: ${diffRemovedColor}; }`); + collector.addRule(`.monaco-workbench .search-view .replace.findInFileMatch { background-color: ${diffRemovedColor}; }`); } const diffInsertedOutlineColor = theme.getColor(diffInsertedOutline); if (diffInsertedOutlineColor) { - collector.addRule(`.monaco-workbench .search-viewlet .replaceMatch:not(:empty) { border: 1px dashed ${diffInsertedOutlineColor}; }`); + collector.addRule(`.monaco-workbench .search-view .replaceMatch:not(:empty) { border: 1px dashed ${diffInsertedOutlineColor}; }`); } const diffRemovedOutlineColor = theme.getColor(diffRemovedOutline); if (diffRemovedOutlineColor) { - collector.addRule(`.monaco-workbench .search-viewlet .replace.findInFileMatch { border: 1px dashed ${diffRemovedOutlineColor}; }`); + collector.addRule(`.monaco-workbench .search-view .replace.findInFileMatch { border: 1px dashed ${diffRemovedOutlineColor}; }`); } const findMatchHighlightBorder = theme.getColor(editorFindMatchHighlightBorder); if (findMatchHighlightBorder) { - collector.addRule(`.monaco-workbench .search-viewlet .findInFileMatch { border: 1px dashed ${findMatchHighlightBorder}; }`); + collector.addRule(`.monaco-workbench .search-view .findInFileMatch { border: 1px dashed ${findMatchHighlightBorder}; }`); } }); diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index 118941a165282..64f8f8d24b0ed 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -32,6 +32,7 @@ import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/findMode import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ISearchConfigurationProperties } from '../../../../platform/search/common/search'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; export interface ISearchWidgetOptions { value?: string; @@ -405,7 +406,7 @@ export function registerContributions() { when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, CONTEXT_FIND_WIDGET_NOT_VISIBLE), primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.Enter, handler: accessor => { - if (isSearchViewletFocused(accessor.get(IViewletService))) { + if (isSearchViewletFocused(accessor.get(IViewletService), accessor.get(IPanelService))) { ReplaceAllAction.INSTANCE.run(); } } diff --git a/src/vs/workbench/parts/search/common/constants.ts b/src/vs/workbench/parts/search/common/constants.ts index d68fafac729f2..eef6d59f5f116 100644 --- a/src/vs/workbench/parts/search/common/constants.ts +++ b/src/vs/workbench/parts/search/common/constants.ts @@ -5,8 +5,6 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -export const VIEWLET_ID = 'workbench.view.search'; - export const FindInFilesActionId = 'workbench.action.findInFiles'; export const FocusActiveEditorCommandId = 'search.action.focusActiveEditor'; @@ -34,4 +32,4 @@ export const FirstMatchFocusKey = new RawContextKey('firstMatchFocus', export const FileMatchOrMatchFocusKey = new RawContextKey('fileMatchOrMatchFocus', false); export const FileFocusKey = new RawContextKey('fileMatchFocus', false); export const FolderFocusKey = new RawContextKey('folderMatchFocus', false); -export const MatchFocusKey = new RawContextKey('matchFocus', false); \ No newline at end of file +export const MatchFocusKey = new RawContextKey('matchFocus', false); diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index f2c681f8adad0..a2c29a99fb390 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -27,7 +27,6 @@ import { getSelectionSearchString } from 'vs/editor/contrib/find/findController' import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ITree } from 'vs/base/parts/tree/browser/tree'; -import * as searchActions from 'vs/workbench/parts/search/browser/searchActions'; import * as Constants from 'vs/workbench/parts/search/common/constants'; import { registerContributions as replaceContributions } from 'vs/workbench/parts/search/browser/replaceContributions'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/parts/search/browser/searchWidget'; @@ -35,7 +34,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding } from 'vs/editor/contrib/find/findModel'; import { ISearchWorkbenchService, SearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { SearchView } from 'vs/workbench/parts/search/browser/searchViewlet'; import { defaultQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { OpenSymbolHandler } from 'vs/workbench/parts/search/browser/openSymbolHandler'; import { OpenAnythingHandler } from 'vs/workbench/parts/search/browser/openAnythingHandler'; @@ -52,6 +51,10 @@ import { IFileService } from 'vs/platform/files/common/files'; import { distinct } from 'vs/base/common/arrays'; import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/files'; import { Schemas } from 'vs/base/common/network'; +import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { openSearchView, getSearchView, ReplaceAllInFolderAction, ReplaceAllAction, CloseReplaceAction, FocusNextInputAction, FocusPreviousInputAction, FocusNextSearchResultAction, FocusPreviousSearchResultAction, ReplaceInFilesAction, FindInFilesAction, FocusActiveEditorCommand, toggleCaseSensitiveCommand, ShowNextSearchTermAction, ShowPreviousSearchTermAction, toggleRegexCommand, ShowNextSearchExcludeAction, ShowPreviousSearchIncludeAction, ShowNextSearchIncludeAction, ShowPreviousSearchExcludeAction, CollapseDeepestExpandedLevelAction, toggleWholeWordCommand, RemoveAction, ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { VIEW_ID } from 'vs/platform/search/common/search'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService); replaceContributions(); @@ -63,9 +66,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: Constants.SearchViewletVisibleKey, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_J, handler: accessor => { - let viewletService = accessor.get(IViewletService); - viewletService.openViewlet(Constants.VIEWLET_ID, true) - .then((viewlet: SearchViewlet) => viewlet.toggleQueryDetails()); + openSearchView(accessor.get(IViewletService), accessor.get(IPanelService), true) + .then(view => view.toggleQueryDetails()); } }); @@ -75,8 +77,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.FirstMatchFocusKey), primary: KeyCode.UpArrow, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - searchViewlet.focusPreviousInputBox(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + searchView.focusPreviousInputBox(); } }); @@ -89,9 +91,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.WinCtrl | KeyCode.Enter }, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - const tree: ITree = searchViewlet.getControl(); - searchViewlet.open(tree.getFocus(), false, true, true); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + const tree: ITree = searchView.getControl(); + searchView.open(tree.getFocus(), false, true, true); } }); @@ -101,8 +103,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, WorkbenchListFocusContextKey), primary: KeyCode.Escape, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - searchViewlet.cancelSearch(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + searchView.cancelSearch(); } }); @@ -115,9 +117,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.CtrlCmd | KeyCode.Backspace, }, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - const tree: ITree = searchViewlet.getControl(); - accessor.get(IInstantiationService).createInstance(searchActions.RemoveAction, tree, tree.getFocus(), searchViewlet).run(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + const tree: ITree = searchView.getControl(); + accessor.get(IInstantiationService).createInstance(RemoveAction, tree, tree.getFocus(), searchView).run(); } }); @@ -127,9 +129,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - const tree: ITree = searchViewlet.getControl(); - accessor.get(IInstantiationService).createInstance(searchActions.ReplaceAction, tree, tree.getFocus(), searchViewlet).run(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + const tree: ITree = searchView.getControl(); + accessor.get(IInstantiationService).createInstance(ReplaceAction, tree, tree.getFocus(), searchView).run(); } }); @@ -139,9 +141,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - const tree: ITree = searchViewlet.getControl(); - accessor.get(IInstantiationService).createInstance(searchActions.ReplaceAllAction, tree, tree.getFocus(), searchViewlet).run(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + const tree: ITree = searchView.getControl(); + accessor.get(IInstantiationService).createInstance(ReplaceAllAction, tree, tree.getFocus(), searchView).run(); } }); @@ -151,9 +153,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter, handler: (accessor, args: any) => { - const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); - const tree: ITree = searchViewlet.getControl(); - accessor.get(IInstantiationService).createInstance(searchActions.ReplaceAllInFolderAction, tree, tree.getFocus()).run(); + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + const tree: ITree = searchView.getControl(); + accessor.get(IInstantiationService).createInstance(ReplaceAllInFolderAction, tree, tree.getFocus()).run(); } }); @@ -163,27 +165,27 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceInputBoxFocusedKey), primary: KeyCode.Escape, handler: (accessor, args: any) => { - accessor.get(IInstantiationService).createInstance(searchActions.CloseReplaceAction, Constants.CloseReplaceWidgetActionId, '').run(); + accessor.get(IInstantiationService).createInstance(CloseReplaceAction, Constants.CloseReplaceWidgetActionId, '').run(); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: searchActions.FocusNextInputAction.ID, + id: FocusNextInputAction.ID, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.InputBoxFocusedKey), primary: KeyCode.DownArrow, handler: (accessor, args: any) => { - accessor.get(IInstantiationService).createInstance(searchActions.FocusNextInputAction, searchActions.FocusNextInputAction.ID, '').run(); + accessor.get(IInstantiationService).createInstance(FocusNextInputAction, FocusNextInputAction.ID, '').run(); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: searchActions.FocusPreviousInputAction.ID, + id: FocusPreviousInputAction.ID, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.InputBoxFocusedKey, Constants.SearchInputBoxFocusedKey.toNegated()), primary: KeyCode.UpArrow, handler: (accessor, args: any) => { - accessor.get(IInstantiationService).createInstance(searchActions.FocusPreviousInputAction, searchActions.FocusPreviousInputAction.ID, '').run(); + accessor.get(IInstantiationService).createInstance(FocusPreviousInputAction, FocusPreviousInputAction.ID, '').run(); } }); @@ -193,10 +195,11 @@ CommandsRegistry.registerCommand({ handler: (accessor, resource?: URI) => { const listService = accessor.get(IListService); const viewletService = accessor.get(IViewletService); + const panelService = accessor.get(IPanelService); const fileService = accessor.get(IFileService); const resources = getMultiSelectedResources(resource, listService, accessor.get(IWorkbenchEditorService)); - return viewletService.openViewlet(Constants.VIEWLET_ID, true).then(viewlet => { + return openSearchView(viewletService, panelService, true).then(searchView => { if (resources && resources.length) { return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(results => { const folders: URI[] = []; @@ -207,7 +210,7 @@ CommandsRegistry.registerCommand({ } }); - (viewlet as SearchViewlet).searchInFolders(distinct(folders, folder => folder.toString()), (from, to) => relative(from, to)); + searchView.searchInFolders(distinct(folders, folder => folder.toString()), (from, to) => relative(from, to)); }); } @@ -220,9 +223,8 @@ const FIND_IN_WORKSPACE_ID = 'filesExplorer.findInWorkspace'; CommandsRegistry.registerCommand({ id: FIND_IN_WORKSPACE_ID, handler: (accessor) => { - const viewletService = accessor.get(IViewletService); - return viewletService.openViewlet(Constants.VIEWLET_ID, true).then(viewlet => { - (viewlet as SearchViewlet).searchInFolders(null, (from, to) => relative(from, to)); + return openSearchView(accessor.get(IViewletService), accessor.get(IPanelService), true).then(searchView => { + searchView.searchInFolders(null, (from, to) => relative(from, to)); }); } }); @@ -280,8 +282,17 @@ class ShowAllSymbolsAction extends Action { // Register Viewlet Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( - SearchViewlet, - Constants.VIEWLET_ID, + SearchView, + VIEW_ID, + nls.localize('name', "Search"), + 'search', + 10 +)); + +// Register Viewlet +Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + SearchView, + VIEW_ID, nls.localize('name', "Search"), 'search', 10 @@ -291,56 +302,56 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie const registry = Registry.as(ActionExtensions.WorkbenchActions); const category = nls.localize('search', "Search"); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FindInFilesAction, Constants.VIEWLET_ID, nls.localize('showSearchViewlet', "Show Search"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, +registry.registerWorkbenchAction(new SyncActionDescriptor(FindInFilesAction, VIEW_ID, nls.localize('showSearchViewlet', "Show Search"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewletVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, +registry.registerWorkbenchAction(new SyncActionDescriptor(FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchInputBoxFocusedKey.toNegated()), 'Find in Files', category); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.FocusActiveEditorCommandId, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey), - handler: searchActions.FocusActiveEditorCommand, + handler: FocusActiveEditorCommand, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusNextSearchResultAction, searchActions.FocusNextSearchResultAction.ID, searchActions.FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }), 'Focus Next Search Result', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusPreviousSearchResultAction, searchActions.FocusPreviousSearchResultAction.ID, searchActions.FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }), 'Focus Previous Search Result', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }), 'Focus Next Search Result', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }), 'Focus Previous Search Result', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ReplaceInFilesAction, searchActions.ReplaceInFilesAction.ID, searchActions.ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ReplaceInFilesAction, ReplaceInFilesAction.ID, ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleCaseSensitiveCommandId, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey), - handler: searchActions.toggleCaseSensitiveCommand + handler: toggleCaseSensitiveCommand }, ToggleCaseSensitiveKeybinding)); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleWholeWordCommandId, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey), - handler: searchActions.toggleWholeWordCommand + handler: toggleWholeWordCommand }, ToggleWholeWordKeybinding)); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleRegexCommandId, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey), - handler: searchActions.toggleRegexCommand + handler: toggleRegexCommand }, ToggleRegexKeybinding)); // Terms navigation actions -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchTermAction, searchActions.ShowNextSearchTermAction.ID, searchActions.ShowNextSearchTermAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Term', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchTermAction, searchActions.ShowPreviousSearchTermAction.ID, searchActions.ShowPreviousSearchTermAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Term', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextSearchTermAction, ShowNextSearchTermAction.ID, ShowNextSearchTermAction.LABEL, ShowNextFindTermKeybinding, ShowNextSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Term', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchTermAction, ShowPreviousSearchTermAction.ID, ShowPreviousSearchTermAction.LABEL, ShowPreviousFindTermKeybinding, ShowPreviousSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Term', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchIncludeAction, searchActions.ShowNextSearchIncludeAction.ID, searchActions.ShowNextSearchIncludeAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Include Pattern', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchIncludeAction, searchActions.ShowPreviousSearchIncludeAction.ID, searchActions.ShowPreviousSearchIncludeAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Include Pattern', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextSearchIncludeAction, ShowNextSearchIncludeAction.ID, ShowNextSearchIncludeAction.LABEL, ShowNextFindTermKeybinding, ShowNextSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Include Pattern', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchIncludeAction, ShowPreviousSearchIncludeAction.ID, ShowPreviousSearchIncludeAction.LABEL, ShowPreviousFindTermKeybinding, ShowPreviousSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Include Pattern', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchExcludeAction, searchActions.ShowNextSearchExcludeAction.ID, searchActions.ShowNextSearchExcludeAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Exclude Pattern', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchExcludeAction, searchActions.ShowPreviousSearchExcludeAction.ID, searchActions.ShowPreviousSearchExcludeAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Exclude Pattern', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextSearchExcludeAction, ShowNextSearchExcludeAction.ID, ShowNextSearchExcludeAction.LABEL, ShowNextFindTermKeybinding, ShowNextSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Next Search Exclude Pattern', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchExcludeAction, ShowPreviousSearchExcludeAction.ID, ShowPreviousSearchExcludeAction.LABEL, ShowPreviousFindTermKeybinding, ShowPreviousSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Search: Show Previous Search Exclude Pattern', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.CollapseDeepestExpandedLevelAction, searchActions.CollapseDeepestExpandedLevelAction.ID, searchActions.CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); @@ -375,68 +386,73 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen // Configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ - 'id': 'search', - 'order': 13, - 'title': nls.localize('searchConfigurationTitle', "Search"), - 'type': 'object', - 'properties': { + id: 'search', + order: 13, + title: nls.localize('searchConfigurationTitle', "Search"), + type: 'object', + properties: { 'search.exclude': { - 'type': 'object', - 'description': nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the files.exclude setting."), - 'default': { '**/node_modules': true, '**/bower_components': true }, - 'additionalProperties': { - 'anyOf': [ + type: 'object', + description: nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the files.exclude setting."), + default: { '**/node_modules': true, '**/bower_components': true }, + additionalProperties: { + anyOf: [ { - 'type': 'boolean', - 'description': nls.localize('exclude.boolean', "The glob pattern to match file paths against. Set to true or false to enable or disable the pattern."), + type: 'boolean', + description: nls.localize('exclude.boolean', "The glob pattern to match file paths against. Set to true or false to enable or disable the pattern."), }, { - 'type': 'object', - 'properties': { - 'when': { - 'type': 'string', // expression ({ "**/*.js": { "when": "$(basename).js" } }) - 'pattern': '\\w*\\$\\(basename\\)\\w*', - 'default': '$(basename).ext', - 'description': nls.localize('exclude.when', 'Additional check on the siblings of a matching file. Use $(basename) as variable for the matching file name.') + type: 'object', + properties: { + when: { + type: 'string', // expression ({ "**/*.js": { "when": "$(basename).js" } }) + pattern: '\\w*\\$\\(basename\\)\\w*', + default: '$(basename).ext', + description: nls.localize('exclude.when', 'Additional check on the siblings of a matching file. Use $(basename) as variable for the matching file name.') } } } ] }, - 'scope': ConfigurationScope.RESOURCE + scope: ConfigurationScope.RESOURCE }, 'search.useRipgrep': { - 'type': 'boolean', - 'description': nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search"), - 'default': true + type: 'boolean', + description: nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search"), + default: true }, 'search.useIgnoreFiles': { - 'type': 'boolean', - 'description': nls.localize('useIgnoreFiles', "Controls whether to use .gitignore and .ignore files when searching for files."), - 'default': true, - 'scope': ConfigurationScope.RESOURCE + type: 'boolean', + description: nls.localize('useIgnoreFiles', "Controls whether to use .gitignore and .ignore files when searching for files."), + default: true, + scope: ConfigurationScope.RESOURCE }, 'search.quickOpen.includeSymbols': { - 'type': 'boolean', - 'description': nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."), - 'default': false + type: 'boolean', + description: nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."), + default: false }, 'search.followSymlinks': { - 'type': 'boolean', - 'description': nls.localize('search.followSymlinks', "Controls whether to follow symlinks while searching."), - 'default': true + type: 'boolean', + description: nls.localize('search.followSymlinks', "Controls whether to follow symlinks while searching."), + default: true }, 'search.smartCase': { - 'type': 'boolean', - 'description': nls.localize('search.smartCase', "Searches case-insensitively if the pattern is all lowercase, otherwise, searches case-sensitively"), - 'default': false + type: 'boolean', + description: nls.localize('search.smartCase', "Searches case-insensitively if the pattern is all lowercase, otherwise, searches case-sensitively"), + default: false }, 'search.globalFindClipboard': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('search.globalFindClipboard', "Controls if the Search Viewlet should read or modify the shared find clipboard on macOS"), - 'included': platform.isMacintosh - } + type: 'boolean', + default: false, + description: nls.localize('search.globalFindClipboard', "Controls if the Search Viewlet should read or modify the shared find clipboard on macOS"), + included: platform.isMacintosh + }, + 'search.location': { + enum: ['sidebar', 'panel'], + default: 'sidebar', + description: nls.localize('search.location', "Controls if the search will be shown as a viewlet in the sidebar or as a panel in the panel area for more horizontal space"), + }, } }); diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 13bd32dbc2a7b..bee988e64775c 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -34,7 +34,7 @@ export interface IPanelService { getActivePanel(): IPanel; /** - * Returns all registered panels + * Returns all enabled panels */ getPanels(): IPanelIdentifier[]; } diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index a556e8389a72b..a760592959182 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -40,7 +40,7 @@ export interface IViewletService { getViewlet(id: string): ViewletDescriptor; /** - * Returns all registered viewlets + * Returns all enabled viewlets */ getViewlets(): ViewletDescriptor[]; diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index 414a9b65990aa..e6b5d3de0abf6 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -15,6 +15,8 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { ISearchConfiguration, VIEW_ID as SEARCH_VIEW_ID } from 'vs/platform/search/common/search'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const ActiveViewletContextId = 'activeViewlet'; export const ActiveViewletContext = new RawContextKey(ActiveViewletContextId, ''); @@ -38,7 +40,8 @@ export class ViewletService implements IViewletService { constructor( sidebarPart: SidebarPart, @IExtensionService private extensionService: IExtensionService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private configurationService: IConfigurationService ) { this.sidebarPart = sidebarPart; this.viewletRegistry = Registry.as(ViewletExtensions.Viewlets); @@ -108,8 +111,11 @@ export class ViewletService implements IViewletService { public getViewlets(): ViewletDescriptor[] { const builtInViewlets = this.getBuiltInViewlets(); + const viewlets = builtInViewlets.concat(this.extensionViewlets); + const searchConfig = this.configurationService.getValue(); + const excludeSearch = searchConfig.search.location !== 'sidebar'; - return builtInViewlets.concat(this.extensionViewlets); + return viewlets.filter(v => !(v.id === SEARCH_VIEW_ID && excludeSearch)); } private getBuiltInViewlets(): ViewletDescriptor[] { diff --git a/test/smoke/src/areas/search/search.ts b/test/smoke/src/areas/search/search.ts index dbd995bcd723a..f2d98866b0249 100644 --- a/test/smoke/src/areas/search/search.ts +++ b/test/smoke/src/areas/search/search.ts @@ -6,7 +6,7 @@ import { SpectronApplication } from '../../spectron/application'; import { Viewlet } from '../workbench/viewlet'; -const VIEWLET = 'div[id="workbench.view.search"] .search-viewlet'; +const VIEWLET = 'div[id="workbench.view.search"] .search-view'; const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox input`; const INCLUDE_INPUT = `${VIEWLET} .query-details .monaco-inputbox input[aria-label="Search Include Patterns"]`; @@ -84,4 +84,4 @@ export class Search extends Viewlet { async waitForResultText(text: string): Promise { await this.spectron.client.waitForText(`${VIEWLET} .messages[aria-hidden="false"] .message>p`, text); } -} \ No newline at end of file +}