diff --git a/src/htmlContent/search-panel.html b/src/htmlContent/search-panel.html
index e46d823f469..0929275fd4b 100644
--- a/src/htmlContent/search-panel.html
+++ b/src/htmlContent/search-panel.html
@@ -1,6 +1,5 @@
diff --git a/src/htmlContent/search-summary.html b/src/htmlContent/search-summary.html
new file mode 100644
index 00000000000..4a9577b5229
--- /dev/null
+++ b/src/htmlContent/search-summary.html
@@ -0,0 +1,8 @@
+{{{summary}}}
+{{#hasPages}}
+
+
+ {{{results}}}
+
+
+{{/hasPages}}
\ No newline at end of file
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 42f71670b1f..0783c15fb59 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -125,15 +125,17 @@ define({
"NO_UPDATE_TITLE" : "You're up to date!",
"NO_UPDATE_MESSAGE" : "You are running the latest version of {APP_NAME}.",
- "FIND_IN_FILES_TITLE" : "for \"{4}\" {5} - {0} {1} in {2} {3}",
+ "FIND_IN_FILES_TITLE" : "\"{4}\" found {5} — {0} {1} in {2} {3}",
"FIND_IN_FILES_SCOPED" : "in
{0}",
"FIND_IN_FILES_NO_SCOPE" : "in project",
"FIND_IN_FILES_FILE" : "file",
"FIND_IN_FILES_FILES" : "files",
"FIND_IN_FILES_MATCH" : "match",
"FIND_IN_FILES_MATCHES" : "matches",
- "FIND_IN_FILES_MORE_THAN" : "More than ",
- "FIND_IN_FILES_MAX" : " (showing the first {0} matches)",
+ "FIND_IN_FILES_MORE_THAN" : "Over ",
+ "FIND_IN_FILES_PAGING" : "{0}—{1}",
+ "FIND_IN_FILES_LESS" : "
Less",
+ "FIND_IN_FILES_MORE" : "
More",
"FIND_IN_FILES_FILE_PATH" : "File:
{0}",
"FIND_IN_FILES_LINE" : "line: {0}",
@@ -275,7 +277,6 @@ define({
// Strings for main-view.html
"EXPERIMENTAL_BUILD" : "experimental build",
"DEVELOPMENT_BUILD" : "development build",
- "SEARCH_RESULTS" : "Search Results",
"OK" : "OK",
"DONT_SAVE" : "Don't Save",
"SAVE" : "Save",
diff --git a/src/search/FindInFiles.js b/src/search/FindInFiles.js
index 4aa671c95b6..e8155d5fa73 100644
--- a/src/search/FindInFiles.js
+++ b/src/search/FindInFiles.js
@@ -25,7 +25,7 @@
/*global define, $, window, Mustache */
/*
- * Adds a "find in files" command to allow the user to find all occurances of a string in all files in
+ * Adds a "find in files" command to allow the user to find all occurrences of a string in all files in
* the project.
*
* The keyboard shortcut is Cmd(Ctrl)-Shift-F.
@@ -34,7 +34,7 @@
* - Proper UI for both dialog and results
* - Refactor dialog class and share with Quick File Open
* - Search files in working set that are *not* in the project
- * - Handle matches that span mulitple lines
+ * - Handle matches that span multiple lines
* - Refactor UI from functionality to enable unit testing
*/
@@ -53,34 +53,58 @@ define(function (require, exports, module) {
EditorManager = require("editor/EditorManager"),
PanelManager = require("view/PanelManager"),
FileIndexManager = require("project/FileIndexManager"),
+ FileViewController = require("project/FileViewController"),
NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem,
FileUtils = require("file/FileUtils"),
KeyEvent = require("utils/KeyEvent"),
AppInit = require("utils/AppInit"),
+ CollectionUtils = require("utils/CollectionUtils"),
StatusBar = require("widgets/StatusBar"),
ModalBar = require("widgets/ModalBar").ModalBar;
var searchDialogTemplate = require("text!htmlContent/search-dialog.html"),
searchPanelTemplate = require("text!htmlContent/search-panel.html"),
+ searchSummaryTemplate = require("text!htmlContent/search-summary.html"),
searchResultsTemplate = require("text!htmlContent/search-results.html");
+ /** @cost Constants used to define the maximum results show per page and found in a single file */
+ var RESULTS_PER_PAGE = 100,
+ FIND_IN_FILE_MAX = 300;
+
+ /**
+ * Map of all the last search results
+ * @type {Object.
, collapsed: boolean}>}
+ */
+ var searchResults = {};
+
+ /** @type {Panel} Bottom panel holding the search results. Initialized in htmlReady() */
+ var searchResultsPanel;
+
+ /** @type {number} The index of the first result that is displayed */
+ var currentStart = 0;
+
+ /** @type {string} The current search query */
+ var currentQuery = "";
+
+ /** @type {Array.} An array of the files where it should look or null/empty to search the entire project */
+ var currentScope = null;
+
+ /** @type {boolean} True if the matches in a file reached FIND_IN_FILE_MAX */
+ var maxHitsFoundInFile = false;
+
/** @type {$.Element} jQuery elements used in the search results */
var $searchResults,
$searchSummary,
$searchContent,
$selectedRow;
- var searchResults = [];
-
- var FIND_IN_FILES_MAX = 100,
- maxHitsFoundInFile = false,
- currentQuery = "",
- currentScope;
-
- // Bottom panel holding the search results. Initialized in htmlReady().
- var searchResultsPanel;
-
+ /**
+ * @private
+ * Returns a regular expression from the given query and shows an error in the modal-bar if it was invalid
+ * @param {!string} query - The query from the modal-bar input
+ * @return {RegExp}
+ */
function _getQueryRegExp(query) {
// Clear any pending RegEx error message
$(".modal-bar .message").css("display", "inline-block");
@@ -113,6 +137,7 @@ define(function (require, exports, module) {
}
/**
+ * @private
* Returns label text to indicate the search scope. Already HTML-escaped.
* @param {?Entry} scope
*/
@@ -160,37 +185,38 @@ define(function (require, exports, module) {
/**
* Shows the search dialog
* @param {?string} initialString Default text to prepopulate the search field with
- * @param {?Entry} scope Search scope, or null to search whole proj
+ * @param {?Entry} scope Search scope, or null to search whole project
* @returns {$.Promise} that is resolved with the string to search for
*/
FindInFilesDialog.prototype.showDialog = function (initialString, scope) {
// Note the prefix label is a simple "Find:" - the "in ..." part comes after the text field
var templateVars = {
- value: initialString || "",
- label: _labelForScope(scope)
- };
- var dialogHTML = Mustache.render(searchDialogTemplate, $.extend(templateVars, Strings));
+ value: initialString || "",
+ label: _labelForScope(scope)
+ },
+ dialogHTML = Mustache.render(searchDialogTemplate, $.extend(templateVars, Strings)),
+ that = this;
- this.result = new $.Deferred();
- this.modalBar = new ModalBar(dialogHTML, false);
+ this.result = new $.Deferred();
+ this.modalBar = new ModalBar(dialogHTML, false);
var $searchField = $("input#searchInput");
- var that = this;
$searchField.get(0).select();
- $searchField.bind("keydown", function (event) {
- if (event.keyCode === KeyEvent.DOM_VK_RETURN || event.keyCode === KeyEvent.DOM_VK_ESCAPE) { // Enter/Return key or Esc key
- event.stopPropagation();
- event.preventDefault();
-
- var query = $searchField.val();
-
- if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) {
- query = null;
+ $searchField
+ .bind("keydown", function (event) {
+ if (event.keyCode === KeyEvent.DOM_VK_RETURN || event.keyCode === KeyEvent.DOM_VK_ESCAPE) { // Enter/Return key or Esc key
+ event.stopPropagation();
+ event.preventDefault();
+
+ var query = $searchField.val();
+
+ if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) {
+ query = null;
+ }
+
+ that._close(query);
}
-
- that._close(query);
- }
- })
+ })
.bind("input", function (event) {
// Check the query expression on every input event. This way the user is alerted
// to any RegEx syntax errors immediately.
@@ -205,6 +231,10 @@ define(function (require, exports, module) {
};
+ /**
+ * @private
+ * Hides the Search Results Panel
+ */
function _hideSearchResults() {
if (searchResultsPanel.isVisible()) {
searchResultsPanel.hide();
@@ -214,7 +244,7 @@ define(function (require, exports, module) {
/**
* @private
- * Searches throught the contents an returns an array of matches
+ * Searches through the contents an returns an array of matches
* @param {string} contents
* @param {RegExp} queryExpr
* @return {Array.<{start: {line:number,ch:number}, end: {line:number,ch:number}, line: string}>}
@@ -225,18 +255,15 @@ define(function (require, exports, module) {
return null;
}
- var trimmedContents = contents;
- var startPos = 0;
- var matchStart;
- var matches = [];
+ var match, lineNum, line, ch, matchLength,
+ lines = StringUtils.getLines(contents),
+ matches = [];
- var match;
- var lines = StringUtils.getLines(contents);
while ((match = queryExpr.exec(contents)) !== null) {
- var lineNum = StringUtils.offsetToLineNum(lines, match.index);
- var line = lines[lineNum];
- var ch = match.index - contents.lastIndexOf("\n", match.index) - 1; // 0-based index
- var matchLength = match[0].length;
+ lineNum = StringUtils.offsetToLineNum(lines, match.index);
+ line = lines[lineNum];
+ ch = match.index - contents.lastIndexOf("\n", match.index) - 1; // 0-based index
+ matchLength = match[0].length;
// Don't store more than 200 chars per line
line = line.substr(0, Math.min(200, line.length));
@@ -249,7 +276,7 @@ define(function (require, exports, module) {
// We have the max hits in just this 1 file. Stop searching this file.
// This fixed issue #1829 where code hangs on too many hits.
- if (matches.length >= FIND_IN_FILES_MAX) {
+ if (matches.length >= FIND_IN_FILE_MAX) {
queryExpr.lastIndex = 0;
maxHitsFoundInFile = true;
break;
@@ -258,13 +285,37 @@ define(function (require, exports, module) {
return matches;
}
+
+ /**
+ * @private
+ * Searches and stores the match results for the given file, if there are matches
+ * @param {string} fullPath
+ * @param {string} contents
+ * @param {RegExp} queryExpr
+ */
+ function _addSearchMatches(fullPath, contents, queryExpr) {
+ var matches = _getSearchMatches(contents, queryExpr);
- function _showSearchResults(searchResults, query, scope) {
- if (searchResults && searchResults.length) {
+ if (matches && matches.length) {
+ searchResults[fullPath] = {
+ matches: matches,
+ collapsed: false
+ };
+ }
+ }
+
+
+ /**
+ * @private
+ * Shows the results in a table and adds the necessary event listeners
+ */
+ function _showSearchResults() {
+ if (!$.isEmptyObject(searchResults)) {
// Count the total number of matches
- var numMatches = 0;
- searchResults.forEach(function (item) {
+ var numFiles = 0, numMatches = 0;
+ CollectionUtils.forEach(searchResults, function (item) {
+ numFiles++;
numMatches += item.matches.length;
});
@@ -280,34 +331,66 @@ define(function (require, exports, module) {
Strings.FIND_IN_FILES_TITLE,
numMatchesStr,
(numMatches > 1) ? Strings.FIND_IN_FILES_MATCHES : Strings.FIND_IN_FILES_MATCH,
- searchResults.length,
- (searchResults.length > 1 ? Strings.FIND_IN_FILES_FILES : Strings.FIND_IN_FILES_FILE),
- StringUtils.htmlEscape(query),
- scope ? _labelForScope(scope) : ""
+ numFiles,
+ (numFiles > 1 ? Strings.FIND_IN_FILES_FILES : Strings.FIND_IN_FILES_FILE),
+ StringUtils.htmlEscape(currentQuery),
+ currentScope ? _labelForScope(currentScope) : ""
);
+ // The last result index displayed
+ var last = currentStart + RESULTS_PER_PAGE > numMatches ? numMatches : currentStart + RESULTS_PER_PAGE;
+
// Insert the search summary
- $searchSummary
- .html(summary +
- (numMatches > FIND_IN_FILES_MAX ? StringUtils.format(Strings.FIND_IN_FILES_MAX, FIND_IN_FILES_MAX) : ""))
- .prepend(" "); // putting a normal space before the "-" is not enough
+ $searchSummary.html(Mustache.render(searchSummaryTemplate, {
+ summary: summary,
+ hasPages: numMatches > RESULTS_PER_PAGE,
+ results: StringUtils.format(Strings.FIND_IN_FILES_PAGING, currentStart + 1, last),
+ hasPrev: currentStart > 0,
+ hasNext: last < numMatches
+ }));
// Create the results template search list
- var searchList = [];
- var resultsDisplayed = 0, i;
- var searchItems, match;
+ var searchItems, match, i,
+ searchList = [],
+ matchesCounter = 0,
+ showMatches = false;
- searchResults.forEach(function (item) {
- if (item && resultsDisplayed < FIND_IN_FILES_MAX) {
+ CollectionUtils.some(searchResults, function (item, fullPath) {
+ showMatches = true;
+
+ // Since the amount of matches on this item plus the amount of matches we skipped until
+ // now is still smaller than the first match that we want to display, skip these.
+ if (matchesCounter + item.matches.length < currentStart) {
+ matchesCounter += item.matches.length;
+ showMatches = false;
+
+ // If we still haven't skipped enough items to get to the first match, but adding the
+ // item matches to the skipped ones is greater the the first match we want to display,
+ // then we can display the matches from this item skipping the first ones
+ } else if (matchesCounter < currentStart) {
+ i = currentStart - matchesCounter;
+ matchesCounter = currentStart;
+
+ // If we already skipped enough matches to get to the first match to display, we can start
+ // displaying from the first match of this item
+ } else if (matchesCounter < last) {
i = 0;
-
+
+ // We can't display more items by now. Break the loop
+ } else {
+ return true;
+ }
+
+ if (showMatches && i < item.matches.length) {
// Add a row for each match in the file
searchItems = [];
- while (i < item.matches.length && resultsDisplayed < FIND_IN_FILES_MAX) {
+
+ // Add matches until we get to the last match of this item, or filling the page
+ while (i < item.matches.length && matchesCounter < last) {
match = item.matches[i];
searchItems.push({
file: searchList.length,
- item: i,
+ item: searchItems.length,
line: StringUtils.format(Strings.FIND_IN_FILES_LINE, (match.start.line + 1)),
pre: match.line.substr(0, match.start.ch),
highlight: match.line.substring(match.start.ch, match.end.ch),
@@ -315,40 +398,60 @@ define(function (require, exports, module) {
start: match.start,
end: match.end
});
- resultsDisplayed++;
+ matchesCounter++;
i++;
}
// Add a row for each file
var displayFileName = StringUtils.format(
Strings.FIND_IN_FILES_FILE_PATH,
- StringUtils.breakableUrl(item.fullPath)
+ StringUtils.breakableUrl(fullPath)
);
searchList.push({
file: searchList.length,
filename: displayFileName,
- fullPath: item.fullPath,
+ fullPath: fullPath,
items: searchItems
});
-
}
});
- // Insert the search results
- $searchContent
- .empty()
- .append(Mustache.render(searchResultsTemplate, {searchList: searchList}))
- .scrollTop(0); // otherwise scroll pos from previous contents is remembered
-
- $searchResults.find(".close")
- .one("click", function () {
+ // Add the listeners for close, prev and next
+ $searchResults
+ .off(".searchList") // Remove the old events
+ .one("click.searchList", ".close", function () {
_hideSearchResults();
+ })
+ // The link to go the first page
+ .one("click.searchList", ".first-page:not(.disabled)", function () {
+ currentStart = 0;
+ _showSearchResults();
+ })
+ // The link to go the previous page
+ .one("click.searchList", ".prev-page:not(.disabled)", function () {
+ currentStart -= RESULTS_PER_PAGE;
+ _showSearchResults();
+ })
+ // The link to go to the next page
+ .one("click.searchList", ".next-page:not(.disabled)", function () {
+ currentStart += RESULTS_PER_PAGE;
+ _showSearchResults();
+ })
+ // The link to go to the last page
+ .one("click.searchList", ".last-page:not(.disabled)", function () {
+ currentStart = Math.floor(numMatches / RESULTS_PER_PAGE) * RESULTS_PER_PAGE;
+ _showSearchResults();
});
- // Add the click event listener directly on the table parent
+ // Insert the search results
$searchContent
+ .empty()
+ .append(Mustache.render(searchResultsTemplate, {searchList: searchList}))
+ .scrollTop(0) // Otherwise scroll pos from previous contents is remembered
.off(".searchList") // Remove the old events
+
+ // Add the click event listener directly on the table parent
.on("click.searchList", function (e) {
var $row = $(e.target).closest("tr");
@@ -359,8 +462,8 @@ define(function (require, exports, module) {
$row.addClass("selected");
$selectedRow = $row;
- var searchItem = searchList[$row.data("file")];
- var fullPath = searchItem.fullPath;
+ var searchItem = searchList[$row.data("file")],
+ fullPath = searchItem.fullPath;
// This is a file title row, expand/collapse on click
if ($row.hasClass("file-section")) {
@@ -369,6 +472,8 @@ define(function (require, exports, module) {
var $triangle = $(".disclosure-triangle", $row);
$triangle.toggleClass("expanded").toggleClass("collapsed");
+
+ searchResults[fullPath].collapsed = !searchResults[fullPath].collapsed;
// This is a file row, show the result on click
} else {
@@ -382,20 +487,40 @@ define(function (require, exports, module) {
});
}
}
+ })
+ // Add the file to the working set on double click
+ .on("dblclick.searchList", "tr:not(.file-section)", function (e) {
+ var item = searchList[$(this).data("file")];
+ FileViewController.addToWorkingSetAndSelect(item.fullPath);
+ })
+ // Restore the collapsed files
+ .find(".file-section").each(function () {
+ var fullPath = searchList[$(this).data("file")].fullPath;
+
+ if (searchResults[fullPath].collapsed) {
+ searchResults[fullPath].collapsed = false;
+ $(this).trigger("click");
+ }
});
+ if ($selectedRow) {
+ $selectedRow.removeClass("selected");
+ $selectedRow = null;
+ }
searchResultsPanel.show();
+
} else {
_hideSearchResults();
}
}
/**
+ * @private
* @param {!FileInfo} fileInfo File in question
* @param {?Entry} scope Search scope, or null if whole project
* @return {boolean}
*/
- function inScope(fileInfo, scope) {
+ function _inScope(fileInfo, scope) {
if (scope) {
if (scope.isDirectory) {
// Dirs always have trailing slash, so we don't have to worry about being
@@ -409,12 +534,12 @@ define(function (require, exports, module) {
}
/**
+ * @private
* Displays a non-modal embedded dialog above the code mirror editor that allows the user to do
* a find operation across all files in the project.
* @param {?Entry} scope Project file/subfolder to search within; else searches whole project.
*/
- function doFindInFiles(scope) {
-
+ function _doFindInFiles(scope) {
if (scope instanceof NativeFileSystem.InaccessibleFileEntry) {
CommandManager.execute(Commands.FILE_OPEN, { fullPath: scope.fullPath }).done(function () {
CommandManager.execute(Commands.EDIT_FIND);
@@ -422,15 +547,14 @@ define(function (require, exports, module) {
return;
}
- var dialog = new FindInFilesDialog();
-
// Default to searching for the current selection
- var currentEditor = EditorManager.getActiveEditor();
- var initialString = currentEditor && currentEditor.getSelectedText();
+ var currentEditor = EditorManager.getActiveEditor(),
+ initialString = currentEditor && currentEditor.getSelectedText(),
+ dialog = new FindInFilesDialog();
- currentQuery = "";
- currentScope = scope;
- searchResults = [];
+ searchResults = {};
+ currentQuery = "";
+ currentScope = scope;
maxHitsFoundInFile = false;
dialog.showDialog(initialString, scope)
@@ -447,20 +571,13 @@ define(function (require, exports, module) {
Async.doInParallel(fileListResult, function (fileInfo) {
var result = new $.Deferred();
- if (!inScope(fileInfo, scope)) {
+ if (!_inScope(fileInfo, scope)) {
result.resolve();
} else {
// Search one file
DocumentManager.getDocumentForPath(fileInfo.fullPath)
.done(function (doc) {
- var matches = _getSearchMatches(doc.getText(), queryExpr);
-
- if (matches && matches.length) {
- searchResults.push({
- fullPath: fileInfo.fullPath,
- matches: matches
- });
- }
+ _addSearchMatches(fileInfo.fullPath, doc.getText(), queryExpr);
result.resolve();
})
.fail(function (error) {
@@ -473,7 +590,7 @@ define(function (require, exports, module) {
})
.done(function () {
// Done searching all files: show results
- _showSearchResults(searchResults, query, scope);
+ _showSearchResults();
StatusBar.hideBusyIndicator();
})
.fail(function () {
@@ -485,31 +602,82 @@ define(function (require, exports, module) {
});
}
- /** Search within the file/subtree defined by the sidebar selection */
- function doFindInSubtree() {
+ /**
+ * @private
+ * Search within the file/subtree defined by the sidebar selection
+ */
+ function _doFindInSubtree() {
var selectedEntry = ProjectManager.getSelectedItem();
- doFindInFiles(selectedEntry);
+ _doFindInFiles(selectedEntry);
+ }
+
+
+ /**
+ * @private
+ * Shows the search results and tries to restore the previous scroll and selection
+ */
+ function _restoreSearchResults() {
+ var scrollTop = $searchContent.scrollTop(),
+ index = $selectedRow ? $selectedRow.index() : null;
+
+ _showSearchResults();
+
+ $searchContent.scrollTop(scrollTop);
+ if ($selectedRow) {
+ $selectedRow = $searchContent.find("tr:eq(" + index + ")");
+ $selectedRow.addClass("selected");
+ }
}
+ /**
+ * @private
+ * Moves the search results from the previous path to the new one and updates the results list, if required
+ * @param {$.Event} event
+ * @param {string} oldName
+ * @param {string} newName
+ */
function _fileNameChangeHandler(event, oldName, newName) {
+ var resultsChanged = false;
+
if (searchResultsPanel.isVisible()) {
// Update the search results
- searchResults.forEach(function (item) {
- item.fullPath = item.fullPath.replace(oldName, newName);
+ CollectionUtils.forEach(searchResults, function (item, fullPath) {
+ if (fullPath.match(oldName)) {
+ searchResults[fullPath.replace(oldName, newName)] = item;
+ delete searchResults[fullPath];
+ resultsChanged = true;
+ }
});
- _showSearchResults(searchResults, currentQuery, currentScope);
+
+ // Restore the results if needed
+ if (resultsChanged) {
+ _restoreSearchResults();
+ }
}
}
-
+
+ /**
+ * @private
+ * Deletes the results from the deleted file and updates the results list, if required
+ * @param {$.Event} event
+ * @param {string} path
+ */
function _pathDeletedHandler(event, path) {
+ var resultsChanged = false;
+
if (searchResultsPanel.isVisible()) {
// Update the search results
- searchResults.forEach(function (item, idx) {
- if (FileUtils.isAffectedWhenRenaming(item.fullPath, path)) {
- searchResults.splice(idx, 1);
+ CollectionUtils.forEach(searchResults, function (item, fullPath) {
+ if (FileUtils.isAffectedWhenRenaming(fullPath, path)) {
+ delete searchResults[fullPath];
+ resultsChanged = true;
}
});
- _showSearchResults(searchResults, currentQuery, currentScope);
+
+ // Restore the results if needed
+ if (resultsChanged) {
+ _restoreSearchResults();
+ }
}
}
@@ -530,6 +698,6 @@ define(function (require, exports, module) {
$(ProjectManager).on("beforeProjectClose", _hideSearchResults);
// Initialize: command handlers
- CommandManager.register(Strings.CMD_FIND_IN_FILES, Commands.EDIT_FIND_IN_FILES, doFindInFiles);
- CommandManager.register(Strings.CMD_FIND_IN_SUBTREE, Commands.EDIT_FIND_IN_SUBTREE, doFindInSubtree);
+ CommandManager.register(Strings.CMD_FIND_IN_FILES, Commands.EDIT_FIND_IN_FILES, _doFindInFiles);
+ CommandManager.register(Strings.CMD_FIND_IN_SUBTREE, Commands.EDIT_FIND_IN_SUBTREE, _doFindInSubtree);
});
diff --git a/src/styles/brackets.less b/src/styles/brackets.less
index b08f7cb6211..b4663b81142 100644
--- a/src/styles/brackets.less
+++ b/src/styles/brackets.less
@@ -723,6 +723,32 @@ a, img {
/* Find in Files results panel - temporary UI, to be replaced with a richer search feature later */
+#search-result-summary {
+ .first-page,
+ .prev-page,
+ .next-page,
+ .last-page {
+ .jstree-sprite;
+ width: 6px;
+ display: inline-block;
+ margin: 0 2px;
+ background-position: 0px 4px;
+ }
+ .first-page {
+ background-position: 0px -80px;
+ margin-left: 10px;
+ }
+ .prev-page {
+ background-position: 0px -24px;
+ }
+ .last-page {
+ background-position: 0px -52px;
+ }
+ .disabled {
+ opacity: 0.3;
+ }
+}
+
#search-results .disclosure-triangle {
.jstree-sprite;
display: inline-block;
diff --git a/src/styles/images/jsTreeSprites.svg b/src/styles/images/jsTreeSprites.svg
index 215abedf14d..afaddb4a16a 100644
--- a/src/styles/images/jsTreeSprites.svg
+++ b/src/styles/images/jsTreeSprites.svg
@@ -1,14 +1,52 @@
-