searchEditor.contribution.ts 12.8 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as objects from 'vs/base/common/objects';
J
Jackson Kearl 已提交
8 9
import { endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
10
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
11 12
import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel';
import { localize } from 'vs/nls';
13
import { MenuId, SyncActionDescriptor, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
14
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
15 16
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
J
Jackson Kearl 已提交
17
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
18 19 20
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
J
Jackson Kearl 已提交
21
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
22 23
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
J
Jackson Kearl 已提交
24
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
R
rebornix 已提交
25
import { Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry, ActiveEditorContext } from 'vs/workbench/common/editor';
26
import * as SearchConstants from 'vs/workbench/contrib/search/common/constants';
J
Jackson Kearl 已提交
27
import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants';
28
import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor';
29
import { modifySearchEditorContextLinesCommand, OpenSearchEditorAction, selectAllSearchEditorMatchesCommand, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand, createEditorFromSearchResult, openNewSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions';
30
import { getOrMakeSearchEditorInput, SearchEditorInput, SearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput';
31
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
32 33
import { parseSavedSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization';
import { Range } from 'vs/editor/common/core/range';
M
Martin Aeschlimann 已提交
34
import { searchRefreshIcon } from 'vs/workbench/contrib/search/browser/searchIcons';
35 36
import { IViewsService } from 'vs/workbench/common/views';
import { getSearchView } from 'vs/workbench/contrib/search/browser/searchActions';
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

//#region Editor Descriptior
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
	EditorDescriptor.create(
		SearchEditor,
		SearchEditor.ID,
		localize('searchEditor', "Search Editor")
	),
	[
		new SyncDescriptor(SearchEditorInput)
	]
);
//#endregion

//#region Startup Contribution
class SearchEditorContribution implements IWorkbenchContribution {
	constructor(
		@IEditorService private readonly editorService: IEditorService,
		@IInstantiationService protected readonly instantiationService: IInstantiationService,
		@ITelemetryService protected readonly telemetryService: ITelemetryService,
		@IContextKeyService protected readonly contextKeyService: IContextKeyService,
	) {

R
rebornix 已提交
60 61 62 63
		this.editorService.overrideOpenEditor({
			open: (editor, options, group) => {
				const resource = editor.resource;
				if (!resource) { return undefined; }
J
Jackson Kearl 已提交
64

R
rebornix 已提交
65 66 67
				if (!endsWith(resource.path, '.code-search')) {
					return undefined;
				}
68

R
rebornix 已提交
69 70 71
				if (group.isOpened(editor) && editor instanceof SearchEditorInput) {
					return undefined;
				}
72

R
rebornix 已提交
73
				this.telemetryService.publicLog2('searchEditor/openSavedSearchEditor');
J
Jackson Kearl 已提交
74

R
rebornix 已提交
75 76 77 78 79 80 81 82
				return {
					override: (async () => {
						const { config } = await instantiationService.invokeFunction(parseSavedSearchEditor, resource);
						const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { backingUri: resource, config });
						return editorService.openEditor(input, { ...options, ignoreOverrides: true }, group);
					})()
				};
			}
83 84 85 86 87 88 89 90 91
		});
	}
}

const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, LifecyclePhase.Starting);
//#endregion

//#region Input Factory
92
type SerializedSearchEditor = { modelUri: string, dirty: boolean, config: SearchConfiguration, name: string, matchRanges: Range[], backingUri: string };
93 94 95 96 97
class SearchEditorInputFactory implements IEditorInputFactory {

	canSerialize() { return true; }

	serialize(input: SearchEditorInput) {
98 99 100
		let modelUri = undefined;
		if (input.modelUri.path || input.modelUri.fragment) {
			modelUri = input.modelUri.toString();
101
		}
102
		if (!modelUri) { return undefined; }
103

104
		const config = input.config;
105
		const dirty = input.isDirty();
106
		const matchRanges = input.getMatchRanges();
107
		const backingUri = input.backingUri;
108

109
		return JSON.stringify({ modelUri: modelUri.toString(), dirty, config, name: input.getName(), matchRanges, backingUri: backingUri?.toString() } as SerializedSearchEditor);
110 111 112
	}

	deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined {
113 114 115
		const { modelUri, dirty, config, matchRanges, backingUri } = JSON.parse(serializedEditorInput) as SerializedSearchEditor;
		if (config && (config.query !== undefined) && (modelUri !== undefined)) {
			const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { config, modelUri: URI.parse(modelUri), backingUri: backingUri ? URI.parse(backingUri) : undefined });
116
			input.setDirty(dirty);
117
			input.setMatchRanges(matchRanges);
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
			return input;
		}
		return undefined;
	}
}

Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
	SearchEditorInput.ID,
	SearchEditorInputFactory);
//#endregion

//#region Commands
KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({
	id: SearchEditorConstants.ToggleSearchEditorCaseSensitiveCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey),
	handler: toggleSearchEditorCaseSensitiveCommand
}, ToggleCaseSensitiveKeybinding));

KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({
	id: SearchEditorConstants.ToggleSearchEditorWholeWordCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey),
	handler: toggleSearchEditorWholeWordCommand
}, ToggleWholeWordKeybinding));

KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({
	id: SearchEditorConstants.ToggleSearchEditorRegexCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey),
	handler: toggleSearchEditorRegexCommand
}, ToggleRegexKeybinding));

KeybindingsRegistry.registerCommandAndKeybindingRule({
	id: SearchEditorConstants.ToggleSearchEditorContextLinesCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor),
	handler: toggleSearchEditorContextLinesCommand,
	primary: KeyMod.Alt | KeyCode.KEY_L,
	mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L }
});
159

160 161 162 163 164
KeybindingsRegistry.registerCommandAndKeybindingRule({
	id: SearchEditorConstants.IncreaseSearchEditorContextLinesCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor),
	handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, true),
J
Jackson Kearl 已提交
165
	primary: KeyMod.Alt | KeyCode.US_EQUAL
166 167 168 169 170 171 172
});

KeybindingsRegistry.registerCommandAndKeybindingRule({
	id: SearchEditorConstants.DecreaseSearchEditorContextLinesCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor),
	handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, false),
J
Jackson Kearl 已提交
173
	primary: KeyMod.Alt | KeyCode.US_MINUS
174 175
});

176 177 178 179 180 181 182 183
KeybindingsRegistry.registerCommandAndKeybindingRule({
	id: SearchEditorConstants.SelectAllSearchEditorMatchesCommandId,
	weight: KeybindingWeight.WorkbenchContrib,
	when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor),
	primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L,
	handler: selectAllSearchEditorMatchesCommand
});

184 185 186
CommandsRegistry.registerCommand(
	SearchEditorConstants.CleanSearchEditorStateCommandId,
	(accessor: ServicesAccessor) => {
187 188 189
		const activeEditorPane = accessor.get(IEditorService).activeEditorPane;
		if (activeEditorPane instanceof SearchEditor) {
			activeEditorPane.cleanState();
190 191
		}
	});
192 193 194 195 196 197
//#endregion

//#region Actions
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
const category = localize('search', "Search Editor");

198
// TODO: Not an action2 becuase used in view pane container action bar, which uses actions
199
registry.registerWorkbenchAction(
200
	SyncActionDescriptor.from(OpenSearchEditorAction),
201
	'Search Editor: Open New Search Editor', category);
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
registerAction2(class extends Action2 {
	constructor() {
		super({
			id: SearchEditorConstants.OpenInEditorCommandId,
			title: localize('search.openResultsInEditor', "Open Results in Editor"),
			category,
			f1: true,
			keybinding: {
				primary: KeyMod.CtrlCmd | KeyCode.Enter,
				when: ContextKeyExpr.and(SearchConstants.HasSearchResults, SearchConstants.SearchViewFocusedKey),
				weight: KeybindingWeight.WorkbenchContrib
			},
		});
	}
	async run(accessor: ServicesAccessor) {
		const viewsService = accessor.get(IViewsService);
		const instantiationService = accessor.get(IInstantiationService);
		const searchView = getSearchView(viewsService);
		if (searchView) {
			await instantiationService.invokeFunction(createEditorFromSearchResult, searchView.searchResult, searchView.searchIncludePattern.getValue(), searchView.searchExcludePattern.getValue());
		}
	}
});
226

227 228 229 230 231 232 233 234 235 236 237 238 239 240
registerAction2(class extends Action2 {
	constructor() {
		super({
			id: SearchEditorConstants.OpenNewEditorToSideCommandId,
			title: localize('search.openNewEditorToSide', "Open New Search Editor to Side"),
			category,
			f1: true,
		});
	}
	async run(accessor: ServicesAccessor) {
		const instantiationService = accessor.get(IInstantiationService);
		await instantiationService.invokeFunction(openNewSearchEditor, true);
	}
});
241

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
registerAction2(class extends Action2 {
	constructor() {
		super({
			id: SearchEditorConstants.RerunSearchEditorSearchCommandId,
			title: localize('search.rerunSearchInEditor', "Search Again"),
			category,
			keybinding: {
				primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R,
				when: SearchEditorConstants.InSearchEditor,
				weight: KeybindingWeight.EditorContrib
			},
			icon: searchRefreshIcon,
			menu: [{
				id: MenuId.EditorTitle,
				group: 'navigation',
				when: ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID)
			},
			{
				id: MenuId.CommandPalette,
				when: ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID)
			}]
		});
	}
	async run(accessor: ServicesAccessor) {
		const editorService = accessor.get(IEditorService);
		const input = editorService.activeEditor;
		if (input instanceof SearchEditorInput) {
			(editorService.activeEditorPane as SearchEditor).triggerSearch({ resetCursor: false });
		}
	}
});
273

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
registerAction2(class extends Action2 {
	constructor() {
		super({
			id: SearchEditorConstants.FocusQueryEditorWidgetCommandId,
			title: localize('search.action.focusQueryEditorWidget', "Focus Search Editor Input"),
			category,
			menu: {
				id: MenuId.CommandPalette,
				when: ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID)
			},
			keybinding: {
				primary: KeyCode.Escape,
				when: SearchEditorConstants.InSearchEditor,
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
	async run(accessor: ServicesAccessor) {
		const editorService = accessor.get(IEditorService);
		const input = editorService.activeEditor;
		if (input instanceof SearchEditorInput) {
			(editorService.activeEditorPane as SearchEditor).focusSearchInput();
		}
	}
298
});
299
//#endregion