Skip to content

Commit

Permalink
Filter code search results by file paths
Browse files Browse the repository at this point in the history
 - Add fileFilter query param that is used to filter code search results down to the subset with matches occurring in filenames matching the fileFilter

 - Filtering or clearing the filter resets to the first page of results

 - Use filteredResults in place of rawResults throughout code-search component; rawResults is used only when clearing the filter
  • Loading branch information
lonelyghost authored and kategengler committed Sep 15, 2017
1 parent f3eb22d commit e59c625
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 25 deletions.
18 changes: 15 additions & 3 deletions app/components/addon-source-usages.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Ember from 'ember';
import { task } from 'ember-concurrency';

const { computed } = Ember;
const { computed, inject, isEmpty } = Ember;

export default Ember.Component.extend({
visibleUsageCount: 25,
Expand All @@ -12,7 +12,9 @@ export default Ember.Component.extend({

regex: false,

codeSearch: Ember.inject.service(),
fileFilter: null,

codeSearch: inject.service(),

visibleUsages: computed('visibleUsageCount', 'usages', function() {
return this.get('usages').slice(0, this.get('visibleUsageCount'));
Expand All @@ -24,7 +26,7 @@ export default Ember.Component.extend({

fetchUsages: task(function* () {
let usages = yield this.get('codeSearch').usages(this.get('addon.name'), this.get('query'), this.get('regex'));
this.set('usages', usages);
this.set('usages', filterByFilePath(usages, this.get('fileFilter')));
}).drop(),

actions: {
Expand All @@ -41,3 +43,13 @@ export default Ember.Component.extend({
}
}
});

function filterByFilePath(usages, filterTerm) {
if (isEmpty(filterTerm)) {
return usages;
}
let filterRegex = new RegExp(filterTerm);
return usages.filter((usage) => {
return usage.filename.match(filterRegex);
});
}
76 changes: 65 additions & 11 deletions app/components/code-search.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import Ember from 'ember';
import { task } from 'ember-concurrency';
import { task, timeout } from 'ember-concurrency';
import config from 'ember-observer/config/environment';

const { computed, inject } = Ember;
const { computed, inject, isEmpty } = Ember;

const PageSize = 50;
const PageSize = config.codeSearchPageSize;

export default Ember.Component.extend({
metrics: inject.service(),

store: inject.service(),

codeQuery: null,

sort: null,

fileFilter: null,

classNames: ['code-search'],

focusNode: '#code-search-input',

codeSearch: inject.service(),

usageCounts: computed.mapBy('results.rawResults', 'count'),
usageCounts: computed.mapBy('results.filteredResults', 'count'),

totalUsageCount: computed.sum('usageCounts'),

isFilterApplied: computed.notEmpty('fileFilter'),

init() {
this._super(...arguments);
this.set('searchInput', this.get('codeQuery') || '');
Expand Down Expand Up @@ -50,16 +56,41 @@ export default Ember.Component.extend({
let results = yield this.get('codeSearch').addons(query, this.get('regex'));
this.set('quotedLastSearch', quoteSearchTerm(query, this.get('regex')));

let firstPageOfResults = yield this._fetchPageOfAddonResults(results, 1, this.get('sort'));
let filteredResults = filterByFilePath(results, this.get('fileFilter'));

let firstPageOfResults = yield this._fetchPageOfAddonResults(filteredResults, 1, this.get('sort'));
this.set('results',
{
displayingResults: firstPageOfResults,
lastResultPageDisplaying: 1,
rawResults: results,
length: results.length
length: results.length,
filteredResults: filteredResults,
filteredResultLength: filteredResults.length
});
}).restartable(),

applyFileFilter: task(function*(fileFilter) {
yield timeout(500);

this.set('fileFilter', fileFilter);
let filteredResults = filterByFilePath((this.get('results.rawResults')), fileFilter);
let firstPageOfFilteredResults = yield this._fetchPageOfAddonResults(filteredResults, 1, this.get('sort'));
this.set('results.displayingResults', firstPageOfFilteredResults);
this.set('results.lastResultPageDisplaying', 1);
this.set('results.filteredResults', filteredResults);
this.set('results.filteredResultLength', filteredResults.length);
}).restartable(),

clearFileFilter: task(function*() {
this.set('fileFilter', null);
let firstPageOfResults = yield this._fetchPageOfAddonResults(this.get('results.rawResults'), 1, this.get('sort'));
this.set('results.displayingResults', firstPageOfResults);
this.set('results.lastResultPageDisplaying', 1);
this.set('results.filteredResults', this.get('results.rawResults'));
this.set('results.filteredResultLength', this.get('results.length'));
}),

_fetchPageOfAddonResults(results, page, sort) {
if (!results || !results.length) {
return Ember.RSVP.resolve(null);
Expand All @@ -72,26 +103,27 @@ export default Ember.Component.extend({
return pageOfResults.map((result) => {
return {
addon: addons.findBy('name', result.addonName),
count: result.count
count: result.count,
files: result.files
};
});
});
},

canViewMore: computed('results.displayingResults.length', 'results.length', function() {
return this.get('results.displayingResults.length') < this.get('results.length');
canViewMore: computed('results.displayingResults.length', 'results.filteredResultLength', function() {
return this.get('results.displayingResults.length') < this.get('results.filteredResultLength');
}),

viewMore: task(function* () {
let pageToFetch = this.get('results.lastResultPageDisplaying') + 1;
let moreAddons = yield this._fetchPageOfAddonResults(this.get('results.rawResults'), pageToFetch, this.get('sort'));
let moreAddons = yield this._fetchPageOfAddonResults(this.get('results.filteredResults'), pageToFetch, this.get('sort'));
this.get('results.displayingResults').pushObjects(moreAddons);
this.set('results.lastResultPageDisplaying', pageToFetch);
}),

sortBy: task(function* (key) {
this.set('sort', key);
let sortedAddons = yield this._fetchPageOfAddonResults(this.get('results.rawResults'), 1, key);
let sortedAddons = yield this._fetchPageOfAddonResults(this.get('results.filteredResults'), 1, key);
this.set('results.displayingResults', sortedAddons);
this.set('results.lastResultPageDisplaying', 1);
}),
Expand Down Expand Up @@ -124,3 +156,25 @@ function quoteSearchTerm(searchTerm, isRegex) {
let character = isRegex ? '/' : '"';
return `${character}${searchTerm}${character}`;
}

function filterByFilePath(results, filterTerm) {
if (isEmpty(filterTerm)) {
return results;
}

let filteredList = [];
let filterRegex = new RegExp(filterTerm);
results.forEach((result) => {
let filteredFiles = result.files.filter((filePath) => {
return filePath.match(filterRegex);
});
if (filteredFiles.length > 0) {
filteredList.push({
addonName: result.addonName,
files: filteredFiles,
count: filteredFiles.length
});
}
});
return filteredList;
}
5 changes: 3 additions & 2 deletions app/controllers/code-search.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Ember from 'ember';

export default Ember.Controller.extend({
queryParams: ['codeQuery', 'sort', 'regex'],
queryParams: ['codeQuery', 'sort', 'regex', 'fileFilter'],
codeQuery: '',
sort: 'name',
regex: false
regex: false,
fileFilter: null
});
2 changes: 1 addition & 1 deletion app/services/code-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default Ember.Service.extend({
}
}).then((response) => {
return response.results.map((item) => {
return { addonName: item.addon, count: item.count };
return { addonName: item.addon, count: item.count, files: item.files };
});
});
},
Expand Down
46 changes: 44 additions & 2 deletions app/styles/components/_code-search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,56 @@
.result-details {
@include clearfix;

h4 {
margin: .5rem 0;
}

h5 {
padding-top: 2.25rem;
float:left;
padding-top: .5rem;
line-height: 2rem;
}

}

.result-controls {
padding: 0 .25rem;
border: 1px solid $light-gray;
border-width: 0 0 1px 0;
margin-top: 1rem;
@include clearfix();

.sort-controls {
float: right;
margin-right: .26rem;
margin-bottom: 1.5rem;

.button-option {
margin-bottom: 0;
}
}

.filter-controls {
float: left;
margin-bottom: 1.5rem;

.filter-input {
position: relative;
width: 300px;

input {
border: 1px solid $base-accent-color;
height: 36px;
width: 100%;
padding: .5rem;
}

.icon-close {
right: .4rem;
color: $base-accent-color;
font-size: 1.25rem;
margin-top: -.7rem;
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/templates/code-search.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{{#page-layout showCategories=true}}
{{code-search codeQuery=codeQuery sort=sort regex=regex}}
{{code-search codeQuery=codeQuery sort=sort regex=regex fileFilter=fileFilter}}
{{/page-layout}}
27 changes: 25 additions & 2 deletions app/templates/components/code-search.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,25 @@
<div class="result-details">
<h4 class="test-last-search">Results for {{quotedLastSearch}}</h4>
<h5 class="test-result-info">
Found {{pluralize-this results.length 'addon'}} ({{pluralize-this totalUsageCount 'usage'}})
Found {{pluralize-this results.filteredResultLength 'addon'}} ({{pluralize-this totalUsageCount 'usage'}})
{{#if isFilterApplied}}
in files matching /{{fileFilter}}/
{{/if}}
</h5>
</div>
<div class="result-controls">
<div class="filter-controls">
Filter by file path:
<div class="filter-input">
<input type="text"
value={{fileFilter}}
oninput={{action (perform applyFileFilter) value="target.value"}}
class="test-file-filter-input">
{{#if isFilterApplied}}
<button type="button" {{action (perform clearFileFilter)}} class="icon-close test-clear-file-filter"></button>
{{/if}}
</div>
</div>
<div class="sort-controls">
Sort by:
<div class="button-select test-sort">
Expand All @@ -45,7 +62,13 @@
{{#each results.displayingResults as |result|}}
<li data-id="{{result.addon.id}}">
{{addon-details addon=result.addon}}
{{addon-source-usages addon=result.addon count=result.count query=codeQuery regex=regex}}
{{addon-source-usages
addon=result.addon
count=result.count
query=codeQuery
regex=regex
fileFilter=fileFilter
}}
</li>
{{/each}}
{{#if canViewMore}}
Expand Down
2 changes: 2 additions & 0 deletions config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = function(environment) {
rootURL: '/',
locationType: 'router-scroll',
historySupportMiddleware: true,
codeSearchPageSize: 50,
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
Expand Down Expand Up @@ -60,6 +61,7 @@ module.exports = function(environment) {
ENV.APP.LOG_VIEW_LOOKUPS = false;

ENV.APP.rootElement = '#ember-testing';
ENV.codeSearchPageSize = 3;
}

if (environment === 'production') {
Expand Down
Loading

0 comments on commit e59c625

Please sign in to comment.