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

Commit ed9a88b

Browse files
committed
Merge pull request #7400 from adobe/pflynn/exclusions-improvements
File exclusions usability improvements (bug #7149)
2 parents bf41182 + 6118786 commit ed9a88b

4 files changed

Lines changed: 207 additions & 119 deletions

File tree

src/nls/root/strings.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ define({
160160
"FIND_IN_FILES_TITLE_PART3" : "— {0} {1} {2} in {3} {4}",
161161
"FIND_IN_FILES_SCOPED" : "in <span class='dialog-filename'>{0}</span>",
162162
"FIND_IN_FILES_NO_SCOPE" : "in project",
163+
"FIND_IN_FILES_ZERO_FILES" : "Filter excludes all files {0}",
163164
"FIND_IN_FILES_FILE" : "file",
164165
"FIND_IN_FILES_FILES" : "files",
165166
"FIND_IN_FILES_MATCH" : "match",
@@ -175,9 +176,12 @@ define({
175176
"NO_FILE_FILTER" : "Exclude files\u2026",
176177
"EDIT_FILE_FILTER" : "Edit\u2026",
177178
"FILE_FILTER_DIALOG" : "Edit Filter",
178-
"FILE_FILTER_INSTRUCTIONS" : "Exclude files and folders matching any of the following strings / substrings or <a href='{0}' title='{0}'>globs</a>. Enter each string on a new line.",
179+
"FILE_FILTER_INSTRUCTIONS" : "Exclude files and folders matching any of the following strings / substrings or <a href='{0}' title='{0}'>wildcards</a>. Enter each string on a new line.",
179180
"FILE_FILTER_LIST_PREFIX" : "except",
180181
"FILE_FILTER_CLIPPED_SUFFIX" : "and {0} more",
182+
"FILTER_COUNTING_FILES" : "Counting files\u2026",
183+
"FILTER_FILE_COUNT" : "Allows {0} of {1} files {2}",
184+
"FILTER_FILE_COUNT_ALL" : "Allows all {0} files {1}",
181185

182186
// Quick Edit
183187
"ERROR_QUICK_EDIT_PROVIDER_NOT_FOUND" : "No Quick Edit available for current cursor position",

src/search/FileFilters.js

Lines changed: 107 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ define(function (require, exports, module) {
3232
"use strict";
3333

3434
var _ = require("thirdparty/lodash"),
35+
ProjectManager = require("project/ProjectManager"),
3536
DefaultDialogs = require("widgets/DefaultDialogs"),
3637
Dialogs = require("widgets/Dialogs"),
3738
DropdownButton = require("widgets/DropdownButton").DropdownButton,
@@ -59,44 +60,6 @@ define(function (require, exports, module) {
5960
}
6061

6162

62-
/**
63-
* Opens a dialog box to edit the given filter. When editing is finished, the value of getLastFilter() changes to
64-
* reflect the edits. If the dialog was canceled, the preference is left unchanged.
65-
* @param {!Array.<string>} filter
66-
* @return {!$.Promise} Dialog box promise
67-
*/
68-
function editFilter(filter) {
69-
var lastFocus = window.document.activeElement;
70-
71-
var html = StringUtils.format(Strings.FILE_FILTER_INSTRUCTIONS, brackets.config.glob_help_url) +
72-
"<textarea class='exclusions-editor'></textarea>";
73-
var buttons = [
74-
{ className : Dialogs.DIALOG_BTN_CLASS_NORMAL, id: Dialogs.DIALOG_BTN_CANCEL, text: Strings.CANCEL },
75-
{ className : Dialogs.DIALOG_BTN_CLASS_PRIMARY, id: Dialogs.DIALOG_BTN_OK, text: Strings.OK }
76-
];
77-
var dialog = Dialogs.showModalDialog(DefaultDialogs.DIALOG_ID_INFO, Strings.FILE_FILTER_DIALOG, html, buttons);
78-
79-
dialog.getElement().find(".exclusions-editor").val(filter.join("\n")).focus();
80-
81-
dialog.done(function (buttonId) {
82-
if (buttonId === Dialogs.DIALOG_BTN_OK) {
83-
var newFilter = dialog.getElement().find(".exclusions-editor").val().split("\n");
84-
85-
// Remove blank lines
86-
newFilter = newFilter.filter(function (glob) {
87-
return glob.trim().length;
88-
});
89-
90-
// Update saved filter preference
91-
setLastFilter(newFilter);
92-
}
93-
lastFocus.focus(); // restore focus to old pos
94-
});
95-
96-
return dialog.getPromise();
97-
}
98-
99-
10063
/**
10164
* Converts a user-specified filter object (as chosen in picker or retrieved from getFilters()) to a 'compiled' form
10265
* that can be used with filterPath()/filterFileList().
@@ -153,6 +116,109 @@ define(function (require, exports, module) {
153116
}
154117

155118

119+
/**
120+
* Returns false if the given path matches any of the exclusion globs in the given filter. Returns true
121+
* if the path does not match any of the globs. If filtering many paths at once, use filterFileList()
122+
* for much better performance.
123+
*
124+
* @param {?string} compiledFilter 'Compiled' filter object as returned by compile(), or null to no-op
125+
* @param {!string} fullPath
126+
* @return {boolean}
127+
*/
128+
function filterPath(compiledFilter, fullPath) {
129+
if (!compiledFilter) {
130+
return true;
131+
}
132+
133+
var re = new RegExp(compiledFilter);
134+
return !fullPath.match(re);
135+
}
136+
137+
/**
138+
* Returns a copy of 'files' filtered to just those that don't match any of the exclusion globs in the filter.
139+
*
140+
* @param {?string} compiledFilter 'Compiled' filter object as returned by compile(), or null to no-op
141+
* @param {!Array.<File>} files
142+
* @return {!Array.<File>}
143+
*/
144+
function filterFileList(compiledFilter, files) {
145+
if (!compiledFilter) {
146+
return files;
147+
}
148+
149+
var re = new RegExp(compiledFilter);
150+
return files.filter(function (f) {
151+
return !f.fullPath.match(re);
152+
});
153+
}
154+
155+
156+
/**
157+
* Opens a dialog box to edit the given filter. When editing is finished, the value of getLastFilter() changes to
158+
* reflect the edits. If the dialog was canceled, the preference is left unchanged.
159+
* @param {!Array.<string>} filter
160+
* @param {?{label:string, promise:$.Promise}} context Info on which files the filter will be applied to. If specified,
161+
* editing UI will indicate how many files are excluded by the filter. Label should be of the form "in ..."
162+
* @return {!$.Promise} Dialog box promise
163+
*/
164+
function editFilter(filter, context) {
165+
var lastFocus = window.document.activeElement;
166+
167+
var html = StringUtils.format(Strings.FILE_FILTER_INSTRUCTIONS, brackets.config.glob_help_url) +
168+
"<textarea class='exclusions-editor'></textarea><div class='exclusions-filecount'>" + Strings.FILTER_COUNTING_FILES + "</div>";
169+
var buttons = [
170+
{ className : Dialogs.DIALOG_BTN_CLASS_NORMAL, id: Dialogs.DIALOG_BTN_CANCEL, text: Strings.CANCEL },
171+
{ className : Dialogs.DIALOG_BTN_CLASS_PRIMARY, id: Dialogs.DIALOG_BTN_OK, text: Strings.OK }
172+
];
173+
var dialog = Dialogs.showModalDialog(DefaultDialogs.DIALOG_ID_INFO, Strings.FILE_FILTER_DIALOG, html, buttons);
174+
175+
var $editField = dialog.getElement().find(".exclusions-editor");
176+
$editField.val(filter.join("\n")).focus();
177+
178+
function getValue() {
179+
var newFilter = $editField.val().split("\n");
180+
181+
// Remove blank lines
182+
return newFilter.filter(function (glob) {
183+
return glob.trim().length;
184+
});
185+
}
186+
187+
dialog.done(function (buttonId) {
188+
if (buttonId === Dialogs.DIALOG_BTN_OK) {
189+
// Update saved filter preference
190+
setLastFilter(getValue());
191+
}
192+
lastFocus.focus(); // restore focus to old pos
193+
});
194+
195+
196+
// Code to update the file count readout at bottom of dialog (if context provided)
197+
var $fileCount = dialog.getElement().find(".exclusions-filecount");
198+
199+
function updateFileCount() {
200+
context.promise.done(function (files) {
201+
var filter = getValue();
202+
if (filter.length) {
203+
var filtered = filterFileList(compile(filter), files);
204+
$fileCount.html(StringUtils.format(Strings.FILTER_FILE_COUNT, filtered.length, files.length, context.label));
205+
} else {
206+
$fileCount.html(StringUtils.format(Strings.FILTER_FILE_COUNT_ALL, files.length, context.label));
207+
}
208+
});
209+
}
210+
211+
if (context) {
212+
$editField.on("input", _.debounce(updateFileCount, 400));
213+
updateFileCount();
214+
} else {
215+
$fileCount.hide();
216+
}
217+
218+
return dialog.getPromise();
219+
}
220+
221+
156222
/**
157223
* Marks the filter picker's currently selected item as most-recently used, and returns the corresponding
158224
* 'compiled' filter object ready for use with filterPath().
@@ -170,9 +236,10 @@ define(function (require, exports, module) {
170236
* client should call commitDropdown() when the UI containing the filter picker is confirmed (which updates the MRU
171237
* order) and then use the returned filter object as needed.
172238
*
239+
* @param {?{label:string, promise:$.Promise}} context Info on files filter will apply to - see editFilter()
173240
* @return {!jQueryObject} Picker UI. To retrieve the selected value, use commitPicker().
174241
*/
175-
function createFilterPicker() {
242+
function createFilterPicker(context) {
176243
var $picker = $("<div class='filter-picker'><span class='filter-label'></span><button class='btn no-focus'></button></div>"),
177244
$button = $picker.find("button");
178245

@@ -208,7 +275,7 @@ define(function (require, exports, module) {
208275
updatePicker();
209276

210277
$button.click(function () {
211-
editFilter(getLastFilter())
278+
editFilter(getLastFilter(), context)
212279
.done(function (buttonId) {
213280
if (buttonId === Dialogs.DIALOG_BTN_OK) {
214281
updatePicker();
@@ -220,43 +287,6 @@ define(function (require, exports, module) {
220287
}
221288

222289

223-
/**
224-
* Returns false if the given path matches any of the exclusion globs in the given filter. Returns true
225-
* if the path does not match any of the globs. If filtering many paths at once, use filterFileList()
226-
* for much better performance.
227-
*
228-
* @param {!string} compiledFilter 'Compiled' filter object as returned by compile()
229-
* @param {!string} fullPath
230-
* @return {boolean}
231-
*/
232-
function filterPath(compiledFilter, fullPath) {
233-
if (!compiledFilter) {
234-
return true;
235-
}
236-
237-
var re = new RegExp(compiledFilter);
238-
return !fullPath.match(re);
239-
}
240-
241-
/**
242-
* Returns a copy of 'files' filtered to just those that don't match any of the exclusion globs in the filter.
243-
*
244-
* @param {!string} compiledFilter 'Compiled' filter object as returned by compile()
245-
* @param {!Array.<File>} files
246-
* @return {!Array.<File>}
247-
*/
248-
function filterFileList(compiledFilter, files) {
249-
if (!compiledFilter) {
250-
return files;
251-
}
252-
253-
var re = new RegExp(compiledFilter);
254-
return files.filter(function (f) {
255-
return !f.fullPath.match(re);
256-
});
257-
}
258-
259-
260290
exports.createFilterPicker = createFilterPicker;
261291
exports.commitPicker = commitPicker;
262292
exports.getLastFilter = getLastFilter;

0 commit comments

Comments
 (0)