Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / sources / SourcesSearchScope.js
blobb7b34cf4c1144f38b2a4d1a9a1831a173fe71132
1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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.
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.
29 /**
30 * @constructor
31 * @implements {WebInspector.SearchScope}
33 WebInspector.SourcesSearchScope = function()
35 // FIXME: Add title once it is used by search controller.
36 this._searchId = 0;
39 /**
40 * @param {!WebInspector.UISourceCode} uiSourceCode1
41 * @param {!WebInspector.UISourceCode} uiSourceCode2
42 * @return {number}
44 WebInspector.SourcesSearchScope._filesComparator = function(uiSourceCode1, uiSourceCode2)
46 if (uiSourceCode1.isDirty() && !uiSourceCode2.isDirty())
47 return -1;
48 if (!uiSourceCode1.isDirty() && uiSourceCode2.isDirty())
49 return 1;
50 var networkURL1 = WebInspector.networkMapping.networkURL(uiSourceCode1);
51 var networkURL2 = WebInspector.networkMapping.networkURL(uiSourceCode2);
52 if (networkURL1 && !networkURL2)
53 return -1;
54 if (!networkURL1 && networkURL2)
55 return 1;
56 return String.naturalOrderComparator(uiSourceCode1.fullDisplayName(), uiSourceCode2.fullDisplayName());
60 WebInspector.SourcesSearchScope.prototype = {
61 /**
62 * @override
63 * @param {!WebInspector.Progress} progress
65 performIndexing: function(progress)
67 this.stopSearch();
69 var projects = this._projects();
70 var compositeProgress = new WebInspector.CompositeProgress(progress);
71 for (var i = 0; i < projects.length; ++i) {
72 var project = projects[i];
73 var projectProgress = compositeProgress.createSubProgress(project.uiSourceCodes().length);
74 project.indexContent(projectProgress);
78 /**
79 * @return {!Array.<!WebInspector.Project>}
81 _projects: function()
83 /**
84 * @param {!WebInspector.Project} project
85 * @return {boolean}
87 function filterOutServiceProjects(project)
89 return !project.isServiceProject() || project.type() === WebInspector.projectTypes.Formatter;
92 /**
93 * @param {!WebInspector.Project} project
94 * @return {boolean}
96 function filterOutContentScriptsIfNeeded(project)
98 return WebInspector.moduleSetting("searchInContentScripts").get() || project.type() !== WebInspector.projectTypes.ContentScripts;
101 return WebInspector.workspace.projects().filter(filterOutServiceProjects).filter(filterOutContentScriptsIfNeeded);
105 * @override
106 * @param {!WebInspector.ProjectSearchConfig} searchConfig
107 * @param {!WebInspector.Progress} progress
108 * @param {function(!WebInspector.FileBasedSearchResult)} searchResultCallback
109 * @param {function(boolean)} searchFinishedCallback
111 performSearch: function(searchConfig, progress, searchResultCallback, searchFinishedCallback)
113 this.stopSearch();
114 this._searchResultCandidates = [];
115 this._searchResultCallback = searchResultCallback;
116 this._searchFinishedCallback = searchFinishedCallback;
117 this._searchConfig = searchConfig;
119 var projects = this._projects();
120 var barrier = new CallbackBarrier();
121 var compositeProgress = new WebInspector.CompositeProgress(progress);
122 var searchContentProgress = compositeProgress.createSubProgress();
123 var findMatchingFilesProgress = new WebInspector.CompositeProgress(compositeProgress.createSubProgress());
124 for (var i = 0; i < projects.length; ++i) {
125 var project = projects[i];
126 var weight = project.uiSourceCodes().length;
127 var findMatchingFilesInProjectProgress = findMatchingFilesProgress.createSubProgress(weight);
128 var barrierCallback = barrier.createCallback();
129 var filesMathingFileQuery = this._projectFilesMatchingFileQuery(project, searchConfig);
130 var callback = this._processMatchingFilesForProject.bind(this, this._searchId, project, filesMathingFileQuery, barrierCallback);
131 project.findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, findMatchingFilesInProjectProgress, callback);
133 barrier.callWhenDone(this._processMatchingFiles.bind(this, this._searchId, searchContentProgress, this._searchFinishedCallback.bind(this, true)));
137 * @param {!WebInspector.Project} project
138 * @param {!WebInspector.ProjectSearchConfig} searchConfig
139 * @param {boolean=} dirtyOnly
140 * @return {!Array.<string>}
142 _projectFilesMatchingFileQuery: function(project, searchConfig, dirtyOnly)
144 var result = [];
145 var uiSourceCodes = project.uiSourceCodes();
146 for (var i = 0; i < uiSourceCodes.length; ++i) {
147 var uiSourceCode = uiSourceCodes[i];
148 if (dirtyOnly && !uiSourceCode.isDirty())
149 continue;
150 if (this._searchConfig.filePathMatchesFileQuery(uiSourceCode.fullDisplayName()))
151 result.push(uiSourceCode.path());
153 result.sort(String.naturalOrderComparator);
154 return result;
158 * @param {number} searchId
159 * @param {!WebInspector.Project} project
160 * @param {!Array.<string>} filesMathingFileQuery
161 * @param {function()} callback
162 * @param {!Array.<string>} files
164 _processMatchingFilesForProject: function(searchId, project, filesMathingFileQuery, callback, files)
166 if (searchId !== this._searchId) {
167 this._searchFinishedCallback(false);
168 return;
171 files.sort(String.naturalOrderComparator);
172 files = files.intersectOrdered(filesMathingFileQuery, String.naturalOrderComparator);
173 var dirtyFiles = this._projectFilesMatchingFileQuery(project, this._searchConfig, true);
174 files = files.mergeOrdered(dirtyFiles, String.naturalOrderComparator);
176 var uiSourceCodes = [];
177 for (var i = 0; i < files.length; ++i) {
178 var uiSourceCode = project.uiSourceCode(files[i]);
179 if (uiSourceCode)
180 uiSourceCodes.push(uiSourceCode);
182 uiSourceCodes.sort(WebInspector.SourcesSearchScope._filesComparator);
183 this._searchResultCandidates = this._searchResultCandidates.mergeOrdered(uiSourceCodes, WebInspector.SourcesSearchScope._filesComparator);
184 callback();
188 * @param {number} searchId
189 * @param {!WebInspector.Progress} progress
190 * @param {function()} callback
192 _processMatchingFiles: function(searchId, progress, callback)
194 if (searchId !== this._searchId) {
195 this._searchFinishedCallback(false);
196 return;
199 var files = this._searchResultCandidates;
200 if (!files.length) {
201 progress.done();
202 callback();
203 return;
206 progress.setTotalWork(files.length);
208 var fileIndex = 0;
209 var maxFileContentRequests = 20;
210 var callbacksLeft = 0;
212 for (var i = 0; i < maxFileContentRequests && i < files.length; ++i)
213 scheduleSearchInNextFileOrFinish.call(this);
216 * @param {!WebInspector.UISourceCode} uiSourceCode
217 * @this {WebInspector.SourcesSearchScope}
219 function searchInNextFile(uiSourceCode)
221 if (uiSourceCode.isDirty())
222 contentLoaded.call(this, uiSourceCode, uiSourceCode.workingCopy());
223 else
224 uiSourceCode.checkContentUpdated(contentUpdated.bind(this, uiSourceCode));
228 * @param {!WebInspector.UISourceCode} uiSourceCode
229 * @this {WebInspector.SourcesSearchScope}
231 function contentUpdated(uiSourceCode)
233 uiSourceCode.requestContent(contentLoaded.bind(this, uiSourceCode));
237 * @this {WebInspector.SourcesSearchScope}
239 function scheduleSearchInNextFileOrFinish()
241 if (fileIndex >= files.length) {
242 if (!callbacksLeft) {
243 progress.done();
244 callback();
245 return;
247 return;
250 ++callbacksLeft;
251 var uiSourceCode = files[fileIndex++];
252 setTimeout(searchInNextFile.bind(this, uiSourceCode), 0);
256 * @param {!WebInspector.UISourceCode} uiSourceCode
257 * @param {?string} content
258 * @this {WebInspector.SourcesSearchScope}
260 function contentLoaded(uiSourceCode, content)
263 * @param {!WebInspector.ContentProvider.SearchMatch} a
264 * @param {!WebInspector.ContentProvider.SearchMatch} b
266 function matchesComparator(a, b)
268 return a.lineNumber - b.lineNumber;
271 progress.worked(1);
272 var matches = [];
273 var queries = this._searchConfig.queries();
274 if (content !== null) {
275 for (var i = 0; i < queries.length; ++i) {
276 var nextMatches = WebInspector.ContentProvider.performSearchInContent(content, queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex());
277 matches = matches.mergeOrdered(nextMatches, matchesComparator);
280 if (matches) {
281 var searchResult = new WebInspector.FileBasedSearchResult(uiSourceCode, matches);
282 this._searchResultCallback(searchResult);
285 --callbacksLeft;
286 scheduleSearchInNextFileOrFinish.call(this);
291 * @override
293 stopSearch: function()
295 ++this._searchId;
299 * @override
300 * @param {!WebInspector.ProjectSearchConfig} searchConfig
301 * @return {!WebInspector.FileBasedSearchResultsPane}
303 createSearchResultsPane: function(searchConfig)
305 return new WebInspector.FileBasedSearchResultsPane(searchConfig);