@theia/search-in-workspace 1.67.0-next.56 → 1.67.0-next.86

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 (77) hide show
  1. package/lib/browser/components/search-in-workspace-input.d.ts +40 -0
  2. package/lib/browser/components/search-in-workspace-input.d.ts.map +1 -0
  3. package/lib/browser/components/search-in-workspace-input.js +124 -0
  4. package/lib/browser/components/search-in-workspace-input.js.map +1 -0
  5. package/lib/browser/components/search-in-workspace-textarea.d.ts +40 -0
  6. package/lib/browser/components/search-in-workspace-textarea.d.ts.map +1 -0
  7. package/lib/browser/components/search-in-workspace-textarea.js +136 -0
  8. package/lib/browser/components/search-in-workspace-textarea.js.map +1 -0
  9. package/lib/browser/search-in-workspace-context-key-service.d.ts +24 -0
  10. package/lib/browser/search-in-workspace-context-key-service.d.ts.map +1 -0
  11. package/lib/browser/search-in-workspace-context-key-service.js +83 -0
  12. package/lib/browser/search-in-workspace-context-key-service.js.map +1 -0
  13. package/lib/browser/search-in-workspace-factory.d.ts +11 -0
  14. package/lib/browser/search-in-workspace-factory.d.ts.map +1 -0
  15. package/lib/browser/search-in-workspace-factory.js +61 -0
  16. package/lib/browser/search-in-workspace-factory.js.map +1 -0
  17. package/lib/browser/search-in-workspace-frontend-contribution.d.ts +58 -0
  18. package/lib/browser/search-in-workspace-frontend-contribution.d.ts.map +1 -0
  19. package/lib/browser/search-in-workspace-frontend-contribution.js +509 -0
  20. package/lib/browser/search-in-workspace-frontend-contribution.js.map +1 -0
  21. package/lib/browser/search-in-workspace-frontend-module.d.ts +7 -0
  22. package/lib/browser/search-in-workspace-frontend-module.d.ts.map +1 -0
  23. package/lib/browser/search-in-workspace-frontend-module.js +73 -0
  24. package/lib/browser/search-in-workspace-frontend-module.js.map +1 -0
  25. package/lib/browser/search-in-workspace-label-provider.d.ts +10 -0
  26. package/lib/browser/search-in-workspace-label-provider.d.ts.map +1 -0
  27. package/lib/browser/search-in-workspace-label-provider.js +50 -0
  28. package/lib/browser/search-in-workspace-label-provider.js.map +1 -0
  29. package/lib/browser/search-in-workspace-result-tree-widget.d.ts +262 -0
  30. package/lib/browser/search-in-workspace-result-tree-widget.d.ts.map +1 -0
  31. package/lib/browser/search-in-workspace-result-tree-widget.js +1164 -0
  32. package/lib/browser/search-in-workspace-result-tree-widget.js.map +1 -0
  33. package/lib/browser/search-in-workspace-service.d.ts +36 -0
  34. package/lib/browser/search-in-workspace-service.d.ts.map +1 -0
  35. package/lib/browser/search-in-workspace-service.js +151 -0
  36. package/lib/browser/search-in-workspace-service.js.map +1 -0
  37. package/lib/browser/search-in-workspace-widget.d.ts +123 -0
  38. package/lib/browser/search-in-workspace-widget.d.ts.map +1 -0
  39. package/lib/browser/search-in-workspace-widget.js +624 -0
  40. package/lib/browser/search-in-workspace-widget.js.map +1 -0
  41. package/lib/browser/search-layout-migrations.d.ts +6 -0
  42. package/lib/browser/search-layout-migrations.d.ts.map +1 -0
  43. package/lib/browser/search-layout-migrations.js +60 -0
  44. package/lib/browser/search-layout-migrations.js.map +1 -0
  45. package/lib/browser-only/browser-only-search-in-workspace-service.d.ts +5 -0
  46. package/lib/browser-only/browser-only-search-in-workspace-service.d.ts.map +1 -0
  47. package/lib/browser-only/browser-only-search-in-workspace-service.js +40 -0
  48. package/lib/browser-only/browser-only-search-in-workspace-service.js.map +1 -0
  49. package/lib/browser-only/browser-search-in-workspace-server.d.ts +80 -0
  50. package/lib/browser-only/browser-search-in-workspace-server.d.ts.map +1 -0
  51. package/lib/browser-only/browser-search-in-workspace-server.js +378 -0
  52. package/lib/browser-only/browser-search-in-workspace-server.js.map +1 -0
  53. package/lib/browser-only/search-in-workspace-frontend-only-module.d.ts +4 -0
  54. package/lib/browser-only/search-in-workspace-frontend-only-module.d.ts.map +1 -0
  55. package/lib/browser-only/search-in-workspace-frontend-only-module.js +37 -0
  56. package/lib/browser-only/search-in-workspace-frontend-only-module.js.map +1 -0
  57. package/lib/common/search-in-workspace-interface.d.ts +117 -0
  58. package/lib/common/search-in-workspace-interface.d.ts.map +1 -0
  59. package/lib/common/search-in-workspace-interface.js +36 -0
  60. package/lib/common/search-in-workspace-interface.js.map +1 -0
  61. package/lib/common/search-in-workspace-preferences.d.ts +19 -0
  62. package/lib/common/search-in-workspace-preferences.d.ts.map +1 -0
  63. package/lib/common/search-in-workspace-preferences.js +89 -0
  64. package/lib/common/search-in-workspace-preferences.js.map +1 -0
  65. package/lib/node/ripgrep-search-in-workspace-server.d.ts +95 -0
  66. package/lib/node/ripgrep-search-in-workspace-server.d.ts.map +1 -0
  67. package/lib/node/ripgrep-search-in-workspace-server.js +420 -0
  68. package/lib/node/ripgrep-search-in-workspace-server.js.map +1 -0
  69. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.d.ts +2 -0
  70. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.d.ts.map +1 -0
  71. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.js +900 -0
  72. package/lib/node/ripgrep-search-in-workspace-server.slow-spec.js.map +1 -0
  73. package/lib/node/search-in-workspace-backend-module.d.ts +4 -0
  74. package/lib/node/search-in-workspace-backend-module.d.ts.map +1 -0
  75. package/lib/node/search-in-workspace-backend-module.js +35 -0
  76. package/lib/node/search-in-workspace-backend-module.js.map +1 -0
  77. package/package.json +8 -8
@@ -0,0 +1,1164 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2018 TypeFox and others.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.SearchInWorkspaceResultTreeWidget = exports.SearchInWorkspaceResultLineNode = exports.SearchInWorkspaceFileNode = exports.SearchInWorkspaceRootFolderNode = exports.SearchInWorkspaceRoot = void 0;
19
+ const tslib_1 = require("tslib");
20
+ const inversify_1 = require("@theia/core/shared/inversify");
21
+ const browser_1 = require("@theia/core/lib/browser");
22
+ const core_1 = require("@theia/core");
23
+ const browser_2 = require("@theia/editor/lib/browser");
24
+ const browser_3 = require("@theia/workspace/lib/browser");
25
+ const browser_4 = require("@theia/filesystem/lib/browser");
26
+ const file_service_1 = require("@theia/filesystem/lib/browser/file-service");
27
+ const search_in_workspace_service_1 = require("./search-in-workspace-service");
28
+ const common_1 = require("@theia/core/lib/common");
29
+ const uri_1 = require("@theia/core/lib/common/uri");
30
+ const React = require("@theia/core/shared/react");
31
+ const search_in_workspace_preferences_1 = require("../common/search-in-workspace-preferences");
32
+ const color_registry_1 = require("@theia/core/lib/browser/color-registry");
33
+ const minimatch_1 = require("minimatch");
34
+ const disposable_1 = require("@theia/core/lib/common/disposable");
35
+ const debounce = require("@theia/core/shared/lodash.debounce");
36
+ const nls_1 = require("@theia/core/lib/common/nls");
37
+ const common_2 = require("@theia/filesystem/lib/common");
38
+ const ROOT_ID = 'ResultTree';
39
+ var SearchInWorkspaceRoot;
40
+ (function (SearchInWorkspaceRoot) {
41
+ function is(node) {
42
+ return browser_1.CompositeTreeNode.is(node) && node.id === ROOT_ID;
43
+ }
44
+ SearchInWorkspaceRoot.is = is;
45
+ })(SearchInWorkspaceRoot || (exports.SearchInWorkspaceRoot = SearchInWorkspaceRoot = {}));
46
+ var SearchInWorkspaceRootFolderNode;
47
+ (function (SearchInWorkspaceRootFolderNode) {
48
+ function is(node) {
49
+ return browser_1.ExpandableTreeNode.is(node) && browser_1.SelectableTreeNode.is(node) && 'path' in node && 'folderUri' in node && !('fileUri' in node);
50
+ }
51
+ SearchInWorkspaceRootFolderNode.is = is;
52
+ })(SearchInWorkspaceRootFolderNode || (exports.SearchInWorkspaceRootFolderNode = SearchInWorkspaceRootFolderNode = {}));
53
+ var SearchInWorkspaceFileNode;
54
+ (function (SearchInWorkspaceFileNode) {
55
+ function is(node) {
56
+ return browser_1.ExpandableTreeNode.is(node) && browser_1.SelectableTreeNode.is(node) && 'path' in node && 'fileUri' in node && !('folderUri' in node);
57
+ }
58
+ SearchInWorkspaceFileNode.is = is;
59
+ })(SearchInWorkspaceFileNode || (exports.SearchInWorkspaceFileNode = SearchInWorkspaceFileNode = {}));
60
+ var SearchInWorkspaceResultLineNode;
61
+ (function (SearchInWorkspaceResultLineNode) {
62
+ function is(node) {
63
+ return browser_1.SelectableTreeNode.is(node) && 'line' in node && 'character' in node && 'lineText' in node;
64
+ }
65
+ SearchInWorkspaceResultLineNode.is = is;
66
+ })(SearchInWorkspaceResultLineNode || (exports.SearchInWorkspaceResultLineNode = SearchInWorkspaceResultLineNode = {}));
67
+ let SearchInWorkspaceResultTreeWidget = class SearchInWorkspaceResultTreeWidget extends browser_1.TreeWidget {
68
+ constructor(props, model, contextMenuRenderer) {
69
+ super(props, model, contextMenuRenderer);
70
+ this._showReplaceButtons = false;
71
+ this._replaceTerm = '';
72
+ this.searchTerm = '';
73
+ this.startSearchOnModification = (activeEditor) => debounce(() => this.searchActiveEditor(activeEditor, this.searchTerm, this.searchOptions), this.searchOnEditorModificationDelay);
74
+ this.searchOnEditorModificationDelay = 300;
75
+ this.toDisposeOnActiveEditorChanged = new disposable_1.DisposableCollection();
76
+ // The default root name to add external search results in the case that a workspace is opened.
77
+ this.defaultRootName = nls_1.nls.localizeByDefault('Other files');
78
+ this.forceVisibleRootNode = false;
79
+ this.appliedDecorations = new Map();
80
+ this.changeEmitter = new core_1.Emitter();
81
+ this.onExpansionChangedEmitter = new core_1.Emitter();
82
+ this.onExpansionChanged = this.onExpansionChangedEmitter.event;
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ this.focusInputEmitter = new core_1.Emitter();
85
+ this.remove = (node, e) => this.doRemove(node, e);
86
+ model.root = {
87
+ id: ROOT_ID,
88
+ parent: undefined,
89
+ visible: false,
90
+ children: []
91
+ };
92
+ this.toDispose.push(model.onSelectionChanged(nodes => {
93
+ const node = nodes[0];
94
+ if (SearchInWorkspaceResultLineNode.is(node)) {
95
+ this.doOpen(node, true, true);
96
+ }
97
+ }));
98
+ this.toDispose.push(model.onOpenNode(node => {
99
+ if (SearchInWorkspaceResultLineNode.is(node)) {
100
+ this.doOpen(node, true, false);
101
+ }
102
+ }));
103
+ this.resultTree = new Map();
104
+ this.toDispose.push(model.onNodeRefreshed(() => this.changeEmitter.fire(this.resultTree)));
105
+ }
106
+ init() {
107
+ super.init();
108
+ this.addClass('resultContainer');
109
+ this.toDispose.push(this.changeEmitter);
110
+ this.toDispose.push(this.focusInputEmitter);
111
+ this.toDispose.push(this.editorManager.onActiveEditorChanged(activeEditor => {
112
+ this.updateCurrentEditorDecorations();
113
+ this.toDisposeOnActiveEditorChanged.dispose();
114
+ this.toDispose.push(this.toDisposeOnActiveEditorChanged);
115
+ if (activeEditor) {
116
+ this.toDisposeOnActiveEditorChanged.push(activeEditor.editor.onDocumentContentChanged(() => {
117
+ if (this.searchTerm !== '' && this.searchInWorkspacePreferences['search.searchOnEditorModification']) {
118
+ this.startSearchOnModification(activeEditor)();
119
+ }
120
+ }));
121
+ }
122
+ }));
123
+ this.toDispose.push(this.searchInWorkspacePreferences.onPreferenceChanged(() => {
124
+ this.update();
125
+ }));
126
+ this.toDispose.push(this.fileService.onDidFilesChange(event => {
127
+ if (event.gotDeleted()) {
128
+ event.getDeleted().forEach(deletedFile => {
129
+ const fileNodes = this.getFileNodesByUri(deletedFile.resource);
130
+ fileNodes.forEach(node => this.removeFileNode(node));
131
+ });
132
+ this.model.refresh();
133
+ }
134
+ }));
135
+ this.toDispose.push(this.model.onExpansionChanged(() => {
136
+ this.onExpansionChangedEmitter.fire(undefined);
137
+ }));
138
+ }
139
+ get fileNumber() {
140
+ let num = 0;
141
+ for (const rootFolderNode of this.resultTree.values()) {
142
+ num += rootFolderNode.children.length;
143
+ }
144
+ return num;
145
+ }
146
+ set showReplaceButtons(srb) {
147
+ this._showReplaceButtons = srb;
148
+ this.update();
149
+ }
150
+ set replaceTerm(rt) {
151
+ this._replaceTerm = rt;
152
+ this.update();
153
+ }
154
+ get isReplacing() {
155
+ return this._replaceTerm !== '' && this._showReplaceButtons;
156
+ }
157
+ get onChange() {
158
+ return this.changeEmitter.event;
159
+ }
160
+ get onFocusInput() {
161
+ return this.focusInputEmitter.event;
162
+ }
163
+ collapseAll() {
164
+ for (const rootFolderNode of this.resultTree.values()) {
165
+ for (const fileNode of rootFolderNode.children) {
166
+ this.expansionService.collapseNode(fileNode);
167
+ }
168
+ if (rootFolderNode.visible) {
169
+ this.expansionService.collapseNode(rootFolderNode);
170
+ }
171
+ }
172
+ }
173
+ expandAll() {
174
+ for (const rootFolderNode of this.resultTree.values()) {
175
+ for (const fileNode of rootFolderNode.children) {
176
+ this.expansionService.expandNode(fileNode);
177
+ }
178
+ if (rootFolderNode.visible) {
179
+ this.expansionService.expandNode(rootFolderNode);
180
+ }
181
+ }
182
+ }
183
+ areResultsCollapsed() {
184
+ for (const rootFolderNode of this.resultTree.values()) {
185
+ for (const fileNode of rootFolderNode.children) {
186
+ if (!browser_1.ExpandableTreeNode.isCollapsed(fileNode)) {
187
+ return false;
188
+ }
189
+ }
190
+ }
191
+ return true;
192
+ }
193
+ selectNextResult() {
194
+ if (!this.model.getFocusedNode()) {
195
+ return this.selectFirstResult();
196
+ }
197
+ let foundNextResult = false;
198
+ while (!foundNextResult) {
199
+ const nextNode = this.model.getNextNode();
200
+ if (!nextNode) {
201
+ return this.selectFirstResult();
202
+ }
203
+ else if (SearchInWorkspaceResultLineNode.is(nextNode)) {
204
+ foundNextResult = true;
205
+ this.selectExpandOpenResultNode(nextNode);
206
+ }
207
+ else {
208
+ this.model.selectNext();
209
+ }
210
+ }
211
+ }
212
+ selectPreviousResult() {
213
+ if (!this.model.getFocusedNode()) {
214
+ return this.selectLastResult();
215
+ }
216
+ let foundSelectedNode = false;
217
+ while (!foundSelectedNode) {
218
+ const prevNode = this.model.getPrevNode();
219
+ if (!prevNode) {
220
+ return this.selectLastResult();
221
+ }
222
+ else if (SearchInWorkspaceResultLineNode.is(prevNode)) {
223
+ foundSelectedNode = true;
224
+ this.selectExpandOpenResultNode(prevNode);
225
+ }
226
+ else if (prevNode.id === 'ResultTree') {
227
+ return this.selectLastResult();
228
+ }
229
+ else {
230
+ this.model.selectPrev();
231
+ }
232
+ }
233
+ }
234
+ selectExpandOpenResultNode(node) {
235
+ this.model.expandNode(node.parent.parent);
236
+ this.model.expandNode(node.parent);
237
+ this.model.selectNode(node);
238
+ this.model.openNode(node);
239
+ }
240
+ selectFirstResult() {
241
+ for (const rootFolder of this.resultTree.values()) {
242
+ for (const file of rootFolder.children) {
243
+ for (const result of file.children) {
244
+ if (browser_1.SelectableTreeNode.is(result)) {
245
+ return this.selectExpandOpenResultNode(result);
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ selectLastResult() {
252
+ const rootFolders = Array.from(this.resultTree.values());
253
+ for (let i = rootFolders.length - 1; i >= 0; i--) {
254
+ const rootFolder = rootFolders[i];
255
+ for (let j = rootFolder.children.length - 1; j >= 0; j--) {
256
+ const file = rootFolder.children[j];
257
+ for (let k = file.children.length - 1; k >= 0; k--) {
258
+ const result = file.children[k];
259
+ if (browser_1.SelectableTreeNode.is(result)) {
260
+ return this.selectExpandOpenResultNode(result);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ /**
267
+ * Find matches for the given editor.
268
+ * @param searchTerm the search term.
269
+ * @param widget the editor widget.
270
+ * @param searchOptions the search options to apply.
271
+ *
272
+ * @returns the list of matches.
273
+ */
274
+ findMatches(searchTerm, widget, searchOptions) {
275
+ if (!widget.editor.document.findMatches) {
276
+ return [];
277
+ }
278
+ const results = widget.editor.document.findMatches({
279
+ searchString: searchTerm,
280
+ isRegex: !!searchOptions.useRegExp,
281
+ matchCase: !!searchOptions.matchCase,
282
+ matchWholeWord: !!searchOptions.matchWholeWord,
283
+ limitResultCount: searchOptions.maxResults
284
+ });
285
+ const matches = [];
286
+ results.forEach(r => {
287
+ const numberOfLines = searchTerm.split('\n').length;
288
+ const lineTexts = [];
289
+ for (let i = 0; i < numberOfLines; i++) {
290
+ lineTexts.push(widget.editor.document.getLineContent(r.range.start.line + i));
291
+ }
292
+ matches.push({
293
+ line: r.range.start.line,
294
+ character: r.range.start.character,
295
+ length: searchTerm.length,
296
+ lineText: lineTexts.join('\n')
297
+ });
298
+ });
299
+ return matches;
300
+ }
301
+ /**
302
+ * Convert a pattern to match all directories.
303
+ * @param workspaceRootUri the uri of the current workspace root.
304
+ * @param pattern the pattern to be converted.
305
+ */
306
+ convertPatternToGlob(workspaceRootUri, pattern) {
307
+ if (pattern.startsWith('**/')) {
308
+ return pattern;
309
+ }
310
+ if (pattern.startsWith('./')) {
311
+ if (workspaceRootUri === undefined) {
312
+ return pattern;
313
+ }
314
+ return workspaceRootUri.toString() + pattern.replace('./', '/');
315
+ }
316
+ return pattern.startsWith('/')
317
+ ? '**' + pattern
318
+ : '**/' + pattern;
319
+ }
320
+ /**
321
+ * Determine if the URI matches any of the patterns.
322
+ * @param uri the editor URI.
323
+ * @param patterns the glob patterns to verify.
324
+ */
325
+ inPatternList(uri, patterns) {
326
+ const opts = { dot: true, matchBase: true };
327
+ return patterns.some(pattern => (0, minimatch_1.minimatch)(uri.toString(), this.convertPatternToGlob(this.workspaceService.getWorkspaceRootUri(uri), pattern), opts));
328
+ }
329
+ /**
330
+ * Determine if the given editor satisfies the filtering criteria.
331
+ * An editor should be searched only if:
332
+ * - it is not excluded through the `excludes` list.
333
+ * - it is not explicitly present in a non-empty `includes` list.
334
+ */
335
+ shouldApplySearch(editorWidget, searchOptions) {
336
+ const excludePatterns = this.getExcludeGlobs(searchOptions.exclude);
337
+ if (this.inPatternList(editorWidget.editor.uri, excludePatterns)) {
338
+ return false;
339
+ }
340
+ const includePatterns = searchOptions.include;
341
+ if (!!(includePatterns === null || includePatterns === void 0 ? void 0 : includePatterns.length) && !this.inPatternList(editorWidget.editor.uri, includePatterns)) {
342
+ return false;
343
+ }
344
+ return true;
345
+ }
346
+ /**
347
+ * Search the active editor only and update the tree with those results.
348
+ */
349
+ searchActiveEditor(activeEditor, searchTerm, searchOptions) {
350
+ const includesExternalResults = () => !!this.resultTree.get(this.defaultRootName);
351
+ // Check if outside workspace results are present before searching.
352
+ const hasExternalResultsBefore = includesExternalResults();
353
+ // Collect search results for the given editor.
354
+ const results = this.searchInEditor(activeEditor, searchTerm, searchOptions);
355
+ // Update the tree by removing the result node, and add new results if applicable.
356
+ this.getFileNodesByUri(activeEditor.editor.uri).forEach(fileNode => this.removeFileNode(fileNode));
357
+ if (results) {
358
+ this.appendToResultTree(results);
359
+ }
360
+ // Check if outside workspace results are present after searching.
361
+ const hasExternalResultsAfter = includesExternalResults();
362
+ // Redo a search to update the tree node visibility if:
363
+ // + `Other files` node was present, now it is not.
364
+ // + `Other files` node was not present, now it is.
365
+ if (hasExternalResultsBefore ? !hasExternalResultsAfter : hasExternalResultsAfter) {
366
+ this.search(this.searchTerm, this.searchOptions);
367
+ return;
368
+ }
369
+ this.handleSearchCompleted();
370
+ }
371
+ /**
372
+ * Perform a search in all open editors.
373
+ * @param searchTerm the search term.
374
+ * @param searchOptions the search options to apply.
375
+ *
376
+ * @returns the tuple of result count, and the list of search results.
377
+ */
378
+ searchInOpenEditors(searchTerm, searchOptions) {
379
+ // Track the number of results found.
380
+ let numberOfResults = 0;
381
+ const searchResults = [];
382
+ this.editorManager.all.forEach(e => {
383
+ const editorResults = this.searchInEditor(e, searchTerm, searchOptions);
384
+ if (editorResults) {
385
+ numberOfResults += editorResults.matches.length;
386
+ searchResults.push(editorResults);
387
+ }
388
+ });
389
+ return {
390
+ numberOfResults,
391
+ matches: searchResults
392
+ };
393
+ }
394
+ /**
395
+ * Perform a search in the target editor.
396
+ * @param editorWidget the editor widget.
397
+ * @param searchTerm the search term.
398
+ * @param searchOptions the search options to apply.
399
+ *
400
+ * @returns the search results from the given editor, undefined if the editor is either filtered or has no matches found.
401
+ */
402
+ searchInEditor(editorWidget, searchTerm, searchOptions) {
403
+ var _a;
404
+ if (!this.shouldApplySearch(editorWidget, searchOptions)) {
405
+ return undefined;
406
+ }
407
+ const matches = this.findMatches(searchTerm, editorWidget, searchOptions);
408
+ if (matches.length <= 0) {
409
+ return undefined;
410
+ }
411
+ const fileUri = editorWidget.editor.uri.toString();
412
+ const root = (_a = this.workspaceService.getWorkspaceRootUri(editorWidget.editor.uri)) === null || _a === void 0 ? void 0 : _a.toString();
413
+ return {
414
+ root: root !== null && root !== void 0 ? root : this.defaultRootName,
415
+ fileUri,
416
+ matches
417
+ };
418
+ }
419
+ /**
420
+ * Append search results to the result tree.
421
+ * @param result Search result.
422
+ */
423
+ appendToResultTree(result) {
424
+ const collapseValue = this.searchInWorkspacePreferences['search.collapseResults'];
425
+ let path;
426
+ if (result.root === this.defaultRootName) {
427
+ path = new uri_1.default(result.fileUri).path.dir.fsPath();
428
+ }
429
+ else {
430
+ path = this.filenameAndPath(result.root, result.fileUri).path;
431
+ }
432
+ const tree = this.resultTree;
433
+ let rootFolderNode = tree.get(result.root);
434
+ if (!rootFolderNode) {
435
+ rootFolderNode = this.createRootFolderNode(result.root);
436
+ tree.set(result.root, rootFolderNode);
437
+ }
438
+ let fileNode = rootFolderNode.children.find(f => f.fileUri === result.fileUri);
439
+ if (!fileNode) {
440
+ fileNode = this.createFileNode(result.root, path, result.fileUri, rootFolderNode);
441
+ rootFolderNode.children.push(fileNode);
442
+ }
443
+ for (const match of result.matches) {
444
+ const line = this.createResultLineNode(result, match, fileNode);
445
+ if (fileNode.children.findIndex(lineNode => lineNode.id === line.id) < 0) {
446
+ fileNode.children.push(line);
447
+ }
448
+ }
449
+ this.collapseFileNode(fileNode, collapseValue);
450
+ }
451
+ /**
452
+ * Handle when searching completed.
453
+ */
454
+ handleSearchCompleted(cancelIndicator) {
455
+ if (cancelIndicator) {
456
+ cancelIndicator.cancel();
457
+ }
458
+ this.sortResultTree();
459
+ this.refreshModelChildren();
460
+ }
461
+ /**
462
+ * Sort the result tree by URIs.
463
+ */
464
+ sortResultTree() {
465
+ // Sort the result map by folder URI.
466
+ const entries = [...this.resultTree.entries()];
467
+ entries.sort(([, a], [, b]) => this.compare(a.folderUri, b.folderUri));
468
+ this.resultTree = new Map(entries);
469
+ // Update the list of children nodes, sorting them by their file URI.
470
+ entries.forEach(([, folder]) => {
471
+ folder.children.sort((a, b) => this.compare(a.fileUri, b.fileUri));
472
+ });
473
+ }
474
+ /**
475
+ * Search and populate the result tree with matches.
476
+ * @param searchTerm the search term.
477
+ * @param searchOptions the search options to apply.
478
+ */
479
+ async search(searchTerm, searchOptions) {
480
+ this.searchTerm = searchTerm;
481
+ this.searchOptions = searchOptions;
482
+ searchOptions = {
483
+ ...searchOptions,
484
+ exclude: this.getExcludeGlobs(searchOptions.exclude)
485
+ };
486
+ this.resultTree.clear();
487
+ this.forceVisibleRootNode = false;
488
+ if (this.cancelIndicator) {
489
+ this.cancelIndicator.cancel();
490
+ }
491
+ if (searchTerm === '') {
492
+ this.refreshModelChildren();
493
+ return;
494
+ }
495
+ this.cancelIndicator = new core_1.CancellationTokenSource();
496
+ const cancelIndicator = this.cancelIndicator;
497
+ const token = this.cancelIndicator.token;
498
+ const progress = await this.progressService.showProgress({ text: `search: ${searchTerm}`, options: { location: 'search' } });
499
+ // Collect search results for opened editors which otherwise may not be found by ripgrep (ex: dirty editors).
500
+ const { numberOfResults, matches } = this.searchInOpenEditors(searchTerm, searchOptions);
501
+ // The root node is visible if outside workspace results are found and workspace root(s) are present.
502
+ this.forceVisibleRootNode = matches.some(m => m.root === this.defaultRootName) && this.workspaceService.opened;
503
+ matches.forEach(m => this.appendToResultTree(m));
504
+ // Exclude files already covered by searching open editors.
505
+ this.editorManager.all.forEach(e => {
506
+ const excludePath = e.editor.uri.path.toString();
507
+ searchOptions.exclude = searchOptions.exclude ? searchOptions.exclude.concat(excludePath) : [excludePath];
508
+ });
509
+ // Reduce `maxResults` due to editor results.
510
+ if (searchOptions.maxResults) {
511
+ searchOptions.maxResults -= numberOfResults;
512
+ }
513
+ let pendingRefreshTimeout;
514
+ const searchId = await this.searchService.search(searchTerm, {
515
+ onResult: (aSearchId, result) => {
516
+ if (token.isCancellationRequested || aSearchId !== searchId) {
517
+ return;
518
+ }
519
+ this.appendToResultTree(result);
520
+ if (pendingRefreshTimeout) {
521
+ clearTimeout(pendingRefreshTimeout);
522
+ }
523
+ // convert type as we are in browser context
524
+ pendingRefreshTimeout = setTimeout(() => this.refreshModelChildren(), 100);
525
+ },
526
+ onDone: () => {
527
+ this.handleSearchCompleted(cancelIndicator);
528
+ }
529
+ }, searchOptions).catch(() => {
530
+ this.handleSearchCompleted(cancelIndicator);
531
+ });
532
+ token.onCancellationRequested(() => {
533
+ progress.cancel();
534
+ if (typeof searchId === 'number') {
535
+ this.searchService.cancel(searchId);
536
+ }
537
+ this.cancelIndicator = undefined;
538
+ this.changeEmitter.fire(this.resultTree);
539
+ });
540
+ }
541
+ focusFirstResult() {
542
+ if (SearchInWorkspaceRoot.is(this.model.root) && this.model.root.children.length > 0) {
543
+ const node = this.model.root.children[0];
544
+ if (browser_1.SelectableTreeNode.is(node)) {
545
+ this.node.focus();
546
+ this.model.selectNode(node);
547
+ }
548
+ }
549
+ }
550
+ /**
551
+ * Collapse the search-in-workspace file node
552
+ * based on the preference value.
553
+ */
554
+ collapseFileNode(node, preferenceValue) {
555
+ if (preferenceValue === 'auto' && node.children.length >= 10) {
556
+ node.expanded = false;
557
+ }
558
+ else if (preferenceValue === 'alwaysCollapse') {
559
+ node.expanded = false;
560
+ }
561
+ else if (preferenceValue === 'alwaysExpand') {
562
+ node.expanded = true;
563
+ }
564
+ }
565
+ handleUp(event) {
566
+ if (!this.model.getPrevSelectableNode(this.model.getFocusedNode())) {
567
+ this.focusInputEmitter.fire(true);
568
+ }
569
+ else {
570
+ super.handleUp(event);
571
+ }
572
+ }
573
+ async refreshModelChildren() {
574
+ if (SearchInWorkspaceRoot.is(this.model.root)) {
575
+ this.model.root.children = Array.from(this.resultTree.values());
576
+ this.model.refresh();
577
+ this.updateCurrentEditorDecorations();
578
+ }
579
+ }
580
+ updateCurrentEditorDecorations() {
581
+ this.shell.allTabBars.forEach(tb => {
582
+ const currentTitle = tb.currentTitle;
583
+ if (currentTitle && currentTitle.owner instanceof browser_2.EditorWidget) {
584
+ const widget = currentTitle.owner;
585
+ const fileNodes = this.getFileNodesByUri(widget.editor.uri);
586
+ if (fileNodes.length > 0) {
587
+ fileNodes.forEach(node => {
588
+ this.decorateEditor(node, widget);
589
+ });
590
+ }
591
+ else {
592
+ this.decorateEditor(undefined, widget);
593
+ }
594
+ }
595
+ });
596
+ const currentWidget = this.editorManager.currentEditor;
597
+ if (currentWidget) {
598
+ const fileNodes = this.getFileNodesByUri(currentWidget.editor.uri);
599
+ fileNodes.forEach(node => {
600
+ this.decorateEditor(node, currentWidget);
601
+ });
602
+ }
603
+ }
604
+ createRootFolderNode(rootUri) {
605
+ const uri = new uri_1.default(rootUri);
606
+ return {
607
+ selected: false,
608
+ path: uri.path.fsPath(),
609
+ folderUri: rootUri,
610
+ uri: new uri_1.default(rootUri),
611
+ children: [],
612
+ expanded: true,
613
+ id: rootUri,
614
+ parent: this.model.root,
615
+ visible: this.forceVisibleRootNode || this.workspaceService.isMultiRootWorkspaceOpened
616
+ };
617
+ }
618
+ createFileNode(rootUri, path, fileUri, parent) {
619
+ return {
620
+ selected: false,
621
+ path,
622
+ children: [],
623
+ expanded: true,
624
+ id: `${rootUri}::${fileUri}`,
625
+ parent,
626
+ fileUri,
627
+ uri: new uri_1.default(fileUri),
628
+ };
629
+ }
630
+ createResultLineNode(result, match, fileNode) {
631
+ return {
632
+ ...result,
633
+ ...match,
634
+ selected: false,
635
+ id: result.fileUri + '-' + match.line + '-' + match.character + '-' + match.length,
636
+ name: typeof match.lineText === 'string' ? match.lineText : match.lineText.text,
637
+ parent: fileNode
638
+ };
639
+ }
640
+ getFileNodesByUri(uri) {
641
+ const nodes = [];
642
+ const fileUri = uri.withScheme('file').toString();
643
+ for (const rootFolderNode of this.resultTree.values()) {
644
+ const rootUri = new uri_1.default(rootFolderNode.path).withScheme('file');
645
+ if (rootUri.isEqualOrParent(uri) || rootFolderNode.id === this.defaultRootName) {
646
+ for (const fileNode of rootFolderNode.children) {
647
+ if (fileNode.fileUri === fileUri) {
648
+ nodes.push(fileNode);
649
+ }
650
+ }
651
+ }
652
+ }
653
+ return nodes;
654
+ }
655
+ filenameAndPath(rootUriStr, uriStr) {
656
+ const uri = new uri_1.default(uriStr);
657
+ const relativePath = new uri_1.default(rootUriStr).relative(uri.parent);
658
+ return {
659
+ name: this.labelProvider.getName(uri),
660
+ path: relativePath ? relativePath.fsPath() : ''
661
+ };
662
+ }
663
+ getDepthPadding(depth) {
664
+ return super.getDepthPadding(depth) + 5;
665
+ }
666
+ renderCaption(node, props) {
667
+ if (SearchInWorkspaceRootFolderNode.is(node)) {
668
+ return this.renderRootFolderNode(node);
669
+ }
670
+ else if (SearchInWorkspaceFileNode.is(node)) {
671
+ return this.renderFileNode(node);
672
+ }
673
+ else if (SearchInWorkspaceResultLineNode.is(node)) {
674
+ return this.renderResultLineNode(node);
675
+ }
676
+ return '';
677
+ }
678
+ renderTailDecorations(node, props) {
679
+ return React.createElement("div", { className: 'result-node-buttons' },
680
+ this._showReplaceButtons && this.renderReplaceButton(node),
681
+ this.renderRemoveButton(node));
682
+ }
683
+ doReplace(node, e) {
684
+ const selection = browser_1.SelectableTreeNode.isSelected(node) ? this.selectionService.selection : [node];
685
+ selection.forEach(n => this.replace(n));
686
+ e.stopPropagation();
687
+ }
688
+ renderReplaceButton(node) {
689
+ const isResultLineNode = SearchInWorkspaceResultLineNode.is(node);
690
+ return React.createElement("span", { className: isResultLineNode ? (0, browser_1.codicon)('replace') : (0, browser_1.codicon)('replace-all'), onClick: e => this.doReplace(node, e), title: isResultLineNode
691
+ ? nls_1.nls.localizeByDefault('Replace')
692
+ : nls_1.nls.localizeByDefault('Replace All') });
693
+ }
694
+ getFileCount(node) {
695
+ if (SearchInWorkspaceRoot.is(node)) {
696
+ return node.children.reduce((acc, current) => acc + this.getFileCount(current), 0);
697
+ }
698
+ else if (SearchInWorkspaceRootFolderNode.is(node)) {
699
+ return node.children.length;
700
+ }
701
+ else if (SearchInWorkspaceFileNode.is(node)) {
702
+ return 1;
703
+ }
704
+ return 0;
705
+ }
706
+ getResultCount(node) {
707
+ if (SearchInWorkspaceRoot.is(node)) {
708
+ return node.children.reduce((acc, current) => acc + this.getResultCount(current), 0);
709
+ }
710
+ else if (SearchInWorkspaceRootFolderNode.is(node)) {
711
+ return node.children.reduce((acc, current) => acc + this.getResultCount(current), 0);
712
+ }
713
+ else if (SearchInWorkspaceFileNode.is(node)) {
714
+ return node.children.length;
715
+ }
716
+ else if (SearchInWorkspaceResultLineNode.is(node)) {
717
+ return 1;
718
+ }
719
+ return 0;
720
+ }
721
+ /**
722
+ * Replace results under the node passed into the function. If node is undefined, replace all results.
723
+ * @param node Node in the tree widget where the "replace all" operation is performed
724
+ */
725
+ async replace(node) {
726
+ const replaceForNode = node || this.model.root;
727
+ const needConfirm = !SearchInWorkspaceFileNode.is(node) && !SearchInWorkspaceResultLineNode.is(node);
728
+ const replacementText = this._replaceTerm;
729
+ if (!needConfirm || await this.confirmReplaceAll(this.getResultCount(replaceForNode), this.getFileCount(replaceForNode), replacementText)) {
730
+ (node ? [node] : Array.from(this.resultTree.values())).forEach(n => {
731
+ this.replaceResult(n, !!node, replacementText);
732
+ this.removeNode(n);
733
+ });
734
+ }
735
+ }
736
+ confirmReplaceAll(resultNumber, fileNumber, replacementText) {
737
+ return new browser_1.ConfirmDialog({
738
+ title: nls_1.nls.localizeByDefault('Replace All'),
739
+ msg: this.buildReplaceAllConfirmationMessage(resultNumber, fileNumber, replacementText)
740
+ }).open();
741
+ }
742
+ buildReplaceAllConfirmationMessage(occurrences, fileCount, replaceValue) {
743
+ if (occurrences === 1) {
744
+ if (fileCount === 1) {
745
+ if (replaceValue) {
746
+ return nls_1.nls.localizeByDefault("Replace {0} occurrence across {1} file with '{2}'?", occurrences, fileCount, replaceValue);
747
+ }
748
+ return nls_1.nls.localizeByDefault('Replace {0} occurrence across {1} file?', occurrences, fileCount);
749
+ }
750
+ if (replaceValue) {
751
+ return nls_1.nls.localizeByDefault("Replace {0} occurrence across {1} files with '{2}'?", occurrences, fileCount, replaceValue);
752
+ }
753
+ return nls_1.nls.localizeByDefault('Replace {0} occurrence across {1} files?', occurrences, fileCount);
754
+ }
755
+ if (fileCount === 1) {
756
+ if (replaceValue) {
757
+ return nls_1.nls.localizeByDefault("Replace {0} occurrences across {1} file with '{2}'?", occurrences, fileCount, replaceValue);
758
+ }
759
+ return nls_1.nls.localizeByDefault('Replace {0} occurrences across {1} file?', occurrences, fileCount);
760
+ }
761
+ if (replaceValue) {
762
+ return nls_1.nls.localizeByDefault("Replace {0} occurrences across {1} files with '{2}'?", occurrences, fileCount, replaceValue);
763
+ }
764
+ return nls_1.nls.localizeByDefault('Replace {0} occurrences across {1} files?', occurrences, fileCount);
765
+ }
766
+ updateRightResults(node) {
767
+ const fileNode = node.parent;
768
+ const rightPositionedNodes = fileNode.children.filter(rl => rl.line === node.line && rl.character > node.character);
769
+ const diff = this._replaceTerm.length - this.searchTerm.length;
770
+ rightPositionedNodes.forEach(r => r.character += diff);
771
+ }
772
+ /**
773
+ * Replace text either in all search matches under a node or in all search matches, and save the changes.
774
+ * @param node - node in the tree widget in which the "replace all" is performed.
775
+ * @param {boolean} replaceOne - whether the function is to replace all matches under a node. If it is false, replace all.
776
+ * @param replacementText - text to be used for all replacements in the current replacement cycle.
777
+ */
778
+ async replaceResult(node, replaceOne, replacementText) {
779
+ const toReplace = [];
780
+ if (SearchInWorkspaceRootFolderNode.is(node)) {
781
+ node.children.forEach(fileNode => this.replaceResult(fileNode, replaceOne, replacementText));
782
+ }
783
+ else if (SearchInWorkspaceFileNode.is(node)) {
784
+ toReplace.push(...node.children);
785
+ }
786
+ else if (SearchInWorkspaceResultLineNode.is(node)) {
787
+ toReplace.push(node);
788
+ this.updateRightResults(node);
789
+ }
790
+ if (toReplace.length > 0) {
791
+ // Store the state of all tracked editors before another editor widget might be created for text replacing.
792
+ const trackedEditors = this.editorManager.all;
793
+ // Open the file only if the function is called to replace all matches under a specific node.
794
+ const widget = replaceOne ? await this.doOpen(toReplace[0]) : await this.doGetWidget(toReplace[0]);
795
+ const source = widget.editor.document.getText();
796
+ const replaceOperations = toReplace.map(resultLineNode => ({
797
+ text: replacementText,
798
+ range: {
799
+ start: {
800
+ line: resultLineNode.line - 1,
801
+ character: resultLineNode.character - 1
802
+ },
803
+ end: this.findEndCharacterPosition(resultLineNode),
804
+ }
805
+ }));
806
+ // Replace the text.
807
+ await widget.editor.replaceText({
808
+ source,
809
+ replaceOperations
810
+ });
811
+ // Save the text replacement changes in the editor.
812
+ await widget.saveable.save();
813
+ // Dispose the widget if it is not opened but created for `replaceAll`.
814
+ if (!replaceOne) {
815
+ if (trackedEditors.indexOf(widget) === -1) {
816
+ widget.dispose();
817
+ }
818
+ }
819
+ }
820
+ }
821
+ doRemove(node, e) {
822
+ const selection = browser_1.SelectableTreeNode.isSelected(node) ? this.selectionService.selection : [node];
823
+ selection.forEach(n => this.removeNode(n));
824
+ e.stopPropagation();
825
+ }
826
+ renderRemoveButton(node) {
827
+ return React.createElement("span", { className: (0, browser_1.codicon)('close'), onClick: e => this.remove(node, e), title: 'Dismiss' });
828
+ }
829
+ removeNode(node) {
830
+ if (SearchInWorkspaceRootFolderNode.is(node)) {
831
+ this.removeRootFolderNode(node);
832
+ }
833
+ else if (SearchInWorkspaceFileNode.is(node)) {
834
+ this.removeFileNode(node);
835
+ }
836
+ else if (SearchInWorkspaceResultLineNode.is(node)) {
837
+ this.removeResultLineNode(node);
838
+ }
839
+ this.refreshModelChildren();
840
+ }
841
+ removeRootFolderNode(node) {
842
+ for (const rootUri of this.resultTree.keys()) {
843
+ if (rootUri === node.folderUri) {
844
+ this.resultTree.delete(rootUri);
845
+ break;
846
+ }
847
+ }
848
+ }
849
+ removeFileNode(node) {
850
+ const rootFolderNode = node.parent;
851
+ const index = rootFolderNode.children.findIndex(fileNode => fileNode.id === node.id);
852
+ if (index > -1) {
853
+ rootFolderNode.children.splice(index, 1);
854
+ }
855
+ if (this.getFileCount(rootFolderNode) === 0) {
856
+ this.removeRootFolderNode(rootFolderNode);
857
+ }
858
+ }
859
+ removeResultLineNode(node) {
860
+ const fileNode = node.parent;
861
+ const index = fileNode.children.findIndex(n => n.fileUri === node.fileUri && n.line === node.line && n.character === node.character);
862
+ if (index > -1) {
863
+ fileNode.children.splice(index, 1);
864
+ if (this.getResultCount(fileNode) === 0) {
865
+ this.removeFileNode(fileNode);
866
+ }
867
+ }
868
+ }
869
+ findEndCharacterPosition(node) {
870
+ const lineText = typeof node.lineText === 'string' ? node.lineText : node.lineText.text;
871
+ const lines = lineText.split('\n');
872
+ const line = node.line + lines.length - 2;
873
+ let character = node.character - 1 + node.length;
874
+ if (lines.length > 1) {
875
+ character = node.length - lines[0].length + node.character - lines.length;
876
+ if (lines.length > 2) {
877
+ for (const lineNum of Array(lines.length - 2).keys()) {
878
+ character -= lines[lineNum + 1].length;
879
+ }
880
+ }
881
+ }
882
+ return { line, character };
883
+ }
884
+ renderRootFolderNode(node) {
885
+ const isRoot = node.path === '/' || node.path === `/${this.defaultRootName}`;
886
+ const name = this.toNodeName(node);
887
+ return React.createElement("div", { className: 'result' },
888
+ React.createElement("div", { className: 'result-head' },
889
+ React.createElement("div", { className: `result-head-info noWrapInfo noselect ${node.selected ? 'selected' : ''}` },
890
+ React.createElement("span", { className: `file-icon ${this.toNodeIcon(node) || ''}` }),
891
+ React.createElement("div", { className: 'noWrapInfo' },
892
+ React.createElement("span", { className: 'file-name' }, name),
893
+ !isRoot &&
894
+ React.createElement("span", { className: 'file-path ' + browser_1.TREE_NODE_INFO_CLASS }, node.path))),
895
+ React.createElement("span", { className: 'notification-count-container highlighted-count-container' },
896
+ React.createElement("span", { className: 'notification-count' }, this.getFileCount(node)))));
897
+ }
898
+ renderFileNode(node) {
899
+ return React.createElement("div", { className: 'result' },
900
+ React.createElement("div", { className: 'result-head' },
901
+ React.createElement("div", { className: `result-head-info noWrapInfo noselect ${node.selected ? 'selected' : ''}`, title: new uri_1.default(node.fileUri).path.fsPath() },
902
+ React.createElement("span", { className: `file-icon ${this.toNodeIcon(node)}` }),
903
+ React.createElement("div", { className: 'noWrapInfo' },
904
+ React.createElement("span", { className: 'file-name' }, this.toNodeName(node)),
905
+ React.createElement("span", { className: 'file-path ' + browser_1.TREE_NODE_INFO_CLASS }, node.path))),
906
+ React.createElement("span", { className: 'notification-count-container' },
907
+ React.createElement("span", { className: 'notification-count' }, this.getResultCount(node)))));
908
+ }
909
+ renderResultLineNode(node) {
910
+ const character = typeof node.lineText === 'string' ? node.character : node.lineText.character;
911
+ const lineText = typeof node.lineText === 'string' ? node.lineText : node.lineText.text;
912
+ let start = Math.max(0, character - 26);
913
+ const wordBreak = /\b/g;
914
+ while (start > 0 && wordBreak.test(lineText) && wordBreak.lastIndex < character) {
915
+ if (character - wordBreak.lastIndex < 26) {
916
+ break;
917
+ }
918
+ start = wordBreak.lastIndex;
919
+ wordBreak.lastIndex++;
920
+ }
921
+ const before = lineText.slice(start, character - 1).trimStart();
922
+ const lineCount = lineText.split('\n').length;
923
+ return React.createElement(React.Fragment, null,
924
+ React.createElement("div", { className: `resultLine noWrapInfo noselect ${node.selected ? 'selected' : ''}`, title: lineText.trim() },
925
+ this.searchInWorkspacePreferences['search.lineNumbers'] && React.createElement("span", { className: 'theia-siw-lineNumber' }, node.line),
926
+ React.createElement("span", null, before),
927
+ this.renderMatchLinePart(node),
928
+ lineCount > 1 || React.createElement("span", null, lineText.slice(node.character + node.length - 1, 250 - before.length + node.length))),
929
+ lineCount > 1 && React.createElement("div", { className: 'match-line-num' },
930
+ "+",
931
+ lineCount - 1));
932
+ }
933
+ renderMatchLinePart(node) {
934
+ const replaceTermLines = this._replaceTerm.split('\n');
935
+ const replaceTerm = this.isReplacing ? React.createElement("span", { className: 'replace-term' }, replaceTermLines[0]) : '';
936
+ const className = `match${this.isReplacing ? ' strike-through' : ''}`;
937
+ const text = typeof node.lineText === 'string' ? node.lineText : node.lineText.text;
938
+ const match = text.substring(node.character - 1, node.character + node.length - 1);
939
+ const matchLines = match.split('\n');
940
+ return React.createElement(React.Fragment, null,
941
+ React.createElement("span", { className: className }, matchLines[0]),
942
+ replaceTerm);
943
+ }
944
+ /**
945
+ * Get the editor widget by the node.
946
+ * @param {SearchInWorkspaceResultLineNode} node - the node representing a match in the search results.
947
+ * @returns The editor widget to which the text replace will be done.
948
+ */
949
+ async doGetWidget(node) {
950
+ const fileUri = new uri_1.default(node.fileUri);
951
+ const editorWidget = await this.editorManager.getOrCreateByUri(fileUri);
952
+ return editorWidget;
953
+ }
954
+ async doOpen(node, asDiffWidget = false, preview = false) {
955
+ let fileUri;
956
+ const resultNode = node.parent;
957
+ if (resultNode && this.isReplacing && asDiffWidget) {
958
+ const leftUri = new uri_1.default(node.fileUri);
959
+ const rightUri = await this.createReplacePreview(resultNode);
960
+ fileUri = browser_1.DiffUris.encode(leftUri, rightUri);
961
+ }
962
+ else {
963
+ fileUri = new uri_1.default(node.fileUri);
964
+ }
965
+ const opts = {
966
+ selection: {
967
+ start: {
968
+ line: node.line - 1,
969
+ character: node.character - 1
970
+ },
971
+ end: this.findEndCharacterPosition(node),
972
+ },
973
+ mode: preview ? 'reveal' : 'activate',
974
+ preview,
975
+ };
976
+ const editorWidget = await this.editorManager.open(fileUri, opts);
977
+ if (!browser_1.DiffUris.isDiffUri(fileUri)) {
978
+ this.decorateEditor(resultNode, editorWidget);
979
+ }
980
+ return editorWidget;
981
+ }
982
+ async createReplacePreview(node) {
983
+ const fileUri = new uri_1.default(node.fileUri).withScheme('file');
984
+ const openedEditor = this.editorManager.all.find(({ editor }) => editor.uri.toString() === fileUri.toString());
985
+ let content;
986
+ if (openedEditor) {
987
+ content = openedEditor.editor.document.getText();
988
+ }
989
+ else {
990
+ const resource = await this.fileResourceResolver.resolve(fileUri);
991
+ content = await resource.readContents();
992
+ }
993
+ const searchTermRegExp = new RegExp(this.searchTerm, 'g');
994
+ return fileUri.withScheme(common_1.MEMORY_TEXT).withQuery(content.replace(searchTermRegExp, this._replaceTerm));
995
+ }
996
+ decorateEditor(node, editorWidget) {
997
+ if (!browser_1.DiffUris.isDiffUri(editorWidget.editor.uri)) {
998
+ const key = `${editorWidget.editor.uri.toString()}#search-in-workspace-matches`;
999
+ const oldDecorations = this.appliedDecorations.get(key) || [];
1000
+ const newDecorations = this.createEditorDecorations(node);
1001
+ const appliedDecorations = editorWidget.editor.deltaDecorations({
1002
+ newDecorations,
1003
+ oldDecorations,
1004
+ });
1005
+ this.appliedDecorations.set(key, appliedDecorations);
1006
+ }
1007
+ }
1008
+ createEditorDecorations(resultNode) {
1009
+ const decorations = [];
1010
+ if (resultNode) {
1011
+ resultNode.children.forEach(res => {
1012
+ decorations.push({
1013
+ range: {
1014
+ start: {
1015
+ line: res.line - 1,
1016
+ character: res.character - 1
1017
+ },
1018
+ end: {
1019
+ line: res.line - 1,
1020
+ character: res.character - 1 + res.length
1021
+ }
1022
+ },
1023
+ options: {
1024
+ overviewRuler: {
1025
+ color: {
1026
+ id: 'editor.findMatchHighlightBackground'
1027
+ },
1028
+ position: browser_2.OverviewRulerLane.Center
1029
+ },
1030
+ className: res.selected ? 'current-search-in-workspace-editor-match' : 'search-in-workspace-editor-match',
1031
+ stickiness: browser_2.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore
1032
+ }
1033
+ });
1034
+ });
1035
+ }
1036
+ return decorations;
1037
+ }
1038
+ /**
1039
+ * Get the list of exclude globs.
1040
+ * @param excludeOptions the exclude search option.
1041
+ *
1042
+ * @returns the list of exclude globs.
1043
+ */
1044
+ getExcludeGlobs(excludeOptions) {
1045
+ const excludePreferences = this.filesystemPreferences['files.exclude'];
1046
+ const excludePreferencesGlobs = Object.keys(excludePreferences).filter(key => !!excludePreferences[key]);
1047
+ return [...new Set([...excludePreferencesGlobs, ...excludeOptions || []])];
1048
+ }
1049
+ /**
1050
+ * Compare two normalized strings.
1051
+ *
1052
+ * @param a {string} the first string.
1053
+ * @param b {string} the second string.
1054
+ */
1055
+ compare(a, b) {
1056
+ const itemA = a.toLowerCase().trim();
1057
+ const itemB = b.toLowerCase().trim();
1058
+ return itemA.localeCompare(itemB);
1059
+ }
1060
+ /**
1061
+ * @param recursive if true, all child nodes will be included in the stringified result.
1062
+ */
1063
+ nodeToString(node, recursive) {
1064
+ if (SearchInWorkspaceFileNode.is(node) || SearchInWorkspaceRootFolderNode.is(node)) {
1065
+ if (recursive) {
1066
+ return this.nodeIteratorToString(new browser_1.TopDownTreeIterator(node, { pruneSiblings: true }));
1067
+ }
1068
+ return this.labelProvider.getLongName(node.uri);
1069
+ }
1070
+ if (SearchInWorkspaceResultLineNode.is(node)) {
1071
+ return ` ${node.line}:${node.character}: ${node.lineText}`;
1072
+ }
1073
+ return '';
1074
+ }
1075
+ treeToString() {
1076
+ return this.nodeIteratorToString(this.getVisibleNodes());
1077
+ }
1078
+ *getVisibleNodes() {
1079
+ for (const { node } of this.rows.values()) {
1080
+ yield node;
1081
+ }
1082
+ }
1083
+ nodeIteratorToString(nodes) {
1084
+ const strings = [];
1085
+ for (const node of nodes) {
1086
+ const string = this.nodeToString(node, false);
1087
+ if (string.length !== 0) {
1088
+ strings.push(string);
1089
+ }
1090
+ }
1091
+ return strings.join(core_1.EOL);
1092
+ }
1093
+ };
1094
+ exports.SearchInWorkspaceResultTreeWidget = SearchInWorkspaceResultTreeWidget;
1095
+ tslib_1.__decorate([
1096
+ (0, inversify_1.inject)(search_in_workspace_service_1.SearchInWorkspaceService),
1097
+ tslib_1.__metadata("design:type", search_in_workspace_service_1.SearchInWorkspaceService)
1098
+ ], SearchInWorkspaceResultTreeWidget.prototype, "searchService", void 0);
1099
+ tslib_1.__decorate([
1100
+ (0, inversify_1.inject)(browser_2.EditorManager),
1101
+ tslib_1.__metadata("design:type", browser_2.EditorManager)
1102
+ ], SearchInWorkspaceResultTreeWidget.prototype, "editorManager", void 0);
1103
+ tslib_1.__decorate([
1104
+ (0, inversify_1.inject)(browser_4.FileResourceResolver),
1105
+ tslib_1.__metadata("design:type", browser_4.FileResourceResolver)
1106
+ ], SearchInWorkspaceResultTreeWidget.prototype, "fileResourceResolver", void 0);
1107
+ tslib_1.__decorate([
1108
+ (0, inversify_1.inject)(browser_1.ApplicationShell),
1109
+ tslib_1.__metadata("design:type", browser_1.ApplicationShell)
1110
+ ], SearchInWorkspaceResultTreeWidget.prototype, "shell", void 0);
1111
+ tslib_1.__decorate([
1112
+ (0, inversify_1.inject)(browser_3.WorkspaceService),
1113
+ tslib_1.__metadata("design:type", browser_3.WorkspaceService)
1114
+ ], SearchInWorkspaceResultTreeWidget.prototype, "workspaceService", void 0);
1115
+ tslib_1.__decorate([
1116
+ (0, inversify_1.inject)(browser_1.TreeExpansionService),
1117
+ tslib_1.__metadata("design:type", Object)
1118
+ ], SearchInWorkspaceResultTreeWidget.prototype, "expansionService", void 0);
1119
+ tslib_1.__decorate([
1120
+ (0, inversify_1.inject)(search_in_workspace_preferences_1.SearchInWorkspacePreferences),
1121
+ tslib_1.__metadata("design:type", Object)
1122
+ ], SearchInWorkspaceResultTreeWidget.prototype, "searchInWorkspacePreferences", void 0);
1123
+ tslib_1.__decorate([
1124
+ (0, inversify_1.inject)(core_1.ProgressService),
1125
+ tslib_1.__metadata("design:type", core_1.ProgressService)
1126
+ ], SearchInWorkspaceResultTreeWidget.prototype, "progressService", void 0);
1127
+ tslib_1.__decorate([
1128
+ (0, inversify_1.inject)(color_registry_1.ColorRegistry),
1129
+ tslib_1.__metadata("design:type", color_registry_1.ColorRegistry)
1130
+ ], SearchInWorkspaceResultTreeWidget.prototype, "colorRegistry", void 0);
1131
+ tslib_1.__decorate([
1132
+ (0, inversify_1.inject)(common_2.FileSystemPreferences),
1133
+ tslib_1.__metadata("design:type", Object)
1134
+ ], SearchInWorkspaceResultTreeWidget.prototype, "filesystemPreferences", void 0);
1135
+ tslib_1.__decorate([
1136
+ (0, inversify_1.inject)(file_service_1.FileService),
1137
+ tslib_1.__metadata("design:type", file_service_1.FileService)
1138
+ ], SearchInWorkspaceResultTreeWidget.prototype, "fileService", void 0);
1139
+ tslib_1.__decorate([
1140
+ (0, inversify_1.postConstruct)(),
1141
+ tslib_1.__metadata("design:type", Function),
1142
+ tslib_1.__metadata("design:paramtypes", []),
1143
+ tslib_1.__metadata("design:returntype", void 0)
1144
+ ], SearchInWorkspaceResultTreeWidget.prototype, "init", null);
1145
+ exports.SearchInWorkspaceResultTreeWidget = SearchInWorkspaceResultTreeWidget = tslib_1.__decorate([
1146
+ (0, inversify_1.injectable)(),
1147
+ tslib_1.__param(0, (0, inversify_1.inject)(browser_1.TreeProps)),
1148
+ tslib_1.__param(1, (0, inversify_1.inject)(browser_1.TreeModel)),
1149
+ tslib_1.__param(2, (0, inversify_1.inject)(browser_1.ContextMenuRenderer)),
1150
+ tslib_1.__metadata("design:paramtypes", [Object, Object, browser_1.ContextMenuRenderer])
1151
+ ], SearchInWorkspaceResultTreeWidget);
1152
+ (function (SearchInWorkspaceResultTreeWidget) {
1153
+ let Menus;
1154
+ (function (Menus) {
1155
+ Menus.BASE = ['siw-tree-context-menu'];
1156
+ /** Dismiss command, or others that only affect the widget itself */
1157
+ Menus.INTERNAL = [...Menus.BASE, '1_internal'];
1158
+ /** Copy a stringified representation of content */
1159
+ Menus.COPY = [...Menus.BASE, '2_copy'];
1160
+ /** Commands that lead out of the widget, like revealing a file in the navigator */
1161
+ Menus.EXTERNAL = [...Menus.BASE, '3_external'];
1162
+ })(Menus = SearchInWorkspaceResultTreeWidget.Menus || (SearchInWorkspaceResultTreeWidget.Menus = {}));
1163
+ })(SearchInWorkspaceResultTreeWidget || (exports.SearchInWorkspaceResultTreeWidget = SearchInWorkspaceResultTreeWidget = {}));
1164
+ //# sourceMappingURL=search-in-workspace-result-tree-widget.js.map