mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Pull out fuzzy-finder package into a separate repo
This commit is contained in:
@@ -72,6 +72,7 @@
|
||||
"bracket-matcher": "0.1.0",
|
||||
"command-logger": "0.1.0",
|
||||
"command-palette": "0.1.0",
|
||||
"fuzzy-finder": "0.1.0",
|
||||
"editor-stats": "0.1.0",
|
||||
"gfm": "0.1.0",
|
||||
"git-diff": "0.1.0",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
'body':
|
||||
'meta-t': 'fuzzy-finder:toggle-file-finder'
|
||||
'meta-b': 'fuzzy-finder:toggle-buffer-finder'
|
||||
'ctrl-.': 'fuzzy-finder:find-under-cursor'
|
||||
'meta-B': 'fuzzy-finder:toggle-git-status-finder'
|
||||
@@ -1,252 +0,0 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
SelectList = require 'select-list'
|
||||
_ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
humanize = require 'humanize-plus'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
PathLoader = require './path-loader'
|
||||
Point = require 'point'
|
||||
|
||||
module.exports =
|
||||
class FuzzyFinderView extends SelectList
|
||||
filenameRegex: /[\w\.\-\/\\]+/
|
||||
finderMode: null
|
||||
|
||||
@viewClass: ->
|
||||
[super, 'fuzzy-finder', 'overlay', 'from-top'].join(' ')
|
||||
|
||||
allowActiveEditorChange: null
|
||||
maxItems: 10
|
||||
projectPaths: null
|
||||
reloadProjectPaths: true
|
||||
filterKey: 'projectRelativePath'
|
||||
|
||||
initialize: (@projectPaths)->
|
||||
super
|
||||
|
||||
@reloadProjectPaths = false if @projectPaths?.length > 0
|
||||
|
||||
@subscribe $(window), 'focus', => @reloadProjectPaths = true
|
||||
@observeConfig 'fuzzy-finder.ignoredNames', => @reloadProjectPaths = true
|
||||
rootView.eachPane (pane) ->
|
||||
pane.activeItem.lastOpened = (new Date) - 1
|
||||
pane.on 'pane:active-item-changed', (e, item) -> item.lastOpened = (new Date) - 1
|
||||
|
||||
@miniEditor.command 'pane:split-left', =>
|
||||
@splitOpenPath (pane, session) -> pane.splitLeft(session)
|
||||
@miniEditor.command 'pane:split-right', =>
|
||||
@splitOpenPath (pane, session) -> pane.splitRight(session)
|
||||
@miniEditor.command 'pane:split-down', =>
|
||||
@splitOpenPath (pane, session) -> pane.splitDown(session)
|
||||
@miniEditor.command 'pane:split-up', =>
|
||||
@splitOpenPath (pane, session) -> pane.splitUp(session)
|
||||
|
||||
itemForElement: ({filePath, projectRelativePath}) ->
|
||||
$$ ->
|
||||
@li class: 'two-lines', =>
|
||||
if git?
|
||||
status = git.statuses[filePath]
|
||||
if git.isStatusNew(status)
|
||||
@div class: 'status new'
|
||||
else if git.isStatusModified(status)
|
||||
@div class: 'status modified'
|
||||
|
||||
ext = path.extname(filePath)
|
||||
if fsUtils.isReadmePath(filePath)
|
||||
typeClass = 'readme-name'
|
||||
else if fsUtils.isCompressedExtension(ext)
|
||||
typeClass = 'compressed-name'
|
||||
else if fsUtils.isImageExtension(ext)
|
||||
typeClass = 'image-name'
|
||||
else if fsUtils.isPdfExtension(ext)
|
||||
typeClass = 'pdf-name'
|
||||
else if fsUtils.isBinaryExtension(ext)
|
||||
typeClass = 'binary-name'
|
||||
else
|
||||
typeClass = 'text-name'
|
||||
|
||||
@div path.basename(filePath), class: "primary-line file #{typeClass}"
|
||||
@div projectRelativePath, class: 'secondary-line path'
|
||||
|
||||
openPath: (filePath, lineNumber) ->
|
||||
return unless filePath
|
||||
|
||||
rootView.open(filePath, {@allowActiveEditorChange})
|
||||
@moveToLine(lineNumber)
|
||||
|
||||
moveToLine: (lineNumber=-1) ->
|
||||
return unless lineNumber >= 0
|
||||
|
||||
if editor = rootView.getActiveView()
|
||||
position = new Point(lineNumber)
|
||||
editor.scrollToBufferPosition(position, center: true)
|
||||
editor.setCursorBufferPosition(position)
|
||||
editor.moveCursorToFirstCharacterOfLine()
|
||||
|
||||
splitOpenPath: (fn) ->
|
||||
{filePath} = @getSelectedElement()
|
||||
return unless filePath
|
||||
|
||||
lineNumber = @getLineNumber()
|
||||
if pane = rootView.getActivePane()
|
||||
fn(pane, project.open(filePath))
|
||||
@moveToLine(lineNumber)
|
||||
else
|
||||
@openPath(filePath, lineNumber)
|
||||
|
||||
confirmed : ({filePath}) ->
|
||||
return unless filePath
|
||||
|
||||
if fsUtils.isDirectorySync(filePath)
|
||||
@setError('Selected path is a directory')
|
||||
setTimeout((=> @setError()), 2000)
|
||||
else
|
||||
lineNumber = @getLineNumber()
|
||||
@cancel()
|
||||
@openPath(filePath, lineNumber)
|
||||
|
||||
toggleFileFinder: ->
|
||||
@finderMode = 'file'
|
||||
if @hasParent()
|
||||
@cancel()
|
||||
else
|
||||
return unless project.getPath()?
|
||||
@allowActiveEditorChange = false
|
||||
@populateProjectPaths()
|
||||
@attach()
|
||||
|
||||
toggleBufferFinder: ->
|
||||
@finderMode = 'buffer'
|
||||
if @hasParent()
|
||||
@cancel()
|
||||
else
|
||||
@allowActiveEditorChange = true
|
||||
@populateOpenBufferPaths()
|
||||
@attach() if @paths?.length
|
||||
|
||||
toggleGitFinder: ->
|
||||
@finderMode = 'git'
|
||||
if @hasParent()
|
||||
@cancel()
|
||||
else
|
||||
return unless project.getPath()? and git?
|
||||
@allowActiveEditorChange = false
|
||||
@populateGitStatusPaths()
|
||||
@attach()
|
||||
|
||||
getEmptyMessage: (itemCount) ->
|
||||
if itemCount is 0
|
||||
switch @finderMode
|
||||
when 'git'
|
||||
'Nothing to commit, working directory clean'
|
||||
when 'buffer'
|
||||
'No open editors'
|
||||
when 'file'
|
||||
'Project is empty'
|
||||
else
|
||||
super
|
||||
else
|
||||
super
|
||||
|
||||
findUnderCursor: ->
|
||||
if @hasParent()
|
||||
@cancel()
|
||||
else
|
||||
return unless project.getPath()?
|
||||
@allowActiveEditorChange = false
|
||||
editor = rootView.getActiveView()
|
||||
currentWord = editor.getWordUnderCursor(wordRegex: @filenameRegex)
|
||||
|
||||
if currentWord.length == 0
|
||||
@attach()
|
||||
@setError("The cursor is not over a filename")
|
||||
else
|
||||
@populateProjectPaths filter: currentWord, done: (paths) =>
|
||||
if paths.length == 0
|
||||
@attach()
|
||||
@setError("No files match '#{currentWord}'")
|
||||
else if paths.length == 1
|
||||
rootView.open(paths[0])
|
||||
else
|
||||
@attach()
|
||||
@miniEditor.setText(currentWord)
|
||||
|
||||
getFilterQuery: ->
|
||||
query = super
|
||||
colon = query.indexOf(':')
|
||||
if colon is -1
|
||||
query
|
||||
else
|
||||
query[0...colon]
|
||||
|
||||
getLineNumber: ->
|
||||
query = @miniEditor.getText()
|
||||
colon = query.indexOf(':')
|
||||
if colon is -1
|
||||
-1
|
||||
else
|
||||
parseInt(query[colon+1..]) - 1
|
||||
|
||||
setArray: (paths) ->
|
||||
projectRelativePaths = paths.map (filePath) ->
|
||||
projectRelativePath = project.relativize(filePath)
|
||||
{filePath, projectRelativePath}
|
||||
|
||||
super(projectRelativePaths)
|
||||
|
||||
populateGitStatusPaths: ->
|
||||
paths = []
|
||||
paths.push(filePath) for filePath, status of git.statuses when fsUtils.isFileSync(filePath)
|
||||
|
||||
@setArray(paths)
|
||||
|
||||
populateProjectPaths: (options = {}) ->
|
||||
if @projectPaths?
|
||||
listedItems =
|
||||
if options.filter?
|
||||
@projectPaths.filter (filePath) ->
|
||||
filePath.indexOf(options.filter) >= 0
|
||||
else
|
||||
@projectPaths
|
||||
@setArray(listedItems)
|
||||
options.done(listedItems) if options.done?
|
||||
else
|
||||
@setLoading("Indexing project...")
|
||||
@loadingBadge.text("")
|
||||
|
||||
if @reloadProjectPaths
|
||||
@loadPathsTask?.terminate()
|
||||
@loadPathsTask = PathLoader.startTask (paths) =>
|
||||
@projectPaths = paths
|
||||
@reloadProjectPaths = false
|
||||
@populateProjectPaths(options)
|
||||
|
||||
pathsFound = 0
|
||||
@loadPathsTask.on 'load-paths:paths-found', (paths) =>
|
||||
pathsFound += paths.length
|
||||
@loadingBadge.text(humanize.intcomma(pathsFound))
|
||||
|
||||
populateOpenBufferPaths: ->
|
||||
editSessions = project.getEditSessions().filter (editSession) ->
|
||||
editSession.getPath()?
|
||||
|
||||
editSessions = _.sortBy editSessions, (editSession) =>
|
||||
if editSession is rootView.getActivePaneItem()
|
||||
0
|
||||
else
|
||||
-(editSession.lastOpened or 1)
|
||||
|
||||
@paths = []
|
||||
@paths.push(editSession.getPath()) for editSession in editSessions
|
||||
|
||||
@setArray(_.uniq(@paths))
|
||||
|
||||
beforeRemove: ->
|
||||
@loadPathsTask?.terminate()
|
||||
|
||||
attach: ->
|
||||
super
|
||||
|
||||
rootView.append(this)
|
||||
@miniEditor.focus()
|
||||
@@ -1,48 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
projectPaths: null
|
||||
fuzzyFinderView: null
|
||||
loadPathsTask: null
|
||||
|
||||
activate: (state) ->
|
||||
rootView.command 'fuzzy-finder:toggle-file-finder', =>
|
||||
@createView().toggleFileFinder()
|
||||
rootView.command 'fuzzy-finder:toggle-buffer-finder', =>
|
||||
@createView().toggleBufferFinder()
|
||||
rootView.command 'fuzzy-finder:find-under-cursor', =>
|
||||
@createView().findUnderCursor()
|
||||
rootView.command 'fuzzy-finder:toggle-git-status-finder', =>
|
||||
@createView().toggleGitFinder()
|
||||
|
||||
if project.getPath()?
|
||||
PathLoader = require './path-loader'
|
||||
@loadPathsTask = PathLoader.startTask (paths) => @projectPaths = paths
|
||||
|
||||
for editSession in project.getEditSessions()
|
||||
editSession.lastOpened = state[editSession.getPath()]
|
||||
|
||||
deactivate: ->
|
||||
if @loadPathsTask?
|
||||
@loadPathsTask.terminate()
|
||||
@loadPathsTask = null
|
||||
if @fuzzyFinderView?
|
||||
@fuzzyFinderView.cancel()
|
||||
@fuzzyFinderView.remove()
|
||||
@fuzzyFinderView = null
|
||||
@projectPaths = null
|
||||
|
||||
serialize: ->
|
||||
if @fuzzyFinderView?
|
||||
paths = {}
|
||||
for editSession in project.getEditSessions()
|
||||
path = editSession.getPath()
|
||||
paths[path] = editSession.lastOpened if path?
|
||||
paths
|
||||
|
||||
createView: ->
|
||||
unless @fuzzyFinderView
|
||||
@loadPathsTask?.terminate()
|
||||
FuzzyFinderView = require './fuzzy-finder-view'
|
||||
@fuzzyFinderView = new FuzzyFinderView(@projectPaths)
|
||||
@fuzzyFinderView
|
||||
@@ -1,58 +0,0 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore'
|
||||
Git = require 'git'
|
||||
|
||||
asyncCallsInProgress = 0
|
||||
pathsChunkSize = 100
|
||||
paths = []
|
||||
repo = null
|
||||
ignoredNames = null
|
||||
callback = null
|
||||
|
||||
isIgnored = (loadedPath) ->
|
||||
repo?.isPathIgnored(loadedPath) or _.indexOf(ignoredNames, path.basename(loadedPath), true) isnt -1
|
||||
|
||||
asyncCallStarting = ->
|
||||
asyncCallsInProgress++
|
||||
|
||||
asyncCallDone = ->
|
||||
if --asyncCallsInProgress is 0
|
||||
repo?.destroy()
|
||||
emit('load-paths:paths-found', paths)
|
||||
callback()
|
||||
|
||||
pathLoaded = (path) ->
|
||||
paths.push(path) unless isIgnored(path)
|
||||
if paths.length is pathsChunkSize
|
||||
emit('load-paths:paths-found', paths)
|
||||
paths = []
|
||||
|
||||
loadPath = (path) ->
|
||||
asyncCallStarting()
|
||||
fs.lstat path, (error, stats) ->
|
||||
unless error?
|
||||
if stats.isSymbolicLink()
|
||||
asyncCallStarting()
|
||||
fs.stat path, (error, stats) ->
|
||||
unless error?
|
||||
pathLoaded(path) if stats.isFile()
|
||||
asyncCallDone()
|
||||
else if stats.isDirectory()
|
||||
loadFolder(path) unless isIgnored(path)
|
||||
else if stats.isFile()
|
||||
pathLoaded(path)
|
||||
asyncCallDone()
|
||||
|
||||
loadFolder = (folderPath) ->
|
||||
asyncCallStarting()
|
||||
fs.readdir folderPath, (error, children=[]) ->
|
||||
loadPath(path.join(folderPath, childName)) for childName in children
|
||||
asyncCallDone()
|
||||
|
||||
module.exports = (rootPath, ignoreVcsIgnores, ignore) ->
|
||||
ignoredNames = ignore
|
||||
callback = @async()
|
||||
repo = Git.open(rootPath, refreshOnWindowFocus: false) if ignoreVcsIgnores
|
||||
ignoredNames.sort()
|
||||
loadFolder(rootPath)
|
||||
@@ -1,17 +0,0 @@
|
||||
Task = require 'task'
|
||||
|
||||
module.exports =
|
||||
startTask: (callback) ->
|
||||
projectPaths = []
|
||||
taskPath = require.resolve('./load-paths-handler')
|
||||
ignoredNames = config.get('fuzzyFinder.ignoredNames') ? []
|
||||
ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? [])
|
||||
ignoreVcsIgnores = config.get('core.excludeVcsIgnoredPaths')
|
||||
|
||||
task = Task.once taskPath, project.getPath(), ignoreVcsIgnores, ignoredNames, ->
|
||||
callback(projectPaths)
|
||||
|
||||
task.on 'load-paths:paths-found', (paths) =>
|
||||
projectPaths.push(paths...)
|
||||
|
||||
task
|
||||
@@ -1,2 +0,0 @@
|
||||
'main': './lib/fuzzy-finder'
|
||||
'description': 'Open an editor to a file in the project with `cmd-t`.'
|
||||
@@ -1,554 +0,0 @@
|
||||
RootView = require 'root-view'
|
||||
FuzzyFinder = require 'fuzzy-finder/lib/fuzzy-finder-view'
|
||||
PathLoader = require 'fuzzy-finder/lib/path-loader'
|
||||
_ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
|
||||
describe 'FuzzyFinder', ->
|
||||
[finderView] = []
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
rootView.enableKeymap()
|
||||
finderView = atom.activatePackage("fuzzy-finder").mainModule.createView()
|
||||
|
||||
describe "file-finder behavior", ->
|
||||
describe "toggling", ->
|
||||
describe "when the root view's project has a path", ->
|
||||
it "shows the FuzzyFinder or hides it and returns focus to the active editor if it already showing", ->
|
||||
rootView.attachToDom()
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
rootView.getActiveView().splitRight()
|
||||
[editor1, editor2] = rootView.getEditors()
|
||||
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).toExist()
|
||||
expect(finderView.miniEditor.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor2.isFocused).toBeFalsy()
|
||||
finderView.miniEditor.insertText('this should not show up next time we toggle')
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(finderView.miniEditor.getText()).toBe ''
|
||||
|
||||
it "shows all relative file paths for the current project and selects the first", ->
|
||||
rootView.attachToDom()
|
||||
finderView.maxItems = Infinity
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
paths = null
|
||||
expect(finderView.find(".loading")).toBeVisible()
|
||||
expect(finderView.find(".loading").text().length).toBeGreaterThan 0
|
||||
|
||||
waitsFor "all project paths to load", 5000, ->
|
||||
unless finderView.reloadProjectPaths
|
||||
paths = finderView.projectPaths
|
||||
true
|
||||
|
||||
runs ->
|
||||
expect(paths.length).toBeGreaterThan 0
|
||||
expect(finderView.list.children('li').length).toBe paths.length
|
||||
for filePath in paths
|
||||
expect(finderView.list.find("li:contains(#{path.basename(filePath)})")).toExist()
|
||||
expect(finderView.list.children().first()).toHaveClass 'selected'
|
||||
expect(finderView.find(".loading")).not.toBeVisible()
|
||||
|
||||
it "includes symlinked file paths", ->
|
||||
rootView.attachToDom()
|
||||
finderView.maxItems = Infinity
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
waitsFor "all project paths to load", 5000, ->
|
||||
not finderView.reloadProjectPaths
|
||||
|
||||
runs ->
|
||||
expect(finderView.list.find("li:contains(symlink-to-file)")).toExist()
|
||||
|
||||
it "excludes symlinked folder paths", ->
|
||||
rootView.attachToDom()
|
||||
finderView.maxItems = Infinity
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
waitsFor "all project paths to load", 5000, ->
|
||||
not finderView.reloadProjectPaths
|
||||
|
||||
runs ->
|
||||
expect(finderView.list.find("li:contains(symlink-to-dir)")).not.toExist()
|
||||
|
||||
describe "when root view's project has no path", ->
|
||||
beforeEach ->
|
||||
project.setPath(null)
|
||||
|
||||
it "does not open the FuzzyFinder", ->
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
|
||||
describe "when a path selection is confirmed", ->
|
||||
it "opens the file associated with that path in the editor", ->
|
||||
rootView.attachToDom()
|
||||
editor1 = rootView.getActiveView()
|
||||
editor2 = editor1.splitRight()
|
||||
expect(rootView.getActiveView()).toBe editor2
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
expectedPath = project.resolve('dir/a')
|
||||
finderView.confirmed({filePath: expectedPath})
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).not.toBe expectedPath
|
||||
expect(editor2.getPath()).toBe expectedPath
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the selected path is a directory", ->
|
||||
it "leaves the the tree view open, doesn't open the path in the editor, and displays an error", ->
|
||||
rootView.attachToDom()
|
||||
editorPath = rootView.getActiveView().getPath()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
finderView.confirmed({filePath: project.resolve('dir')})
|
||||
expect(finderView.hasParent()).toBeTruthy()
|
||||
expect(rootView.getActiveView().getPath()).toBe editorPath
|
||||
expect(finderView.error.text().length).toBeGreaterThan 0
|
||||
advanceClock(2000)
|
||||
expect(finderView.error.text().length).toBe 0
|
||||
|
||||
describe "buffer-finder behavior", ->
|
||||
describe "toggling", ->
|
||||
describe "when there are pane items with paths", ->
|
||||
beforeEach ->
|
||||
rootView.open('sample.txt')
|
||||
|
||||
it "shows the FuzzyFinder if it isn't showing, or hides it and returns focus to the active editor", ->
|
||||
rootView.attachToDom()
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
rootView.getActiveView().splitRight()
|
||||
[editor1, editor2] = rootView.getEditors()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).toExist()
|
||||
expect(rootView.find('.fuzzy-finder input:focus')).toExist()
|
||||
finderView.miniEditor.insertText('this should not show up next time we toggle')
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(finderView.miniEditor.getText()).toBe ''
|
||||
|
||||
it "lists the paths of the current items, sorted by most recently opened but with the current item last", ->
|
||||
rootView.attachToDom()
|
||||
rootView.open 'sample-with-tabs.coffee'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample.txt', 'sample.js', 'sample-with-tabs.coffee']
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
rootView.open 'sample.txt'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample-with-tabs.coffee', 'sample.js', 'sample.txt']
|
||||
expect(finderView.list.children().first()).toHaveClass 'selected'
|
||||
|
||||
it "serializes the list of paths and their last opened time", ->
|
||||
rootView.open 'sample-with-tabs.coffee'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
rootView.open 'sample.js'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
rootView.open()
|
||||
|
||||
atom.deactivatePackage('fuzzy-finder')
|
||||
states = _.map atom.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ]
|
||||
expect(states.length).toBe 3
|
||||
states = _.sortBy states, (path, time) -> -time
|
||||
|
||||
paths = [ 'sample-with-tabs.coffee', 'sample.txt', 'sample.js' ]
|
||||
|
||||
for [time, bufferPath] in states
|
||||
expect(_.last bufferPath.split '/').toBe paths.shift()
|
||||
expect(time).toBeGreaterThan 50000
|
||||
|
||||
describe "when there are only panes with anonymous items", ->
|
||||
it "does not open", ->
|
||||
rootView.getActivePane().remove()
|
||||
rootView.open()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
|
||||
describe "when there are no pane items", ->
|
||||
it "does not open", ->
|
||||
rootView.getActivePane().remove()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
|
||||
describe "when multiple sessions are opened on the same path", ->
|
||||
it "does not display duplicates for that path in the list", ->
|
||||
rootView.open 'sample.js'
|
||||
rootView.getActivePane().splitRight()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(_.pluck(finderView.list.find('li > div.file'), 'outerText')).toEqual ['sample.js']
|
||||
|
||||
describe "when a path selection is confirmed", ->
|
||||
[editor1, editor2] = []
|
||||
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
editor1 = rootView.getActiveView()
|
||||
editor2 = editor1.splitRight()
|
||||
expect(rootView.getActiveView()).toBe editor2
|
||||
rootView.open('sample.txt')
|
||||
editor2.trigger 'pane:show-previous-item'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
describe "when the active pane has an item for the selected path", ->
|
||||
it "switches to the item for the selected path", ->
|
||||
expectedPath = project.resolve('sample.txt')
|
||||
finderView.confirmed({filePath: expectedPath})
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).not.toBe expectedPath
|
||||
expect(editor2.getPath()).toBe expectedPath
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the active pane does not have an item for the selected path", ->
|
||||
it "adds a new item to the active pane for the selcted path", ->
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
editor1.focus()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
expect(rootView.getActiveView()).toBe editor1
|
||||
|
||||
expectedPath = project.resolve('sample.txt')
|
||||
finderView.confirmed({filePath: expectedPath})
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).toBe expectedPath
|
||||
expect(editor1.isFocused).toBeTruthy()
|
||||
|
||||
describe "git-status-finder behavior", ->
|
||||
[originalText, originalPath, newPath] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = rootView.getActiveView()
|
||||
originalText = editor.getText()
|
||||
originalPath = editor.getPath()
|
||||
fsUtils.writeSync(originalPath, 'making a change for the better')
|
||||
git.getPathStatus(originalPath)
|
||||
|
||||
newPath = project.resolve('newsample.js')
|
||||
fsUtils.writeSync(newPath, '')
|
||||
git.getPathStatus(newPath)
|
||||
|
||||
afterEach ->
|
||||
fsUtils.writeSync(originalPath, originalText)
|
||||
fsUtils.remove(newPath) if fsUtils.exists(newPath)
|
||||
|
||||
it "displays all new and modified paths", ->
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
rootView.trigger 'fuzzy-finder:toggle-git-status-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).toExist()
|
||||
|
||||
expect(finderView.find('.file').length).toBe 2
|
||||
|
||||
expect(finderView.find('.status.modified').length).toBe 1
|
||||
expect(finderView.find('.status.new').length).toBe 1
|
||||
|
||||
describe "common behavior between file and buffer finder", ->
|
||||
describe "when the fuzzy finder is cancelled", ->
|
||||
describe "when an editor is open", ->
|
||||
it "detaches the finder and focuses the previously focused element", ->
|
||||
rootView.attachToDom()
|
||||
activeEditor = rootView.getActiveView()
|
||||
activeEditor.focus()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(finderView.hasParent()).toBeTruthy()
|
||||
expect(activeEditor.isFocused).toBeFalsy()
|
||||
expect(finderView.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
finderView.cancel()
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(activeEditor.isFocused).toBeTruthy()
|
||||
expect(finderView.miniEditor.isFocused).toBeFalsy()
|
||||
|
||||
describe "when no editors are open", ->
|
||||
it "detaches the finder and focuses the previously focused element", ->
|
||||
rootView.attachToDom()
|
||||
rootView.getActivePane().remove()
|
||||
|
||||
inputView = $$ -> @input()
|
||||
rootView.append(inputView)
|
||||
inputView.focus()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(finderView.hasParent()).toBeTruthy()
|
||||
expect(finderView.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
finderView.cancel()
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(document.activeElement).toBe inputView[0]
|
||||
expect(finderView.miniEditor.isFocused).toBeFalsy()
|
||||
|
||||
describe "cached file paths", ->
|
||||
it "caches file paths after first time", ->
|
||||
spyOn(PathLoader, "startTask").andCallThrough()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(PathLoader.startTask).toHaveBeenCalled()
|
||||
PathLoader.startTask.reset()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(PathLoader.startTask).not.toHaveBeenCalled()
|
||||
|
||||
it "doesn't cache buffer paths", ->
|
||||
spyOn(project, "getEditSessions").andCallThrough()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(project.getEditSessions).toHaveBeenCalled()
|
||||
project.getEditSessions.reset()
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(project.getEditSessions).toHaveBeenCalled()
|
||||
|
||||
it "busts the cache when the window gains focus", ->
|
||||
spyOn(PathLoader, "startTask").andCallThrough()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(PathLoader.startTask).toHaveBeenCalled()
|
||||
PathLoader.startTask.reset()
|
||||
$(window).trigger 'focus'
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
expect(PathLoader.startTask).toHaveBeenCalled()
|
||||
|
||||
describe "path ignoring", ->
|
||||
it "ignores paths that match entries in config.fuzzyFinder.ignoredNames", ->
|
||||
config.set("fuzzyFinder.ignoredNames", ["tree-view.js"])
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
finderView.maxItems = Infinity
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(finderView.list.find("li:contains(tree-view.js)")).not.toExist()
|
||||
|
||||
describe "when core.excludeVcsIgnoredPaths is set to true", ->
|
||||
ignoreFile = null
|
||||
|
||||
beforeEach ->
|
||||
ignoreFile = path.join(project.getPath(), '.gitignore')
|
||||
fsUtils.writeSync(ignoreFile, 'sample.js')
|
||||
config.set("core.excludeVcsIgnoredPaths", true)
|
||||
|
||||
afterEach ->
|
||||
fsUtils.remove(ignoreFile) if fsUtils.exists(ignoreFile)
|
||||
|
||||
it "ignores paths that are git ignored", ->
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
finderView.maxItems = Infinity
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(finderView.list.find("li:contains(sample.js)")).not.toExist()
|
||||
|
||||
describe "fuzzy find by content under cursor", ->
|
||||
editor = null
|
||||
|
||||
beforeEach ->
|
||||
editor = rootView.getActiveView()
|
||||
rootView.attachToDom()
|
||||
|
||||
it "opens the fuzzy finder window when there are multiple matches", ->
|
||||
editor.setText("sample")
|
||||
rootView.trigger 'fuzzy-finder:find-under-cursor'
|
||||
|
||||
waitsFor ->
|
||||
finderView.list.children('li').length > 0
|
||||
|
||||
runs ->
|
||||
expect(finderView).toBeVisible()
|
||||
expect(rootView.find('.fuzzy-finder input:focus')).toExist()
|
||||
|
||||
it "opens a file directly when there is a single match", ->
|
||||
editor.setText("sample.txt")
|
||||
rootView.trigger 'fuzzy-finder:find-under-cursor'
|
||||
|
||||
openedPath = null
|
||||
spyOn(rootView, "open").andCallFake (path) ->
|
||||
openedPath = path
|
||||
|
||||
waitsFor ->
|
||||
openedPath != null
|
||||
|
||||
runs ->
|
||||
expect(finderView).not.toBeVisible()
|
||||
expect(openedPath).toBe project.resolve("sample.txt")
|
||||
|
||||
it "displays an error when the word under the cursor doesn't match any files", ->
|
||||
editor.setText("moogoogaipan")
|
||||
editor.setCursorBufferPosition([0,5])
|
||||
|
||||
rootView.trigger 'fuzzy-finder:find-under-cursor'
|
||||
|
||||
waitsFor ->
|
||||
finderView.is(':visible')
|
||||
|
||||
runs ->
|
||||
expect(finderView.error.text().length).toBeGreaterThan 0
|
||||
|
||||
it "displays error when there is no word under the cursor", ->
|
||||
editor.setText("&&&&&&&&&&&&&&& sample")
|
||||
editor.setCursorBufferPosition([0,5])
|
||||
|
||||
rootView.trigger 'fuzzy-finder:find-under-cursor'
|
||||
|
||||
waitsFor ->
|
||||
finderView.is(':visible')
|
||||
|
||||
runs ->
|
||||
expect(finderView.error.text().length).toBeGreaterThan 0
|
||||
|
||||
describe "opening a path into a split", ->
|
||||
it "opens the path by splitting the active editor left", ->
|
||||
expect(rootView.getPanes().length).toBe 1
|
||||
pane = rootView.getActivePane()
|
||||
spyOn(pane, "splitLeft").andCallThrough()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
{filePath} = finderView.getSelectedElement()
|
||||
finderView.miniEditor.trigger 'pane:split-left'
|
||||
|
||||
expect(rootView.getPanes().length).toBe 2
|
||||
expect(pane.splitLeft).toHaveBeenCalled()
|
||||
expect(rootView.getActiveView().getPath()).toBe project.resolve(filePath)
|
||||
|
||||
it "opens the path by splitting the active editor right", ->
|
||||
expect(rootView.getPanes().length).toBe 1
|
||||
pane = rootView.getActivePane()
|
||||
spyOn(pane, "splitRight").andCallThrough()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
{filePath} = finderView.getSelectedElement()
|
||||
finderView.miniEditor.trigger 'pane:split-right'
|
||||
|
||||
expect(rootView.getPanes().length).toBe 2
|
||||
expect(pane.splitRight).toHaveBeenCalled()
|
||||
expect(rootView.getActiveView().getPath()).toBe project.resolve(filePath)
|
||||
|
||||
it "opens the path by splitting the active editor up", ->
|
||||
expect(rootView.getPanes().length).toBe 1
|
||||
pane = rootView.getActivePane()
|
||||
spyOn(pane, "splitUp").andCallThrough()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
{filePath} = finderView.getSelectedElement()
|
||||
finderView.miniEditor.trigger 'pane:split-up'
|
||||
|
||||
expect(rootView.getPanes().length).toBe 2
|
||||
expect(pane.splitUp).toHaveBeenCalled()
|
||||
expect(rootView.getActiveView().getPath()).toBe project.resolve(filePath)
|
||||
|
||||
it "opens the path by splitting the active editor down", ->
|
||||
expect(rootView.getPanes().length).toBe 1
|
||||
pane = rootView.getActivePane()
|
||||
spyOn(pane, "splitDown").andCallThrough()
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
{filePath} = finderView.getSelectedElement()
|
||||
finderView.miniEditor.trigger 'pane:split-down'
|
||||
|
||||
expect(rootView.getPanes().length).toBe 2
|
||||
expect(pane.splitDown).toHaveBeenCalled()
|
||||
expect(rootView.getActiveView().getPath()).toBe project.resolve(filePath)
|
||||
|
||||
describe "git status decorations", ->
|
||||
[originalText, originalPath, editor, newPath] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = rootView.getActiveView()
|
||||
originalText = editor.getText()
|
||||
originalPath = editor.getPath()
|
||||
newPath = project.resolve('newsample.js')
|
||||
fsUtils.writeSync(newPath, '')
|
||||
|
||||
afterEach ->
|
||||
fsUtils.writeSync(originalPath, originalText)
|
||||
fsUtils.remove(newPath) if fsUtils.exists(newPath)
|
||||
|
||||
describe "when a modified file is shown in the list", ->
|
||||
it "displays the modified icon", ->
|
||||
editor.setText('modified')
|
||||
editor.activeEditSession.save()
|
||||
git.getPathStatus(editor.getPath())
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(finderView.find('.status.modified').length).toBe 1
|
||||
expect(finderView.find('.status.modified').closest('li').find('.file').text()).toBe 'sample.js'
|
||||
|
||||
|
||||
describe "when a new file is shown in the list", ->
|
||||
it "displays the new icon", ->
|
||||
rootView.open('newsample.js')
|
||||
git.getPathStatus(editor.getPath())
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(finderView.find('.status.new').length).toBe 1
|
||||
expect(finderView.find('.status.new').closest('li').find('.file').text()).toBe 'newsample.js'
|
||||
|
||||
describe "when the filter text contains a colon followed by a number", ->
|
||||
it "opens the selected path to that line number", ->
|
||||
rootView.attachToDom()
|
||||
expect(rootView.find('.fuzzy-finder')).not.toExist()
|
||||
[editor] = rootView.getEditors()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).toExist()
|
||||
finderView.miniEditor.insertText(':4')
|
||||
finderView.trigger 'core:confirm'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [3, 4]
|
||||
|
||||
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
|
||||
expect(rootView.find('.fuzzy-finder')).toExist()
|
||||
finderView.miniEditor.insertText(':10')
|
||||
finderView.miniEditor.trigger 'pane:split-left'
|
||||
|
||||
expect(rootView.getActiveView()).not.toBe editor
|
||||
expect(rootView.getActiveView().getCursorBufferPosition()).toEqual [9, 2]
|
||||
@@ -1,67 +0,0 @@
|
||||
@import "octicon-utf-codes.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
.fuzzy-finder {
|
||||
|
||||
&.select-list li {
|
||||
padding: 5px 10px 5px 10px;
|
||||
}
|
||||
|
||||
.path {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
margin-left: 26px;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.status {
|
||||
.icon(16px);
|
||||
margin-left: 5px;
|
||||
color: #9d9d9d;
|
||||
float: right;
|
||||
|
||||
&.new:before {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
content: @diff-added;
|
||||
}
|
||||
|
||||
&.modified:before {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
content: @diff-modified;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
&:before {
|
||||
.icon(16px);
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
color: #9d9d9d;
|
||||
}
|
||||
|
||||
&.text-name:before {
|
||||
content: @file-text;
|
||||
}
|
||||
|
||||
&.image-name:before {
|
||||
content: @file-media;
|
||||
}
|
||||
|
||||
&.compressed-name:before {
|
||||
content: @file-zip;
|
||||
}
|
||||
|
||||
&.pdf-name:before {
|
||||
content: @file-pdf;
|
||||
}
|
||||
|
||||
&.readme-name:before {
|
||||
content: @book;
|
||||
}
|
||||
|
||||
&.binary-name:before {
|
||||
content: @file-binary;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user