blob: 0f6214e1b7a5034bef9e9c30d1ed0929900813e2 [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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/**
29 * @implements {Search.SearchScope}
30 */
31Sources.SourcesSearchScope = class {
32 constructor() {
33 // FIXME: Add title once it is used by search controller.
34 this._searchId = 0;
35 /** @type {!Array<!Workspace.UISourceCode>} */
36 this._searchResultCandidates = [];
37 /** @type {?function(!Search.SearchResult)} */
38 this._searchResultCallback = null;
39 /** @type {?function(boolean)} */
40 this._searchFinishedCallback = null;
41 /** @type {?Workspace.ProjectSearchConfig} */
42 this._searchConfig = null;
43 }
44
45 /**
46 * @param {!Workspace.UISourceCode} uiSourceCode1
47 * @param {!Workspace.UISourceCode} uiSourceCode2
48 * @return {number}
49 */
50 static _filesComparator(uiSourceCode1, uiSourceCode2) {
Tim van der Lippe1d6e57a2019-09-30 11:55:3451 if (uiSourceCode1.isDirty() && !uiSourceCode2.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:3752 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3453 }
54 if (!uiSourceCode1.isDirty() && uiSourceCode2.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:3755 return 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3456 }
Joel Einbinderef3555d2018-12-04 02:14:4457 const isFileSystem1 = uiSourceCode1.project().type() === Workspace.projectTypes.FileSystem &&
58 !Persistence.persistence.binding(uiSourceCode1);
59 const isFileSystem2 = uiSourceCode2.project().type() === Workspace.projectTypes.FileSystem &&
60 !Persistence.persistence.binding(uiSourceCode2);
Tim van der Lippe1d6e57a2019-09-30 11:55:3461 if (isFileSystem1 !== isFileSystem2) {
Joel Einbinderef3555d2018-12-04 02:14:4462 return isFileSystem1 ? 1 : -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3463 }
Blink Reformat4c46d092018-04-07 15:32:3764 const url1 = uiSourceCode1.url();
65 const url2 = uiSourceCode2.url();
Tim van der Lippe1d6e57a2019-09-30 11:55:3466 if (url1 && !url2) {
Blink Reformat4c46d092018-04-07 15:32:3767 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3468 }
69 if (!url1 && url2) {
Blink Reformat4c46d092018-04-07 15:32:3770 return 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:3471 }
Blink Reformat4c46d092018-04-07 15:32:3772 return String.naturalOrderComparator(uiSourceCode1.fullDisplayName(), uiSourceCode2.fullDisplayName());
73 }
74
75 /**
76 * @override
77 * @param {!Common.Progress} progress
78 */
79 performIndexing(progress) {
80 this.stopSearch();
81
82 const projects = this._projects();
83 const compositeProgress = new Common.CompositeProgress(progress);
84 for (let i = 0; i < projects.length; ++i) {
85 const project = projects[i];
86 const projectProgress = compositeProgress.createSubProgress(project.uiSourceCodes().length);
87 project.indexContent(projectProgress);
88 }
89 }
90
91 /**
92 * @return {!Array.<!Workspace.Project>}
93 */
94 _projects() {
95 const searchInAnonymousAndContentScripts = Common.moduleSetting('searchInAnonymousAndContentScripts').get();
96
97 return Workspace.workspace.projects().filter(project => {
Tim van der Lippe1d6e57a2019-09-30 11:55:3498 if (project.type() === Workspace.projectTypes.Service) {
Blink Reformat4c46d092018-04-07 15:32:3799 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34100 }
101 if (!searchInAnonymousAndContentScripts && project.isServiceProject()) {
Blink Reformat4c46d092018-04-07 15:32:37102 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34103 }
104 if (!searchInAnonymousAndContentScripts && project.type() === Workspace.projectTypes.ContentScripts) {
Blink Reformat4c46d092018-04-07 15:32:37105 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34106 }
Blink Reformat4c46d092018-04-07 15:32:37107 return true;
108 });
109 }
110
111 /**
112 * @override
113 * @param {!Workspace.ProjectSearchConfig} searchConfig
114 * @param {!Common.Progress} progress
115 * @param {function(!Search.SearchResult)} searchResultCallback
116 * @param {function(boolean)} searchFinishedCallback
117 */
118 performSearch(searchConfig, progress, searchResultCallback, searchFinishedCallback) {
119 this.stopSearch();
120 this._searchResultCandidates = [];
121 this._searchResultCallback = searchResultCallback;
122 this._searchFinishedCallback = searchFinishedCallback;
123 this._searchConfig = searchConfig;
124
125 const promises = [];
126 const compositeProgress = new Common.CompositeProgress(progress);
127 const searchContentProgress = compositeProgress.createSubProgress();
128 const findMatchingFilesProgress = new Common.CompositeProgress(compositeProgress.createSubProgress());
129 for (const project of this._projects()) {
130 const weight = project.uiSourceCodes().length;
131 const findMatchingFilesInProjectProgress = findMatchingFilesProgress.createSubProgress(weight);
132 const filesMathingFileQuery = this._projectFilesMatchingFileQuery(project, searchConfig);
133 const promise =
134 project
135 .findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, findMatchingFilesInProjectProgress)
136 .then(this._processMatchingFilesForProject.bind(
137 this, this._searchId, project, searchConfig, filesMathingFileQuery));
138 promises.push(promise);
139 }
140
141 Promise.all(promises).then(this._processMatchingFiles.bind(
142 this, this._searchId, searchContentProgress, this._searchFinishedCallback.bind(this, true)));
143 }
144
145 /**
146 * @param {!Workspace.Project} project
147 * @param {!Workspace.ProjectSearchConfig} searchConfig
148 * @param {boolean=} dirtyOnly
149 * @return {!Array.<string>}
150 */
151 _projectFilesMatchingFileQuery(project, searchConfig, dirtyOnly) {
152 const result = [];
153 const uiSourceCodes = project.uiSourceCodes();
154 for (let i = 0; i < uiSourceCodes.length; ++i) {
155 const uiSourceCode = uiSourceCodes[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34156 if (!uiSourceCode.contentType().isTextType()) {
Blink Reformat4c46d092018-04-07 15:32:37157 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34158 }
Blink Reformat4c46d092018-04-07 15:32:37159 const binding = Persistence.persistence.binding(uiSourceCode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34160 if (binding && binding.network === uiSourceCode) {
Blink Reformat4c46d092018-04-07 15:32:37161 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34162 }
163 if (dirtyOnly && !uiSourceCode.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:37164 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34165 }
166 if (searchConfig.filePathMatchesFileQuery(uiSourceCode.fullDisplayName())) {
Blink Reformat4c46d092018-04-07 15:32:37167 result.push(uiSourceCode.url());
Tim van der Lippe1d6e57a2019-09-30 11:55:34168 }
Blink Reformat4c46d092018-04-07 15:32:37169 }
170 result.sort(String.naturalOrderComparator);
171 return result;
172 }
173
174 /**
175 * @param {number} searchId
176 * @param {!Workspace.Project} project
177 * @param {!Workspace.ProjectSearchConfig} searchConfig
178 * @param {!Array<string>} filesMathingFileQuery
179 * @param {!Array<string>} files
180 */
181 _processMatchingFilesForProject(searchId, project, searchConfig, filesMathingFileQuery, files) {
182 if (searchId !== this._searchId) {
183 this._searchFinishedCallback(false);
184 return;
185 }
186
187 files.sort(String.naturalOrderComparator);
188 files = files.intersectOrdered(filesMathingFileQuery, String.naturalOrderComparator);
189 const dirtyFiles = this._projectFilesMatchingFileQuery(project, searchConfig, true);
190 files = files.mergeOrdered(dirtyFiles, String.naturalOrderComparator);
191
192 const uiSourceCodes = [];
193 for (const file of files) {
194 const uiSourceCode = project.uiSourceCodeForURL(file);
Tim van der Lippe1d6e57a2019-09-30 11:55:34195 if (!uiSourceCode) {
Blink Reformat4c46d092018-04-07 15:32:37196 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34197 }
Blink Reformat4c46d092018-04-07 15:32:37198 const script = Bindings.DefaultScriptMapping.scriptForUISourceCode(uiSourceCode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34199 if (script && !script.isAnonymousScript()) {
Blink Reformat4c46d092018-04-07 15:32:37200 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34201 }
Blink Reformat4c46d092018-04-07 15:32:37202 uiSourceCodes.push(uiSourceCode);
203 }
204 uiSourceCodes.sort(Sources.SourcesSearchScope._filesComparator);
205 this._searchResultCandidates =
206 this._searchResultCandidates.mergeOrdered(uiSourceCodes, Sources.SourcesSearchScope._filesComparator);
207 }
208
209 /**
210 * @param {number} searchId
211 * @param {!Common.Progress} progress
212 * @param {function()} callback
213 */
214 _processMatchingFiles(searchId, progress, callback) {
215 if (searchId !== this._searchId) {
216 this._searchFinishedCallback(false);
217 return;
218 }
219
220 const files = this._searchResultCandidates;
221 if (!files.length) {
222 progress.done();
223 callback();
224 return;
225 }
226
227 progress.setTotalWork(files.length);
228
229 let fileIndex = 0;
230 const maxFileContentRequests = 20;
231 let callbacksLeft = 0;
232
Tim van der Lippe1d6e57a2019-09-30 11:55:34233 for (let i = 0; i < maxFileContentRequests && i < files.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37234 scheduleSearchInNextFileOrFinish.call(this);
Tim van der Lippe1d6e57a2019-09-30 11:55:34235 }
Blink Reformat4c46d092018-04-07 15:32:37236
237 /**
238 * @param {!Workspace.UISourceCode} uiSourceCode
239 * @this {Sources.SourcesSearchScope}
240 */
241 function searchInNextFile(uiSourceCode) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34242 if (uiSourceCode.isDirty()) {
Blink Reformat4c46d092018-04-07 15:32:37243 contentLoaded.call(this, uiSourceCode, uiSourceCode.workingCopy());
Tim van der Lippe1d6e57a2019-09-30 11:55:34244 } else {
Blink Reformat4c46d092018-04-07 15:32:37245 uiSourceCode.requestContent().then(contentLoaded.bind(this, uiSourceCode));
Tim van der Lippe1d6e57a2019-09-30 11:55:34246 }
Blink Reformat4c46d092018-04-07 15:32:37247 }
248
249 /**
250 * @this {Sources.SourcesSearchScope}
251 */
252 function scheduleSearchInNextFileOrFinish() {
253 if (fileIndex >= files.length) {
254 if (!callbacksLeft) {
255 progress.done();
256 callback();
257 return;
258 }
259 return;
260 }
261
262 ++callbacksLeft;
263 const uiSourceCode = files[fileIndex++];
264 setTimeout(searchInNextFile.bind(this, uiSourceCode), 0);
265 }
266
267 /**
268 * @param {!Workspace.UISourceCode} uiSourceCode
269 * @param {?string} content
270 * @this {Sources.SourcesSearchScope}
271 */
272 function contentLoaded(uiSourceCode, content) {
273 /**
274 * @param {!Common.ContentProvider.SearchMatch} a
275 * @param {!Common.ContentProvider.SearchMatch} b
276 */
277 function matchesComparator(a, b) {
278 return a.lineNumber - b.lineNumber;
279 }
280
281 progress.worked(1);
282 let matches = [];
283 const queries = this._searchConfig.queries();
284 if (content !== null) {
285 for (let i = 0; i < queries.length; ++i) {
286 const nextMatches = Common.ContentProvider.performSearchInContent(
287 content, queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex());
288 matches = matches.mergeOrdered(nextMatches, matchesComparator);
289 }
290 }
291 if (matches) {
292 const searchResult = new Sources.FileBasedSearchResult(uiSourceCode, matches);
293 this._searchResultCallback(searchResult);
294 }
295
296 --callbacksLeft;
297 scheduleSearchInNextFileOrFinish.call(this);
298 }
299 }
300
301 /**
302 * @override
303 */
304 stopSearch() {
305 ++this._searchId;
306 }
307};
308
309
310/**
311 * @implements {Search.SearchResult}
312 */
313Sources.FileBasedSearchResult = class {
314 /**
315 * @param {!Workspace.UISourceCode} uiSourceCode
316 * @param {!Array.<!Common.ContentProvider.SearchMatch>} searchMatches
317 */
318 constructor(uiSourceCode, searchMatches) {
319 this._uiSourceCode = uiSourceCode;
320 this._searchMatches = searchMatches;
321 }
322
323 /**
324 * @override
325 * @return {string}
326 */
327 label() {
328 return this._uiSourceCode.displayName();
329 }
330
331 /**
332 * @override
333 * @return {string}
334 */
335 description() {
336 return this._uiSourceCode.fullDisplayName();
337 }
338
339 /**
340 * @override
341 * @return {number}
342 */
343 matchesCount() {
344 return this._searchMatches.length;
345 }
346
347 /**
348 * @override
349 * @param {number} index
350 * @return {string}
351 */
352 matchLineContent(index) {
353 return this._searchMatches[index].lineContent;
354 }
355
356 /**
357 * @override
358 * @param {number} index
359 * @return {!Object}
360 */
361 matchRevealable(index) {
362 const match = this._searchMatches[index];
Tim van der Lippeffa78622019-09-16 12:07:12363 return this._uiSourceCode.uiLocation(match.lineNumber, undefined);
Blink Reformat4c46d092018-04-07 15:32:37364 }
365
366 /**
367 * @override
368 * @param {number} index
369 * @return {?}
370 */
371 matchLabel(index) {
372 return this._searchMatches[index].lineNumber + 1;
373 }
374};