blob: 6d90ac065dd5809ba89fbd36a909e80f160817a6 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
Tim van der Lippe83f02be2020-01-23 11:11:408 * * Redistributions of source code must retain the above copyright
Blink Reformat4c46d092018-04-07 15:32:379 * notice, this list of conditions and the following disclaimer.
Tim van der Lippe83f02be2020-01-23 11:11:4010 * * Redistributions in binary form must reproduce the above
Blink Reformat4c46d092018-04-07 15:32:3711 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
Tim van der Lippe83f02be2020-01-23 11:11:4014 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
Blink Reformat4c46d092018-04-07 15:32:3717 *
Tim van der Lippe83f02be2020-01-23 11:11:4018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Blink Reformat4c46d092018-04-07 15:32:3719 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Tim van der Lippe83f02be2020-01-23 11:11:4021 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Blink Reformat4c46d092018-04-07 15:32:3723 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
Tim van der Lippe8987f8f2020-01-03 15:03:1630
Blink Reformat4c46d092018-04-07 15:32:3731/**
32 * @implements {Search.SearchScope}
33 */
Paul Lewis39944952020-01-22 15:45:1834export class SourcesSearchScope {
Blink Reformat4c46d092018-04-07 15:32:3735 constructor() {
36 // FIXME: Add title once it is used by search controller.
37 this._searchId = 0;
38 /** @type {!Array<!Workspace.UISourceCode>} */
39 this._searchResultCandidates = [];
40 /** @type {?function(!Search.SearchResult)} */
41 this._searchResultCallback = null;
42 /** @type {?function(boolean)} */
43 this._searchFinishedCallback = null;
44 /** @type {?Workspace.ProjectSearchConfig} */
45 this._searchConfig = null;
46 }
47
48 /**
49 * @param {!Workspace.UISourceCode} uiSourceCode1
50 * @param {!Workspace.UISourceCode} uiSourceCode2
51 * @return {number}
52 */
53 static _filesComparator(uiSourceCode1, uiSourceCode2) {
Tim van der Lippe1d6e57a2019-09-30 11:55:3454 if (uiSourceCode1.isDirty() && !uiSourceCode2.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:3755 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3456 }
57 if (!uiSourceCode1.isDirty() && uiSourceCode2.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:3758 return 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3459 }
Joel Einbinderef3555d2018-12-04 02:14:4460 const isFileSystem1 = uiSourceCode1.project().type() === Workspace.projectTypes.FileSystem &&
61 !Persistence.persistence.binding(uiSourceCode1);
62 const isFileSystem2 = uiSourceCode2.project().type() === Workspace.projectTypes.FileSystem &&
63 !Persistence.persistence.binding(uiSourceCode2);
Tim van der Lippe1d6e57a2019-09-30 11:55:3464 if (isFileSystem1 !== isFileSystem2) {
Joel Einbinderef3555d2018-12-04 02:14:4465 return isFileSystem1 ? 1 : -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3466 }
Blink Reformat4c46d092018-04-07 15:32:3767 const url1 = uiSourceCode1.url();
68 const url2 = uiSourceCode2.url();
Tim van der Lippe1d6e57a2019-09-30 11:55:3469 if (url1 && !url2) {
Blink Reformat4c46d092018-04-07 15:32:3770 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3471 }
72 if (!url1 && url2) {
Blink Reformat4c46d092018-04-07 15:32:3773 return 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3474 }
Blink Reformat4c46d092018-04-07 15:32:3775 return String.naturalOrderComparator(uiSourceCode1.fullDisplayName(), uiSourceCode2.fullDisplayName());
76 }
77
78 /**
79 * @override
80 * @param {!Common.Progress} progress
81 */
82 performIndexing(progress) {
83 this.stopSearch();
84
85 const projects = this._projects();
86 const compositeProgress = new Common.CompositeProgress(progress);
87 for (let i = 0; i < projects.length; ++i) {
88 const project = projects[i];
89 const projectProgress = compositeProgress.createSubProgress(project.uiSourceCodes().length);
90 project.indexContent(projectProgress);
91 }
92 }
93
94 /**
95 * @return {!Array.<!Workspace.Project>}
96 */
97 _projects() {
Paul Lewis4b64b3f2020-01-23 11:41:2098 const searchInAnonymousAndContentScripts =
99 self.Common.settings.moduleSetting('searchInAnonymousAndContentScripts').get();
Blink Reformat4c46d092018-04-07 15:32:37100
101 return Workspace.workspace.projects().filter(project => {
Tim van der Lippe1d6e57a2019-09-30 11:55:34102 if (project.type() === Workspace.projectTypes.Service) {
Blink Reformat4c46d092018-04-07 15:32:37103 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34104 }
105 if (!searchInAnonymousAndContentScripts && project.isServiceProject()) {
Blink Reformat4c46d092018-04-07 15:32:37106 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34107 }
108 if (!searchInAnonymousAndContentScripts && project.type() === Workspace.projectTypes.ContentScripts) {
Blink Reformat4c46d092018-04-07 15:32:37109 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34110 }
Blink Reformat4c46d092018-04-07 15:32:37111 return true;
112 });
113 }
114
115 /**
116 * @override
117 * @param {!Workspace.ProjectSearchConfig} searchConfig
118 * @param {!Common.Progress} progress
119 * @param {function(!Search.SearchResult)} searchResultCallback
120 * @param {function(boolean)} searchFinishedCallback
121 */
122 performSearch(searchConfig, progress, searchResultCallback, searchFinishedCallback) {
123 this.stopSearch();
124 this._searchResultCandidates = [];
125 this._searchResultCallback = searchResultCallback;
126 this._searchFinishedCallback = searchFinishedCallback;
127 this._searchConfig = searchConfig;
128
129 const promises = [];
130 const compositeProgress = new Common.CompositeProgress(progress);
131 const searchContentProgress = compositeProgress.createSubProgress();
132 const findMatchingFilesProgress = new Common.CompositeProgress(compositeProgress.createSubProgress());
133 for (const project of this._projects()) {
134 const weight = project.uiSourceCodes().length;
135 const findMatchingFilesInProjectProgress = findMatchingFilesProgress.createSubProgress(weight);
136 const filesMathingFileQuery = this._projectFilesMatchingFileQuery(project, searchConfig);
137 const promise =
138 project
139 .findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, findMatchingFilesInProjectProgress)
140 .then(this._processMatchingFilesForProject.bind(
141 this, this._searchId, project, searchConfig, filesMathingFileQuery));
142 promises.push(promise);
143 }
144
145 Promise.all(promises).then(this._processMatchingFiles.bind(
146 this, this._searchId, searchContentProgress, this._searchFinishedCallback.bind(this, true)));
147 }
148
149 /**
150 * @param {!Workspace.Project} project
151 * @param {!Workspace.ProjectSearchConfig} searchConfig
152 * @param {boolean=} dirtyOnly
153 * @return {!Array.<string>}
154 */
155 _projectFilesMatchingFileQuery(project, searchConfig, dirtyOnly) {
156 const result = [];
157 const uiSourceCodes = project.uiSourceCodes();
158 for (let i = 0; i < uiSourceCodes.length; ++i) {
159 const uiSourceCode = uiSourceCodes[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34160 if (!uiSourceCode.contentType().isTextType()) {
Blink Reformat4c46d092018-04-07 15:32:37161 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34162 }
Blink Reformat4c46d092018-04-07 15:32:37163 const binding = Persistence.persistence.binding(uiSourceCode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34164 if (binding && binding.network === uiSourceCode) {
Blink Reformat4c46d092018-04-07 15:32:37165 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34166 }
167 if (dirtyOnly && !uiSourceCode.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:37168 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34169 }
170 if (searchConfig.filePathMatchesFileQuery(uiSourceCode.fullDisplayName())) {
Blink Reformat4c46d092018-04-07 15:32:37171 result.push(uiSourceCode.url());
Tim van der Lippe1d6e57a2019-09-30 11:55:34172 }
Blink Reformat4c46d092018-04-07 15:32:37173 }
174 result.sort(String.naturalOrderComparator);
175 return result;
176 }
177
178 /**
179 * @param {number} searchId
180 * @param {!Workspace.Project} project
181 * @param {!Workspace.ProjectSearchConfig} searchConfig
182 * @param {!Array<string>} filesMathingFileQuery
183 * @param {!Array<string>} files
184 */
185 _processMatchingFilesForProject(searchId, project, searchConfig, filesMathingFileQuery, files) {
186 if (searchId !== this._searchId) {
187 this._searchFinishedCallback(false);
188 return;
189 }
190
191 files.sort(String.naturalOrderComparator);
192 files = files.intersectOrdered(filesMathingFileQuery, String.naturalOrderComparator);
193 const dirtyFiles = this._projectFilesMatchingFileQuery(project, searchConfig, true);
194 files = files.mergeOrdered(dirtyFiles, String.naturalOrderComparator);
195
196 const uiSourceCodes = [];
197 for (const file of files) {
198 const uiSourceCode = project.uiSourceCodeForURL(file);
Tim van der Lippe1d6e57a2019-09-30 11:55:34199 if (!uiSourceCode) {
Blink Reformat4c46d092018-04-07 15:32:37200 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34201 }
Blink Reformat4c46d092018-04-07 15:32:37202 const script = Bindings.DefaultScriptMapping.scriptForUISourceCode(uiSourceCode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34203 if (script && !script.isAnonymousScript()) {
Blink Reformat4c46d092018-04-07 15:32:37204 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34205 }
Blink Reformat4c46d092018-04-07 15:32:37206 uiSourceCodes.push(uiSourceCode);
207 }
Tim van der Lippe8987f8f2020-01-03 15:03:16208 uiSourceCodes.sort(SourcesSearchScope._filesComparator);
Blink Reformat4c46d092018-04-07 15:32:37209 this._searchResultCandidates =
Tim van der Lippe8987f8f2020-01-03 15:03:16210 this._searchResultCandidates.mergeOrdered(uiSourceCodes, SourcesSearchScope._filesComparator);
Blink Reformat4c46d092018-04-07 15:32:37211 }
212
213 /**
214 * @param {number} searchId
215 * @param {!Common.Progress} progress
216 * @param {function()} callback
217 */
218 _processMatchingFiles(searchId, progress, callback) {
219 if (searchId !== this._searchId) {
220 this._searchFinishedCallback(false);
221 return;
222 }
223
224 const files = this._searchResultCandidates;
225 if (!files.length) {
226 progress.done();
227 callback();
228 return;
229 }
230
231 progress.setTotalWork(files.length);
232
233 let fileIndex = 0;
234 const maxFileContentRequests = 20;
235 let callbacksLeft = 0;
236
Tim van der Lippe1d6e57a2019-09-30 11:55:34237 for (let i = 0; i < maxFileContentRequests && i < files.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37238 scheduleSearchInNextFileOrFinish.call(this);
Tim van der Lippe1d6e57a2019-09-30 11:55:34239 }
Blink Reformat4c46d092018-04-07 15:32:37240
241 /**
242 * @param {!Workspace.UISourceCode} uiSourceCode
Tim van der Lippe8987f8f2020-01-03 15:03:16243 * @this {SourcesSearchScope}
Blink Reformat4c46d092018-04-07 15:32:37244 */
245 function searchInNextFile(uiSourceCode) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34246 if (uiSourceCode.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:37247 contentLoaded.call(this, uiSourceCode, uiSourceCode.workingCopy());
Tim van der Lippe1d6e57a2019-09-30 11:55:34248 } else {
Rob Paveza2eb8c142019-10-13 18:02:38249 uiSourceCode.requestContent().then(deferredContent => {
250 contentLoaded.call(this, uiSourceCode, deferredContent.content || '');
251 });
Tim van der Lippe1d6e57a2019-09-30 11:55:34252 }
Blink Reformat4c46d092018-04-07 15:32:37253 }
254
255 /**
Tim van der Lippe8987f8f2020-01-03 15:03:16256 * @this {SourcesSearchScope}
Blink Reformat4c46d092018-04-07 15:32:37257 */
258 function scheduleSearchInNextFileOrFinish() {
259 if (fileIndex >= files.length) {
260 if (!callbacksLeft) {
261 progress.done();
262 callback();
263 return;
264 }
265 return;
266 }
267
268 ++callbacksLeft;
269 const uiSourceCode = files[fileIndex++];
270 setTimeout(searchInNextFile.bind(this, uiSourceCode), 0);
271 }
272
273 /**
274 * @param {!Workspace.UISourceCode} uiSourceCode
Rob Paveza2eb8c142019-10-13 18:02:38275 * @param {string} content
Tim van der Lippe8987f8f2020-01-03 15:03:16276 * @this {SourcesSearchScope}
Blink Reformat4c46d092018-04-07 15:32:37277 */
278 function contentLoaded(uiSourceCode, content) {
279 /**
280 * @param {!Common.ContentProvider.SearchMatch} a
281 * @param {!Common.ContentProvider.SearchMatch} b
282 */
283 function matchesComparator(a, b) {
284 return a.lineNumber - b.lineNumber;
285 }
286
287 progress.worked(1);
288 let matches = [];
289 const queries = this._searchConfig.queries();
290 if (content !== null) {
291 for (let i = 0; i < queries.length; ++i) {
292 const nextMatches = Common.ContentProvider.performSearchInContent(
293 content, queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex());
294 matches = matches.mergeOrdered(nextMatches, matchesComparator);
295 }
296 }
297 if (matches) {
Tim van der Lippe8987f8f2020-01-03 15:03:16298 const searchResult = new FileBasedSearchResult(uiSourceCode, matches);
Blink Reformat4c46d092018-04-07 15:32:37299 this._searchResultCallback(searchResult);
300 }
301
302 --callbacksLeft;
303 scheduleSearchInNextFileOrFinish.call(this);
304 }
305 }
306
307 /**
308 * @override
309 */
310 stopSearch() {
311 ++this._searchId;
312 }
Tim van der Lippe8987f8f2020-01-03 15:03:16313}
Blink Reformat4c46d092018-04-07 15:32:37314
315/**
316 * @implements {Search.SearchResult}
317 */
Tim van der Lippe8987f8f2020-01-03 15:03:16318export class FileBasedSearchResult {
Blink Reformat4c46d092018-04-07 15:32:37319 /**
320 * @param {!Workspace.UISourceCode} uiSourceCode
321 * @param {!Array.<!Common.ContentProvider.SearchMatch>} searchMatches
322 */
323 constructor(uiSourceCode, searchMatches) {
324 this._uiSourceCode = uiSourceCode;
325 this._searchMatches = searchMatches;
326 }
327
328 /**
329 * @override
330 * @return {string}
331 */
332 label() {
333 return this._uiSourceCode.displayName();
334 }
335
336 /**
337 * @override
338 * @return {string}
339 */
340 description() {
341 return this._uiSourceCode.fullDisplayName();
342 }
343
344 /**
345 * @override
346 * @return {number}
347 */
348 matchesCount() {
349 return this._searchMatches.length;
350 }
351
352 /**
353 * @override
354 * @param {number} index
355 * @return {string}
356 */
357 matchLineContent(index) {
358 return this._searchMatches[index].lineContent;
359 }
360
361 /**
362 * @override
363 * @param {number} index
364 * @return {!Object}
365 */
366 matchRevealable(index) {
367 const match = this._searchMatches[index];
Tim van der Lippeffa78622019-09-16 12:07:12368 return this._uiSourceCode.uiLocation(match.lineNumber, undefined);
Blink Reformat4c46d092018-04-07 15:32:37369 }
370
371 /**
372 * @override
373 * @param {number} index
374 * @return {?}
375 */
376 matchLabel(index) {
377 return this._searchMatches[index].lineNumber + 1;
378 }
Tim van der Lippe8987f8f2020-01-03 15:03:16379}