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
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
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.
31 * @implements {WebInspector.SearchScope}
33 WebInspector.SourcesSearchScope = function()
35 // FIXME: Add title once it is used by search controller.
37 this._workspace = WebInspector.workspace;
40 WebInspector.SourcesSearchScope.prototype = {
42 * @param {!WebInspector.Progress} progress
43 * @param {function(boolean)} indexingFinishedCallback
45 performIndexing: function(progress, indexingFinishedCallback)
49 var projects = this._projects();
50 var compositeProgress = new WebInspector.CompositeProgress(progress);
51 progress.addEventListener(WebInspector.Progress.Events.Canceled, indexingCanceled);
52 for (var i = 0; i < projects.length; ++i) {
53 var project = projects[i];
54 var projectProgress = compositeProgress.createSubProgress(project.uiSourceCodes().length);
55 project.indexContent(projectProgress);
57 compositeProgress.addEventListener(WebInspector.Progress.Events.Done, indexingFinishedCallback.bind(this, true));
59 function indexingCanceled()
61 indexingFinishedCallback(false);
67 * @return {!Array.<!WebInspector.Project>}
72 * @param {!WebInspector.Project} project
75 function filterOutServiceProjects(project)
77 return !project.isServiceProject() || project.type() === WebInspector.projectTypes.Formatter;
81 * @param {!WebInspector.Project} project
84 function filterOutContentScriptsIfNeeded(project)
86 return WebInspector.settings.searchInContentScripts.get() || project.type() !== WebInspector.projectTypes.ContentScripts;
89 return this._workspace.projects().filter(filterOutServiceProjects).filter(filterOutContentScriptsIfNeeded);
93 * @param {!WebInspector.ProjectSearchConfig} searchConfig
94 * @param {!WebInspector.Progress} progress
95 * @param {function(!WebInspector.FileBasedSearchResult)} searchResultCallback
96 * @param {function(boolean)} searchFinishedCallback
98 performSearch: function(searchConfig, progress, searchResultCallback, searchFinishedCallback)
101 this._searchResultCallback = searchResultCallback;
102 this._searchFinishedCallback = searchFinishedCallback;
103 this._searchConfig = searchConfig;
105 var projects = this._projects();
106 var barrier = new CallbackBarrier();
107 var compositeProgress = new WebInspector.CompositeProgress(progress);
108 for (var i = 0; i < projects.length; ++i) {
109 var project = projects[i];
110 var weight = project.uiSourceCodes().length;
111 var projectProgress = new WebInspector.CompositeProgress(compositeProgress.createSubProgress(weight));
112 var findMatchingFilesProgress = projectProgress.createSubProgress();
113 var searchContentProgress = projectProgress.createSubProgress();
114 var barrierCallback = barrier.createCallback();
115 var callback = this._processMatchingFilesForProject.bind(this, this._searchId, project, searchContentProgress, barrierCallback);
116 project.findFilesMatchingSearchRequest(searchConfig, findMatchingFilesProgress, callback);
118 barrier.callWhenDone(this._searchFinishedCallback.bind(this, true));
122 * @param {number} searchId
123 * @param {!WebInspector.Project} project
124 * @param {!WebInspector.Progress} progress
125 * @param {function()} callback
126 * @param {!Array.<string>} files
128 _processMatchingFilesForProject: function(searchId, project, progress, callback, files)
130 if (searchId !== this._searchId) {
131 this._searchFinishedCallback(false);
135 addDirtyFiles.call(this);
143 progress.setTotalWork(files.length);
146 var maxFileContentRequests = 20;
147 var callbacksLeft = 0;
149 for (var i = 0; i < maxFileContentRequests && i < files.length; ++i)
150 scheduleSearchInNextFileOrFinish.call(this);
153 * @this {WebInspector.SourcesSearchScope}
155 function addDirtyFiles()
157 var matchingFiles = StringSet.fromArray(files);
158 var uiSourceCodes = project.uiSourceCodes();
159 for (var i = 0; i < uiSourceCodes.length; ++i) {
160 if (!uiSourceCodes[i].isDirty())
162 var path = uiSourceCodes[i].path();
163 if (!matchingFiles.contains(path) && this._searchConfig.filePathMatchesFileQuery(path))
169 * @param {string} path
170 * @this {WebInspector.SourcesSearchScope}
172 function searchInNextFile(path)
174 var uiSourceCode = project.uiSourceCode(path);
178 scheduleSearchInNextFileOrFinish.call(this);
181 if (uiSourceCode.isDirty())
182 contentLoaded.call(this, uiSourceCode.path(), uiSourceCode.workingCopy());
184 uiSourceCode.checkContentUpdated(contentUpdated.bind(this, uiSourceCode));
188 * @param {!WebInspector.UISourceCode} uiSourceCode
189 * @this {WebInspector.SourcesSearchScope}
191 function contentUpdated(uiSourceCode)
193 uiSourceCode.requestContent(contentLoaded.bind(this, uiSourceCode.path()));
197 * @this {WebInspector.SourcesSearchScope}
199 function scheduleSearchInNextFileOrFinish()
201 if (fileIndex >= files.length) {
202 if (!callbacksLeft) {
211 var path = files[fileIndex++];
212 setTimeout(searchInNextFile.bind(this, path), 0);
216 * @param {string} path
217 * @param {?string} content
218 * @this {WebInspector.SourcesSearchScope}
220 function contentLoaded(path, content)
223 * @param {!WebInspector.ContentProvider.SearchMatch} a
224 * @param {!WebInspector.ContentProvider.SearchMatch} b
226 function matchesComparator(a, b)
228 return a.lineNumber - b.lineNumber;
233 var queries = this._searchConfig.queries();
234 if (content !== null) {
235 for (var i = 0; i < queries.length; ++i) {
236 var nextMatches = WebInspector.ContentProvider.performSearchInContent(content, queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex())
237 matches = matches.mergeOrdered(nextMatches, matchesComparator);
240 var uiSourceCode = project.uiSourceCode(path);
241 if (matches && uiSourceCode) {
242 var searchResult = new WebInspector.FileBasedSearchResult(uiSourceCode, matches);
243 this._searchResultCallback(searchResult);
247 scheduleSearchInNextFileOrFinish.call(this);
251 stopSearch: function()
257 * @param {!WebInspector.ProjectSearchConfig} searchConfig
258 * @return {!WebInspector.FileBasedSearchResultsPane}
260 createSearchResultsPane: function(searchConfig)
262 return new WebInspector.FileBasedSearchResultsPane(searchConfig);