diff --git a/packages/fuzzy-finder/README.md b/packages/fuzzy-finder/README.md
index b7cc3a9fe..a1ed6f24f 100644
--- a/packages/fuzzy-finder/README.md
+++ b/packages/fuzzy-finder/README.md
@@ -12,6 +12,7 @@ When opening a file, you can control the behavior.
* shift-enter defaults to switching to another pane if the file is already open there
* cmd-k right *(macOS)* or ctrl-k right *(Linux/Windows)* (or any other directional arrow) will open the highlighted file in a new pane on the side indicated by the arrow
* Adding `:` to the end of your search will go directly to the line number you specify, or the last line if the number is larger
+ * Beginning your search with `!` will search for VCS ignored files (only if using ripgrep indexing)
Turning on the "Search All Panes" setting reverses the behavior of enter and shift-enter so enter opens the file in any pane and shift-enter creates a new tab in the current pane.
diff --git a/packages/fuzzy-finder/lib/fuzzy-finder-view.js b/packages/fuzzy-finder/lib/fuzzy-finder-view.js
index dab6afb72..01ec60775 100644
--- a/packages/fuzzy-finder/lib/fuzzy-finder-view.js
+++ b/packages/fuzzy-finder/lib/fuzzy-finder-view.js
@@ -12,7 +12,9 @@ const MAX_RESULTS = 10
module.exports = class FuzzyFinderView {
constructor () {
this.previousQueryWasLineJump = false
+ this.previousQueryOverrodeIgnore = false
this.items = []
+ this.ignoredItems = []
this.filterFn = this.filterFn.bind(this)
this.selectListView = new SelectListView({
@@ -25,6 +27,11 @@ module.exports = class FuzzyFinderView {
if (colon !== -1) {
query = query.slice(0, colon)
}
+
+ if (query.indexOf('!') === 0 ) {
+ query = query.slice(1)
+ }
+
// Normalize to backslashes on Windows
if (process.platform === 'win32') {
query = query.replace(/\//g, '\\')
@@ -45,7 +52,11 @@ module.exports = class FuzzyFinderView {
this.iconDisposables = null
}
const isLineJump = this.isQueryALineJump()
+ const overridesIgnore = this.queryOverridesIgnore()
+
+ // if last query was not line jump and this one is, clear the list of items
if (isLineJump) {
+
this.previousQueryWasLineJump = true
const query = this.selectListView.getQuery()
let emptyMessage = null
@@ -65,8 +76,24 @@ module.exports = class FuzzyFinderView {
emptyMessage: emptyMessage,
errorMessage: errorMessage
})
- } else if (this.previousQueryWasLineJump) {
+
+ // if last query did not override ignored paths and this one does,
+ // set the filter items to the ignored paths
+ } else if (!this.previousQueryOverrodeIgnore && overridesIgnore) {
+ this.previousQueryOverrodeIgnore = true
+ this.selectListView.update({
+ items: this.ignoredItems,
+ emptyMessage: this.getEmptyMessage(),
+ errorMessage: null
+ })
+
+ // if last query was line jump and this one is not,
+ // OR if last query overrode ignored paths and this one doesn't
+ // reset the filter items to the regular items
+ } else if ((this.previousQueryWasLineJump && !isLineJump) ||
+ (this.previousQueryOverrodeIgnore && !overridesIgnore)) {
this.previousQueryWasLineJump = false
+ this.previousQueryOverrodeIgnore = false
this.selectListView.update({
items: this.items,
emptyMessage: this.getEmptyMessage(),
@@ -124,10 +151,13 @@ module.exports = class FuzzyFinderView {
}
})
- if (!this.nativeFuzzy) {
- this.nativeFuzzy = atom.ui.fuzzyMatcher.setCandidates(
+ if (!this.nativeFuzzyTracked) {
+ this.nativeFuzzyTracked = atom.ui.fuzzyMatcher.setCandidates(
this.items.map(el => el.label)
);
+ this.nativeFuzzyIgnored = atom.ui.fuzzyMatcher.setCandidates(
+ this.ignoredItems.map(el => el.label)
+ )
// We need a separate instance of the fuzzy finder to calculate the
// matched paths only for the returned results. This speeds up considerably
// the filtering of items.
@@ -268,6 +298,10 @@ module.exports = class FuzzyFinderView {
)
}
+ queryOverridesIgnore () {
+ return this.selectListView.getQuery().indexOf('!') === 0
+ }
+
getCaretPosition () {
const query = this.selectListView.getQuery()
const firstColon = query.indexOf(':')
@@ -287,12 +321,18 @@ module.exports = class FuzzyFinderView {
return position
}
- setItems (items) {
+ setItems (items, ignoredItems = []) {
this.items = items
+ this.ignoredItems = ignoredItems
+
atom.ui.fuzzyMatcher.setCandidates(
- this.nativeFuzzy,
+ this.nativeFuzzyTracked,
this.items.map(item => item.label)
);
+ atom.ui.fuzzyMatcher.setCandidates(
+ this.nativeFuzzyIgnored,
+ this.ignoredItems.map(item => item.label)
+ );
if (this.isQueryALineJump()) {
this.selectListView.update({
@@ -303,7 +343,9 @@ module.exports = class FuzzyFinderView {
})
} else {
this.selectListView.update({
- items: this.items,
+ items: this.queryOverridesIgnore()
+ ? this.ignoredItems
+ : this.items,
infoMessage: null,
loadingMessage: null,
loadingBadge: null
@@ -337,7 +379,15 @@ module.exports = class FuzzyFinderView {
filterFn(items, query) {
if (!query) return items
- return this.nativeFuzzy.match(query, {maxResults: MAX_RESULTS, algorithm: 'command-t'})
+
+ if (this.queryOverridesIgnore()) {
+ return this.nativeFuzzyIgnored
+ .match(query, {maxResults: MAX_RESULTS, algorithm: 'command-t'})
+ .map(({id}) => this.ignoredItems[id])
+ }
+
+ return this.nativeFuzzyTracked
+ .match(query, {maxResults: MAX_RESULTS, algorithm: 'command-t'})
.map(({id}) => this.items[id])
}
}
diff --git a/packages/fuzzy-finder/spec/fuzzy-finder-spec.js b/packages/fuzzy-finder/spec/fuzzy-finder-spec.js
index acbdb2c41..b5a0c20a3 100644
--- a/packages/fuzzy-finder/spec/fuzzy-finder-spec.js
+++ b/packages/fuzzy-finder/spec/fuzzy-finder-spec.js
@@ -1644,7 +1644,7 @@ describe('FuzzyFinder', () => {
})
if (useRipGrep) {
- it('does excludes paths that are git ignored', async () => {
+ it('excludes paths that are git ignored', async () => {
fs.writeFileSync(path.join(projectPath, 'dir', 'a.txt'), 'something')
await projectView.toggle()
@@ -1693,6 +1693,81 @@ describe('FuzzyFinder', () => {
a.textContent.includes('HEAD'))).not.toBeDefined()
})
})
+
+ describe('when the query starts with an exclamation point', () => {
+ beforeEach(() => {
+ const ignoreFile = path.join(projectPath, '.gitignore')
+ fs.writeFileSync(ignoreFile, "ignored.txt\nanother.txt")
+
+ fs.writeFileSync(
+ path.join(projectPath, 'ignored.txt'),
+ 'this text is not important'
+ )
+ fs.writeFileSync(
+ path.join(projectPath, 'another.txt'),
+ 'this text is not important'
+ )
+ })
+
+ it('excludes paths that are tracked when indexIgnoredPaths is true', async () => {
+ atom.config.set('fuzzy-finder.indexIgnoredPaths', true)
+ projectView.selectListView.refs.queryEditor.insertText('!')
+
+ await projectView.toggle()
+ await waitForPathsToDisplay(projectView)
+
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('a.txt'))
+ ).not.toBeDefined()
+ })
+
+ it('includes paths that are git ignored when indexIgnoredPaths is true', async () => {
+ atom.config.set('fuzzy-finder.indexIgnoredPaths', true)
+ projectView.selectListView.refs.queryEditor.insertText('!')
+
+ await projectView.toggle()
+ await waitForPathsToDisplay(projectView)
+
+ expect(projectView.queryOverridesIgnore()).toBe(true)
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('ignored.txt'))
+ ).toBeDefined()
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('another.txt'))
+ ).toBeDefined()
+ })
+
+ it('matches paths that are git ignored when indexIgnoredPaths is true', async () => {
+ atom.config.set('fuzzy-finder.indexIgnoredPaths', true)
+ projectView.selectListView.refs.queryEditor.insertText('!anoth')
+
+ await projectView.toggle()
+ await waitForPathsToDisplay(projectView)
+
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('ignored.txt'))
+ ).not.toBeDefined()
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('another.txt'))
+ ).toBeDefined()
+ })
+
+ it('excludes paths that are git ignored when indexIgnoredPaths is false', async () => {
+ atom.config.set('fuzzy-finder.indexIgnoredPaths', false)
+ projectView.selectListView.refs.queryEditor.insertText('!ig')
+
+ await projectView.toggle()
+ await waitForReCrawlerToFinish(projectView)
+
+ expect(projectView.queryOverridesIgnore()).toBe(true)
+ expect(projectView.element.querySelectorAll('li').length).toBe(0)
+ })
+ })
})
describe('when core.excludeVcsIgnoredPaths is set to false', () => {
@@ -1715,6 +1790,29 @@ describe('FuzzyFinder', () => {
expect(Array.from(projectView.element.querySelectorAll('li')).find(a => a.textContent.includes('ignored.txt'))).toBeDefined()
})
})
+
+ describe('when the query starts with an exclamation point', () => {
+ beforeEach(() => {
+ const ignoreFile = path.join(projectPath, '.gitignore')
+ fs.writeFileSync(ignoreFile, 'ignored.txt')
+
+ const ignoredFile = path.join(projectPath, 'ignored.txt')
+ fs.writeFileSync(ignoredFile, 'ignored text')
+ })
+
+ it('excludes paths that are git ignored when indexIgnoredPaths is true', async () => {
+ atom.config.set('fuzzy-finder.indexIgnoredPaths', false)
+ projectView.selectListView.refs.queryEditor.insertText('!ig')
+
+ await projectView.toggle()
+ await waitForReCrawlerToFinish(projectView)
+
+ expect(
+ Array.from(projectView.element.querySelectorAll('li'))
+ .find(a => a.textContent.includes('ignored.txt'))
+ ).not.toBeDefined()
+ })
+ })
})
})