searchActions.ts 14.4 KB
Newer Older
S
Sandeep Somavarapu 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import nls = require('vs/nls');
7
import DOM = require('vs/base/browser/dom');
S
Sandeep Somavarapu 已提交
8 9 10 11 12 13 14
import errors = require('vs/base/common/errors');
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { Action } from 'vs/base/common/actions';
import { ToggleViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { ITree } from 'vs/base/parts/tree/browser/tree';
S
Sandeep Somavarapu 已提交
15
import { INavigator } from 'vs/base/common/iterator';
S
Sandeep Somavarapu 已提交
16
import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet';
17 18
import { SearchResult, Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel';
import { IReplaceService } from 'vs/workbench/parts/search/common/replace';
S
Sandeep Somavarapu 已提交
19 20 21 22
import * as Constants from 'vs/workbench/parts/search/common/constants';
import { CollapseAllAction as TreeCollapseAction } from 'vs/base/parts/tree/browser/treeDefaults';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { OpenGlobalSettingsAction } from 'vs/workbench/browser/actions/openSettings';
23
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
24
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
A
Alexandru Dima 已提交
25 26
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Keybinding } from 'vs/base/common/keybinding';
27
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
S
Sandeep Somavarapu 已提交
28
import { asFileEditorInput } from 'vs/workbench/common/editor';
S
Sandeep Somavarapu 已提交
29

S
Sandeep Somavarapu 已提交
30 31 32
export function isSearchViewletFocussed(viewletService: IViewletService): boolean {
	let activeViewlet = viewletService.getActiveViewlet();
	let activeElement = document.activeElement;
33 34 35
	return activeViewlet && activeViewlet.getId() === Constants.VIEWLET_ID && activeElement && DOM.isAncestor(activeElement, (<SearchViewlet>activeViewlet).getContainer().getHTMLElement());
}

S
Sandeep Somavarapu 已提交
36 37 38 39
export function appendKeyBindingLabel(label: string, keyBinding: Keybinding, keyBindingService2: IKeybindingService): string
export function appendKeyBindingLabel(label: string, keyBinding: number, keyBindingService2: IKeybindingService): string
export function appendKeyBindingLabel(label: string, keyBinding: any, keyBindingService2: IKeybindingService): string {
	keyBinding = typeof keyBinding === 'number' ? new Keybinding(keyBinding) : keyBinding;
A
Alex Dima 已提交
40
	return label + ' (' + keyBindingService2.getLabelFor(keyBinding) + ')';
41 42
}

S
Sandeep Somavarapu 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
export class ToggleCaseSensitiveAction extends Action {

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		let searchViewlet = <SearchViewlet>this.viewletService.getActiveViewlet();
		searchViewlet.toggleCaseSensitive();
		return TPromise.as(null);
	}
}

export class ToggleWholeWordAction extends Action {

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		let searchViewlet = <SearchViewlet>this.viewletService.getActiveViewlet();
		searchViewlet.toggleWholeWords();
		return TPromise.as(null);
	}
}

export class ToggleRegexAction extends Action {

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		let searchViewlet = <SearchViewlet>this.viewletService.getActiveViewlet();
		searchViewlet.toggleRegex();
		return TPromise.as(null);
	}
}

82 83
export class ShowNextSearchTermAction extends Action {

84
	public static ID = 'search.history.showNext';
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
	public static LABEL = nls.localize('nextSearchTerm', "Show next search term");

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		let searchAndReplaceWidget = (<SearchViewlet>this.viewletService.getActiveViewlet()).searchAndReplaceWidget;
		searchAndReplaceWidget.showNextSearchTerm();
		return TPromise.as(null);
	}
}

export class ShowPreviousSearchTermAction extends Action {

100
	public static ID = 'search.history.showPrevious';
101 102 103 104 105 106 107 108 109 110 111 112 113
	public static LABEL = nls.localize('previousSearchTerm', "Show previous search term");

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		let searchAndReplaceWidget = (<SearchViewlet>this.viewletService.getActiveViewlet()).searchAndReplaceWidget;
		searchAndReplaceWidget.showPreviousSearchTerm();
		return TPromise.as(null);
	}
}

114 115 116
export class FocusNextInputAction extends Action {

	public static ID = 'search.focus.nextInputBox';
S
Sandeep Somavarapu 已提交
117
	public static LABEL = nls.localize('focusNextInputBox', "Focus next input box");
118 119 120 121 122 123 124 125 126 127 128 129 130

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		(<SearchViewlet>this.viewletService.getActiveViewlet()).focusNextInputBox();
		return TPromise.as(null);
	}
}

export class FocusPreviousInputAction extends Action {

S
Sandeep Somavarapu 已提交
131 132
	public static ID = 'search.focus.previousInputBox';
	public static LABEL = nls.localize('focusPreviousInputBox', "Focus previous input box");
133 134 135 136 137 138 139 140 141 142 143

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		(<SearchViewlet>this.viewletService.getActiveViewlet()).focusPreviousInputBox();
		return TPromise.as(null);
	}
}

S
Sandeep Somavarapu 已提交
144 145 146 147 148 149 150 151
export class OpenSearchViewletAction extends ToggleViewletAction {

	public static ID = Constants.VIEWLET_ID;
	public static LABEL = nls.localize('showSearchViewlet', "Show Search");

	constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IWorkbenchEditorService editorService: IWorkbenchEditorService) {
		super(id, label, Constants.VIEWLET_ID, viewletService, editorService);
	}
152

S
Sandeep Somavarapu 已提交
153 154
}

S
Sandeep Somavarapu 已提交
155 156 157 158 159 160 161 162 163 164 165
export class ReplaceInFilesAction extends Action {

	public static ID = 'workbench.action.replaceInFiles';
	public static LABEL = nls.localize('replaceInFiles', "Replace in Files");

	constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) {
		super(id, label);
	}

	public run(): TPromise<any> {
		return this.viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet) => {
S
Sandeep Somavarapu 已提交
166
			let searchAndReplaceWidget = (<SearchViewlet>viewlet).searchAndReplaceWidget;
167 168
			searchAndReplaceWidget.toggleReplace(true);
			searchAndReplaceWidget.focus(false, true);
S
Sandeep Somavarapu 已提交
169 170 171 172
		});
	}
}

S
Sandeep Somavarapu 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
export class FindInFolderAction extends Action {

	private resource: URI;

	constructor(resource: URI, @IViewletService private viewletService: IViewletService) {
		super('workbench.search.action.findInFolder', nls.localize('findInFolder', "Find in Folder"));

		this.resource = resource;
	}

	public run(event?: any): TPromise<any> {
		return this.viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet: SearchViewlet) => {
			viewlet.searchInFolder(this.resource);
		});
	}
}

export class RefreshAction extends Action {

	constructor(private viewlet: SearchViewlet) {
		super('refresh');

		this.label = nls.localize('RefreshAction.label', "Refresh");
		this.enabled = false;
		this.class = 'search-action refresh';
	}

	public run(): TPromise<void> {
		this.viewlet.onQueryChanged(true);

		return TPromise.as(null);
	}
}

export class CollapseAllAction extends TreeCollapseAction {

	constructor(viewlet: SearchViewlet) {
		super(viewlet.getControl(), false);
		this.class = 'search-action collapse';
	}
}

export class ClearSearchResultsAction extends Action {

	constructor(private viewlet: SearchViewlet) {
		super('clearSearchResults');

		this.label = nls.localize('ClearSearchResultsAction.label', "Clear Search Results");
		this.enabled = false;
		this.class = 'search-action clear-search-results';
	}

	public run(): TPromise<void> {
		this.viewlet.clearSearchResults();

		return TPromise.as(null);
	}
}

S
Sandeep Somavarapu 已提交
232 233
export abstract class AbstractSearchAndReplaceAction extends Action {

S
Sandeep Somavarapu 已提交
234 235 236
	/**
	 * Returns element to focus after removing the given element
	 */
S
Sandeep Somavarapu 已提交
237
	public getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: FileMatchOrMatch): FileMatchOrMatch {
S
Sandeep Somavarapu 已提交
238 239 240 241 242 243
		let elementToFocus = this.getNextElementAfterRemoved(viewer, elementToBeRemoved);
		if (!elementToFocus) {
			elementToFocus = this.getPreviousElementAfterRemoved(viewer, elementToBeRemoved);
		}
		return elementToFocus;
	}
S
Sandeep Somavarapu 已提交
244

S
Sandeep Somavarapu 已提交
245
	public getNextElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
S
Sandeep Somavarapu 已提交
246 247 248 249 250 251 252 253 254
		let navigator: INavigator<any> = this.getNavigatorAt(element, viewer);
		if (element instanceof FileMatch) {
			// If file match is removed then next element is the next file match
			while (!!navigator.next() && !(navigator.current() instanceof FileMatch)) { };
		} else {
			navigator.next();
		}
		return navigator.current();
	}
S
Sandeep Somavarapu 已提交
255

S
Sandeep Somavarapu 已提交
256
	public getPreviousElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
S
Sandeep Somavarapu 已提交
257 258 259 260 261 262
		let navigator: INavigator<any> = this.getNavigatorAt(element, viewer);
		let previousElement = navigator.previous();
		if (element instanceof Match && element.parent().matches().length === 1) {
			// If this is the only match, then the file match is also removed
			// Hence take the previous element to file match
			previousElement = navigator.previous();
S
Sandeep Somavarapu 已提交
263
		}
S
Sandeep Somavarapu 已提交
264 265 266 267 268 269 270
		return previousElement;
	}

	private getNavigatorAt(element: FileMatchOrMatch, viewer: ITree): INavigator<any> {
		let navigator:INavigator<any> = viewer.getNavigator();
		while (navigator.current() !== element && !!navigator.next()) { };
		return navigator;
S
Sandeep Somavarapu 已提交
271 272 273 274
	}
}

export class RemoveAction extends AbstractSearchAndReplaceAction {
S
Sandeep Somavarapu 已提交
275

276 277
	constructor(private viewer: ITree, private element: FileMatchOrMatch) {
		super('remove', nls.localize('RemoveAction.label', "Remove"), 'action-remove');
S
Sandeep Somavarapu 已提交
278 279
	}

280
	public run(): TPromise<any> {
S
Sandeep Somavarapu 已提交
281
		let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.element);
S
Sandeep Somavarapu 已提交
282 283
		if (nextFocusElement) {
			this.viewer.setFocus(nextFocusElement);
284 285
		}

286
		let elementToRefresh: any;
287
		if (this.element instanceof FileMatch) {
S
Sandeep Somavarapu 已提交
288
			let parent: SearchResult = <SearchResult>this.element.parent();
289
			parent.remove(<FileMatch>this.element);
S
Sandeep Somavarapu 已提交
290
			elementToRefresh = parent;
S
Sandeep Somavarapu 已提交
291
		} else {
S
Sandeep Somavarapu 已提交
292
			let parent: FileMatch = <FileMatch>this.element.parent();
293
			parent.remove(<Match>this.element);
S
Sandeep Somavarapu 已提交
294
			elementToRefresh = parent.count() === 0 ? parent.parent() : parent;
S
Sandeep Somavarapu 已提交
295
		}
296

S
Sandeep Somavarapu 已提交
297
		this.viewer.DOMFocus();
298 299 300
		return this.viewer.refresh(elementToRefresh);
	}

301
}
S
Sandeep Somavarapu 已提交
302

S
Sandeep Somavarapu 已提交
303
export class ReplaceAllAction extends AbstractSearchAndReplaceAction {
S
Sandeep Somavarapu 已提交
304

305
	public static get KEY_BINDING(): number {
A
Alexandru Dima 已提交
306
		return KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter;
307 308
	}

309
	constructor(private viewer: ITree, private fileMatch: FileMatch, private viewlet: SearchViewlet,
S
Sandeep Somavarapu 已提交
310 311 312
		@IReplaceService private replaceService: IReplaceService,
		@IKeybindingService keyBindingService2: IKeybindingService,
		@ITelemetryService private telemetryService: ITelemetryService) {
A
Alex Dima 已提交
313
		super('file-action-replace-all', appendKeyBindingLabel(nls.localize('file.replaceAll.label', "Replace All"), ReplaceAllAction.KEY_BINDING, keyBindingService2), 'action-replace-all');
S
Sandeep Somavarapu 已提交
314 315
	}

316
	public run(): TPromise<any> {
317
		this.telemetryService.publicLog('replaceAll.action.selected');
S
Sandeep Somavarapu 已提交
318
		let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.fileMatch);
S
Sandeep Somavarapu 已提交
319
		return this.fileMatch.parent().replace(this.fileMatch).then(() => {
S
Sandeep Somavarapu 已提交
320 321 322 323 324
			if (nextFocusElement) {
				this.viewer.setFocus(nextFocusElement);
			}
			this.viewer.DOMFocus();
			this.viewlet.open(this.fileMatch, true);
S
Sandeep Somavarapu 已提交
325 326 327 328
		});
	}
}

S
Sandeep Somavarapu 已提交
329
export class ReplaceAction extends AbstractSearchAndReplaceAction {
S
Sandeep Somavarapu 已提交
330

331 332 333 334
	public static get KEY_BINDING(): number {
		return KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1;
	}

335
	constructor(private viewer: ITree, private element: Match, private viewlet: SearchViewlet,
S
Sandeep Somavarapu 已提交
336 337 338 339
		@IReplaceService private replaceService: IReplaceService,
		@IKeybindingService keyBindingService2: IKeybindingService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@ITelemetryService private telemetryService: ITelemetryService) {
A
Alex Dima 已提交
340
		super('action-replace', appendKeyBindingLabel(nls.localize('match.replace.label', "Replace"), ReplaceAction.KEY_BINDING, keyBindingService2), 'action-replace');
S
Sandeep Somavarapu 已提交
341 342 343
	}

	public run(): TPromise<any> {
344
		this.telemetryService.publicLog('replace.action.selected');
S
Sandeep Somavarapu 已提交
345 346
		let elementToFocus = this.getElementToFocusAfterRemoved(this.viewer, this.element);
		let elementToShowReplacePreview = this.getElementToShowReplacePreview(elementToFocus);
S
Sandeep Somavarapu 已提交
347

S
Sandeep Somavarapu 已提交
348
		return this.element.parent().replace(this.element).then(() => {
S
Sandeep Somavarapu 已提交
349 350
			if (elementToFocus) {
				this.viewer.setFocus(elementToFocus);
S
Sandeep Somavarapu 已提交
351 352
			}
			this.viewer.DOMFocus();
S
Sandeep Somavarapu 已提交
353 354
			if (!elementToShowReplacePreview || this.hasToOpenFile()) {
				this.viewlet.open(this.element, true);
S
Sandeep Somavarapu 已提交
355
			} else {
S
Sandeep Somavarapu 已提交
356
				this.replaceService.openReplacePreviewEditor(elementToShowReplacePreview, true);
S
Sandeep Somavarapu 已提交
357
			}
358
		});
S
Sandeep Somavarapu 已提交
359
	}
S
Sandeep Somavarapu 已提交
360

S
Sandeep Somavarapu 已提交
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
	private getElementToShowReplacePreview(elementToFocus: FileMatchOrMatch): Match {
		if (this.hasSameParent(elementToFocus)) {
			return <Match>elementToFocus;
		}
		let previousElement = this.getPreviousElementAfterRemoved(this.viewer, this.element);
		if (this.hasSameParent(previousElement)) {
			return <Match>previousElement;
		}
		return null;
	}

	private hasSameParent(element: FileMatchOrMatch): boolean {
		return element && element instanceof Match && element.parent().resource() === this.element.parent().resource();
	}

	private hasToOpenFile(): boolean {
S
Sandeep Somavarapu 已提交
377 378 379
		const editorInput = asFileEditorInput(this.editorService.getActiveEditorInput());
		if (editorInput) {
			return editorInput.getResource().fsPath === this.element.parent().resource().fsPath;
S
Sandeep Somavarapu 已提交
380 381 382
		}
		return false;
	}
S
Sandeep Somavarapu 已提交
383 384 385 386
}

export class ConfigureGlobalExclusionsAction extends Action {

S
Sandeep Somavarapu 已提交
387
	constructor( @IInstantiationService private instantiationService: IInstantiationService) {
S
Sandeep Somavarapu 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401
		super('configureGlobalExclusionsAction');

		this.label = nls.localize('ConfigureGlobalExclusionsAction.label', "Open Settings");
		this.enabled = true;
		this.class = 'search-configure-exclusions';
	}

	public run(): TPromise<void> {
		let action = this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL);
		action.run().done(() => action.dispose(), errors.onUnexpectedError);

		return TPromise.as(null);
	}
}