• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @constructor
7 * @extends {WebInspector.SearchResultsPane}
8 * @param {!WebInspector.ProjectSearchConfig} searchConfig
9 */
10WebInspector.FileBasedSearchResultsPane = function(searchConfig)
11{
12    WebInspector.SearchResultsPane.call(this, searchConfig);
13
14    this._searchResults = [];
15
16    this.element.id = "search-results-pane-file-based";
17
18    this._treeOutlineElement = document.createElement("ol");
19    this._treeOutlineElement.className = "search-results-outline-disclosure";
20    this.element.appendChild(this._treeOutlineElement);
21    this._treeOutline = new TreeOutline(this._treeOutlineElement);
22
23    this._matchesExpandedCount = 0;
24}
25
26WebInspector.FileBasedSearchResultsPane.matchesExpandedByDefaultCount = 20;
27WebInspector.FileBasedSearchResultsPane.fileMatchesShownAtOnce = 20;
28
29WebInspector.FileBasedSearchResultsPane.prototype = {
30    /**
31     * @param {!WebInspector.FileBasedSearchResult} searchResult
32     */
33    addSearchResult: function(searchResult)
34    {
35        this._searchResults.push(searchResult);
36        var uiSourceCode = searchResult.uiSourceCode;
37        if (!uiSourceCode)
38            return;
39        this._addFileTreeElement(searchResult);
40    },
41
42    /**
43     * @param {!WebInspector.FileBasedSearchResult} searchResult
44     */
45    _addFileTreeElement: function(searchResult)
46    {
47        var fileTreeElement = new WebInspector.FileBasedSearchResultsPane.FileTreeElement(this._searchConfig, searchResult);
48        this._treeOutline.appendChild(fileTreeElement);
49        // Expand until at least a certain number of matches is expanded.
50        if (this._matchesExpandedCount < WebInspector.FileBasedSearchResultsPane.matchesExpandedByDefaultCount)
51            fileTreeElement.expand();
52        this._matchesExpandedCount += searchResult.searchMatches.length;
53    },
54
55    __proto__: WebInspector.SearchResultsPane.prototype
56}
57
58/**
59 * @constructor
60 * @extends {TreeElement}
61 * @param {!WebInspector.ProjectSearchConfig} searchConfig
62 * @param {!WebInspector.FileBasedSearchResult} searchResult
63 */
64WebInspector.FileBasedSearchResultsPane.FileTreeElement = function(searchConfig, searchResult)
65{
66    TreeElement.call(this, "", null, true);
67    this._searchConfig = searchConfig;
68    this._searchResult = searchResult;
69
70    this.toggleOnClick = true;
71    this.selectable = false;
72}
73
74WebInspector.FileBasedSearchResultsPane.FileTreeElement.prototype = {
75    onexpand: function()
76    {
77        if (this._initialized)
78            return;
79
80        this._updateMatchesUI();
81        this._initialized = true;
82    },
83
84    _updateMatchesUI: function()
85    {
86        this.removeChildren();
87        var toIndex = Math.min(this._searchResult.searchMatches.length, WebInspector.FileBasedSearchResultsPane.fileMatchesShownAtOnce);
88        if (toIndex < this._searchResult.searchMatches.length) {
89            this._appendSearchMatches(0, toIndex - 1);
90            this._appendShowMoreMatchesElement(toIndex - 1);
91        } else {
92            this._appendSearchMatches(0, toIndex);
93        }
94    },
95
96    onattach: function()
97    {
98        this._updateSearchMatches();
99    },
100
101    _updateSearchMatches: function()
102    {
103        this.listItemElement.classList.add("search-result");
104
105        var fileNameSpan = document.createElement("span");
106        fileNameSpan.className = "search-result-file-name";
107        fileNameSpan.textContent = this._searchResult.uiSourceCode.fullDisplayName();
108        this.listItemElement.appendChild(fileNameSpan);
109
110        var matchesCountSpan = document.createElement("span");
111        matchesCountSpan.className = "search-result-matches-count";
112
113        var searchMatchesCount = this._searchResult.searchMatches.length;
114        if (searchMatchesCount === 1)
115            matchesCountSpan.textContent = WebInspector.UIString("(%d match)", searchMatchesCount);
116        else
117            matchesCountSpan.textContent = WebInspector.UIString("(%d matches)", searchMatchesCount);
118
119        this.listItemElement.appendChild(matchesCountSpan);
120        if (this.expanded)
121            this._updateMatchesUI();
122    },
123
124    /**
125     * @param {number} fromIndex
126     * @param {number} toIndex
127     */
128    _appendSearchMatches: function(fromIndex, toIndex)
129    {
130        var searchResult = this._searchResult;
131        var uiSourceCode = searchResult.uiSourceCode;
132        var searchMatches = searchResult.searchMatches;
133
134        var queries = this._searchConfig.queries();
135        var regexes = [];
136        for (var i = 0; i < queries.length; ++i)
137            regexes.push(createSearchRegex(queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex()));
138
139        for (var i = fromIndex; i < toIndex; ++i) {
140            var lineNumber = searchMatches[i].lineNumber;
141            var lineContent = searchMatches[i].lineContent;
142            var matchRanges = [];
143            for (var j = 0; j < regexes.length; ++j)
144                matchRanges = matchRanges.concat(this._regexMatchRanges(lineContent, regexes[j]));
145
146            var anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
147
148            var numberString = numberToStringWithSpacesPadding(lineNumber + 1, 4);
149            var lineNumberSpan = document.createElement("span");
150            lineNumberSpan.classList.add("search-match-line-number");
151            lineNumberSpan.textContent = numberString;
152            anchor.appendChild(lineNumberSpan);
153
154            var contentSpan = this._createContentSpan(lineContent, matchRanges);
155            anchor.appendChild(contentSpan);
156
157            var searchMatchElement = new TreeElement("");
158            searchMatchElement.selectable = false;
159            this.appendChild(searchMatchElement);
160            searchMatchElement.listItemElement.className = "search-match source-code";
161            searchMatchElement.listItemElement.appendChild(anchor);
162        }
163    },
164
165    /**
166     * @param {number} startMatchIndex
167     */
168    _appendShowMoreMatchesElement: function(startMatchIndex)
169    {
170        var matchesLeftCount = this._searchResult.searchMatches.length - startMatchIndex;
171        var showMoreMatchesText = WebInspector.UIString("Show all matches (%d more).", matchesLeftCount);
172        this._showMoreMatchesTreeElement = new TreeElement(showMoreMatchesText);
173        this.appendChild(this._showMoreMatchesTreeElement);
174        this._showMoreMatchesTreeElement.listItemElement.classList.add("show-more-matches");
175        this._showMoreMatchesTreeElement.onselect = this._showMoreMatchesElementSelected.bind(this, startMatchIndex);
176    },
177
178    /**
179     * @param {!WebInspector.UISourceCode} uiSourceCode
180     * @param {number} lineNumber
181     * @param {number} columnNumber
182     * @return {!Element}
183     */
184    _createAnchor: function(uiSourceCode, lineNumber, columnNumber)
185    {
186        return WebInspector.Linkifier.linkifyUsingRevealer(uiSourceCode.uiLocation(lineNumber, columnNumber), "", uiSourceCode.url, lineNumber);
187    },
188
189    /**
190     * @param {string} lineContent
191     * @param {!Array.<!WebInspector.SourceRange>} matchRanges
192     */
193    _createContentSpan: function(lineContent, matchRanges)
194    {
195        var contentSpan = document.createElement("span");
196        contentSpan.className = "search-match-content";
197        contentSpan.textContent = lineContent;
198        WebInspector.highlightRangesWithStyleClass(contentSpan, matchRanges, "highlighted-match");
199        return contentSpan;
200    },
201
202    /**
203     * @param {string} lineContent
204     * @param {!RegExp} regex
205     * @return {!Array.<!WebInspector.SourceRange>}
206     */
207    _regexMatchRanges: function(lineContent, regex)
208    {
209        regex.lastIndex = 0;
210        var match;
211        var offset = 0;
212        var matchRanges = [];
213        while ((regex.lastIndex < lineContent.length) && (match = regex.exec(lineContent)))
214            matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
215
216        return matchRanges;
217    },
218
219    /**
220     * @param {number} startMatchIndex
221     * @return {boolean}
222     */
223    _showMoreMatchesElementSelected: function(startMatchIndex)
224    {
225        this.removeChild(this._showMoreMatchesTreeElement);
226        this._appendSearchMatches(startMatchIndex, this._searchResult.searchMatches.length);
227        return false;
228    },
229
230    __proto__: TreeElement.prototype
231}
232