@theia/search-in-workspace 1.34.2 → 1.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +641 -641
  2. package/README.md +40 -40
  3. package/lib/browser/components/search-in-workspace-input.d.ts +39 -39
  4. package/lib/browser/components/search-in-workspace-input.js +120 -120
  5. package/lib/browser/search-in-workspace-context-key-service.d.ts +23 -23
  6. package/lib/browser/search-in-workspace-context-key-service.js +90 -90
  7. package/lib/browser/search-in-workspace-factory.d.ts +10 -10
  8. package/lib/browser/search-in-workspace-factory.js +68 -68
  9. package/lib/browser/search-in-workspace-frontend-contribution.d.ts +53 -53
  10. package/lib/browser/search-in-workspace-frontend-contribution.js +410 -410
  11. package/lib/browser/search-in-workspace-frontend-module.d.ts +6 -6
  12. package/lib/browser/search-in-workspace-frontend-module.js +70 -70
  13. package/lib/browser/search-in-workspace-label-provider.d.ts +9 -9
  14. package/lib/browser/search-in-workspace-label-provider.js +57 -57
  15. package/lib/browser/search-in-workspace-preferences.d.ts +17 -17
  16. package/lib/browser/search-in-workspace-preferences.js +82 -82
  17. package/lib/browser/search-in-workspace-result-tree-widget.d.ts +253 -253
  18. package/lib/browser/search-in-workspace-result-tree-widget.js +1072 -1072
  19. package/lib/browser/search-in-workspace-service.d.ts +35 -35
  20. package/lib/browser/search-in-workspace-service.js +158 -158
  21. package/lib/browser/search-in-workspace-widget.d.ts +121 -121
  22. package/lib/browser/search-in-workspace-widget.js +602 -602
  23. package/lib/browser/search-layout-migrations.d.ts +5 -5
  24. package/lib/browser/search-layout-migrations.js +64 -64
  25. package/lib/common/search-in-workspace-interface.d.ts +112 -112
  26. package/lib/common/search-in-workspace-interface.js +35 -35
  27. package/lib/node/ripgrep-search-in-workspace-server.d.ts +94 -94
  28. package/lib/node/ripgrep-search-in-workspace-server.js +423 -423
  29. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.d.ts +1 -1
  30. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.js +899 -899
  31. package/lib/node/search-in-workspace-backend-module.d.ts +3 -3
  32. package/lib/node/search-in-workspace-backend-module.js +32 -32
  33. package/package.json +9 -9
  34. package/src/browser/components/search-in-workspace-input.tsx +139 -139
  35. package/src/browser/search-in-workspace-context-key-service.ts +93 -93
  36. package/src/browser/search-in-workspace-factory.ts +59 -59
  37. package/src/browser/search-in-workspace-frontend-contribution.ts +402 -402
  38. package/src/browser/search-in-workspace-frontend-module.ts +82 -82
  39. package/src/browser/search-in-workspace-label-provider.ts +48 -48
  40. package/src/browser/search-in-workspace-preferences.ts +91 -91
  41. package/src/browser/search-in-workspace-result-tree-widget.tsx +1225 -1225
  42. package/src/browser/search-in-workspace-service.ts +152 -152
  43. package/src/browser/search-in-workspace-widget.tsx +711 -711
  44. package/src/browser/search-layout-migrations.ts +53 -53
  45. package/src/browser/styles/index.css +367 -367
  46. package/src/browser/styles/search.svg +6 -6
  47. package/src/common/search-in-workspace-interface.ts +149 -149
  48. package/src/node/ripgrep-search-in-workspace-server.slow-spec.ts +1073 -1073
  49. package/src/node/ripgrep-search-in-workspace-server.ts +482 -482
  50. package/src/node/search-in-workspace-backend-module.ts +33 -33
@@ -1,402 +1,402 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2018 TypeFox and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
-
17
- import {
18
- AbstractViewContribution, KeybindingRegistry, LabelProvider, CommonMenus, FrontendApplication,
19
- FrontendApplicationContribution, CommonCommands, StylingParticipant, ColorTheme, CssStyleCollector
20
- } from '@theia/core/lib/browser';
21
- import { SearchInWorkspaceWidget } from './search-in-workspace-widget';
22
- import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
23
- import { CommandRegistry, MenuModelRegistry, SelectionService, Command } from '@theia/core';
24
- import { codicon, Widget } from '@theia/core/lib/browser/widgets';
25
- import { FileNavigatorCommands, NavigatorContextMenu } from '@theia/navigator/lib/browser/navigator-contribution';
26
- import { UriCommandHandler, UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
27
- import URI from '@theia/core/lib/common/uri';
28
- import { WorkspaceService } from '@theia/workspace/lib/browser';
29
- import { SearchInWorkspaceContextKeyService } from './search-in-workspace-context-key-service';
30
- import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
31
- import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
32
- import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
33
- import { FileService } from '@theia/filesystem/lib/browser/file-service';
34
- import { SEARCH_VIEW_CONTAINER_ID } from './search-in-workspace-factory';
35
- import { SearchInWorkspaceResultTreeWidget } from './search-in-workspace-result-tree-widget';
36
- import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection';
37
- import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
38
- import { isHighContrast } from '@theia/core/lib/common/theme';
39
-
40
- export namespace SearchInWorkspaceCommands {
41
- const SEARCH_CATEGORY = 'Search';
42
- export const TOGGLE_SIW_WIDGET = {
43
- id: 'search-in-workspace.toggle'
44
- };
45
- export const OPEN_SIW_WIDGET = Command.toDefaultLocalizedCommand({
46
- id: 'search-in-workspace.open',
47
- category: SEARCH_CATEGORY,
48
- label: 'Find in Files'
49
- });
50
- export const REPLACE_IN_FILES = Command.toDefaultLocalizedCommand({
51
- id: 'search-in-workspace.replace',
52
- category: SEARCH_CATEGORY,
53
- label: 'Replace in Files'
54
- });
55
- export const FIND_IN_FOLDER = Command.toDefaultLocalizedCommand({
56
- id: 'search-in-workspace.in-folder',
57
- category: SEARCH_CATEGORY,
58
- label: 'Find in Folder...'
59
- });
60
- export const REFRESH_RESULTS = Command.toDefaultLocalizedCommand({
61
- id: 'search-in-workspace.refresh',
62
- category: SEARCH_CATEGORY,
63
- label: 'Refresh',
64
- iconClass: codicon('refresh')
65
- });
66
- export const CANCEL_SEARCH = Command.toDefaultLocalizedCommand({
67
- id: 'search-in-workspace.cancel',
68
- category: SEARCH_CATEGORY,
69
- label: 'Cancel Search',
70
- iconClass: codicon('search-stop')
71
- });
72
- export const COLLAPSE_ALL = Command.toDefaultLocalizedCommand({
73
- id: 'search-in-workspace.collapse-all',
74
- category: SEARCH_CATEGORY,
75
- label: 'Collapse All',
76
- iconClass: codicon('collapse-all')
77
- });
78
- export const EXPAND_ALL = Command.toDefaultLocalizedCommand({
79
- id: 'search-in-workspace.expand-all',
80
- category: SEARCH_CATEGORY,
81
- label: 'Expand All',
82
- iconClass: codicon('expand-all')
83
- });
84
- export const CLEAR_ALL = Command.toDefaultLocalizedCommand({
85
- id: 'search-in-workspace.clear-all',
86
- category: SEARCH_CATEGORY,
87
- label: 'Clear Search Results',
88
- iconClass: codicon('clear-all')
89
- });
90
- export const COPY_ALL = Command.toDefaultLocalizedCommand({
91
- id: 'search.action.copyAll',
92
- category: SEARCH_CATEGORY,
93
- label: 'Copy All',
94
- });
95
- export const COPY_ONE = Command.toDefaultLocalizedCommand({
96
- id: 'search.action.copyMatch',
97
- category: SEARCH_CATEGORY,
98
- label: 'Copy',
99
- });
100
- export const DISMISS_RESULT = Command.toDefaultLocalizedCommand({
101
- id: 'search.action.remove',
102
- category: SEARCH_CATEGORY,
103
- label: 'Dismiss',
104
- });
105
- }
106
-
107
- @injectable()
108
- export class SearchInWorkspaceFrontendContribution extends AbstractViewContribution<SearchInWorkspaceWidget> implements
109
- FrontendApplicationContribution,
110
- TabBarToolbarContribution,
111
- StylingParticipant {
112
-
113
- @inject(SelectionService) protected readonly selectionService: SelectionService;
114
- @inject(LabelProvider) protected readonly labelProvider: LabelProvider;
115
- @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
116
- @inject(FileService) protected readonly fileService: FileService;
117
- @inject(EditorManager) protected readonly editorManager: EditorManager;
118
- @inject(ClipboardService) protected readonly clipboardService: ClipboardService;
119
-
120
- @inject(SearchInWorkspaceContextKeyService)
121
- protected readonly contextKeyService: SearchInWorkspaceContextKeyService;
122
-
123
- constructor() {
124
- super({
125
- viewContainerId: SEARCH_VIEW_CONTAINER_ID,
126
- widgetId: SearchInWorkspaceWidget.ID,
127
- widgetName: SearchInWorkspaceWidget.LABEL,
128
- defaultWidgetOptions: {
129
- area: 'left',
130
- rank: 200
131
- },
132
- toggleCommandId: SearchInWorkspaceCommands.TOGGLE_SIW_WIDGET.id
133
- });
134
- }
135
-
136
- @postConstruct()
137
- protected init(): void {
138
- const updateFocusContextKey = () =>
139
- this.contextKeyService.searchViewletFocus.set(this.shell.activeWidget instanceof SearchInWorkspaceWidget);
140
- updateFocusContextKey();
141
- this.shell.onDidChangeActiveWidget(updateFocusContextKey);
142
- }
143
-
144
- async initializeLayout(app: FrontendApplication): Promise<void> {
145
- await this.openView({ activate: false });
146
- }
147
-
148
- override async registerCommands(commands: CommandRegistry): Promise<void> {
149
- super.registerCommands(commands);
150
- commands.registerCommand(SearchInWorkspaceCommands.OPEN_SIW_WIDGET, {
151
- isEnabled: () => this.workspaceService.tryGetRoots().length > 0,
152
- execute: async () => {
153
- const widget = await this.openView({ activate: true });
154
- widget.updateSearchTerm(this.getSearchTerm());
155
- }
156
- });
157
-
158
- commands.registerCommand(SearchInWorkspaceCommands.REPLACE_IN_FILES, {
159
- isEnabled: () => this.workspaceService.tryGetRoots().length > 0,
160
- execute: async () => {
161
- const widget = await this.openView({ activate: true });
162
- widget.updateSearchTerm(this.getSearchTerm(), true);
163
- }
164
- });
165
-
166
- commands.registerCommand(SearchInWorkspaceCommands.FIND_IN_FOLDER, this.newMultiUriAwareCommandHandler({
167
- execute: async uris => {
168
- const resources: string[] = [];
169
- for (const { stat } of await this.fileService.resolveAll(uris.map(resource => ({ resource })))) {
170
- if (stat) {
171
- const uri = stat.resource;
172
- let uriStr = this.labelProvider.getLongName(uri);
173
- if (stat && !stat.isDirectory) {
174
- uriStr = this.labelProvider.getLongName(uri.parent);
175
- }
176
- resources.push(uriStr);
177
- }
178
- }
179
- const widget = await this.openView({ activate: true });
180
- widget.findInFolder(resources);
181
- }
182
- }));
183
-
184
- commands.registerCommand(SearchInWorkspaceCommands.CANCEL_SEARCH, {
185
- execute: w => this.withWidget(w, widget => widget.getCancelIndicator() && widget.getCancelIndicator()!.cancel()),
186
- isEnabled: w => this.withWidget(w, widget => widget.getCancelIndicator() !== undefined),
187
- isVisible: w => this.withWidget(w, widget => widget.getCancelIndicator() !== undefined)
188
- });
189
- commands.registerCommand(SearchInWorkspaceCommands.REFRESH_RESULTS, {
190
- execute: w => this.withWidget(w, widget => widget.refresh()),
191
- isEnabled: w => this.withWidget(w, widget => (widget.hasResultList() || widget.hasSearchTerm()) && this.workspaceService.tryGetRoots().length > 0),
192
- isVisible: w => this.withWidget(w, () => true)
193
- });
194
- commands.registerCommand(SearchInWorkspaceCommands.COLLAPSE_ALL, {
195
- execute: w => this.withWidget(w, widget => widget.collapseAll()),
196
- isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
197
- isVisible: w => this.withWidget(w, widget => !widget.areResultsCollapsed())
198
- });
199
- commands.registerCommand(SearchInWorkspaceCommands.EXPAND_ALL, {
200
- execute: w => this.withWidget(w, widget => widget.expandAll()),
201
- isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
202
- isVisible: w => this.withWidget(w, widget => widget.areResultsCollapsed())
203
- });
204
- commands.registerCommand(SearchInWorkspaceCommands.CLEAR_ALL, {
205
- execute: w => this.withWidget(w, widget => widget.clear()),
206
- isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
207
- isVisible: w => this.withWidget(w, () => true)
208
- });
209
- commands.registerCommand(SearchInWorkspaceCommands.DISMISS_RESULT, {
210
- isEnabled: () => this.withWidget(undefined, widget => {
211
- const { selection } = this.selectionService;
212
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
213
- }),
214
- isVisible: () => this.withWidget(undefined, widget => {
215
- const { selection } = this.selectionService;
216
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
217
- }),
218
- execute: () => this.withWidget(undefined, widget => {
219
- const { selection } = this.selectionService;
220
- if (TreeWidgetSelection.is(selection)) {
221
- widget.resultTreeWidget.removeNode(selection[0]);
222
- }
223
- })
224
- });
225
- commands.registerCommand(SearchInWorkspaceCommands.COPY_ONE, {
226
- isEnabled: () => this.withWidget(undefined, widget => {
227
- const { selection } = this.selectionService;
228
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
229
- }),
230
- isVisible: () => this.withWidget(undefined, widget => {
231
- const { selection } = this.selectionService;
232
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
233
- }),
234
- execute: () => this.withWidget(undefined, widget => {
235
- const { selection } = this.selectionService;
236
- if (TreeWidgetSelection.is(selection)) {
237
- const string = widget.resultTreeWidget.nodeToString(selection[0], true);
238
- if (string.length !== 0) {
239
- this.clipboardService.writeText(string);
240
- }
241
- }
242
- })
243
- });
244
- commands.registerCommand(SearchInWorkspaceCommands.COPY_ALL, {
245
- isEnabled: () => this.withWidget(undefined, widget => {
246
- const { selection } = this.selectionService;
247
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
248
- }),
249
- isVisible: () => this.withWidget(undefined, widget => {
250
- const { selection } = this.selectionService;
251
- return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
252
- }),
253
- execute: () => this.withWidget(undefined, widget => {
254
- const { selection } = this.selectionService;
255
- if (TreeWidgetSelection.is(selection)) {
256
- const string = widget.resultTreeWidget.treeToString();
257
- if (string.length !== 0) {
258
- this.clipboardService.writeText(string);
259
- }
260
- }
261
- })
262
- });
263
- }
264
-
265
- protected withWidget<T>(widget: Widget | undefined = this.tryGetWidget(), fn: (widget: SearchInWorkspaceWidget) => T): T | false {
266
- if (widget instanceof SearchInWorkspaceWidget && widget.id === SearchInWorkspaceWidget.ID) {
267
- return fn(widget);
268
- }
269
- return false;
270
- }
271
-
272
- /**
273
- * Get the search term based on current editor selection.
274
- * @returns the selection if available.
275
- */
276
- protected getSearchTerm(): string {
277
- if (!this.editorManager.currentEditor) {
278
- return '';
279
- }
280
- // Get the current editor selection.
281
- const selection = this.editorManager.currentEditor.editor.selection;
282
- // Compute the selection range.
283
- const selectedRange: Range = Range.create(
284
- selection.start.line,
285
- selection.start.character,
286
- selection.end.line,
287
- selection.end.character
288
- );
289
- // Return the selection text if available, else return empty.
290
- return this.editorManager.currentEditor
291
- ? this.editorManager.currentEditor.editor.document.getText(selectedRange)
292
- : '';
293
- }
294
-
295
- override registerKeybindings(keybindings: KeybindingRegistry): void {
296
- super.registerKeybindings(keybindings);
297
- keybindings.registerKeybinding({
298
- command: SearchInWorkspaceCommands.OPEN_SIW_WIDGET.id,
299
- keybinding: 'ctrlcmd+shift+f'
300
- });
301
- keybindings.registerKeybinding({
302
- command: SearchInWorkspaceCommands.FIND_IN_FOLDER.id,
303
- keybinding: 'shift+alt+f',
304
- when: 'explorerResourceIsFolder'
305
- });
306
- }
307
-
308
- override registerMenus(menus: MenuModelRegistry): void {
309
- super.registerMenus(menus);
310
- menus.registerMenuAction(NavigatorContextMenu.SEARCH, {
311
- commandId: SearchInWorkspaceCommands.FIND_IN_FOLDER.id,
312
- when: 'explorerResourceIsFolder'
313
- });
314
- menus.registerMenuAction(CommonMenus.EDIT_FIND, {
315
- commandId: SearchInWorkspaceCommands.OPEN_SIW_WIDGET.id,
316
- order: '2'
317
- });
318
- menus.registerMenuAction(CommonMenus.EDIT_FIND, {
319
- commandId: SearchInWorkspaceCommands.REPLACE_IN_FILES.id,
320
- order: '3'
321
- });
322
- menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.INTERNAL, {
323
- commandId: SearchInWorkspaceCommands.DISMISS_RESULT.id,
324
- order: '1'
325
- });
326
- menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
327
- commandId: SearchInWorkspaceCommands.COPY_ONE.id,
328
- order: '1',
329
- });
330
- menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
331
- commandId: CommonCommands.COPY_PATH.id,
332
- order: '2',
333
- });
334
- menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
335
- commandId: SearchInWorkspaceCommands.COPY_ALL.id,
336
- order: '3',
337
- });
338
- menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.EXTERNAL, {
339
- commandId: FileNavigatorCommands.REVEAL_IN_NAVIGATOR.id,
340
- order: '1',
341
- });
342
- }
343
-
344
- async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise<void> {
345
- const widget = await this.widget;
346
- const onDidChange = widget.onDidUpdate;
347
- toolbarRegistry.registerItem({
348
- id: SearchInWorkspaceCommands.CANCEL_SEARCH.id,
349
- command: SearchInWorkspaceCommands.CANCEL_SEARCH.id,
350
- tooltip: SearchInWorkspaceCommands.CANCEL_SEARCH.label,
351
- priority: 0,
352
- onDidChange
353
- });
354
- toolbarRegistry.registerItem({
355
- id: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
356
- command: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
357
- tooltip: SearchInWorkspaceCommands.REFRESH_RESULTS.label,
358
- priority: 1,
359
- onDidChange
360
- });
361
- toolbarRegistry.registerItem({
362
- id: SearchInWorkspaceCommands.CLEAR_ALL.id,
363
- command: SearchInWorkspaceCommands.CLEAR_ALL.id,
364
- tooltip: SearchInWorkspaceCommands.CLEAR_ALL.label,
365
- priority: 2,
366
- onDidChange
367
- });
368
- toolbarRegistry.registerItem({
369
- id: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
370
- command: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
371
- tooltip: SearchInWorkspaceCommands.COLLAPSE_ALL.label,
372
- priority: 3,
373
- onDidChange
374
- });
375
- toolbarRegistry.registerItem({
376
- id: SearchInWorkspaceCommands.EXPAND_ALL.id,
377
- command: SearchInWorkspaceCommands.EXPAND_ALL.id,
378
- tooltip: SearchInWorkspaceCommands.EXPAND_ALL.label,
379
- priority: 3,
380
- onDidChange
381
- });
382
- }
383
-
384
- protected newUriAwareCommandHandler(handler: UriCommandHandler<URI>): UriAwareCommandHandler<URI> {
385
- return UriAwareCommandHandler.MonoSelect(this.selectionService, handler);
386
- }
387
-
388
- protected newMultiUriAwareCommandHandler(handler: UriCommandHandler<URI[]>): UriAwareCommandHandler<URI[]> {
389
- return UriAwareCommandHandler.MultiSelect(this.selectionService, handler);
390
- }
391
-
392
- registerThemeStyle(theme: ColorTheme, collector: CssStyleCollector): void {
393
- const contrastBorder = theme.getColor('contrastBorder');
394
- if (contrastBorder && isHighContrast(theme.type)) {
395
- collector.addRule(`
396
- .t-siw-search-container .searchHeader .search-field-container {
397
- border-color: ${contrastBorder};
398
- }
399
- `);
400
- }
401
- }
402
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import {
18
+ AbstractViewContribution, KeybindingRegistry, LabelProvider, CommonMenus, FrontendApplication,
19
+ FrontendApplicationContribution, CommonCommands, StylingParticipant, ColorTheme, CssStyleCollector
20
+ } from '@theia/core/lib/browser';
21
+ import { SearchInWorkspaceWidget } from './search-in-workspace-widget';
22
+ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
23
+ import { CommandRegistry, MenuModelRegistry, SelectionService, Command } from '@theia/core';
24
+ import { codicon, Widget } from '@theia/core/lib/browser/widgets';
25
+ import { FileNavigatorCommands, NavigatorContextMenu } from '@theia/navigator/lib/browser/navigator-contribution';
26
+ import { UriCommandHandler, UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
27
+ import URI from '@theia/core/lib/common/uri';
28
+ import { WorkspaceService } from '@theia/workspace/lib/browser';
29
+ import { SearchInWorkspaceContextKeyService } from './search-in-workspace-context-key-service';
30
+ import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
31
+ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
32
+ import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
33
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
34
+ import { SEARCH_VIEW_CONTAINER_ID } from './search-in-workspace-factory';
35
+ import { SearchInWorkspaceResultTreeWidget } from './search-in-workspace-result-tree-widget';
36
+ import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection';
37
+ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
38
+ import { isHighContrast } from '@theia/core/lib/common/theme';
39
+
40
+ export namespace SearchInWorkspaceCommands {
41
+ const SEARCH_CATEGORY = 'Search';
42
+ export const TOGGLE_SIW_WIDGET = {
43
+ id: 'search-in-workspace.toggle'
44
+ };
45
+ export const OPEN_SIW_WIDGET = Command.toDefaultLocalizedCommand({
46
+ id: 'search-in-workspace.open',
47
+ category: SEARCH_CATEGORY,
48
+ label: 'Find in Files'
49
+ });
50
+ export const REPLACE_IN_FILES = Command.toDefaultLocalizedCommand({
51
+ id: 'search-in-workspace.replace',
52
+ category: SEARCH_CATEGORY,
53
+ label: 'Replace in Files'
54
+ });
55
+ export const FIND_IN_FOLDER = Command.toDefaultLocalizedCommand({
56
+ id: 'search-in-workspace.in-folder',
57
+ category: SEARCH_CATEGORY,
58
+ label: 'Find in Folder...'
59
+ });
60
+ export const REFRESH_RESULTS = Command.toDefaultLocalizedCommand({
61
+ id: 'search-in-workspace.refresh',
62
+ category: SEARCH_CATEGORY,
63
+ label: 'Refresh',
64
+ iconClass: codicon('refresh')
65
+ });
66
+ export const CANCEL_SEARCH = Command.toDefaultLocalizedCommand({
67
+ id: 'search-in-workspace.cancel',
68
+ category: SEARCH_CATEGORY,
69
+ label: 'Cancel Search',
70
+ iconClass: codicon('search-stop')
71
+ });
72
+ export const COLLAPSE_ALL = Command.toDefaultLocalizedCommand({
73
+ id: 'search-in-workspace.collapse-all',
74
+ category: SEARCH_CATEGORY,
75
+ label: 'Collapse All',
76
+ iconClass: codicon('collapse-all')
77
+ });
78
+ export const EXPAND_ALL = Command.toDefaultLocalizedCommand({
79
+ id: 'search-in-workspace.expand-all',
80
+ category: SEARCH_CATEGORY,
81
+ label: 'Expand All',
82
+ iconClass: codicon('expand-all')
83
+ });
84
+ export const CLEAR_ALL = Command.toDefaultLocalizedCommand({
85
+ id: 'search-in-workspace.clear-all',
86
+ category: SEARCH_CATEGORY,
87
+ label: 'Clear Search Results',
88
+ iconClass: codicon('clear-all')
89
+ });
90
+ export const COPY_ALL = Command.toDefaultLocalizedCommand({
91
+ id: 'search.action.copyAll',
92
+ category: SEARCH_CATEGORY,
93
+ label: 'Copy All',
94
+ });
95
+ export const COPY_ONE = Command.toDefaultLocalizedCommand({
96
+ id: 'search.action.copyMatch',
97
+ category: SEARCH_CATEGORY,
98
+ label: 'Copy',
99
+ });
100
+ export const DISMISS_RESULT = Command.toDefaultLocalizedCommand({
101
+ id: 'search.action.remove',
102
+ category: SEARCH_CATEGORY,
103
+ label: 'Dismiss',
104
+ });
105
+ }
106
+
107
+ @injectable()
108
+ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribution<SearchInWorkspaceWidget> implements
109
+ FrontendApplicationContribution,
110
+ TabBarToolbarContribution,
111
+ StylingParticipant {
112
+
113
+ @inject(SelectionService) protected readonly selectionService: SelectionService;
114
+ @inject(LabelProvider) protected readonly labelProvider: LabelProvider;
115
+ @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
116
+ @inject(FileService) protected readonly fileService: FileService;
117
+ @inject(EditorManager) protected readonly editorManager: EditorManager;
118
+ @inject(ClipboardService) protected readonly clipboardService: ClipboardService;
119
+
120
+ @inject(SearchInWorkspaceContextKeyService)
121
+ protected readonly contextKeyService: SearchInWorkspaceContextKeyService;
122
+
123
+ constructor() {
124
+ super({
125
+ viewContainerId: SEARCH_VIEW_CONTAINER_ID,
126
+ widgetId: SearchInWorkspaceWidget.ID,
127
+ widgetName: SearchInWorkspaceWidget.LABEL,
128
+ defaultWidgetOptions: {
129
+ area: 'left',
130
+ rank: 200
131
+ },
132
+ toggleCommandId: SearchInWorkspaceCommands.TOGGLE_SIW_WIDGET.id
133
+ });
134
+ }
135
+
136
+ @postConstruct()
137
+ protected init(): void {
138
+ const updateFocusContextKey = () =>
139
+ this.contextKeyService.searchViewletFocus.set(this.shell.activeWidget instanceof SearchInWorkspaceWidget);
140
+ updateFocusContextKey();
141
+ this.shell.onDidChangeActiveWidget(updateFocusContextKey);
142
+ }
143
+
144
+ async initializeLayout(app: FrontendApplication): Promise<void> {
145
+ await this.openView({ activate: false });
146
+ }
147
+
148
+ override async registerCommands(commands: CommandRegistry): Promise<void> {
149
+ super.registerCommands(commands);
150
+ commands.registerCommand(SearchInWorkspaceCommands.OPEN_SIW_WIDGET, {
151
+ isEnabled: () => this.workspaceService.tryGetRoots().length > 0,
152
+ execute: async () => {
153
+ const widget = await this.openView({ activate: true });
154
+ widget.updateSearchTerm(this.getSearchTerm());
155
+ }
156
+ });
157
+
158
+ commands.registerCommand(SearchInWorkspaceCommands.REPLACE_IN_FILES, {
159
+ isEnabled: () => this.workspaceService.tryGetRoots().length > 0,
160
+ execute: async () => {
161
+ const widget = await this.openView({ activate: true });
162
+ widget.updateSearchTerm(this.getSearchTerm(), true);
163
+ }
164
+ });
165
+
166
+ commands.registerCommand(SearchInWorkspaceCommands.FIND_IN_FOLDER, this.newMultiUriAwareCommandHandler({
167
+ execute: async uris => {
168
+ const resources: string[] = [];
169
+ for (const { stat } of await this.fileService.resolveAll(uris.map(resource => ({ resource })))) {
170
+ if (stat) {
171
+ const uri = stat.resource;
172
+ let uriStr = this.labelProvider.getLongName(uri);
173
+ if (stat && !stat.isDirectory) {
174
+ uriStr = this.labelProvider.getLongName(uri.parent);
175
+ }
176
+ resources.push(uriStr);
177
+ }
178
+ }
179
+ const widget = await this.openView({ activate: true });
180
+ widget.findInFolder(resources);
181
+ }
182
+ }));
183
+
184
+ commands.registerCommand(SearchInWorkspaceCommands.CANCEL_SEARCH, {
185
+ execute: w => this.withWidget(w, widget => widget.getCancelIndicator() && widget.getCancelIndicator()!.cancel()),
186
+ isEnabled: w => this.withWidget(w, widget => widget.getCancelIndicator() !== undefined),
187
+ isVisible: w => this.withWidget(w, widget => widget.getCancelIndicator() !== undefined)
188
+ });
189
+ commands.registerCommand(SearchInWorkspaceCommands.REFRESH_RESULTS, {
190
+ execute: w => this.withWidget(w, widget => widget.refresh()),
191
+ isEnabled: w => this.withWidget(w, widget => (widget.hasResultList() || widget.hasSearchTerm()) && this.workspaceService.tryGetRoots().length > 0),
192
+ isVisible: w => this.withWidget(w, () => true)
193
+ });
194
+ commands.registerCommand(SearchInWorkspaceCommands.COLLAPSE_ALL, {
195
+ execute: w => this.withWidget(w, widget => widget.collapseAll()),
196
+ isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
197
+ isVisible: w => this.withWidget(w, widget => !widget.areResultsCollapsed())
198
+ });
199
+ commands.registerCommand(SearchInWorkspaceCommands.EXPAND_ALL, {
200
+ execute: w => this.withWidget(w, widget => widget.expandAll()),
201
+ isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
202
+ isVisible: w => this.withWidget(w, widget => widget.areResultsCollapsed())
203
+ });
204
+ commands.registerCommand(SearchInWorkspaceCommands.CLEAR_ALL, {
205
+ execute: w => this.withWidget(w, widget => widget.clear()),
206
+ isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
207
+ isVisible: w => this.withWidget(w, () => true)
208
+ });
209
+ commands.registerCommand(SearchInWorkspaceCommands.DISMISS_RESULT, {
210
+ isEnabled: () => this.withWidget(undefined, widget => {
211
+ const { selection } = this.selectionService;
212
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
213
+ }),
214
+ isVisible: () => this.withWidget(undefined, widget => {
215
+ const { selection } = this.selectionService;
216
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
217
+ }),
218
+ execute: () => this.withWidget(undefined, widget => {
219
+ const { selection } = this.selectionService;
220
+ if (TreeWidgetSelection.is(selection)) {
221
+ widget.resultTreeWidget.removeNode(selection[0]);
222
+ }
223
+ })
224
+ });
225
+ commands.registerCommand(SearchInWorkspaceCommands.COPY_ONE, {
226
+ isEnabled: () => this.withWidget(undefined, widget => {
227
+ const { selection } = this.selectionService;
228
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
229
+ }),
230
+ isVisible: () => this.withWidget(undefined, widget => {
231
+ const { selection } = this.selectionService;
232
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
233
+ }),
234
+ execute: () => this.withWidget(undefined, widget => {
235
+ const { selection } = this.selectionService;
236
+ if (TreeWidgetSelection.is(selection)) {
237
+ const string = widget.resultTreeWidget.nodeToString(selection[0], true);
238
+ if (string.length !== 0) {
239
+ this.clipboardService.writeText(string);
240
+ }
241
+ }
242
+ })
243
+ });
244
+ commands.registerCommand(SearchInWorkspaceCommands.COPY_ALL, {
245
+ isEnabled: () => this.withWidget(undefined, widget => {
246
+ const { selection } = this.selectionService;
247
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
248
+ }),
249
+ isVisible: () => this.withWidget(undefined, widget => {
250
+ const { selection } = this.selectionService;
251
+ return TreeWidgetSelection.isSource(selection, widget.resultTreeWidget) && selection.length > 0;
252
+ }),
253
+ execute: () => this.withWidget(undefined, widget => {
254
+ const { selection } = this.selectionService;
255
+ if (TreeWidgetSelection.is(selection)) {
256
+ const string = widget.resultTreeWidget.treeToString();
257
+ if (string.length !== 0) {
258
+ this.clipboardService.writeText(string);
259
+ }
260
+ }
261
+ })
262
+ });
263
+ }
264
+
265
+ protected withWidget<T>(widget: Widget | undefined = this.tryGetWidget(), fn: (widget: SearchInWorkspaceWidget) => T): T | false {
266
+ if (widget instanceof SearchInWorkspaceWidget && widget.id === SearchInWorkspaceWidget.ID) {
267
+ return fn(widget);
268
+ }
269
+ return false;
270
+ }
271
+
272
+ /**
273
+ * Get the search term based on current editor selection.
274
+ * @returns the selection if available.
275
+ */
276
+ protected getSearchTerm(): string {
277
+ if (!this.editorManager.currentEditor) {
278
+ return '';
279
+ }
280
+ // Get the current editor selection.
281
+ const selection = this.editorManager.currentEditor.editor.selection;
282
+ // Compute the selection range.
283
+ const selectedRange: Range = Range.create(
284
+ selection.start.line,
285
+ selection.start.character,
286
+ selection.end.line,
287
+ selection.end.character
288
+ );
289
+ // Return the selection text if available, else return empty.
290
+ return this.editorManager.currentEditor
291
+ ? this.editorManager.currentEditor.editor.document.getText(selectedRange)
292
+ : '';
293
+ }
294
+
295
+ override registerKeybindings(keybindings: KeybindingRegistry): void {
296
+ super.registerKeybindings(keybindings);
297
+ keybindings.registerKeybinding({
298
+ command: SearchInWorkspaceCommands.OPEN_SIW_WIDGET.id,
299
+ keybinding: 'ctrlcmd+shift+f'
300
+ });
301
+ keybindings.registerKeybinding({
302
+ command: SearchInWorkspaceCommands.FIND_IN_FOLDER.id,
303
+ keybinding: 'shift+alt+f',
304
+ when: 'explorerResourceIsFolder'
305
+ });
306
+ }
307
+
308
+ override registerMenus(menus: MenuModelRegistry): void {
309
+ super.registerMenus(menus);
310
+ menus.registerMenuAction(NavigatorContextMenu.SEARCH, {
311
+ commandId: SearchInWorkspaceCommands.FIND_IN_FOLDER.id,
312
+ when: 'explorerResourceIsFolder'
313
+ });
314
+ menus.registerMenuAction(CommonMenus.EDIT_FIND, {
315
+ commandId: SearchInWorkspaceCommands.OPEN_SIW_WIDGET.id,
316
+ order: '2'
317
+ });
318
+ menus.registerMenuAction(CommonMenus.EDIT_FIND, {
319
+ commandId: SearchInWorkspaceCommands.REPLACE_IN_FILES.id,
320
+ order: '3'
321
+ });
322
+ menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.INTERNAL, {
323
+ commandId: SearchInWorkspaceCommands.DISMISS_RESULT.id,
324
+ order: '1'
325
+ });
326
+ menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
327
+ commandId: SearchInWorkspaceCommands.COPY_ONE.id,
328
+ order: '1',
329
+ });
330
+ menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
331
+ commandId: CommonCommands.COPY_PATH.id,
332
+ order: '2',
333
+ });
334
+ menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.COPY, {
335
+ commandId: SearchInWorkspaceCommands.COPY_ALL.id,
336
+ order: '3',
337
+ });
338
+ menus.registerMenuAction(SearchInWorkspaceResultTreeWidget.Menus.EXTERNAL, {
339
+ commandId: FileNavigatorCommands.REVEAL_IN_NAVIGATOR.id,
340
+ order: '1',
341
+ });
342
+ }
343
+
344
+ async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise<void> {
345
+ const widget = await this.widget;
346
+ const onDidChange = widget.onDidUpdate;
347
+ toolbarRegistry.registerItem({
348
+ id: SearchInWorkspaceCommands.CANCEL_SEARCH.id,
349
+ command: SearchInWorkspaceCommands.CANCEL_SEARCH.id,
350
+ tooltip: SearchInWorkspaceCommands.CANCEL_SEARCH.label,
351
+ priority: 0,
352
+ onDidChange
353
+ });
354
+ toolbarRegistry.registerItem({
355
+ id: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
356
+ command: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
357
+ tooltip: SearchInWorkspaceCommands.REFRESH_RESULTS.label,
358
+ priority: 1,
359
+ onDidChange
360
+ });
361
+ toolbarRegistry.registerItem({
362
+ id: SearchInWorkspaceCommands.CLEAR_ALL.id,
363
+ command: SearchInWorkspaceCommands.CLEAR_ALL.id,
364
+ tooltip: SearchInWorkspaceCommands.CLEAR_ALL.label,
365
+ priority: 2,
366
+ onDidChange
367
+ });
368
+ toolbarRegistry.registerItem({
369
+ id: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
370
+ command: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
371
+ tooltip: SearchInWorkspaceCommands.COLLAPSE_ALL.label,
372
+ priority: 3,
373
+ onDidChange
374
+ });
375
+ toolbarRegistry.registerItem({
376
+ id: SearchInWorkspaceCommands.EXPAND_ALL.id,
377
+ command: SearchInWorkspaceCommands.EXPAND_ALL.id,
378
+ tooltip: SearchInWorkspaceCommands.EXPAND_ALL.label,
379
+ priority: 3,
380
+ onDidChange
381
+ });
382
+ }
383
+
384
+ protected newUriAwareCommandHandler(handler: UriCommandHandler<URI>): UriAwareCommandHandler<URI> {
385
+ return UriAwareCommandHandler.MonoSelect(this.selectionService, handler);
386
+ }
387
+
388
+ protected newMultiUriAwareCommandHandler(handler: UriCommandHandler<URI[]>): UriAwareCommandHandler<URI[]> {
389
+ return UriAwareCommandHandler.MultiSelect(this.selectionService, handler);
390
+ }
391
+
392
+ registerThemeStyle(theme: ColorTheme, collector: CssStyleCollector): void {
393
+ const contrastBorder = theme.getColor('contrastBorder');
394
+ if (contrastBorder && isHighContrast(theme.type)) {
395
+ collector.addRule(`
396
+ .t-siw-search-container .searchHeader .search-field-container {
397
+ border-color: ${contrastBorder};
398
+ }
399
+ `);
400
+ }
401
+ }
402
+ }