Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #6175 from adobe/pflynn/findreplace-ui
Browse files Browse the repository at this point in the history
Improved Find/Replace UI
  • Loading branch information
couzteau committed Dec 10, 2013
2 parents 0d7b9f0 + a7976ec commit 19af046
Show file tree
Hide file tree
Showing 11 changed files with 794 additions and 670 deletions.
9 changes: 9 additions & 0 deletions src/htmlContent/findinfiles-bar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{CMD_FIND}}:
<div class="search-input-container"><input type="text" id="find-what" value="{{value}}" /><div class="error"></div><!--
--><button id="find-case-sensitive" class="btn no-focus" tabindex="-1" title="{{BUTTON_CASESENSITIVE_HINT}}"><div class="button-icon"></div></button><!--
--><button id="find-regexp" class="btn no-focus" tabindex="-1" title="{{BUTTON_REGEXP_HINT}}"><div class="button-icon"></div></button>

<div class="no-results-message">{{FIND_NO_RESULTS}}</div></div>
<div class="message">
<span id="searchlabel">{{{label}}}</span>
</div>
17 changes: 17 additions & 0 deletions src/htmlContent/findreplace-bar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div id="find-group"><!--
--><div class="search-input-container"><input type="text" id="find-what"/><div class="error"></div><span id="find-counter"></span></div><!--
--><button id="find-case-sensitive" class="btn no-focus" tabindex="-1" title="{{BUTTON_CASESENSITIVE_HINT}}"><div class="button-icon"></div></button><!--
--><button id="find-regexp" class="btn no-focus" tabindex="-1" title="{{BUTTON_REGEXP_HINT}}"><div class="button-icon"></div></button><!--
--><div class="navigator"><!--
--><button id="find-prev" class="btn no-focus" tabindex="-1" title="{{BUTTON_PREV_HINT}}">{{BUTTON_PREV}}</button><!--
--><button id="find-next" class="btn no-focus" tabindex="-1" title="{{BUTTON_NEXT_HINT}}">{{BUTTON_NEXT}}</button><!--
--></div><!--
--></div><!--
{{#replace}}
--><div id="replace-group"><!--
--><input type="text" id="replace-with" placeholder="{{REPLACE_PLACEHOLDER}}"/><!--
--><button id="replace-yes" class="btn no-focus" tabindex="-1">{{BUTTON_REPLACE}}</button><!--
--><button id="replace-all" class="btn no-focus" tabindex="-1">{{BUTTON_REPLACE_ALL}}</button><!--
--></div>
{{/replace}}
7 changes: 0 additions & 7 deletions src/htmlContent/search-dialog.html

This file was deleted.

25 changes: 14 additions & 11 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,28 @@ define({
"EXT_MODIFIED_MESSAGE" : "<span class='dialog-filename'>{0}</span> has been modified on disk, but also has unsaved changes in {APP_NAME}.<br /><br />Which version do you want to keep?",
"EXT_DELETED_MESSAGE" : "<span class='dialog-filename'>{0}</span> has been deleted on disk, but has unsaved changes in {APP_NAME}.<br /><br />Do you want to keep your changes?",

// Generic dialog/button labels
"OK" : "OK",
"CANCEL" : "Cancel",
"DONT_SAVE" : "Don't Save",
"SAVE" : "Save",
"DELETE" : "Delete",
"BUTTON_YES" : "Yes",
"BUTTON_NO" : "No",

// Find, Replace, Find in Files
"SEARCH_REGEXP_INFO" : "Use /re/ syntax for regexp search",
"FIND_RESULT_COUNT" : "{0} results",
"FIND_RESULT_COUNT_SINGLE" : "1 result",
"FIND_NO_RESULTS" : "No results",
"WITH" : "With",
"BUTTON_YES" : "Yes",
"BUTTON_NO" : "No",
"REPLACE_PLACEHOLDER" : "Replace with\u2026",
"BUTTON_REPLACE_ALL" : "All\u2026",
"BUTTON_STOP" : "Stop",
"BUTTON_REPLACE" : "Replace",

"BUTTON_NEXT" : "\u25B6",
"BUTTON_PREV" : "\u25C0",
"BUTTON_NEXT_HINT" : "Next Match",
"BUTTON_PREV_HINT" : "Previous Match",
"BUTTON_CASESENSITIVE_HINT" : "Match Case",
"BUTTON_REGEXP_HINT" : "Regular Expression",

"OPEN_FILE" : "Open File",
"SAVE_FILE_AS" : "Save File",
Expand All @@ -135,10 +141,12 @@ define({
"NO_UPDATE_TITLE" : "You're up to date!",
"NO_UPDATE_MESSAGE" : "You are running the latest version of {APP_NAME}.",

// Replace All (in single file)
"FIND_REPLACE_TITLE_PART1" : "Replace \"",
"FIND_REPLACE_TITLE_PART2" : "\" with \"",
"FIND_REPLACE_TITLE_PART3" : "\" &mdash; {2} {0} {1}",

// Find in Files
"FIND_IN_FILES_TITLE_PART1" : "\"",
"FIND_IN_FILES_TITLE_PART2" : "\" found",
"FIND_IN_FILES_TITLE_PART3" : "&mdash; {0} {1} {2} in {3} {4}",
Expand Down Expand Up @@ -307,11 +315,6 @@ define({
// Strings for main-view.html
"EXPERIMENTAL_BUILD" : "experimental build",
"DEVELOPMENT_BUILD" : "development build",
"OK" : "OK",
"DONT_SAVE" : "Don't Save",
"SAVE" : "Save",
"CANCEL" : "Cancel",
"DELETE" : "Delete",
"RELOAD_FROM_DISK" : "Reload from Disk",
"KEEP_CHANGES_IN_EDITOR" : "Keep Changes in Editor",
"CLOSE_DONT_SAVE" : "Close (Don't Save)",
Expand Down
104 changes: 63 additions & 41 deletions src/search/FindInFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ define(function (require, exports, module) {
FileSystem = require("filesystem/FileSystem"),
FileUtils = require("file/FileUtils"),
FileViewController = require("project/FileViewController"),
FindReplace = require("search/FindReplace"),
PerfUtils = require("utils/PerfUtils"),
InMemoryFile = require("document/InMemoryFile"),
PanelManager = require("view/PanelManager"),
Expand All @@ -65,7 +66,7 @@ define(function (require, exports, module) {
StatusBar = require("widgets/StatusBar"),
ModalBar = require("widgets/ModalBar").ModalBar;

var searchDialogTemplate = require("text!htmlContent/search-dialog.html"),
var searchDialogTemplate = require("text!htmlContent/findinfiles-bar.html"),
searchPanelTemplate = require("text!htmlContent/search-panel.html"),
searchSummaryTemplate = require("text!htmlContent/search-summary.html"),
searchResultsTemplate = require("text!htmlContent/search-results.html");
Expand Down Expand Up @@ -119,38 +120,29 @@ define(function (require, exports, module) {
* @return {RegExp}
*/
function _getQueryRegExp(query) {
$(".modal-bar .error").hide(); // Clear any pending RegEx error message

if (!query) {
return null;
}

// Clear any pending RegEx error message
$(".modal-bar .message").css("display", "inline-block");
$(".modal-bar .error").css("display", "none");

// If query is a regular expression, use it directly
var isRE = query.match(/^\/(.*)\/(g|i)*$/);
if (isRE) {
// Make sure the 'g' flag is set
var flags = isRE[2] || "g";
if (flags.search("g") === -1) {
flags += "g";
}
var caseSensitive = $("#find-case-sensitive").is(".active");

// Is it a (non-blank) regex?
if ($("#find-regexp").is(".active")) {
try {
return new RegExp(isRE[1], flags);
return new RegExp(query, caseSensitive ? "g" : "gi");
} catch (e) {
$(".modal-bar .message").css("display", "none");
$(".modal-bar .error")
.css("display", "inline-block")
.html("<div class='alert' style='margin-bottom: 0'>" + e.message + "</div>");
.show()
.text(e.message);
return null;
}
}

// Query is a string. Turn it into a case-insensitive regexp

// Escape regex special chars
query = StringUtils.regexEscape(query);
return new RegExp(query, "gi");
} else {
// Query is a plain string. Turn it into a regexp
return new RegExp(StringUtils.regexEscape(query), caseSensitive ? "g" : "gi");
}
}

/**
Expand Down Expand Up @@ -477,7 +469,6 @@ define(function (require, exports, module) {

if (dialog) {
dialog._close();
dialog = null;
}
} else {

Expand All @@ -487,9 +478,7 @@ define(function (require, exports, module) {
dialog.getDialogTextField().addClass("no-results")
.removeAttr("disabled")
.get(0).select();

$(".modal-bar .message").css("display", "none");
$(".modal-bar .error").css("display", "inline-block").html(Strings.FIND_NO_RESULTS);
$(".modal-bar .no-results-message").show();
}
}
}
Expand Down Expand Up @@ -665,7 +654,6 @@ define(function (require, exports, module) {
if (!currentQueryExpr) {
StatusBar.hideBusyIndicator();
dialog._close();
dialog = null;
return;
}

Expand Down Expand Up @@ -730,16 +718,22 @@ define(function (require, exports, module) {


/**
* Closes the search dialog and resolves the promise that showDialog returned
* Closes the search dialog and resolves the promise that showDialog returned.
* @param {boolean=} suppressAnimation Used to hide the search bar immediately, when another
* one is synchronously about to be shown.
*/
FindInFilesDialog.prototype._close = function (value) {
FindInFilesDialog.prototype._close = function (suppressAnimation) {
if (this.closed) {
return;
}

// Hide error popup, since it hangs down low enough to make the slide-out look awkward
$(".modal-bar .error").hide();

this.closed = true;
this.modalBar.close();
this.modalBar.close(true, !suppressAnimation);
EditorManager.focusEditor();
dialog = null;
};

/**
Expand All @@ -757,8 +751,24 @@ define(function (require, exports, module) {
dialogHTML = Mustache.render(searchDialogTemplate, $.extend(templateVars, Strings)),
that = this;

// Synchronously close Find/Replace bar first, if open (TODO: remove once #6203 fixed)
// (Any previous open FindInFiles bar instance was already handled by our caller)
FindReplace._closeFindBar();

this.modalBar = new ModalBar(dialogHTML, false);
var $searchField = $("input#searchInput");

var $searchField = $("input#find-what");

function handleQueryChange() {
// Check the query expression on every input event. This way the user is alerted
// to any RegEx syntax errors immediately.
var query = _getQueryRegExp($searchField.val());

// Clear any no-results indicator since query has changed
// But input field may still have error style if its content is an invalid regexp
that.getDialogTextField().toggleClass("no-results", Boolean($searchField.val() && query === null));
$(".modal-bar .no-results-message").hide();
}

$searchField.get(0).select();
$searchField
Expand All @@ -770,27 +780,33 @@ define(function (require, exports, module) {
var query = $searchField.val();

if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) {
that._close(null);
that._close();
} else if (event.keyCode === KeyEvent.DOM_VK_RETURN) {
StatusBar.showBusyIndicator(true);
that.getDialogTextField().attr("disabled", "disabled");
_doSearch(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.
_getQueryRegExp($searchField.val());
that.getDialogTextField().removeClass("no-results");
})
.bind("input", handleQueryChange)
.blur(function () {
if (that.getDialogTextField().attr("disabled")) {
return;
}
that._close(null);
that._close();
})
.focus();

this.modalBar.getRoot().on("click", "#find-case-sensitive, #find-regexp", function (e) {
$(e.currentTarget).toggleClass('active');
FindReplace._updatePrefsFromSearchBar();

handleQueryChange(); // re-validate regexp if needed
});

// Initial UI state (including prepopulated initialString passed into template)
FindReplace._updateSearchBarFromPrefs();
handleQueryChange();
};

/**
Expand Down Expand Up @@ -821,7 +837,7 @@ define(function (require, exports, module) {
// The modalBar was already up. When creating the new modalBar, copy the
// current query instead of using the passed-in selected text.
initialString = dialog.getDialogTextField().val();
dialog.modalBar.close(true, false);
dialog._close(true);
}

dialog = new FindInFilesDialog();
Expand Down Expand Up @@ -938,6 +954,12 @@ define(function (require, exports, module) {

FileSystem.on("change", _fileSystemChangeHandler);

FindReplace._registerFindInFilesCloser(function () {
if (dialog) {
dialog._close(true);
}
});

// 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);
Expand Down
Loading

0 comments on commit 19af046

Please sign in to comment.