mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Pull out tree-view package into a separate repo
This commit is contained in:
@@ -87,6 +87,7 @@
|
||||
"tabs": "0.1.0",
|
||||
"terminal": "0.3.0",
|
||||
"toml": "0.1.0",
|
||||
"tree-view": "0.1.0",
|
||||
"whitespace": "0.1.0",
|
||||
"wrap-guide": "0.1.0"
|
||||
},
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
'body':
|
||||
'meta-\\': 'tree-view:toggle'
|
||||
'meta-|': 'tree-view:reveal-active-file'
|
||||
|
||||
'.tree-view':
|
||||
'right': 'tree-view:expand-directory'
|
||||
'ctrl-]': 'tree-view:expand-directory'
|
||||
'left': 'tree-view:collapse-directory'
|
||||
'ctrl-[': 'tree-view:collapse-directory'
|
||||
'enter': 'tree-view:open-selected-entry'
|
||||
'm': 'tree-view:move'
|
||||
'a': 'tree-view:add'
|
||||
'delete': 'tree-view:remove'
|
||||
'backspace': 'tree-view:remove'
|
||||
'k': 'core:move-up'
|
||||
'j': 'core:move-down'
|
||||
|
||||
'.tree-view-dialog .mini.editor':
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
@@ -1,44 +0,0 @@
|
||||
{View} = require 'space-pen'
|
||||
Editor = require 'editor'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class Dialog extends View
|
||||
@content: ({prompt} = {}) ->
|
||||
@div class: 'tree-view-dialog', =>
|
||||
@div outlet: 'prompt', class: 'prompt', =>
|
||||
@span prompt, outlet: 'promptText'
|
||||
@subview 'miniEditor', new Editor(mini: true)
|
||||
|
||||
initialize: ({initialPath, @onConfirm, select, iconClass} = {}) ->
|
||||
@prompt.addClass(iconClass) if iconClass
|
||||
@miniEditor.focus()
|
||||
@on 'core:confirm', => @onConfirm(@miniEditor.getText())
|
||||
@on 'core:cancel', => @cancel()
|
||||
@miniEditor.on 'focusout', => @remove()
|
||||
|
||||
@miniEditor.setText(initialPath)
|
||||
|
||||
if select
|
||||
extension = path.extname(initialPath)
|
||||
baseName = path.basename(initialPath)
|
||||
if baseName is extension
|
||||
selectionEnd = initialPath.length
|
||||
else
|
||||
selectionEnd = initialPath.length - extension.length
|
||||
range = [[0, initialPath.length - baseName.length], [0, selectionEnd]]
|
||||
@miniEditor.setSelectedBufferRange(range)
|
||||
|
||||
close: ->
|
||||
@remove()
|
||||
rootView.focus()
|
||||
|
||||
cancel: ->
|
||||
@remove()
|
||||
$('.tree-view').focus()
|
||||
|
||||
showError: (message) ->
|
||||
@promptText.text(message)
|
||||
@flashError()
|
||||
@@ -1,127 +0,0 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
FileView = require './file-view'
|
||||
Directory = require 'directory'
|
||||
$ = require 'jquery'
|
||||
fs = require 'fs'
|
||||
|
||||
module.exports =
|
||||
class DirectoryView extends View
|
||||
@content: ({directory, isExpanded} = {}) ->
|
||||
@li class: 'directory entry', =>
|
||||
@span class: 'highlight'
|
||||
@div outlet: 'header', class: 'header', =>
|
||||
@span class: 'disclosure-arrow', outlet: 'disclosureArrow'
|
||||
@span directory.getBaseName(), class: 'name', outlet: 'directoryName'
|
||||
|
||||
directory: null
|
||||
entries: null
|
||||
header: null
|
||||
project: null
|
||||
|
||||
initialize: ({@directory, isExpanded, @project, parent} = {}) ->
|
||||
@expand() if isExpanded
|
||||
@disclosureArrow.on 'click', => @toggleExpansion()
|
||||
|
||||
if @directory.symlink
|
||||
iconClass = 'symlink-icon'
|
||||
else
|
||||
iconClass = 'directory-icon'
|
||||
|
||||
if git?
|
||||
path = @directory.getPath()
|
||||
if parent
|
||||
if git.isSubmodule(path)
|
||||
iconClass = 'submodule-icon'
|
||||
else
|
||||
@subscribe git, 'status-changed', (path, status) =>
|
||||
@updateStatus() if path.indexOf("#{@getPath()}/") is 0
|
||||
@subscribe git, 'statuses-changed', =>
|
||||
@updateStatus()
|
||||
@updateStatus()
|
||||
else
|
||||
iconClass = 'repository-icon' if @isRepositoryRoot()
|
||||
|
||||
@directoryName.addClass(iconClass)
|
||||
|
||||
updateStatus: ->
|
||||
@removeClass('ignored modified new')
|
||||
path = @directory.getPath()
|
||||
if git.isPathIgnored(path)
|
||||
@addClass('ignored')
|
||||
else
|
||||
status = git.getDirectoryStatus(path)
|
||||
if git.isStatusModified(status)
|
||||
@addClass('modified')
|
||||
else if git.isStatusNew(status)
|
||||
@addClass('new')
|
||||
|
||||
getPath: ->
|
||||
@directory.path
|
||||
|
||||
isRepositoryRoot: ->
|
||||
try
|
||||
git? and git.getWorkingDirectory() is fs.realpathSync(@getPath())
|
||||
catch e
|
||||
false
|
||||
|
||||
isPathIgnored: (path) ->
|
||||
config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(path)
|
||||
|
||||
buildEntries: ->
|
||||
@unwatchDescendantEntries()
|
||||
@entries?.remove()
|
||||
@entries = $$ -> @ol class: 'entries list-unstyled'
|
||||
for entry in @directory.getEntries()
|
||||
continue if @isPathIgnored(entry.path)
|
||||
if entry instanceof Directory
|
||||
@entries.append(new DirectoryView(directory: entry, isExpanded: false, project: @project, parent: @directory))
|
||||
else
|
||||
@entries.append(new FileView(file: entry, project: @project))
|
||||
@append(@entries)
|
||||
|
||||
toggleExpansion: ->
|
||||
if @isExpanded then @collapse() else @expand()
|
||||
|
||||
expand: ->
|
||||
return if @isExpanded
|
||||
@addClass('expanded')
|
||||
@buildEntries()
|
||||
@watchEntries()
|
||||
@deserializeEntryExpansionStates(@entryStates) if @entryStates?
|
||||
@isExpanded = true
|
||||
false
|
||||
|
||||
collapse: ->
|
||||
@entryStates = @serializeEntryExpansionStates()
|
||||
@removeClass('expanded')
|
||||
@unwatchEntries()
|
||||
@entries.remove()
|
||||
@entries = null
|
||||
@isExpanded = false
|
||||
|
||||
watchEntries: ->
|
||||
@directory.on "contents-changed.tree-view", =>
|
||||
@buildEntries()
|
||||
@trigger "tree-view:directory-modified"
|
||||
|
||||
unwatchEntries: ->
|
||||
@unwatchDescendantEntries()
|
||||
@directory.off ".tree-view"
|
||||
|
||||
unwatchDescendantEntries: ->
|
||||
@find('.expanded.directory').each ->
|
||||
$(this).view().unwatchEntries()
|
||||
|
||||
serializeEntryExpansionStates: ->
|
||||
entryStates = {}
|
||||
@entries?.find('> .directory.expanded').each ->
|
||||
view = $(this).view()
|
||||
entryStates[view.directory.getBaseName()] = view.serializeEntryExpansionStates()
|
||||
entryStates
|
||||
|
||||
deserializeEntryExpansionStates: (entryStates) ->
|
||||
for directoryName, childEntryStates of entryStates
|
||||
@entries.find("> .directory:contains('#{directoryName}')").each ->
|
||||
view = $(this).view()
|
||||
view.entryStates = childEntryStates
|
||||
view.expand()
|
||||
@@ -1,57 +0,0 @@
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
|
||||
module.exports =
|
||||
class FileView extends View
|
||||
|
||||
@content: ({file} = {}) ->
|
||||
@li class: 'file entry', =>
|
||||
@span class: 'highlight'
|
||||
@span file.getBaseName(), class: 'name', outlet: 'fileName'
|
||||
|
||||
file: null
|
||||
|
||||
initialize: ({@file, @project} = {}) ->
|
||||
if @file.symlink
|
||||
@fileName.addClass('symlink-icon')
|
||||
else
|
||||
extension = path.extname(@getPath())
|
||||
if fsUtils.isReadmePath(@getPath())
|
||||
@fileName.addClass('readme-icon')
|
||||
else if fsUtils.isCompressedExtension(extension)
|
||||
@fileName.addClass('compressed-icon')
|
||||
else if fsUtils.isImageExtension(extension)
|
||||
@fileName.addClass('image-icon')
|
||||
else if fsUtils.isPdfExtension(extension)
|
||||
@fileName.addClass('pdf-icon')
|
||||
else if fsUtils.isBinaryExtension(extension)
|
||||
@fileName.addClass('binary-icon')
|
||||
else
|
||||
@fileName.addClass('text-icon')
|
||||
|
||||
if git?
|
||||
@subscribe git, 'status-changed', (changedPath, status) =>
|
||||
@updateStatus() if changedPath is @getPath()
|
||||
@subscribe git, 'statuses-changed', =>
|
||||
@updateStatus()
|
||||
|
||||
@updateStatus()
|
||||
|
||||
updateStatus: ->
|
||||
@removeClass('ignored modified new')
|
||||
return unless git?
|
||||
|
||||
filePath = @getPath()
|
||||
if git.isPathIgnored(filePath)
|
||||
@addClass('ignored')
|
||||
else
|
||||
status = git.statuses[filePath]
|
||||
if git.isStatusModified(status)
|
||||
@addClass('modified')
|
||||
else if git.isStatusNew(status)
|
||||
@addClass('new')
|
||||
|
||||
getPath: ->
|
||||
@file.path
|
||||
@@ -1,342 +0,0 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
ScrollView = require 'scroll-view'
|
||||
Directory = require 'directory'
|
||||
DirectoryView = require './directory-view'
|
||||
FileView = require './file-view'
|
||||
Dialog = require './dialog'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
shell = require 'shell'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class TreeView extends ScrollView
|
||||
@content: (rootView) ->
|
||||
@div class: 'tree-view-resizer', =>
|
||||
@div class: 'tree-view-scroller', outlet: 'scroller', =>
|
||||
@ol class: 'list-unstyled tree-view tool-panel', tabindex: -1, outlet: 'list'
|
||||
@div class: 'tree-view-resize-handle', outlet: 'resizeHandle'
|
||||
|
||||
root: null
|
||||
focusAfterAttach: false
|
||||
scrollTopAfterAttach: -1
|
||||
selectedPath: null
|
||||
|
||||
initialize: (state) ->
|
||||
super
|
||||
@on 'click', '.entry', (e) => @entryClicked(e)
|
||||
@on 'mousedown', '.tree-view-resize-handle', (e) => @resizeStarted(e)
|
||||
@command 'core:move-up', => @moveUp()
|
||||
@command 'core:move-down', => @moveDown()
|
||||
@command 'core:close', => @detach(); false
|
||||
@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.on 'pane:active-item-changed.tree-view pane:became-active.tree-view', => @selectActiveFile()
|
||||
project.on 'path-changed.tree-view', => @updateRoot()
|
||||
@observeConfig 'core.hideGitIgnoredFiles', => @updateRoot()
|
||||
|
||||
if @root
|
||||
@selectEntry(@root)
|
||||
@root.deserializeEntryExpansionStates(state.directoryExpansionStates)
|
||||
@selectEntryForPath(state.selectedPath) if state.selectedPath
|
||||
@focusAfterAttach = state.hasFocus
|
||||
@scrollTopAfterAttach = state.scrollTop if state.scrollTop
|
||||
@width(state.width) if state.width
|
||||
@attach() if state.attached
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
@focus() if @focusAfterAttach
|
||||
@scrollTop(@scrollTopAfterAttach) if @scrollTopAfterAttach > 0
|
||||
|
||||
serialize: ->
|
||||
directoryExpansionStates: @root?.serializeEntryExpansionStates()
|
||||
selectedPath: @selectedEntry()?.getPath()
|
||||
hasFocus: @hasFocus()
|
||||
attached: @hasParent()
|
||||
scrollTop: @scrollTop()
|
||||
width: @width()
|
||||
|
||||
deactivate: ->
|
||||
@root?.unwatchEntries()
|
||||
rootView.off('.tree-view')
|
||||
project.off('.tree-view')
|
||||
@remove()
|
||||
|
||||
toggle: ->
|
||||
if @hasFocus()
|
||||
@detach()
|
||||
else
|
||||
@attach() unless @hasParent()
|
||||
@focus()
|
||||
|
||||
attach: ->
|
||||
return unless project.getPath()
|
||||
rootView.horizontal.prepend(this)
|
||||
|
||||
detach: ->
|
||||
@scrollTopAfterAttach = @scrollTop()
|
||||
super
|
||||
rootView.focus()
|
||||
|
||||
focus: ->
|
||||
@list.focus()
|
||||
|
||||
hasFocus: ->
|
||||
@list.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.getActiveView().focus()
|
||||
else if entry.is('.selected.directory')
|
||||
entry.toggleExpansion()
|
||||
|
||||
false
|
||||
|
||||
resizeStarted: (e) =>
|
||||
$(document.body).on('mousemove', @resizeTreeView)
|
||||
$(document.body).on('mouseup', @resizeStopped)
|
||||
|
||||
resizeStopped: (e) =>
|
||||
$(document.body).off('mousemove', @resizeTreeView)
|
||||
$(document.body).off('mouseup', @resizeStopped)
|
||||
|
||||
resizeTreeView: (e) =>
|
||||
@css(width: e.pageX)
|
||||
|
||||
updateRoot: ->
|
||||
@root?.remove()
|
||||
|
||||
if rootDirectory = project.getRootDirectory()
|
||||
@root = new DirectoryView(directory: rootDirectory, isExpanded: true, project: project)
|
||||
@list.append(@root)
|
||||
else
|
||||
@root = null
|
||||
|
||||
getActivePath: -> rootView.getActivePaneItem()?.getPath?()
|
||||
|
||||
selectActiveFile: ->
|
||||
if activeFilePath = @getActivePath()
|
||||
@selectEntryForPath(activeFilePath)
|
||||
else
|
||||
@deselect()
|
||||
|
||||
revealActiveFile: ->
|
||||
@attach()
|
||||
@focus()
|
||||
|
||||
return unless activeFilePath = @getActivePath()
|
||||
|
||||
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)
|
||||
@scrollToEntry(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
|
||||
|
||||
@list.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('.entry'))
|
||||
selectedEntry = selectedEntry.parents('.entry:first')
|
||||
break unless selectedEntry.length
|
||||
else
|
||||
@selectEntry(@root)
|
||||
|
||||
@scrollToEntry(@selectedEntry())
|
||||
|
||||
moveUp: ->
|
||||
selectedEntry = @selectedEntry()
|
||||
if selectedEntry
|
||||
if previousEntry = @selectEntry(selectedEntry.prev('.entry'))
|
||||
if previousEntry.is('.expanded.directory')
|
||||
@selectEntry(previousEntry.find('.entry:last'))
|
||||
else
|
||||
@selectEntry(selectedEntry.parents('.directory').first())
|
||||
else
|
||||
@selectEntry(@list.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 and entry isnt @root
|
||||
oldPath = entry.getPath()
|
||||
if entry instanceof FileView
|
||||
prompt = "Enter the new path for the file."
|
||||
else
|
||||
prompt = "Enter the new path for the directory."
|
||||
|
||||
dialog = new Dialog
|
||||
prompt: prompt
|
||||
initialPath: project.relativize(oldPath)
|
||||
select: true
|
||||
iconClass: 'move'
|
||||
onConfirm: (newPath) =>
|
||||
newPath = project.resolve(newPath)
|
||||
if oldPath is newPath
|
||||
dialog.close()
|
||||
return
|
||||
|
||||
if fsUtils.exists(newPath)
|
||||
dialog.showError("Error: #{newPath} already exists. Try a different path.")
|
||||
return
|
||||
|
||||
directoryPath = path.dirname(newPath)
|
||||
try
|
||||
fsUtils.makeTree(directoryPath) unless fsUtils.exists(directoryPath)
|
||||
fsUtils.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", (=> shell.moveItemToTrash(entry.getPath())),
|
||||
"Cancel", null
|
||||
"Delete", (=> fsUtils.remove(entry.getPath()))
|
||||
)
|
||||
|
||||
add: ->
|
||||
selectedEntry = @selectedEntry() or @root
|
||||
selectedPath = selectedEntry.getPath()
|
||||
directoryPath = if fsUtils.isFileSync(selectedPath) then path.dirname(selectedPath) else selectedPath
|
||||
relativeDirectoryPath = project.relativize(directoryPath)
|
||||
relativeDirectoryPath += '/' if relativeDirectoryPath.length > 0
|
||||
|
||||
dialog = new Dialog
|
||||
prompt: "Enter the path for the new file/directory. Directories end with a '/'."
|
||||
initialPath: relativeDirectoryPath
|
||||
select: false
|
||||
iconClass: 'add-directory'
|
||||
|
||||
onConfirm: (relativePath) =>
|
||||
endsWithDirectorySeparator = /\/$/.test(relativePath)
|
||||
pathToCreate = project.resolve(relativePath)
|
||||
try
|
||||
if fsUtils.exists(pathToCreate)
|
||||
pathType = if fsUtils.isFileSync(pathToCreate) then "file" else "directory"
|
||||
dialog.showError("Error: A #{pathType} already exists at path '#{pathToCreate}'. Try a different path.")
|
||||
else if endsWithDirectorySeparator
|
||||
fsUtils.makeTree(pathToCreate)
|
||||
dialog.cancel()
|
||||
@entryForPath(pathToCreate).buildEntries()
|
||||
@selectEntryForPath(pathToCreate)
|
||||
else
|
||||
fsUtils.writeSync(pathToCreate, "")
|
||||
rootView.open(pathToCreate)
|
||||
dialog.close()
|
||||
catch e
|
||||
dialog.showError("Error: #{e.message} Try a different path.")
|
||||
|
||||
dialog.miniEditor.getBuffer().on 'changed', =>
|
||||
if /\/$/.test(dialog.miniEditor.getText())
|
||||
dialog.prompt.removeClass('add-file').addClass('add-directory')
|
||||
else
|
||||
dialog.prompt.removeClass('add-directory').addClass('add-file')
|
||||
|
||||
rootView.append(dialog)
|
||||
|
||||
selectedEntry: ->
|
||||
@list.find('.selected')?.view()
|
||||
|
||||
selectEntry: (entry) ->
|
||||
return false unless entry.get(0)
|
||||
entry = entry.view() unless entry instanceof View
|
||||
@selectedPath = entry.getPath()
|
||||
@deselect()
|
||||
entry.addClass('selected')
|
||||
|
||||
deselect: ->
|
||||
@list.find('.selected').removeClass('selected')
|
||||
|
||||
scrollTop: (top) ->
|
||||
if top?
|
||||
@scroller.scrollTop(top)
|
||||
else
|
||||
@scroller.scrollTop()
|
||||
|
||||
scrollBottom: (bottom) ->
|
||||
if bottom?
|
||||
@scroller.scrollBottom(bottom)
|
||||
else
|
||||
@scroller.scrollBottom()
|
||||
|
||||
scrollToEntry: (entry) ->
|
||||
displayElement = if entry instanceof DirectoryView then entry.header else entry
|
||||
top = displayElement.position().top
|
||||
bottom = top + displayElement.outerHeight()
|
||||
if bottom > @scrollBottom()
|
||||
@scrollBottom(bottom)
|
||||
if top < @scrollTop()
|
||||
@scrollTop(top)
|
||||
|
||||
scrollToBottom: ->
|
||||
@selectEntry(@root.find('.entry:last')) if @root
|
||||
@scrollToEntry(@root.find('.entry:last')) if @root
|
||||
|
||||
scrollToTop: ->
|
||||
@selectEntry(@root) if @root
|
||||
@scrollTop(0)
|
||||
@@ -1,25 +0,0 @@
|
||||
module.exports =
|
||||
treeView: null
|
||||
|
||||
activate: (@state) ->
|
||||
@state.attached ?= true unless rootView.getActivePaneItem()
|
||||
|
||||
@createView() if @state.attached
|
||||
rootView.command 'tree-view:toggle', => @createView().toggle()
|
||||
rootView.command 'tree-view:reveal-active-file', => @createView().revealActiveFile()
|
||||
|
||||
deactivate: ->
|
||||
@treeView?.deactivate()
|
||||
@treeView = null
|
||||
|
||||
serialize: ->
|
||||
if @treeView?
|
||||
@treeView.serialize()
|
||||
else
|
||||
@state
|
||||
|
||||
createView: ->
|
||||
unless @treeView?
|
||||
TreeView = require 'tree-view/lib/tree-view'
|
||||
@treeView = new TreeView(@state)
|
||||
@treeView
|
||||
@@ -1,2 +0,0 @@
|
||||
'main': './lib/tree'
|
||||
'description': 'Display, add, move, and delete the files and folders in the current project.'
|
||||
@@ -1,976 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
TreeView = require 'tree-view/lib/tree-view'
|
||||
RootView = require 'root-view'
|
||||
Directory = require 'directory'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
|
||||
describe "TreeView", ->
|
||||
[treeView, sampleJs, sampleTxt] = []
|
||||
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('tree-view'))
|
||||
window.rootView = new RootView
|
||||
|
||||
atom.activatePackage("tree-view")
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
treeView.root = treeView.find('ol > 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
|
||||
|
||||
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')).not.toHaveClass('expanded')
|
||||
expect(treeView.root.find('> .header .name')).toHaveText('tree-view')
|
||||
|
||||
rootEntries = treeView.root.find('.entries')
|
||||
subdir0 = rootEntries.find('> li:eq(0)')
|
||||
expect(subdir0).not.toHaveClass('expanded')
|
||||
expect(subdir0.find('.name')).toHaveText('dir1')
|
||||
expect(subdir0.find('.entries')).not.toExist()
|
||||
|
||||
subdir2 = rootEntries.find('> li:eq(1)')
|
||||
expect(subdir2).not.toHaveClass('expanded')
|
||||
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 ->
|
||||
project.setPath(undefined)
|
||||
atom.deactivatePackage("tree-view")
|
||||
treeView = atom.activatePackage("tree-view").mainModule.createView()
|
||||
|
||||
it "does not attach to the root view or create a root node when initialized", ->
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root).not.toExist()
|
||||
|
||||
it "does not attach to the root view or create a root node when attach() is called", ->
|
||||
treeView.attach()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root).not.toExist()
|
||||
|
||||
it "serializes without throwing an exception", ->
|
||||
expect(-> treeView.serialize()).not.toThrow()
|
||||
|
||||
describe "when the project is assigned a path because a new buffer is saved", ->
|
||||
it "creates a root directory view but does not attach to the root view", ->
|
||||
rootView.open()
|
||||
rootView.getActivePaneItem().saveAs("/tmp/test.txt")
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root.getPath()).toBe '/tmp'
|
||||
expect(treeView.root.parent()).toMatchSelector(".tree-view")
|
||||
|
||||
describe "when the root view is opened to a file path", ->
|
||||
it "does not attach to the root view but does create a root node when initialized", ->
|
||||
atom.deactivatePackage("tree-view")
|
||||
atom.packageStates = {}
|
||||
rootView.open('tree-view.js')
|
||||
treeView = atom.activatePackage("tree-view").mainModule.createView()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root).toExist()
|
||||
|
||||
describe "when the root view is opened to a directory", ->
|
||||
it "attaches to the root view", ->
|
||||
treeView = atom.activatePackage("tree-view").mainModule.createView()
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView.root).toExist()
|
||||
|
||||
describe "serialization", ->
|
||||
it "restores expanded directories and selected file when deserialized", ->
|
||||
treeView.find('.directory:contains(dir1)').click()
|
||||
sampleJs.click()
|
||||
|
||||
atom.deactivatePackage("tree-view")
|
||||
atom.activatePackage("tree-view")
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
|
||||
expect(treeView).toExist()
|
||||
expect(treeView.selectedEntry()).toMatchSelector(".file:contains(tree-view.js)")
|
||||
expect(treeView.find(".directory:contains(dir1)")).toHaveClass("expanded")
|
||||
|
||||
it "restores the focus state of the tree view", ->
|
||||
rootView.attachToDom()
|
||||
treeView.focus()
|
||||
expect(treeView.list).toMatchSelector ':focus'
|
||||
atom.deactivatePackage("tree-view")
|
||||
atom.activatePackage("tree-view")
|
||||
treeView = rootView.find(".tree-view").view()
|
||||
expect(treeView.list).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.list).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.list).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.getActivePaneItem().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.getActivePaneItem()).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.list).toMatchSelector(':focus')
|
||||
treeView.trigger 'tool-panel:unfocus'
|
||||
expect(treeView).toBeVisible()
|
||||
expect(treeView.list).not.toMatchSelector(':focus')
|
||||
expect(rootView.getActiveView().isFocused).toBeTruthy()
|
||||
|
||||
describe "when core:close is triggered on the tree view", ->
|
||||
it "detaches the TreeView, focuses the RootView and does not bubble the core:close event", ->
|
||||
treeView.attach()
|
||||
treeView.focus()
|
||||
rootViewCloseHandler = jasmine.createSpy('rootViewCloseHandler')
|
||||
rootView.on 'core:close', rootViewCloseHandler
|
||||
spyOn(rootView, 'focus')
|
||||
|
||||
treeView.trigger('core:close')
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(rootViewCloseHandler).not.toHaveBeenCalled()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
|
||||
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).not.toHaveClass('expanded')
|
||||
expect(subdir.find('.entries')).not.toExist()
|
||||
|
||||
subdir.disclosureArrow.click()
|
||||
|
||||
expect(subdir).toHaveClass('expanded')
|
||||
expect(subdir.find('.entries')).toExist()
|
||||
|
||||
subdir.disclosureArrow.click()
|
||||
expect(subdir).not.toHaveClass('expanded')
|
||||
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.getActiveView()).toBeUndefined()
|
||||
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(sampleJs).toHaveClass 'selected'
|
||||
expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveView().isFocused).toBeFalsy()
|
||||
|
||||
sampleTxt.trigger clickEvent(originalEvent: { detail: 1 })
|
||||
expect(sampleTxt).toHaveClass 'selected'
|
||||
expect(treeView.find('.selected').length).toBe 1
|
||||
expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.txt')
|
||||
expect(rootView.getActiveView().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.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveView().isFocused).toBeFalsy()
|
||||
|
||||
sampleJs.trigger clickEvent(originalEvent: { detail: 2 })
|
||||
expect(rootView.getActiveView().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.getActiveView().isFocused).toBeFalsy()
|
||||
|
||||
describe "when the active item changes on the active pane", ->
|
||||
describe "when the item has a path", ->
|
||||
it "selects the entry with that path in the tree view if it is 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 "selects the path's parent dir if its entry is not visible", ->
|
||||
rootView.open('dir1/sub-dir1/sub-file1')
|
||||
dirView = treeView.root.find('.directory:contains(dir1)').view()
|
||||
expect(dirView).toHaveClass 'selected'
|
||||
|
||||
describe "when the item has no path", ->
|
||||
it "deselects the previously selected entry", ->
|
||||
sampleJs.click()
|
||||
rootView.getActivePane().showItem($$ -> @div('hello'))
|
||||
expect(rootView.find('.selected')).not.toExist()
|
||||
|
||||
describe "when a different editor becomes active", ->
|
||||
it "selects the file in that is open in that editor", ->
|
||||
sampleJs.click()
|
||||
leftEditor = rootView.getActiveView()
|
||||
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.list.outerHeight()).toBeGreaterThan treeView.scroller.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
|
||||
|
||||
it "selects the root entry", ->
|
||||
entryCount = treeView.find(".entry").length
|
||||
_.times entryCount, -> treeView.moveDown()
|
||||
|
||||
expect(treeView.root).not.toHaveClass 'selected'
|
||||
treeView.trigger 'core:move-to-top'
|
||||
expect(treeView.root).toHaveClass 'selected'
|
||||
|
||||
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.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight()
|
||||
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
treeView.trigger 'core:move-to-bottom'
|
||||
expect(treeView.scrollBottom()).toBe treeView.root.outerHeight()
|
||||
|
||||
it "selects the last entry", ->
|
||||
expect(treeView.root).toHaveClass 'selected'
|
||||
treeView.trigger 'core:move-to-bottom'
|
||||
expect(treeView.root.find('.entry:last')).toHaveClass 'selected'
|
||||
|
||||
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.list.outerHeight()).toBeGreaterThan treeView.scroller.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.list.outerHeight()).toBeGreaterThan treeView.scroller.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.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight()
|
||||
|
||||
treeView.moveDown()
|
||||
expect(treeView.scrollTop()).toBe 0
|
||||
|
||||
entryCount = treeView.find(".entry").length
|
||||
entryHeight = treeView.find('.file').height()
|
||||
|
||||
_.times entryCount, -> treeView.moveDown()
|
||||
expect(treeView.scrollBottom()).toBeGreaterThan (entryCount * entryHeight) - 1
|
||||
|
||||
_.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.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js')
|
||||
expect(rootView.getActiveView().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.getActiveView()).toBeUndefined()
|
||||
|
||||
describe "file modification", ->
|
||||
[dirView, fileView, rootDirPath, dirPath, filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.deactivatePackage('tree-view')
|
||||
|
||||
rootDirPath = path.join(fsUtils.absolute("/tmp"), "atom-tests")
|
||||
fsUtils.remove(rootDirPath) if fsUtils.exists(rootDirPath)
|
||||
|
||||
dirPath = path.join(rootDirPath, "test-dir")
|
||||
filePath = path.join(dirPath, "test-file.txt")
|
||||
fsUtils.makeTree(rootDirPath)
|
||||
fsUtils.makeTree(dirPath)
|
||||
fsUtils.writeSync(filePath, "doesn't matter")
|
||||
|
||||
project.setPath(rootDirPath)
|
||||
|
||||
atom.activatePackage('tree-view')
|
||||
rootView.trigger 'tree-view:toggle'
|
||||
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 ->
|
||||
fsUtils.remove(rootDirPath) if fsUtils.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-changed'
|
||||
expect(directoryChangeHandler).toHaveBeenCalled()
|
||||
expect(treeView.find('.selected').text()).toBe path.basename(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 = path.join(dirPath, "new-test-file.txt")
|
||||
addDialog.miniEditor.insertText(path.basename(newPath))
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fsUtils.exists(newPath)).toBeTruthy()
|
||||
expect(fsUtils.isFileSync(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveView().getPath()).toBe newPath
|
||||
|
||||
waitsFor "tree view to be updated", ->
|
||||
dirView.entries.find("> .file").length > 1
|
||||
|
||||
runs ->
|
||||
expect(treeView.find('.selected').text()).toBe path.basename(newPath)
|
||||
|
||||
describe "when a file already exists at that location", ->
|
||||
it "shows an error message and does not close the dialog", ->
|
||||
newPath = path.join(dirPath, "new-test-file.txt")
|
||||
fsUtils.writeSync(newPath, '')
|
||||
addDialog.miniEditor.insertText(path.basename(newPath))
|
||||
addDialog.trigger 'core:confirm'
|
||||
|
||||
expect(addDialog.prompt.text()).toContain 'Error'
|
||||
expect(addDialog.prompt.text()).toContain 'already exists'
|
||||
expect(addDialog).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 = path.join(dirPath, "new/dir")
|
||||
addDialog.miniEditor.insertText("new/dir/")
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fsUtils.exists(newPath)).toBeTruthy()
|
||||
expect(fsUtils.isDirectorySync(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveView().getPath()).not.toBe newPath
|
||||
expect(treeView.find(".tree-view")).toMatchSelector(':focus')
|
||||
expect(rootView.getActiveView().isFocused).toBeFalsy()
|
||||
expect(dirView.find('.directory.selected:contains(new)').length).toBe(1)
|
||||
|
||||
it "selects the created directory", ->
|
||||
treeView.attachToDom()
|
||||
newPath = path.join(dirPath, "new2/")
|
||||
addDialog.miniEditor.insertText("new2/")
|
||||
addDialog.trigger 'core:confirm'
|
||||
expect(fsUtils.exists(newPath)).toBeTruthy()
|
||||
expect(fsUtils.isDirectorySync(newPath)).toBeTruthy()
|
||||
expect(addDialog.parent()).not.toExist()
|
||||
expect(rootView.getActiveView().getPath()).not.toBe newPath
|
||||
expect(treeView.find(".tree-view")).toMatchSelector(':focus')
|
||||
expect(rootView.getActiveView().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 = path.join(dirPath, "new-dir")
|
||||
fsUtils.makeTree(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).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.find(".tree-view")).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.getActiveView().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 "when there is no entry selected", ->
|
||||
it "opens an add dialog with no path populated", ->
|
||||
addDialog.cancel()
|
||||
treeView.root.click()
|
||||
treeView.root.removeClass('selected')
|
||||
expect(treeView.selectedEntry()).toBeUndefined()
|
||||
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()
|
||||
|
||||
afterEach ->
|
||||
waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
|
||||
|
||||
it "opens a move dialog with the file's current path (excluding extension) populated", ->
|
||||
extension = path.extname(filePath)
|
||||
fileNameWithoutExtension = path.basename(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 path.basename(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 = path.join(rootDirPath, 'renamed-test-file.txt')
|
||||
moveDialog.miniEditor.setText(newPath)
|
||||
|
||||
moveDialog.trigger 'core:confirm'
|
||||
|
||||
expect(fsUtils.exists(newPath)).toBeTruthy()
|
||||
expect(fsUtils.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 = path.join(rootDirPath, 'new/directory', 'renamed-test-file.txt')
|
||||
moveDialog.miniEditor.setText(newPath)
|
||||
|
||||
moveDialog.trigger 'core:confirm'
|
||||
|
||||
waitsFor "tree view to update", ->
|
||||
treeView.root.find('> .entries > .directory:contains(new)').length > 0
|
||||
|
||||
runs ->
|
||||
expect(fsUtils.exists(newPath)).toBeTruthy()
|
||||
expect(fsUtils.exists(filePath)).toBeFalsy()
|
||||
|
||||
describe "when a file or directory already exists at the target path", ->
|
||||
it "shows an error message and does not close the dialog", ->
|
||||
runs ->
|
||||
fsUtils.writeSync(path.join(rootDirPath, 'target.txt'), '')
|
||||
newPath = path.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).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.find(".tree-view")).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.getActiveView().isFocused).toBeTruthy()
|
||||
|
||||
describe "when a file is selected that's name starts with a '.'", ->
|
||||
[dotFilePath, dotFileView, moveDialog] = []
|
||||
|
||||
beforeEach ->
|
||||
dotFilePath = path.join(dirPath, ".dotfile")
|
||||
fsUtils.writeSync(dotFilePath, "dot")
|
||||
dirView.collapse()
|
||||
dirView.expand()
|
||||
dotFileView = treeView.find('.file:contains(.dotfile)').view()
|
||||
dotFileView.click()
|
||||
treeView.trigger "tree-view:move"
|
||||
moveDialog = rootView.find(".tree-view-dialog").view()
|
||||
|
||||
it "selects the entire file name", ->
|
||||
expect(moveDialog).toExist()
|
||||
expect(moveDialog.miniEditor.getText()).toBe(project.relativize(dotFilePath))
|
||||
expect(moveDialog.miniEditor.getSelectedText()).toBe '.dotfile'
|
||||
|
||||
describe "when the project is selected", ->
|
||||
it "doesn't display the move dialog", ->
|
||||
treeView.root.click()
|
||||
treeView.trigger "tree-view:move"
|
||||
expect(rootView.find(".tree-view-dialog").view()).not.toExist()
|
||||
|
||||
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 = path.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), 'temporary')
|
||||
if fsUtils.exists(temporaryFilePath)
|
||||
fsUtils.remove(temporaryFilePath)
|
||||
waits(20)
|
||||
|
||||
afterEach ->
|
||||
fsUtils.remove(temporaryFilePath) if fsUtils.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(fsUtils.exists(temporaryFilePath)).toBeFalsy()
|
||||
entriesCountBefore = treeView.root.entries.find('.entry').length
|
||||
fsUtils.writeSync 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()
|
||||
fsUtils.remove(temporaryFilePath)
|
||||
|
||||
waitsFor "directory view contens to refresh", ->
|
||||
treeView.root.entries.find('.entry').length == entriesCountBefore
|
||||
|
||||
describe "when config.core.hideGitIgnoredFiles is changed", ->
|
||||
[ignoreFile] = []
|
||||
|
||||
beforeEach ->
|
||||
ignoreFile = path.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), '.gitignore')
|
||||
fsUtils.writeSync(ignoreFile, 'tree-view.js')
|
||||
config.set "core.hideGitIgnoredFiles", false
|
||||
|
||||
afterEach ->
|
||||
fsUtils.remove(ignoreFile) if fsUtils.exists(ignoreFile)
|
||||
|
||||
it "hides git-ignored files if the option is set, but otherwise shows them", ->
|
||||
expect(treeView.find('.file:contains(tree-view.js)').length).toBe 1
|
||||
|
||||
config.set("core.hideGitIgnoredFiles", true)
|
||||
|
||||
expect(treeView.find('.file:contains(tree-view.js)').length).toBe 0
|
||||
|
||||
config.set("core.hideGitIgnoredFiles", false)
|
||||
|
||||
expect(treeView.find('.file:contains(tree-view.js)').length).toBe 1
|
||||
|
||||
describe "Git status decorations", ->
|
||||
[ignoreFile, newFile, modifiedFile, originalFileContent] = []
|
||||
|
||||
beforeEach ->
|
||||
config.set "core.hideGitIgnoredFiles", false
|
||||
ignoreFile = path.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), '.gitignore')
|
||||
fsUtils.writeSync(ignoreFile, 'tree-view.js')
|
||||
git.getPathStatus(ignoreFile)
|
||||
|
||||
newFile = path.join(fsUtils.resolveOnLoadPath('fixtures/tree-view/dir2'), 'new2')
|
||||
fsUtils.writeSync(newFile, '')
|
||||
git.getPathStatus(newFile)
|
||||
|
||||
modifiedFile = path.join(fsUtils.resolveOnLoadPath('fixtures/tree-view/dir1'), 'file1')
|
||||
originalFileContent = fsUtils.read(modifiedFile)
|
||||
fsUtils.writeSync modifiedFile, 'ch ch changes'
|
||||
git.getPathStatus(modifiedFile)
|
||||
|
||||
treeView.updateRoot()
|
||||
treeView.root.entries.find('.directory:contains(dir2)').view().expand()
|
||||
|
||||
afterEach ->
|
||||
fsUtils.remove(ignoreFile) if fsUtils.exists(ignoreFile)
|
||||
fsUtils.remove(newFile) if fsUtils.exists(newFile)
|
||||
fsUtils.writeSync modifiedFile, originalFileContent
|
||||
|
||||
describe "when a file is modified", ->
|
||||
it "adds a custom style", ->
|
||||
treeView.root.entries.find('.directory:contains(dir1)').view().expand()
|
||||
expect(treeView.find('.file:contains(file1)')).toHaveClass 'modified'
|
||||
|
||||
describe "when a directory if modified", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.directory:contains(dir1)')).toHaveClass 'modified'
|
||||
|
||||
describe "when a file is new", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.file:contains(.gitignore)')).toHaveClass 'new'
|
||||
|
||||
describe "when a directory is new", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.directory:contains(dir2)')).toHaveClass 'new'
|
||||
|
||||
describe "when a file is ignored", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.file:contains(tree-view.js)')).toHaveClass 'ignored'
|
||||
@@ -1,129 +0,0 @@
|
||||
@import "bootstrap/less/variables.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
.tree-view-resizer {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
min-width: 50px;
|
||||
z-index: 2;
|
||||
|
||||
.tree-view-resize-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -5px;
|
||||
bottom: 0;
|
||||
width: 10px;
|
||||
cursor: col-resize;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-view-scroller {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tree-view {
|
||||
min-width: -webkit-min-content;
|
||||
min-height: 100%;
|
||||
@item-line-height: @line-height-base * 1.25;
|
||||
@disclosure-arrow-size: 12px;
|
||||
@icon-margin: @line-height-base / 4;
|
||||
|
||||
position: relative;
|
||||
padding: 0 @icon-margin;
|
||||
margin-bottom: 0;
|
||||
cursor: default;
|
||||
|
||||
.entry {
|
||||
text-wrap: none;
|
||||
white-space: nowrap;
|
||||
|
||||
.name {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.selected > .highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: @item-line-height;
|
||||
}
|
||||
}
|
||||
|
||||
.directory {
|
||||
> .header {
|
||||
line-height: @item-line-height;
|
||||
.disclosure-arrow { margin-right: @icon-margin; }
|
||||
}
|
||||
|
||||
.entries {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
line-height: @item-line-height;
|
||||
|
||||
.name {
|
||||
margin-left: @disclosure-arrow-size + @icon-margin;
|
||||
}
|
||||
}
|
||||
|
||||
// icons
|
||||
.name:before {
|
||||
margin-right: @icon-margin;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.directory > .header {
|
||||
.disclosure-arrow {
|
||||
.octicon(chevron-right, @disclosure-arrow-size);
|
||||
position: relative;
|
||||
}
|
||||
.directory-icon { .octicon(file-directory); }
|
||||
.repository-icon { .octicon(repo); }
|
||||
.submodule-icon { .octicon(file-submodule); }
|
||||
.symlink-icon { .octicon(file-symlink-directory); }
|
||||
}
|
||||
|
||||
.directory.expanded > .header {
|
||||
.disclosure-arrow { .octicon(chevron-down, @disclosure-arrow-size); }
|
||||
}
|
||||
|
||||
.file {
|
||||
.text-icon { .octicon(file-text); }
|
||||
.image-icon { .octicon(file-media); }
|
||||
.compressed-icon { .octicon(file-zip); }
|
||||
.pdf-icon { .octicon(file-pdf); }
|
||||
.readme-icon { .octicon(book); }
|
||||
.binary-icon { .octicon(file-binary); }
|
||||
.symlink-icon { .octicon(file-symlink-file); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tree-view-dialog {
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
|
||||
.prompt {
|
||||
padding-bottom: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
&:before { margin-right: @line-height-base / 4; }
|
||||
&.add-file { .octicon(file-add); }
|
||||
&.add-directory { .octicon(file-directory-create); }
|
||||
&.prompt.move { .octicon(arrow-right); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user