mirror of
https://github.com/atom/atom.git
synced 2026-02-12 23:55:10 -05:00
Extensions have a src and specs directory now. Move existing extension specs.
Move the extensions spec code inside of the extension's spec directory. Move source code to the extension's src directory
This commit is contained in:
@@ -1 +1 @@
|
||||
module.exports = require 'tree-view/tree-view'
|
||||
module.exports = require 'tree-view/src/tree-view'
|
||||
|
||||
835
src/extensions/tree-view/spec/tree-view-spec.coffee
Normal file
835
src/extensions/tree-view/spec/tree-view-spec.coffee
Normal file
@@ -0,0 +1,835 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
TreeView = require 'tree-view'
|
||||
RootView = require 'root-view'
|
||||
Directory = require 'directory'
|
||||
Native = require 'native'
|
||||
fs = require 'fs'
|
||||
|
||||
describe "TreeView", ->
|
||||
[rootView, project, treeView, sampleJs, sampleTxt] = []
|
||||
|
||||
beforeEach ->
|
||||
rootView = new RootView(require.resolve('fixtures/tree-view'))
|
||||
project = rootView.project
|
||||
|
||||
rootView.activateExtension(TreeView)
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
treeView.root = treeView.find('> li:first').view()
|
||||
sampleJs = treeView.find('.file:contains(tree-view.js)')
|
||||
sampleTxt = treeView.find('.file:contains(tree-view.txt)')
|
||||
|
||||
expect(treeView.root.directory.subscriptionCount()).toBeGreaterThan 0
|
||||
|
||||
afterEach ->
|
||||
rootView.deactivate()
|
||||
|
||||
describe ".initialize(project)", ->
|
||||
it "renders the root of the project and its contents alphabetically with subdirectories first in a collapsed state", ->
|
||||
expect(treeView.root.find('> .header .disclosure-arrow')).toHaveText('▾')
|
||||
expect(treeView.root.find('> .header .name')).toHaveText('tree-view/')
|
||||
|
||||
rootEntries = treeView.root.find('.entries')
|
||||
subdir0 = rootEntries.find('> li:eq(0)')
|
||||
expect(subdir0.find('.disclosure-arrow')).toHaveText('▸')
|
||||
expect(subdir0.find('.name')).toHaveText('dir1/')
|
||||
expect(subdir0.find('.entries')).not.toExist()
|
||||
|
||||
subdir2 = rootEntries.find('> li:eq(1)')
|
||||
expect(subdir2.find('.disclosure-arrow')).toHaveText('▸')
|
||||
expect(subdir2.find('.name')).toHaveText('dir2/')
|
||||
expect(subdir2.find('.entries')).not.toExist()
|
||||
|
||||
expect(rootEntries.find('> .file:contains(tree-view.js)')).toExist()
|
||||
expect(rootEntries.find('> .file:contains(tree-view.txt)')).toExist()
|
||||
|
||||
it "selects the rootview", ->
|
||||
expect(treeView.selectedEntry()).toEqual treeView.root
|
||||
|
||||
describe "when the project has no path", ->
|
||||
beforeEach ->
|
||||
rootView.deactivate()
|
||||
|
||||
rootView = new RootView
|
||||
rootView.activateExtension(TreeView)
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
|
||||
it "does not create a root node", ->
|
||||
expect(treeView.root).not.toExist()
|
||||
|
||||
it "serializes without throwing an exception", ->
|
||||
expect(-> treeView.serialize()).not.toThrow()
|
||||
|
||||
it "creates a root view when the project path is created", ->
|
||||
rootView.open(require.resolve('fixtures/sample.js'))
|
||||
expect(treeView.root.getPath()).toBe require.resolve('fixtures')
|
||||
expect(treeView.root.parent()).toMatchSelector(".tree-view")
|
||||
|
||||
oldRoot = treeView.root
|
||||
|
||||
rootView.project.setPath('/tmp')
|
||||
expect(treeView.root).not.toEqual oldRoot
|
||||
expect(oldRoot.hasParent()).toBeFalsy()
|
||||
|
||||
describe "when the prototypes deactivate method is called", ->
|
||||
it "calls the deactivate on tree view instance", ->
|
||||
spyOn(treeView, "deactivate").andCallThrough()
|
||||
rootView.deactivateExtension(TreeView)
|
||||
expect(treeView.deactivate).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
[newRootView, newTreeView] = []
|
||||
|
||||
afterEach ->
|
||||
newRootView?.deactivate()
|
||||
|
||||
it "restores expanded directories and selected file when deserialized", ->
|
||||
treeView.find('.directory:contains(dir1)').click()
|
||||
sampleJs.click()
|
||||
newRootView = RootView.deserialize(rootView.serialize())
|
||||
rootView.deactivate() # Deactivates previous TreeView
|
||||
|
||||
newRootView.activateExtension(TreeView)
|
||||
|
||||
newTreeView = newRootView.find(".tree-view").view()
|
||||
|
||||
expect(newTreeView).toExist()
|
||||
expect(newTreeView.selectedEntry()).toMatchSelector(".file:contains(tree-view.js)")
|
||||
expect(newTreeView.find(".directory:contains(dir1)")).toHaveClass("expanded")
|
||||
|
||||
it "restores the focus state of the tree view", ->
|
||||
rootView.attachToDom()
|
||||
treeView.focus()
|
||||
expect(treeView).toMatchSelector ':focus'
|
||||
|
||||
newRootView = RootView.deserialize(rootView.serialize())
|
||||
rootView.deactivate() # Deactivates previous TreeView
|
||||
|
||||
newRootView.attachToDom()
|
||||
newRootView.activateExtension(TreeView)
|
||||
|
||||
newTreeView = newRootView.find(".tree-view").view()
|
||||
expect(newTreeView).toMatchSelector ':focus'
|
||||
|
||||
it "restores the scroll top when toggled", ->
|
||||
rootView.height(5)
|
||||
rootView.attachToDom()
|
||||
expect(treeView).toBeVisible()
|
||||
treeView.focus()
|
||||
|
||||
treeView.scrollTop(10)
|
||||
expect(treeView.scrollTop()).toBe(10)
|
||||
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
expect(treeView).toBeHidden()
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
expect(treeView).toBeVisible()
|
||||
expect(treeView.scrollTop()).toBe(10)
|
||||
|
||||
describe "when tree-view:toggle is triggered on the root view", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when the tree view is visible", ->
|
||||
beforeEach ->
|
||||
expect(treeView).toBeVisible()
|
||||
|
||||
describe "when the tree view is focused", ->
|
||||
it "hides the tree view", ->
|
||||
treeView.focus()
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
expect(treeView).toBeHidden()
|
||||
|
||||
describe "when the tree view is not focused", ->
|
||||
it "shifts focus to the tree view", ->
|
||||
rootView.open() # When we call focus below, we want an editor to become focused
|
||||
rootView.focus()
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
expect(treeView).toBeVisible()
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
|
||||
describe "when the tree view is hidden", ->
|
||||
it "shows and focuses the tree view", ->
|
||||
treeView.detach()
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
|
||||
describe "when tree-view:reveal-current-file is triggered on the root view", ->
|
||||
beforeEach ->
|
||||
treeView.detach()
|
||||
spyOn(treeView, 'focus')
|
||||
|
||||
describe "if the current file has a path", ->
|
||||
it "shows and focuses the tree view and selects the file", ->
|
||||
rootView.open('dir1/file1')
|
||||
rootView.trigger 'tree-view:reveal-active-file'
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView.focus).toHaveBeenCalled()
|
||||
expect(treeView.selectedEntry().getPath()).toMatch /dir1\/file1$/
|
||||
|
||||
describe "if the current file has no path", ->
|
||||
it "shows and focuses the tree view, but does not attempt to select a specific file", ->
|
||||
rootView.open()
|
||||
expect(rootView.getActiveEditSession().getPath()).toBeUndefined()
|
||||
rootView.trigger 'tree-view:reveal-active-file'
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView.focus).toHaveBeenCalled()
|
||||
|
||||
describe "if there is no editor open", ->
|
||||
it "shows and focuses the tree view, but does not attempt to select a specific file", ->
|
||||
expect(rootView.getActiveEditSession()).toBeUndefined()
|
||||
rootView.trigger 'tree-view:reveal-active-file'
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when tool-panel:unfocus is triggered on the tree view", ->
|
||||
it "surrenders focus to the root view but remains open", ->
|
||||
rootView.open() # When we trigger 'tool-panel:unfocus' below, we want an editor to become focused
|
||||
rootView.attachToDom()
|
||||
treeView.focus()
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
treeView.trigger 'tool-panel:unfocus'
|
||||
expect(treeView).toBeVisible()
|
||||
expect(treeView).not.toMatchSelector(':focus')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "when a directory's disclosure arrow is clicked", ->
|
||||
it "expands / collapses the associated directory", ->
|
||||
subdir = treeView.root.find('.entries > li:contains(dir1/)').view()
|
||||
|
||||
expect(subdir.disclosureArrow).toHaveText('▸')
|
||||
expect(subdir.find('.entries')).not.toExist()
|
||||
|
||||
subdir.disclosureArrow.click()
|
||||
|
||||
expect(subdir.disclosureArrow).toHaveText('▾')
|
||||
expect(subdir.find('.entries')).toExist()
|
||||
|
||||
subdir.disclosureArrow.click()
|
||||
expect(subdir.disclosureArrow).toHaveText('▸')
|
||||
expect(subdir.find('.entries')).not.toExist()
|
||||
|
||||
it "restores the expansion state of descendant directories", ->
|
||||
child = treeView.root.find('.entries > li:contains(dir1/)').view()
|
||||
child.disclosureArrow.click()
|
||||
|
||||
grandchild = child.find('.entries > li:contains(sub-dir1/)').view()
|
||||
grandchild.disclosureArrow.click()
|
||||
|
||||
treeView.root.disclosureArrow.click()
|
||||
expect(treeView.root.find('.entries')).not.toExist()
|
||||
treeView.root.disclosureArrow.click()
|
||||
|
||||
# previously expanded descendants remain expanded
|
||||
expect(treeView.root.find('> .entries > li:contains(dir1/) > .entries > li:contains(sub-dir1/) > .entries').length).toBe 1
|
||||
|
||||
# collapsed descendants remain collapsed
|
||||
expect(treeView.root.find('> .entries > li.contains(dir2/) > .entries')).not.toExist()
|
||||
|
||||
it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", ->
|
||||
child = treeView.root.entries.find('li:contains(dir1/)').view()
|
||||
child.disclosureArrow.click()
|
||||
|
||||
grandchild = child.entries.find('li:contains(sub-dir1/)').view()
|
||||
grandchild.disclosureArrow.click()
|
||||
|
||||
expect(treeView.root.directory.subscriptionCount()).toBe 1
|
||||
expect(child.directory.subscriptionCount()).toBe 1
|
||||
expect(grandchild.directory.subscriptionCount()).toBe 1
|
||||
|
||||
treeView.root.disclosureArrow.click()
|
||||
|
||||
expect(treeView.root.directory.subscriptionCount()).toBe 0
|
||||
expect(child.directory.subscriptionCount()).toBe 0
|
||||
expect(grandchild.directory.subscriptionCount()).toBe 0
|
||||
|
||||
describe "when a file is single-clicked", ->
|
||||
it "selects the files and opens it in the active editor, without changing focus", ->
|
||||
expect(rootView.getActiveEditor()).toBeUndefined()
|
||||
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(sampleJs).toHaveClass 'selected'
|
||||
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
|
||||
sampleTxt.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(sampleTxt).toHaveClass 'selected'
|
||||
expect(treeView.find('.selected').length).toBe 1
|
||||
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.txt')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
|
||||
describe "when a file is double-clicked", ->
|
||||
it "selects the file and opens it in the active editor on the first click, then changes focus to the active editor on the second", ->
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(sampleJs).toHaveClass 'selected'
|
||||
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 2 })
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "when a directory is single-clicked", ->
|
||||
it "is selected", ->
|
||||
subdir = treeView.root.find('.directory:first').view()
|
||||
subdir.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(subdir).toHaveClass 'selected'
|
||||
|
||||
describe "when a directory is double-clicked", ->
|
||||
it "toggles the directory expansion state and does not change the focus to the editor", ->
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
subdir = treeView.root.find('.directory:first').view()
|
||||
subdir.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(subdir).toHaveClass 'selected'
|
||||
subdir.trigger clickEvent(originalEvent: { detail: 2 })
|
||||
expect(subdir).toHaveClass 'expanded'
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
|
||||
describe "when a new file is opened in the active editor", ->
|
||||
it "is selected in the tree view if the file's entry visible", ->
|
||||
sampleJs.click()
|
||||
rootView.open(require.resolve('fixtures/tree-view/tree-view.txt'))
|
||||
|
||||
expect(sampleTxt).toHaveClass 'selected'
|
||||
expect(treeView.find('.selected').length).toBe 1
|
||||
|
||||
it "selected a file's parent dir if the file's entry is not visible", ->
|
||||
rootView.open(require.resolve('fixtures/tree-view/dir1/sub-dir1/sub-file1'))
|
||||
|
||||
dirView = treeView.root.find('.directory:contains(dir1)').view()
|
||||
expect(dirView).toHaveClass 'selected'
|
||||
|
||||
describe "when a different editor becomes active", ->
|
||||
it "selects the file in that is open in that editor", ->
|
||||
sampleJs.click()
|
||||
leftEditor = rootView.getActiveEditor()
|
||||
rightEditor = leftEditor.splitRight()
|
||||
sampleTxt.click()
|
||||
|
||||
expect(sampleTxt).toHaveClass('selected')
|
||||
leftEditor.focus()
|
||||
expect(sampleJs).toHaveClass('selected')
|
||||
|
||||
describe "keyboard navigation", ->
|
||||
afterEach ->
|
||||
expect(treeView.find('.selected').length).toBeLessThan 2
|
||||
|
||||
describe "core:move-down", ->
|
||||
describe "when a collapsed directory is selected", ->
|
||||
it "skips to the next directory", ->
|
||||
treeView.root.find('.directory:eq(0)').click()
|
||||
treeView.trigger 'core:move-down'
|
||||
expect(treeView.root.find('.directory:eq(1)')).toHaveClass 'selected'
|
||||
|
||||
describe "when an expanded directory is selected", ->
|
||||
it "selects the first entry of the directory", ->
|
||||
subdir = treeView.root.find('.directory:eq(1)').view()
|
||||
subdir.expand()
|
||||
subdir.click()
|
||||
|
||||
treeView.trigger 'core:move-down'
|
||||
|
||||
expect(subdir.entries.find('.entry:first')).toHaveClass 'selected'
|
||||
|
||||
describe "when the last entry of an expanded directory is selected", ->
|
||||
it "selects the entry after its parent directory", ->
|
||||
subdir1 = treeView.root.find('.directory:eq(1)').view()
|
||||
subdir1.expand()
|
||||
subdir1.entries.find('.entry:last').click()
|
||||
|
||||
treeView.trigger 'core:move-down'
|
||||
|
||||
expect(treeView.root.find('.entries > .entry:eq(2)')).toHaveClass 'selected'
|
||||
|
||||
describe "when the last directory of another last directory is selected", ->
|
||||
[nested, nested2] = []
|
||||
|
||||
beforeEach ->
|
||||
nested = treeView.root.find('.directory:eq(2)').view()
|
||||
expect(nested.find('.header').text()).toContain 'nested/'
|
||||
nested.expand()
|
||||
nested2 = nested.entries.find('.entry:last').view()
|
||||
nested2.click()
|
||||
|
||||
describe "when the directory is collapsed", ->
|
||||
it "selects the entry after its grandparent directory", ->
|
||||
treeView.trigger 'core:move-down'
|
||||
expect(nested.next()).toHaveClass 'selected'
|
||||
|
||||
describe "when the directory is expanded", ->
|
||||
it "selects the entry after its grandparent directory", ->
|
||||
nested2.expand()
|
||||
nested2.find('.file').remove() # kill the .gitkeep file, which has to be there but screws the test
|
||||
treeView.trigger 'core:move-down'
|
||||
expect(nested.next()).toHaveClass 'selected'
|
||||
|
||||
describe "when the last entry of the last directory is selected", ->
|
||||
it "does not change the selection", ->
|
||||
lastEntry = treeView.root.find('> .entries .entry:last')
|
||||
lastEntry.click()
|
||||
|
||||
treeView.trigger 'core:move-down'
|
||||
|
||||
expect(lastEntry).toHaveClass 'selected'
|
||||
|
||||
describe "core:move-up", ->
|
||||
describe "when there is an expanded directory before the currently selected entry", ->
|
||||
it "selects the last entry in the expanded directory", ->
|
||||
lastDir = treeView.root.find('.directory:last').view()
|
||||
fileAfterDir = lastDir.next().view()
|
||||
lastDir.expand()
|
||||
fileAfterDir.click()
|
||||
|
||||
treeView.trigger 'core:move-up'
|
||||
expect(lastDir.find('.entry:last')).toHaveClass 'selected'
|
||||
|
||||
describe "when there is an entry before the currently selected entry", ->
|
||||
it "selects the previous entry", ->
|
||||
lastEntry = treeView.root.find('.entry:last')
|
||||
lastEntry.click()
|
||||
|
||||
treeView.trigger 'core:move-up'
|
||||
|
||||
expect(lastEntry.prev()).toHaveClass 'selected'
|
||||
|
||||
describe "when there is no entry before the currently selected entry, but there is a parent directory", ->
|
||||
it "selects the parent directory", ->
|
||||
subdir = treeView.root.find('.directory:first').view()
|
||||
subdir.expand()
|
||||
subdir.find('> .entries > .entry:first').click()
|
||||
|
||||
treeView.trigger 'core:move-up'
|
||||
|
||||
expect(subdir).toHaveClass 'selected'
|
||||
|
||||
describe "when there is no parent directory or previous entry", ->
|
||||
it "does not change the selection", ->
|
||||
treeView.root.click()
|
||||
treeView.trigger 'core:move-up'
|
||||
expect(treeView.root).toHaveClass 'selected'
|
||||
|
||||
describe "core:move-to-top", ->
|
||||
it "scrolls to the top", ->
|
||||
treeView.height(100)
|
||||
treeView.attachToDom()
|
||||
$(element).view().expand() for element in treeView.find('.directory')
|
||||
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
|
||||
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
|
||||
entryCount = treeView.find(".entry").length
|
||||
_.times entryCount, -> treeView.moveDown()
|
||||
expect(treeView.scrollTop()).toBeGreaterThan 0
|
||||
|
||||
treeView.trigger 'core:move-to-top'
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
|
||||
describe "core:move-to-bottom", ->
|
||||
it "scrolls to the bottom", ->
|
||||
treeView.height(100)
|
||||
treeView.attachToDom()
|
||||
$(element).view().expand() for element in treeView.find('.directory')
|
||||
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
|
||||
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
treeView.trigger 'core:move-to-bottom'
|
||||
expect(treeView.scrollBottom()).toBe treeView.prop('scrollHeight')
|
||||
|
||||
describe "core:page-up", ->
|
||||
it "scrolls up a page", ->
|
||||
treeView.height(5)
|
||||
treeView.attachToDom()
|
||||
$(element).view().expand() for element in treeView.find('.directory')
|
||||
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
|
||||
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
treeView.scrollToBottom()
|
||||
scrollTop = treeView.scrollTop()
|
||||
expect(scrollTop).toBeGreaterThan 0
|
||||
|
||||
treeView.trigger 'core:page-up'
|
||||
expect(treeView.scrollTop()).toBe scrollTop - treeView.height()
|
||||
|
||||
describe "core:page-down", ->
|
||||
it "scrolls down a page", ->
|
||||
treeView.height(5)
|
||||
treeView.attachToDom()
|
||||
$(element).view().expand() for element in treeView.find('.directory')
|
||||
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
|
||||
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
treeView.trigger 'core:page-down'
|
||||
expect(treeView.scrollTop()).toBe treeView.height()
|
||||
|
||||
describe "movement outside of viewable region", ->
|
||||
it "scrolls the tree view to the selected item", ->
|
||||
treeView.height(100)
|
||||
treeView.attachToDom()
|
||||
$(element).view().expand() for element in treeView.find('.directory')
|
||||
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
|
||||
|
||||
treeView.moveDown()
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
|
||||
entryCount = treeView.find(".entry").length
|
||||
_.times entryCount, -> treeView.moveDown()
|
||||
expect(treeView.scrollBottom()).toBe treeView.prop('scrollHeight')
|
||||
|
||||
_.times entryCount, -> treeView.moveUp()
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
|
||||
describe "tree-view:expand-directory", ->
|
||||
describe "when a directory entry is selected", ->
|
||||
it "expands the current directory", ->
|
||||
subdir = treeView.root.find('.directory:first')
|
||||
subdir.click()
|
||||
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
treeView.trigger 'tree-view:expand-directory'
|
||||
expect(subdir).toHaveClass 'expanded'
|
||||
|
||||
describe "when a file entry is selected", ->
|
||||
it "does nothing", ->
|
||||
treeView.root.find('.file').click()
|
||||
treeView.trigger 'tree-view:expand-directory'
|
||||
|
||||
describe "tree-view:collapse-directory", ->
|
||||
subdir = null
|
||||
|
||||
beforeEach ->
|
||||
subdir = treeView.root.find('> .entries > .directory').eq(0).view()
|
||||
subdir.expand()
|
||||
|
||||
describe "when an expanded directory is selected", ->
|
||||
it "collapses the selected directory", ->
|
||||
expect(subdir).toHaveClass 'expanded'
|
||||
|
||||
subdir.click()
|
||||
treeView.trigger 'tree-view:collapse-directory'
|
||||
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
expect(treeView.root).toHaveClass 'expanded'
|
||||
|
||||
describe "when a collapsed directory is selected", ->
|
||||
it "collapses and selects the selected directory's parent directory", ->
|
||||
subdir.find('.directory').click()
|
||||
treeView.trigger 'tree-view:collapse-directory'
|
||||
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
expect(subdir).toHaveClass 'selected'
|
||||
expect(treeView.root).toHaveClass 'expanded'
|
||||
|
||||
describe "when collapsed root directory is selected", ->
|
||||
it "does not raise an error", ->
|
||||
treeView.root.collapse()
|
||||
treeView.selectEntry(treeView.root)
|
||||
|
||||
treeView.trigger 'tree-view:collapse-directory'
|
||||
|
||||
describe "when a file is selected", ->
|
||||
it "collapses and selects the selected file's parent directory", ->
|
||||
subdir.find('.file').click()
|
||||
treeView.trigger 'tree-view:collapse-directory'
|
||||
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
expect(subdir).toHaveClass 'selected'
|
||||
expect(treeView.root).toHaveClass 'expanded'
|
||||
|
||||
describe "tree-view:open-selected-entry", ->
|
||||
describe "when a file is selected", ->
|
||||
it "opens the file in the editor and focuses it", ->
|
||||
treeView.root.find('.file:contains(tree-view.js)').click()
|
||||
treeView.root.trigger 'tree-view:open-selected-entry'
|
||||
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "when a directory is selected", ->
|
||||
it "expands or collapses the directory", ->
|
||||
subdir = treeView.root.find('.directory').first()
|
||||
subdir.click()
|
||||
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
treeView.root.trigger 'tree-view:open-selected-entry'
|
||||
expect(subdir).toHaveClass 'expanded'
|
||||
treeView.root.trigger 'tree-view:open-selected-entry'
|
||||
expect(subdir).not.toHaveClass 'expanded'
|
||||
|
||||
describe "when nothing is selected", ->
|
||||
it "does nothing", ->
|
||||
treeView.root.trigger 'tree-view:open-selected-entry'
|
||||
expect(rootView.getActiveEditor()).toBeUndefined()
|
||||
|
||||
describe "file modification", ->
|
||||
[dirView, fileView, rootDirPath, dirPath, filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
rootView.deactivate()
|
||||
|
||||
rootDirPath = "/tmp/atom-tests"
|
||||
fs.remove(rootDirPath) if fs.exists(rootDirPath)
|
||||
|
||||
dirPath = fs.join(rootDirPath, "test-dir")
|
||||
filePath = fs.join(dirPath, "test-file.txt")
|
||||
fs.makeDirectory(rootDirPath)
|
||||
fs.makeDirectory(dirPath)
|
||||
fs.write(filePath, "doesn't matter")
|
||||
|
||||
rootView = new RootView(rootDirPath)
|
||||
project = rootView.project
|
||||
rootView.activateExtension(TreeView)
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()
|
||||
dirView.expand()
|
||||
fileView = treeView.find('.file:contains(test-file.txt)').view()
|
||||
|
||||
afterEach ->
|
||||
fs.remove(rootDirPath) if fs.exists(rootDirPath)
|
||||
|
||||
describe "tree-view:add", ->
|
||||
addDialog = null
|
||||
|
||||
beforeEach ->
|
||||
fileView.click()
|
||||
treeView.trigger "tree-view:add"
|
||||
addDialog = rootView.find(".tree-view-dialog").view()
|
||||
|
||||
describe "when a file is selected", ->
|
||||
it "opens an add dialog with the file's current directory path populated", ->
|
||||
expect(addDialog).toExist()
|
||||
expect(addDialog.prompt.text()).toBeTruthy()
|
||||
expect(project.relativize(dirPath)).toMatch(/[^\/]$/)
|
||||
expect(addDialog.miniEditor.getText()).toBe(project.relativize(dirPath) + "/")
|
||||
expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length
|
||||
expect(addDialog.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when parent directory of the selected file changes", ->
|
||||
it "active file is still shown as selected in the tree view", ->
|
||||
directoryChangeHandler = jasmine.createSpy("directory-change")
|
||||
dirView.on "tree-view:directory-modified", directoryChangeHandler
|
||||
|
||||
dirView.directory.trigger 'contents-change'
|
||||
expect(directoryChangeHandler).toHaveBeenCalled()
|
||||
expect(treeView.find('.selected').text()).toBe fs.base(filePath)
|
||||
|
||||
describe "when the path without a trailing '/' is changed and confirmed", ->
|
||||
describe "when no file exists at that location", ->
|
||||
it "add a file, closes the dialog and selects the file in the tree-view", ->
|
||||
newPath = fs.join(dirPath, "new-test-file.txt")
|
||||
addDialog.miniEditor.insertText(fs.base(newPath))
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fs.exists(newPath)).toBeTruthy()
|
||||
expect(fs.isFile(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveEditor().getPath()).toBe newPath
|
||||
|
||||
waitsFor "tree view to be updated", ->
|
||||
dirView.entries.find("> .file").length > 1
|
||||
|
||||
runs ->
|
||||
expect(treeView.find('.selected').text()).toBe fs.base(newPath)
|
||||
|
||||
describe "when a file already exists at that location", ->
|
||||
it "shows an error message and does not close the dialog", ->
|
||||
newPath = fs.join(dirPath, "new-test-file.txt")
|
||||
fs.write(newPath, '')
|
||||
addDialog.miniEditor.insertText(fs.base(newPath))
|
||||
addDialog.trigger 'core:confirm'
|
||||
|
||||
expect(addDialog.prompt.text()).toContain 'Error'
|
||||
expect(addDialog.prompt.text()).toContain 'already exists'
|
||||
expect(addDialog.prompt).toHaveClass('error')
|
||||
expect(addDialog.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when the path with a trailing '/' is changed and confirmed", ->
|
||||
describe "when no file or directory exists at the given path", ->
|
||||
it "adds a directory and closes the dialog", ->
|
||||
treeView.attachToDom()
|
||||
newPath = fs.join(dirPath, "new/dir")
|
||||
addDialog.miniEditor.insertText("new/dir/")
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fs.exists(newPath)).toBeTruthy()
|
||||
expect(fs.isDirectory(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveEditor().getPath()).not.toBe newPath
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
expect(dirView.find('.directory.selected:contains(new/)').length).toBe(1)
|
||||
|
||||
it "selects the created directory", ->
|
||||
treeView.attachToDom()
|
||||
newPath = fs.join(dirPath, "new2/")
|
||||
addDialog.miniEditor.insertText("new2/")
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fs.exists(newPath)).toBeTruthy()
|
||||
expect(fs.isDirectory(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveEditor().getPath()).not.toBe newPath
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
|
||||
expect(dirView.find('.directory.selected:contains(new2/)').length).toBe(1)
|
||||
|
||||
describe "when a file or directory already exists at the given path", ->
|
||||
it "shows an error message and does not close the dialog", ->
|
||||
newPath = fs.join(dirPath, "new-dir")
|
||||
fs.makeDirectory(newPath)
|
||||
addDialog.miniEditor.insertText("new-dir/")
|
||||
addDialog.trigger 'core:confirm'
|
||||
|
||||
expect(addDialog.prompt.text()).toContain 'Error'
|
||||
expect(addDialog.prompt.text()).toContain 'already exists'
|
||||
expect(addDialog.prompt).toHaveClass('error')
|
||||
expect(addDialog.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when 'core:cancel' is triggered on the add dialog", ->
|
||||
it "removes the dialog and focuses the tree view", ->
|
||||
treeView.attachToDom()
|
||||
addDialog.trigger 'core:cancel'
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
|
||||
describe "when the add dialog's editor loses focus", ->
|
||||
it "removes the dialog and focuses root view", ->
|
||||
rootView.attachToDom()
|
||||
rootView.focus()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "when a directory is selected", ->
|
||||
it "opens an add dialog with the directory's path populated", ->
|
||||
addDialog.cancel()
|
||||
dirView.click()
|
||||
treeView.trigger "tree-view:add"
|
||||
addDialog = rootView.find(".tree-view-dialog").view()
|
||||
|
||||
expect(addDialog).toExist()
|
||||
expect(addDialog.prompt.text()).toBeTruthy()
|
||||
expect(project.relativize(dirPath)).toMatch(/[^\/]$/)
|
||||
expect(addDialog.miniEditor.getText()).toBe(project.relativize(dirPath) + "/")
|
||||
expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length
|
||||
expect(addDialog.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the root directory is selected", ->
|
||||
it "opens an add dialog with no path populated", ->
|
||||
addDialog.cancel()
|
||||
treeView.root.click()
|
||||
treeView.trigger "tree-view:add"
|
||||
addDialog = rootView.find(".tree-view-dialog").view()
|
||||
|
||||
expect(addDialog.miniEditor.getText().length).toBe 0
|
||||
|
||||
describe "tree-view:move", ->
|
||||
describe "when a file is selected", ->
|
||||
moveDialog = null
|
||||
|
||||
beforeEach ->
|
||||
fileView.click()
|
||||
treeView.trigger "tree-view:move"
|
||||
moveDialog = rootView.find(".tree-view-dialog").view()
|
||||
|
||||
it "opens a move dialog with the file's current path (excluding extension) populated", ->
|
||||
extension = fs.extension(filePath)
|
||||
fileNameWithoutExtension = fs.base(filePath, extension)
|
||||
expect(moveDialog).toExist()
|
||||
expect(moveDialog.prompt.text()).toBe "Enter the new path for the file:"
|
||||
expect(moveDialog.miniEditor.getText()).toBe(project.relativize(filePath))
|
||||
expect(moveDialog.miniEditor.getSelectedText()).toBe fs.base(fileNameWithoutExtension)
|
||||
expect(moveDialog.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the path is changed and confirmed", ->
|
||||
describe "when all the directories along the new path exist", ->
|
||||
it "moves the file, updates the tree view, and closes the dialog", ->
|
||||
newPath = fs.join(rootDirPath, 'renamed-test-file.txt')
|
||||
moveDialog.miniEditor.setText(newPath)
|
||||
|
||||
moveDialog.trigger 'core:confirm'
|
||||
|
||||
expect(fs.exists(newPath)).toBeTruthy()
|
||||
expect(fs.exists(filePath)).toBeFalsy()
|
||||
expect(moveDialog.parent()).not.toExist()
|
||||
|
||||
waitsFor "tree view to update", ->
|
||||
treeView.root.find('> .entries > .file:contains(renamed-test-file.txt)').length > 0
|
||||
|
||||
runs ->
|
||||
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()
|
||||
dirView.expand()
|
||||
expect(dirView.entries.children().length).toBe 0
|
||||
|
||||
describe "when the directories along the new path don't exist", ->
|
||||
it "creates the target directory before moving the file", ->
|
||||
newPath = fs.join(rootDirPath, 'new/directory', 'renamed-test-file.txt')
|
||||
moveDialog.miniEditor.setText(newPath)
|
||||
|
||||
moveDialog.trigger 'core:confirm'
|
||||
|
||||
expect(fs.exists(newPath)).toBeTruthy()
|
||||
expect(fs.exists(filePath)).toBeFalsy()
|
||||
|
||||
waits 50 # TODO: remove this workaround once we fix the race condition in fs events
|
||||
|
||||
describe "when a file or directory already exists at the target path", ->
|
||||
it "shows an error message and does not close the dialog", ->
|
||||
runs ->
|
||||
fs.write(fs.join(rootDirPath, 'target.txt'), '')
|
||||
newPath = fs.join(rootDirPath, 'target.txt')
|
||||
moveDialog.miniEditor.setText(newPath)
|
||||
|
||||
moveDialog.trigger 'core:confirm'
|
||||
|
||||
expect(moveDialog.prompt.text()).toContain 'Error'
|
||||
expect(moveDialog.prompt.text()).toContain 'already exists'
|
||||
expect(moveDialog.prompt).toHaveClass('error')
|
||||
expect(moveDialog.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when 'core:cancel' is triggered on the move dialog", ->
|
||||
it "removes the dialog and focuses the tree view", ->
|
||||
treeView.attachToDom()
|
||||
moveDialog.trigger 'core:cancel'
|
||||
expect(moveDialog.parent()).not.toExist()
|
||||
expect(treeView).toMatchSelector(':focus')
|
||||
|
||||
describe "when the move dialog's editor loses focus", ->
|
||||
it "removes the dialog and focuses root view", ->
|
||||
rootView.attachToDom()
|
||||
rootView.focus()
|
||||
expect(moveDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "tree-view:remove", ->
|
||||
it "shows the native alert dialog", ->
|
||||
fileView.click()
|
||||
spyOn(atom, 'confirm')
|
||||
treeView.trigger 'tree-view:remove'
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "file system events", ->
|
||||
temporaryFilePath = null
|
||||
|
||||
beforeEach ->
|
||||
temporaryFilePath = fs.join(require.resolve('fixtures/tree-view'), 'temporary')
|
||||
if fs.exists(temporaryFilePath)
|
||||
fs.remove(temporaryFilePath)
|
||||
waits(20)
|
||||
|
||||
afterEach ->
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
|
||||
describe "when a file is added or removed in an expanded directory", ->
|
||||
it "updates the directory view to display the directory's new contents", ->
|
||||
entriesCountBefore = null
|
||||
|
||||
runs ->
|
||||
expect(fs.exists(temporaryFilePath)).toBeFalsy()
|
||||
entriesCountBefore = treeView.root.entries.find('.entry').length
|
||||
fs.write temporaryFilePath, 'hi'
|
||||
|
||||
waitsFor "directory view contens to refresh", ->
|
||||
treeView.root.entries.find('.entry').length == entriesCountBefore + 1
|
||||
|
||||
runs ->
|
||||
expect(treeView.root.entries.find('.entry').length).toBe entriesCountBefore + 1
|
||||
expect(treeView.root.entries.find('.file:contains(temporary)')).toExist()
|
||||
fs.remove(temporaryFilePath)
|
||||
|
||||
waitsFor "directory view contens to refresh", ->
|
||||
treeView.root.entries.find('.entry').length == entriesCountBefore
|
||||
@@ -1,5 +1,5 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
FileView = require 'tree-view/file-view'
|
||||
FileView = require 'tree-view/src/file-view'
|
||||
Directory = require 'directory'
|
||||
$ = require 'jquery'
|
||||
|
||||
292
src/extensions/tree-view/src/tree-view.coffee
Normal file
292
src/extensions/tree-view/src/tree-view.coffee
Normal file
@@ -0,0 +1,292 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
ScrollView = require 'scroll-view'
|
||||
Directory = require 'directory'
|
||||
DirectoryView = require 'tree-view/src/directory-view'
|
||||
FileView = require 'tree-view/src/file-view'
|
||||
Dialog = require 'tree-view/src/dialog'
|
||||
Native = require 'native'
|
||||
fs = require 'fs'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class TreeView extends ScrollView
|
||||
@activate: (rootView, state) ->
|
||||
requireStylesheet 'tree-view.css'
|
||||
|
||||
if state
|
||||
@instance = TreeView.deserialize(state, rootView)
|
||||
else
|
||||
@instance = new TreeView(rootView)
|
||||
@instance.attach()
|
||||
|
||||
@deactivate: () ->
|
||||
@instance.deactivate()
|
||||
|
||||
@serialize: ->
|
||||
@instance.serialize()
|
||||
|
||||
@content: (rootView) ->
|
||||
@div class: 'tree-view tool-panel', tabindex: -1, =>
|
||||
if rootView.project.getRootDirectory()
|
||||
@subview 'root', new DirectoryView(directory: rootView.project.getRootDirectory(), isExpanded: true)
|
||||
|
||||
@deserialize: (state, rootView) ->
|
||||
treeView = new TreeView(rootView)
|
||||
treeView.root.deserializeEntryExpansionStates(state.directoryExpansionStates)
|
||||
treeView.selectEntryForPath(state.selectedPath)
|
||||
treeView.focusAfterAttach = state.hasFocus
|
||||
treeView.scrollTopAfterAttach = state.scrollTop
|
||||
treeView.attach() if state.attached
|
||||
treeView
|
||||
|
||||
root: null
|
||||
focusAfterAttach: false
|
||||
scrollTopAfterAttach: -1
|
||||
selectedPath: null
|
||||
|
||||
initialize: (@rootView) ->
|
||||
super
|
||||
@on 'click', '.entry', (e) => @entryClicked(e)
|
||||
@command 'core:move-up', => @moveUp()
|
||||
@command 'core:move-down', => @moveDown()
|
||||
@command 'core:close', => @detatch()
|
||||
@command 'tree-view:expand-directory', => @expandDirectory()
|
||||
@command 'tree-view:collapse-directory', => @collapseDirectory()
|
||||
@command 'tree-view:open-selected-entry', => @openSelectedEntry(true)
|
||||
@command 'tree-view:move', => @moveSelectedEntry()
|
||||
@command 'tree-view:add', => @add()
|
||||
@command 'tree-view:remove', => @removeSelectedEntry()
|
||||
@command 'tool-panel:unfocus', => @rootView.focus()
|
||||
@command 'tree-view:directory-modified', =>
|
||||
if @hasFocus()
|
||||
@selectEntryForPath(@selectedPath) if @selectedPath
|
||||
else
|
||||
@selectActiveFile()
|
||||
@rootView.command 'tree-view:toggle', => @toggle()
|
||||
@rootView.command 'tree-view:reveal-active-file', => @revealActiveFile()
|
||||
@rootView.on 'active-editor-path-change', => @selectActiveFile()
|
||||
@rootView.project.on 'path-change', => @updateRoot()
|
||||
|
||||
@selectEntry(@root) if @root
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
@focus() if @focusAfterAttach
|
||||
@scrollTop(@scrollTopAfterAttach) if @scrollTopAfterAttach > 0
|
||||
|
||||
serialize: ->
|
||||
directoryExpansionStates: @root?.serializeEntryExpansionStates()
|
||||
selectedPath: @selectedEntry()?.getPath()
|
||||
hasFocus: @hasFocus()
|
||||
attached: @hasParent()
|
||||
scrollTop: @scrollTop()
|
||||
|
||||
deactivate: ->
|
||||
@root?.unwatchEntries()
|
||||
|
||||
toggle: ->
|
||||
if @hasFocus()
|
||||
@detach()
|
||||
else
|
||||
if @hasParent()
|
||||
@focus()
|
||||
else
|
||||
@attach()
|
||||
|
||||
attach: ->
|
||||
@rootView.horizontal.prepend(this)
|
||||
@focus()
|
||||
|
||||
detach: ->
|
||||
@scrollTopAfterAttach = @scrollTop()
|
||||
super
|
||||
@rootView.focus()
|
||||
|
||||
hasFocus: ->
|
||||
@is(':focus')
|
||||
|
||||
entryClicked: (e) ->
|
||||
entry = $(e.currentTarget).view()
|
||||
switch e.originalEvent?.detail ? 1
|
||||
when 1
|
||||
@selectEntry(entry)
|
||||
@openSelectedEntry(false) if (entry instanceof FileView)
|
||||
when 2
|
||||
if entry.is('.selected.file')
|
||||
@rootView.getActiveEditor().focus()
|
||||
else if entry.is('.selected.directory')
|
||||
entry.toggleExpansion()
|
||||
|
||||
false
|
||||
|
||||
updateRoot: ->
|
||||
@root?.remove()
|
||||
if @rootView.project.getRootDirectory()
|
||||
@root = new DirectoryView(directory: @rootView.project.getRootDirectory(), isExpanded: true)
|
||||
@append(@root)
|
||||
else
|
||||
@root = null
|
||||
|
||||
selectActiveFile: ->
|
||||
activeFilePath = @rootView.getActiveEditor()?.getPath()
|
||||
@selectEntryForPath(activeFilePath) if activeFilePath
|
||||
|
||||
revealActiveFile: ->
|
||||
@attach()
|
||||
@focus()
|
||||
|
||||
return unless activeFilePath = @rootView.getActiveEditor()?.getPath()
|
||||
|
||||
project = @rootView.project
|
||||
activePathComponents = project.relativize(activeFilePath).split('/')
|
||||
currentPath = project.getPath().replace(/\/$/, '')
|
||||
for pathComponent in activePathComponents
|
||||
currentPath += '/' + pathComponent
|
||||
entry = @entryForPath(currentPath)
|
||||
if entry.hasClass('directory')
|
||||
entry.expand()
|
||||
else
|
||||
@selectEntry(entry)
|
||||
|
||||
entryForPath: (path) ->
|
||||
fn = (bestMatchEntry, element) ->
|
||||
entry = $(element).view()
|
||||
regex = new RegExp("^" + _.escapeRegExp(entry.getPath()))
|
||||
if regex.test(path) and entry.getPath().length > bestMatchEntry.getPath().length
|
||||
entry
|
||||
else
|
||||
bestMatchEntry
|
||||
|
||||
@find(".entry").toArray().reduce(fn, @root)
|
||||
|
||||
selectEntryForPath: (path) ->
|
||||
@selectEntry(@entryForPath(path))
|
||||
|
||||
moveDown: ->
|
||||
selectedEntry = @selectedEntry()
|
||||
if selectedEntry
|
||||
if selectedEntry.is('.expanded.directory')
|
||||
return if @selectEntry(selectedEntry.find('.entry:first'))
|
||||
until @selectEntry(selectedEntry.next())
|
||||
selectedEntry = selectedEntry.parents('.entry:first')
|
||||
break unless selectedEntry.length
|
||||
else
|
||||
@selectEntry(@root)
|
||||
|
||||
@scrollToEntry(@selectedEntry())
|
||||
|
||||
moveUp: ->
|
||||
selectedEntry = @selectedEntry()
|
||||
if selectedEntry
|
||||
if previousEntry = @selectEntry(selectedEntry.prev())
|
||||
if previousEntry.is('.expanded.directory')
|
||||
@selectEntry(previousEntry.find('.entry:last'))
|
||||
else
|
||||
@selectEntry(selectedEntry.parents('.directory').first())
|
||||
else
|
||||
@selectEntry(@find('.entry').last())
|
||||
|
||||
@scrollToEntry(@selectedEntry())
|
||||
|
||||
expandDirectory: ->
|
||||
selectedEntry = @selectedEntry()
|
||||
selectedEntry.view().expand() if (selectedEntry instanceof DirectoryView)
|
||||
|
||||
collapseDirectory: ->
|
||||
selectedEntry = @selectedEntry()
|
||||
if directory = selectedEntry.closest('.expanded.directory').view()
|
||||
directory.collapse()
|
||||
@selectEntry(directory)
|
||||
|
||||
openSelectedEntry: (changeFocus) ->
|
||||
selectedEntry = @selectedEntry()
|
||||
if (selectedEntry instanceof DirectoryView)
|
||||
selectedEntry.view().toggleExpansion()
|
||||
else if (selectedEntry instanceof FileView)
|
||||
@rootView.open(selectedEntry.getPath(), { changeFocus })
|
||||
|
||||
moveSelectedEntry: ->
|
||||
entry = @selectedEntry()
|
||||
return unless entry
|
||||
oldPath = entry.getPath()
|
||||
|
||||
dialog = new Dialog
|
||||
prompt: "Enter the new path for the file:"
|
||||
path: @rootView.project.relativize(oldPath)
|
||||
select: true
|
||||
onConfirm: (newPath) =>
|
||||
newPath = @rootView.project.resolve(newPath)
|
||||
directoryPath = fs.directory(newPath)
|
||||
try
|
||||
fs.makeTree(directoryPath) unless fs.exists(directoryPath)
|
||||
fs.move(oldPath, newPath)
|
||||
dialog.close()
|
||||
catch e
|
||||
dialog.showError("Error: " + e.message + " Try a different path:")
|
||||
|
||||
@rootView.append(dialog)
|
||||
|
||||
removeSelectedEntry: ->
|
||||
entry = @selectedEntry()
|
||||
return unless entry
|
||||
|
||||
entryType = if entry instanceof DirectoryView then "directory" else "file"
|
||||
atom.confirm(
|
||||
"Are you sure you would like to delete the selected #{entryType}?",
|
||||
"You are deleting #{entry.getPath()}",
|
||||
"Move to Trash", (=> Native.moveToTrash(entry.getPath())),
|
||||
"Cancel", null
|
||||
"Delete", (=> fs.remove(entry.getPath()))
|
||||
)
|
||||
|
||||
add: ->
|
||||
selectedPath = @selectedEntry().getPath()
|
||||
directoryPath = if fs.isFile(selectedPath) then fs.directory(selectedPath) else selectedPath
|
||||
relativeDirectoryPath = @rootView.project.relativize(directoryPath)
|
||||
relativeDirectoryPath += '/' if relativeDirectoryPath.length > 0
|
||||
|
||||
dialog = new Dialog
|
||||
prompt: "Enter the path for the new file/directory. Directories end with '/':"
|
||||
path: relativeDirectoryPath
|
||||
select: false
|
||||
onConfirm: (relativePath) =>
|
||||
endsWithDirectorySeparator = /\/$/.test(relativePath)
|
||||
path = @rootView.project.resolve(relativePath)
|
||||
try
|
||||
if fs.exists(path)
|
||||
pathType = if fs.isFile(path) then "file" else "directory"
|
||||
dialog.showError("Error: A #{pathType} already exists at path '#{path}'. Try a different path:")
|
||||
else if endsWithDirectorySeparator
|
||||
fs.makeTree(path)
|
||||
dialog.cancel()
|
||||
@entryForPath(path).buildEntries()
|
||||
@selectEntryForPath(path)
|
||||
else
|
||||
fs.write(path, "")
|
||||
@rootView.open(path)
|
||||
dialog.close()
|
||||
catch e
|
||||
dialog.showError("Error: " + e.message + " Try a different path:")
|
||||
|
||||
@rootView.append(dialog)
|
||||
|
||||
selectedEntry: ->
|
||||
@find('.selected')?.view()
|
||||
|
||||
selectEntry: (entry) ->
|
||||
return false unless entry.get(0)
|
||||
entry = entry.view() unless entry instanceof View
|
||||
@selectedPath = entry.getPath()
|
||||
@find('.selected').removeClass('selected')
|
||||
entry.addClass('selected')
|
||||
|
||||
scrollToEntry: (entry) ->
|
||||
displayElement = if (entry instanceof DirectoryView) then entry.header else entry
|
||||
|
||||
top = @scrollTop() + displayElement.position().top
|
||||
bottom = top + displayElement.outerHeight()
|
||||
|
||||
if bottom > @scrollBottom()
|
||||
@scrollBottom(bottom)
|
||||
if top < @scrollTop()
|
||||
@scrollTop(top)
|
||||
Reference in New Issue
Block a user