Home | History | Annotate | Download | only in sources
      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  */
     10 WebInspector.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 
     26 WebInspector.FileBasedSearchResultsPane.matchesExpandedByDefaultCount = 20;
     27 WebInspector.FileBasedSearchResultsPane.fileMatchesShownAtOnce = 20;
     28 
     29 WebInspector.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  */
     64 WebInspector.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 
     74 WebInspector.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