From 41f20f35b7d21e4c1a63e05d28174b2235e8a931 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sat, 27 Sep 2014 01:41:53 +0300 Subject: [PATCH 001/263] Added functionality to focus parent folder on deletion --- lib/tree-view.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 20777f33..657c6919 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -414,6 +414,9 @@ class TreeView extends ScrollView dialog.attach() removeSelectedEntries: -> + parents = _.uniq(_.map(@selectedPaths(), (path) -> + return path.substring(0, path.lastIndexOf('/')) + )) if @hasFocus() selectedPaths = @selectedPaths() else if activePath = @getActivePath() @@ -430,9 +433,10 @@ class TreeView extends ScrollView message: "Are you sure you want to delete the selected #{if selectedPaths.length > 1 then 'items' else 'item'}?" detailedMessage: "You are deleting:\n#{selectedPaths.join('\n')}" buttons: - "Move to Trash": -> + "Move to Trash": => for selectedPath in selectedPaths shell.moveItemToTrash(selectedPath) + @selectEntry(@list.find("[data-path='#{parents[0]}']").parents('li').first()?[0]) "Cancel": null # Public: Copy the path of the selected entry element. From 04d99a6dcb602ced8ad43d0736b001143402159a Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Fri, 3 Oct 2014 07:07:02 +0300 Subject: [PATCH 002/263] Renamed variable and uses instead of hardcoded separator --- lib/tree-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 657c6919..82c6e895 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -414,8 +414,8 @@ class TreeView extends ScrollView dialog.attach() removeSelectedEntries: -> - parents = _.uniq(_.map(@selectedPaths(), (path) -> - return path.substring(0, path.lastIndexOf('/')) + parents = _.uniq(_.map(@selectedPaths(), (selected_path) -> + return selected_path.substring(0, selected_path.lastIndexOf(path.sep)) )) if @hasFocus() selectedPaths = @selectedPaths() From 8b965439b13aeea48bc62813d715e78711a3d04e Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Fri, 3 Oct 2014 12:20:17 +0300 Subject: [PATCH 003/263] Fixed parameter naming according to convention --- lib/tree-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 82c6e895..8c6f99ea 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -414,8 +414,8 @@ class TreeView extends ScrollView dialog.attach() removeSelectedEntries: -> - parents = _.uniq(_.map(@selectedPaths(), (selected_path) -> - return selected_path.substring(0, selected_path.lastIndexOf(path.sep)) + parents = _.uniq(_.map(@selectedPaths(), (selectedPath) -> + return selectedPath.substring(0, selectedPath.lastIndexOf(path.sep)) )) if @hasFocus() selectedPaths = @selectedPaths() From 3036669be5cd7b13c739a56a99db66d8a5341f1f Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sat, 18 Jul 2015 14:25:48 -0700 Subject: [PATCH 004/263] Allow reordering project folders via drag and drop --- lib/directory-view.coffee | 1 + lib/project-folder-dnd-handler.coffee | 174 ++++++++++++++++++++++++++ lib/tree-view.coffee | 3 + styles/tree-view.less | 31 +++++ 4 files changed, 209 insertions(+) create mode 100644 lib/project-folder-dnd-handler.coffee diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 784c64f2..6781457a 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -49,6 +49,7 @@ class DirectoryView extends HTMLElement if @directory.isRoot @classList.add('project-root') + @header.classList.add('project-root-header') else @draggable = true @subscriptions.add @directory.onDidStatusChange => @updateStatus() diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee new file mode 100644 index 00000000..fa186732 --- /dev/null +++ b/lib/project-folder-dnd-handler.coffee @@ -0,0 +1,174 @@ +BrowserWindow = null # Defer require until actually used +RendererIpc = require 'ipc' + +{$, View} = require 'atom-space-pen-views' +_ = require 'underscore-plus' + +module.exports = +class ProjectFolderDragAndDropHandler + constructor: (@treeView) -> + @treeView.on 'dragstart', '.project-root-header', @onDragStart + @treeView.on 'dragend', '.project-root-header', @onDragEnd + @treeView.on 'dragleave', @onDragLeave + @treeView.on 'dragover', @onDragOver + @treeView.on 'drop', @onDrop + + RendererIpc.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) + + # unused + unsubscribe: -> + RendererIpc.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) + + onDragStart: (event) => + event.originalEvent.dataTransfer.setData 'atom-event', 'true' + + element = $(event.target).closest('.project-root-header') + projectRoot = $(event.target).closest('.project-root') + event.originalEvent.dataTransfer.setDragImage(projectRoot[0], 0, 0) + directory = projectRoot[0].directory + + event.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() + + rootIndex = -1 + _.find(@treeView.roots, (root, index) -> root.directory is directory and ((rootIndex = index) or true)) + event.originalEvent.dataTransfer.setData 'from-root-index', rootIndex + event.originalEvent.dataTransfer.setData 'from-root-path', directory.path + event.originalEvent.dataTransfer.setData 'from-process-id', @getProcessId() + event.originalEvent.dataTransfer.setData 'from-routing-id', @getRoutingId() + + event.originalEvent.dataTransfer.setData 'text/plain', directory.path + + if process.platform is 'darwin' + pathUri = "file://#{directory.path}" unless @uriHasProtocol(directory.path) + event.originalEvent.dataTransfer.setData 'text/uri-list', pathUri + + uriHasProtocol: (uri) -> + try + require('url').parse(uri).protocol? + catch error + false + + onDragLeave: (event) => + @removePlaceholder() + + onDragEnd: (event) => + @clearDropTarget() + + onDragOver: (event) => + unless event.originalEvent.dataTransfer.getData('atom-event') is 'true' + event.preventDefault() + event.stopPropagation() + return + + event.preventDefault() + event.stopPropagation() + newDropTargetIndex = @getDropTargetIndex(event) + return unless newDropTargetIndex? + + @removeDropTargetClasses() + + projectRoots = $(@treeView.roots) + + if newDropTargetIndex < projectRoots.length + element = projectRoots.eq(newDropTargetIndex).addClass 'is-drop-target' + @getPlaceholder().insertBefore(element) + else + element = projectRoots.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' + @getPlaceholder().insertAfter(element) + + onDropOnOtherWindow: (fromItemIndex) => + paths = atom.project.getPaths() + paths.splice(fromItemIndex, 1) + atom.project.setPaths(paths) + + @clearDropTarget() + + clearDropTarget: -> + element = @treeView.find(".is-dragging") + element.removeClass 'is-dragging' + element[0]?.updateTooltip() + @removeDropTargetClasses() + @removePlaceholder() + + onDrop: (event) => + event.preventDefault() + {dataTransfer} = event.originalEvent + + return unless dataTransfer.getData('atom-event') is 'true' + + fromProcessId = parseInt(dataTransfer.getData('from-process-id')) + fromRoutingId = parseInt(dataTransfer.getData('from-routing-id')) + fromRootPath = dataTransfer.getData('from-root-path') + fromIndex = parseInt(dataTransfer.getData('project-root-index')) + fromRootIndex = parseInt(dataTransfer.getData('from-root-index')) + + toIndex = @getDropTargetIndex(event) + + @clearDropTarget() + + if fromProcessId is @getProcessId() + unless fromIndex is toIndex + projectPaths = atom.project.getPaths() + projectPaths.splice(fromIndex, 1) + if toIndex > fromIndex then toIndex -= 1 + projectPaths.splice(toIndex, 0, fromRootPath) + atom.project.setPaths(projectPaths) + else + projectPaths = atom.project.getPaths() + projectPaths.splice(toIndex, 0, fromRootPath) + atom.project.setPaths(projectPaths) + + if not isNaN(fromProcessId) and not isNaN(fromRoutingId) + # Let the window where the drag started know that the tab was dropped + browserWindow = @browserWindowForProcessIdAndRoutingId(fromProcessId, fromRoutingId) + browserWindow?.webContents.send('tree-view:project-folder-dropped', fromIndex) + + removeDropTargetClasses: -> + workspaceElement = $(atom.views.getView(atom.workspace)) + workspaceElement.find('.tree-view .is-drop-target').removeClass 'is-drop-target' + workspaceElement.find('.tree-view .drop-target-is-after').removeClass 'drop-target-is-after' + + getDropTargetIndex: (event) -> + target = $(event.target) + + return if @isPlaceholder(target) + + projectRoots = $(@treeView.roots) + element = target.closest('.project-root') + element = projectRoots.last() if element.length is 0 + + return 0 unless element.length + + elementCenter = element.offset().top + element.height() / 2 + + if event.originalEvent.pageY < elementCenter + projectRoots.index(element) + else if element.next('.project-root').length > 0 + projectRoots.index(element.next('.project-root')) + else + projectRoots.index(element) + 1 + + + getPlaceholder: -> + @placeholderEl ?= $('
  • ', class: 'placeholder') + + removePlaceholder: -> + @placeholderEl?.remove() + @placeholderEl = null + + isPlaceholder: (element) -> + element.is('.placeholder') + + getProcessId: -> + @processId ?= atom.getCurrentWindow().getProcessId() + + getRoutingId: -> + @routingId ?= atom.getCurrentWindow().getRoutingId() + + browserWindowForProcessIdAndRoutingId: (processId, routingId) -> + BrowserWindow ?= require('remote').require('browser-window') + for browserWindow in BrowserWindow.getAllWindows() + if browserWindow.getProcessId() is processId and browserWindow.getRoutingId() is routingId + return browserWindow + + return diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 457a299c..c74a3600 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -15,6 +15,7 @@ Minimatch = null # Defer requiring until actually needed Directory = require './directory' DirectoryView = require './directory-view' FileView = require './file-view' +ProjectFolderDragAndDropHandler = require './project-folder-dnd-handler' LocalStorage = window.localStorage toggleConfig = (keyPath) -> @@ -40,6 +41,8 @@ class TreeView extends View @ignoredPatterns = [] @dragEventCounts = new WeakMap + // TODO: use dragEventCounts & existing drag handlers + @projectFolderDragAndDropHandler = new ProjectFolderDragAndDropHandler(this) @handleEvents() diff --git a/styles/tree-view.less b/styles/tree-view.less index e9b08a15..89b70ee6 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -40,6 +40,10 @@ } } +.project-root-header { + -webkit-user-drag: element; +} + .tree-view-scroller { display: flex; flex-direction: column; @@ -72,6 +76,33 @@ position: absolute; } } + + /* Drag and Drop */ + .placeholder { + position: absolute; + left: @component-icon-padding; + z-index: 999; + display: inline-block; + height: 2px; + margin: -1px; padding: 0; + + width: 100%; + background: @background-color-info; + + list-style: none; + + &:after { + content: ""; + position: absolute; + left: 0; + margin: -1px; + width: 4px; + height: 4px; + background: @background-color-info; + border-radius: 4px; + border: 1px solid transparent; + } + } } .platform-win32 { From e388330a23bcd68adf8bec00a7130d7c8ab805d9 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Fri, 4 Sep 2015 13:09:36 -0700 Subject: [PATCH 005/263] Fix project folder dnd to work with file movement --- lib/project-folder-dnd-handler.coffee | 30 +++++++++++++++++---------- lib/tree-view.coffee | 20 ++++++++++++++++-- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index fa186732..361ad74d 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -7,11 +7,7 @@ _ = require 'underscore-plus' module.exports = class ProjectFolderDragAndDropHandler constructor: (@treeView) -> - @treeView.on 'dragstart', '.project-root-header', @onDragStart @treeView.on 'dragend', '.project-root-header', @onDragEnd - @treeView.on 'dragleave', @onDragLeave - @treeView.on 'dragover', @onDragOver - @treeView.on 'drop', @onDrop RendererIpc.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) @@ -20,11 +16,11 @@ class ProjectFolderDragAndDropHandler RendererIpc.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) onDragStart: (event) => - event.originalEvent.dataTransfer.setData 'atom-event', 'true' + unless $(event.target).closest('.project-root-header').size() + return - element = $(event.target).closest('.project-root-header') + event.originalEvent.dataTransfer.setData 'atom-event', 'true' projectRoot = $(event.target).closest('.project-root') - event.originalEvent.dataTransfer.setDragImage(projectRoot[0], 0, 0) directory = projectRoot[0].directory event.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() @@ -56,12 +52,13 @@ class ProjectFolderDragAndDropHandler onDragOver: (event) => unless event.originalEvent.dataTransfer.getData('atom-event') is 'true' - event.preventDefault() - event.stopPropagation() return - event.preventDefault() - event.stopPropagation() + entry = event.currentTarget + if entry.classList.contains('selected') + @clearDropTarget() + return + newDropTargetIndex = @getDropTargetIndex(event) return unless newDropTargetIndex? @@ -148,6 +145,17 @@ class ProjectFolderDragAndDropHandler else projectRoots.index(element) + 1 + isMovingProjectFolders: (event) -> + target = $(event.target) + return null if @isPlaceholder(target) + + element = target.closest('.project-root-header') + return false unless element.length + + elementCenter = element.offset().top + element.height() / 2 + return true if event.originalEvent.pageY < elementCenter + + false getPlaceholder: -> @placeholderEl ?= $('
  • ', class: 'placeholder') diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index c74a3600..b9ee1ce9 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -41,7 +41,6 @@ class TreeView extends View @ignoredPatterns = [] @dragEventCounts = new WeakMap - // TODO: use dragEventCounts & existing drag handlers @projectFolderDragAndDropHandler = new ProjectFolderDragAndDropHandler(this) @handleEvents() @@ -812,6 +811,7 @@ class TreeView extends View entry = e.currentTarget.parentNode @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) entry.classList.remove('selected') if @dragEventCounts.get(entry) is 0 + @projectFolderDragAndDropHandler.onDragLeave(e) # Handle entry name object dragstart event onDragStart: (e) -> @@ -838,21 +838,37 @@ class TreeView extends View window.requestAnimationFrame -> fileNameElement.remove() + @projectFolderDragAndDropHandler.onDragStart(e) + # Handle entry dragover event; reset default dragover actions onDragOver: (e) -> e.preventDefault() e.stopPropagation() + entry = e.currentTarget + if @projectFolderDragAndDropHandler.isMovingProjectFolders(e) + entry.classList.remove('selected') + else if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') + entry.classList.add('selected') + + @projectFolderDragAndDropHandler.onDragOver(e) + # Handle entry drop event onDrop: (e) -> e.preventDefault() e.stopPropagation() entry = e.currentTarget - return unless entry instanceof DirectoryView + unless entry instanceof DirectoryView + @projectFolderDragAndDropHandler.onDrop(e) + return entry.classList.remove('selected') + if @projectFolderDragAndDropHandler.isMovingProjectFolders(e) + @projectFolderDragAndDropHandler.onDrop(e) + return + newDirectoryPath = $(entry).find(".name").data("path") return false unless newDirectoryPath From 016910296fd436636c12bea0f3517b8dbd49b47f Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sat, 19 Sep 2015 20:38:21 -0700 Subject: [PATCH 006/263] replace use of deprecated apis --- lib/project-folder-dnd-handler.coffee | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 361ad74d..6a744bd5 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -29,8 +29,7 @@ class ProjectFolderDragAndDropHandler _.find(@treeView.roots, (root, index) -> root.directory is directory and ((rootIndex = index) or true)) event.originalEvent.dataTransfer.setData 'from-root-index', rootIndex event.originalEvent.dataTransfer.setData 'from-root-path', directory.path - event.originalEvent.dataTransfer.setData 'from-process-id', @getProcessId() - event.originalEvent.dataTransfer.setData 'from-routing-id', @getRoutingId() + event.originalEvent.dataTransfer.setData 'from-window-id', @getWindowId() event.originalEvent.dataTransfer.setData 'text/plain', directory.path @@ -93,8 +92,7 @@ class ProjectFolderDragAndDropHandler return unless dataTransfer.getData('atom-event') is 'true' - fromProcessId = parseInt(dataTransfer.getData('from-process-id')) - fromRoutingId = parseInt(dataTransfer.getData('from-routing-id')) + fromWindowId = parseInt(dataTransfer.getData('from-window-id')) fromRootPath = dataTransfer.getData('from-root-path') fromIndex = parseInt(dataTransfer.getData('project-root-index')) fromRootIndex = parseInt(dataTransfer.getData('from-root-index')) @@ -103,7 +101,7 @@ class ProjectFolderDragAndDropHandler @clearDropTarget() - if fromProcessId is @getProcessId() + if fromWindowId is @getWindowId() unless fromIndex is toIndex projectPaths = atom.project.getPaths() projectPaths.splice(fromIndex, 1) @@ -115,9 +113,9 @@ class ProjectFolderDragAndDropHandler projectPaths.splice(toIndex, 0, fromRootPath) atom.project.setPaths(projectPaths) - if not isNaN(fromProcessId) and not isNaN(fromRoutingId) + if not isNaN(fromWindowId) # Let the window where the drag started know that the tab was dropped - browserWindow = @browserWindowForProcessIdAndRoutingId(fromProcessId, fromRoutingId) + browserWindow = @browserWindowForId(fromWindowId) browserWindow?.webContents.send('tree-view:project-folder-dropped', fromIndex) removeDropTargetClasses: -> @@ -167,16 +165,9 @@ class ProjectFolderDragAndDropHandler isPlaceholder: (element) -> element.is('.placeholder') - getProcessId: -> - @processId ?= atom.getCurrentWindow().getProcessId() + getWindowId: -> + @processId ?= atom.getCurrentWindow().id - getRoutingId: -> - @routingId ?= atom.getCurrentWindow().getRoutingId() - - browserWindowForProcessIdAndRoutingId: (processId, routingId) -> + browserWindowForId: (id) -> BrowserWindow ?= require('remote').require('browser-window') - for browserWindow in BrowserWindow.getAllWindows() - if browserWindow.getProcessId() is processId and browserWindow.getRoutingId() is routingId - return browserWindow - - return + BrowserWindow.fromId id From d96e59bebee5d42861c00c2148fb46f28746f228 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 5 Oct 2015 15:08:05 -0700 Subject: [PATCH 007/263] Update drag and drop behaviour Now, instead of the top 50% being used for sorting project folders and the bottom 50% for moving the actual files, the middle 40% is used for moving the actual files, and the top & bottom 30% is used for sorting project folders. --- lib/project-folder-dnd-handler.coffee | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 6a744bd5..59f7fd4d 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -129,19 +129,21 @@ class ProjectFolderDragAndDropHandler return if @isPlaceholder(target) projectRoots = $(@treeView.roots) - element = target.closest('.project-root') - element = projectRoots.last() if element.length is 0 + projectRoot = target.closest('.project-root') + projectRoot = projectRoots.last() if projectRoot.length is 0 - return 0 unless element.length + return 0 unless projectRoot.length + + element = projectRoot.find('.project-root-header') elementCenter = element.offset().top + element.height() / 2 if event.originalEvent.pageY < elementCenter - projectRoots.index(element) - else if element.next('.project-root').length > 0 - projectRoots.index(element.next('.project-root')) + projectRoots.index(projectRoot) + else if projectRoot.next('.project-root').length > 0 + projectRoots.index(projectRoot.next('.project-root')) else - projectRoots.index(element) + 1 + projectRoots.index(projectRoot) + 1 isMovingProjectFolders: (event) -> target = $(event.target) @@ -150,8 +152,10 @@ class ProjectFolderDragAndDropHandler element = target.closest('.project-root-header') return false unless element.length - elementCenter = element.offset().top + element.height() / 2 - return true if event.originalEvent.pageY < elementCenter + elementTop30 = element.offset().top + element.height() * 0.3 + return true if event.originalEvent.pageY < elementTop30 + elementBottom30 = element.offset().top + element.height() * 0.7 + return true if event.originalEvent.pageY > elementBottom30 false From c5925356fb27e79e432fa00cee4356b1f4445149 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 16 Nov 2015 12:36:58 -0800 Subject: [PATCH 008/263] Fix placeholder width --- styles/tree-view.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index 89b70ee6..13b6ddb7 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -86,7 +86,7 @@ height: 2px; margin: -1px; padding: 0; - width: 100%; + width: calc(~"100% -" @component-icon-padding); background: @background-color-info; list-style: none; From 7833d59ac79058af620fba97a183ceb7d097f5bf Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 16 Nov 2015 16:47:54 -0800 Subject: [PATCH 009/263] Add tests for drag and drop root folders --- lib/project-folder-dnd-handler.coffee | 3 +- spec/event-helpers.coffee | 38 +++++++- spec/tree-view-spec.coffee | 127 ++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 59f7fd4d..036201d6 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -33,7 +33,7 @@ class ProjectFolderDragAndDropHandler event.originalEvent.dataTransfer.setData 'text/plain', directory.path - if process.platform is 'darwin' + if process.platform in ['darwin', 'linux'] pathUri = "file://#{directory.path}" unless @uriHasProtocol(directory.path) event.originalEvent.dataTransfer.setData 'text/uri-list', pathUri @@ -90,6 +90,7 @@ class ProjectFolderDragAndDropHandler event.preventDefault() {dataTransfer} = event.originalEvent + # TODO: support dragging folders from the filesystem -- electron needs to add support first return unless dataTransfer.getData('atom-event') is 'true' fromWindowId = parseInt(dataTransfer.getData('from-window-id')) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 9ebb803e..8c5707e6 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -39,4 +39,40 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> for filePath in filePaths dropEvent.originalEvent.dataTransfer.files.push({path: filePath}) - dropEvent \ No newline at end of file + dropEvent + +buildElementPositionalDragEvents = (el, dataTransfer) -> + if not el? + return {} + $el = $(el) + topEvent = $.Event() + topEvent.target = el + topEvent.currentTarget = el + topEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().top} + + middleEvent = $.Event() + middleEvent.target = el + middleEvent.currentTarget = el + middleEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().top + $el.height() * 0.5} + + bottomEvent = $.Event() + bottomEvent.target = el + bottomEvent.currentTarget = el + bottomEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().bottom} + + {top: topEvent, middle: middleEvent, bottom: bottomEvent} + + +module.exports.buildPositionalDragEvents = (dragged, target) -> + dataTransfer = + data: {} + setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values + getData: (key) -> @data[key] + setDragImage: (@image) -> return + + dragStartEvent = $.Event() + dragStartEvent.target = dragged + dragStartEvent.currentTarget = dragged + dragStartEvent.originalEvent = {dataTransfer, "atom-event": true} + + [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer)] diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index fd316180..96655a86 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2941,3 +2941,130 @@ describe "TreeView", -> runs -> expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4 + + describe "Dragging and dropping root folders", -> + [alphaDirPath, gammaDirPath, thetaDirPath] = [] + beforeEach -> + rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) + + alphaFilePath = path.join(rootDirPath, "alpha.txt") + zetaFilePath = path.join(rootDirPath, "zeta.txt") + + alphaDirPath = path.join(rootDirPath, "alpha") + betaFilePath = path.join(alphaDirPath, "beta.txt") + + gammaDirPath = path.join(rootDirPath, "gamma") + deltaFilePath = path.join(gammaDirPath, "delta.txt") + epsilonFilePath = path.join(gammaDirPath, "epsilon.txt") + thetaDirPath = path.join(rootDirPath, "theta") + + fs.writeFileSync(alphaFilePath, "doesn't matter") + fs.writeFileSync(zetaFilePath, "doesn't matter") + + fs.makeTreeSync(alphaDirPath) + fs.writeFileSync(betaFilePath, "doesn't matter") + + fs.makeTreeSync(gammaDirPath) + fs.writeFileSync(deltaFilePath, "doesn't matter") + fs.writeFileSync(epsilonFilePath, "doesn't matter") + fs.makeTreeSync(thetaDirPath) + + atom.project.setPaths([alphaDirPath, gammaDirPath, thetaDirPath]) + + jasmine.attachToDOM(workspaceElement) + + afterEach -> + [alphaDirPath, gammaDirPath, thetaDirPath] = [] + + describe "when dragging a project root's header onto a different project root's header", -> + describe "when dragging on the top part of the header", -> + it "should add the placeholder above the directory", -> + # Dragging gammaDir onto alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent, dragOverEvents] = + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDragOver(dragOverEvents.top) + expect(alphaDir[0].previousSibling).toHaveClass('placeholder') + + # Is removed when drag ends + treeView.projectFolderDragAndDropHandler.onDragEnd() + expect('.placeholder').not.toExist() + + describe "when dragging on the middle part of the header", -> + it "should not add the placeholder", -> + # Dragging gammaDir onto alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent, dragOverEvents] = + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDragEnter(dragOverEvents.middle) + treeView.onDragOver(dragOverEvents.middle) + expect(alphaDir).toHaveClass('selected') + expect('.placeholder').not.toExist() + + # Is removed when drag ends + treeView.projectFolderDragAndDropHandler.onDragEnd() + expect('.placeholder').not.toExist() + + describe "when dragging on the bottom part of the header", -> + it "should add the placeholder below the directory", -> + # Dragging gammaDir onto alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent, dragOverEvents] = + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDragOver(dragOverEvents.bottom) + expect(alphaDir[0].nextSibling).toHaveClass('placeholder') + + # Is removed when drag ends + treeView.projectFolderDragAndDropHandler.onDragEnd() + expect('.placeholder').not.toExist() + + describe "when dropping a project root's header onto a different project root's header", -> + describe "when dropping on the top part of the header", -> + it "should add the placeholder above the directory", -> + # dropping gammaDir above alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent, dragDropEvents] = + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dragDropEvents.top) + projectPaths = atom.project.getPaths() + expect(projectPaths[0]).toEqual(gammaDirPath); + expect(projectPaths[1]).toEqual(alphaDirPath); + + # Is removed when drag ends + expect('.placeholder').not.toExist() + + describe "when dropping on the bottom part of the header", -> + it "should add the placeholder below the directory", -> + # dropping thetaDir below alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + thetaDir = $(treeView).find('.project-root:contains(theta):first') + [dragStartEvent, dragDropEvents] = + eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dragDropEvents.bottom) + projectPaths = atom.project.getPaths() + expect(projectPaths[0]).toEqual(alphaDirPath); + expect(projectPaths[1]).toEqual(thetaDirPath); + expect(projectPaths[2]).toEqual(gammaDirPath); + + # Is removed when drag ends + expect('.placeholder').not.toExist() + + describe "when dropped from another window", -> + # TODO + + describe "when dropped to another window", -> + # TODO From 917afe075dc86e216b13cc5c4c8d3e470126a579 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 16 Nov 2015 17:28:56 -0800 Subject: [PATCH 010/263] Finish tree view drag and drop tests --- spec/event-helpers.coffee | 8 +++--- spec/tree-view-spec.coffee | 54 +++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 8c5707e6..c294b44c 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -48,17 +48,17 @@ buildElementPositionalDragEvents = (el, dataTransfer) -> topEvent = $.Event() topEvent.target = el topEvent.currentTarget = el - topEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().top} + topEvent.originalEvent = {dataTransfer, pageY: $el.offset().top} middleEvent = $.Event() middleEvent.target = el middleEvent.currentTarget = el - middleEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().top + $el.height() * 0.5} + middleEvent.originalEvent = {dataTransfer, pageY: $el.offset().top + $el.height() * 0.5} bottomEvent = $.Event() bottomEvent.target = el bottomEvent.currentTarget = el - bottomEvent.originalEvent = {dataTransfer, "atom-event": true, pageY: $el.offset().bottom} + bottomEvent.originalEvent = {dataTransfer, pageY: $el.offset().bottom} {top: topEvent, middle: middleEvent, bottom: bottomEvent} @@ -73,6 +73,6 @@ module.exports.buildPositionalDragEvents = (dragged, target) -> dragStartEvent = $.Event() dragStartEvent.target = dragged dragStartEvent.currentTarget = dragged - dragStartEvent.originalEvent = {dataTransfer, "atom-event": true} + dragStartEvent.originalEvent = {dataTransfer} [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer)] diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 96655a86..6de74c9c 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2943,7 +2943,7 @@ describe "TreeView", -> expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4 describe "Dragging and dropping root folders", -> - [alphaDirPath, gammaDirPath, thetaDirPath] = [] + [alphaDirPath, gammaDirPath, thetaDirPath, etaDirPath] = [] beforeEach -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) @@ -2956,7 +2956,9 @@ describe "TreeView", -> gammaDirPath = path.join(rootDirPath, "gamma") deltaFilePath = path.join(gammaDirPath, "delta.txt") epsilonFilePath = path.join(gammaDirPath, "epsilon.txt") + thetaDirPath = path.join(rootDirPath, "theta") + etaDirPath = path.join(rootDirPath, "eta") fs.writeFileSync(alphaFilePath, "doesn't matter") fs.writeFileSync(zetaFilePath, "doesn't matter") @@ -2968,13 +2970,14 @@ describe "TreeView", -> fs.writeFileSync(deltaFilePath, "doesn't matter") fs.writeFileSync(epsilonFilePath, "doesn't matter") fs.makeTreeSync(thetaDirPath) + fs.makeTreeSync(etaDirPath) atom.project.setPaths([alphaDirPath, gammaDirPath, thetaDirPath]) jasmine.attachToDOM(workspaceElement) afterEach -> - [alphaDirPath, gammaDirPath, thetaDirPath] = [] + [alphaDirPath, gammaDirPath, thetaDirPath, etaDirPath] = [] describe "when dragging a project root's header onto a different project root's header", -> describe "when dragging on the top part of the header", -> @@ -3063,8 +3066,47 @@ describe "TreeView", -> # Is removed when drag ends expect('.placeholder').not.toExist() - describe "when dropped from another window", -> - # TODO + describe "when a root folder is dragged out of application", -> + it "should carry the folder's information", -> + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) + treeView.onDragStart(dragStartEvent) + + expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath + if process.platform in ['darwin', 'linux'] + expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual "file://#{gammaDirPath}" + + describe "when a root folder is dropped from another Atom window", -> + it "adds the root folder to the window", -> + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0]) + + dropEvent = dragDropEvents.bottom + dropEvent.originalEvent.dataTransfer.setData('atom-event', true) + dropEvent.originalEvent.dataTransfer.setData('from-window-id', treeView.projectFolderDragAndDropHandler.getWindowId() + 1) + dropEvent.originalEvent.dataTransfer.setData('from-root-path', etaDirPath) + + # mock browserWindowForId + browserWindowMock = {webContents: {send: ->}} + treeView.projectFolderDragAndDropHandler.browserWindowForId = -> browserWindowMock + spyOn(browserWindowMock.webContents, 'send') + + treeView.onDrop(dropEvent) + + waitsFor -> + browserWindowMock.webContents.send.callCount > 0 + + runs -> + expect(atom.project.getPaths()).toContain etaDirPath + expect('.placeholder').not.toExist() + + + describe "when a root folder is dropped to another Atom window", -> + it "removes the root folder from the first window", -> + gammaDir = $(treeView).find('.project-root:contains(gamma):first') + [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) + treeView.onDragStart(dragStartEvent) + treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow(gammaDir.index()) - describe "when dropped to another window", -> - # TODO + expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] + expect('.placeholder').not.toExist() From 4c62607540eda111f1fe4eed6416d83322f6fef7 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 16 Nov 2015 17:48:09 -0800 Subject: [PATCH 011/263] Fix lint issues --- spec/tree-view-spec.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6de74c9c..2f3d0880 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3042,8 +3042,8 @@ describe "TreeView", -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dragDropEvents.top) projectPaths = atom.project.getPaths() - expect(projectPaths[0]).toEqual(gammaDirPath); - expect(projectPaths[1]).toEqual(alphaDirPath); + expect(projectPaths[0]).toEqual(gammaDirPath) + expect(projectPaths[1]).toEqual(alphaDirPath) # Is removed when drag ends expect('.placeholder').not.toExist() @@ -3059,9 +3059,9 @@ describe "TreeView", -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dragDropEvents.bottom) projectPaths = atom.project.getPaths() - expect(projectPaths[0]).toEqual(alphaDirPath); - expect(projectPaths[1]).toEqual(thetaDirPath); - expect(projectPaths[2]).toEqual(gammaDirPath); + expect(projectPaths[0]).toEqual(alphaDirPath) + expect(projectPaths[1]).toEqual(thetaDirPath) + expect(projectPaths[2]).toEqual(gammaDirPath) # Is removed when drag ends expect('.placeholder').not.toExist() From 7fcbb6c00f28489e980f3d2ebe5abda4c85f905b Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 16 Nov 2015 18:15:34 -0800 Subject: [PATCH 012/263] Replace _.find with comprehension --- lib/project-folder-dnd-handler.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 036201d6..054d99bc 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -26,7 +26,8 @@ class ProjectFolderDragAndDropHandler event.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() rootIndex = -1 - _.find(@treeView.roots, (root, index) -> root.directory is directory and ((rootIndex = index) or true)) + (rootIndex = index; break) for root, index in @treeView.roots when root.directory is directory + event.originalEvent.dataTransfer.setData 'from-root-index', rootIndex event.originalEvent.dataTransfer.setData 'from-root-path', directory.path event.originalEvent.dataTransfer.setData 'from-window-id', @getWindowId() From 9d3758f3e032ee18b2d3423ae53e6bc222b9e888 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Mon, 7 Mar 2016 11:33:10 +0100 Subject: [PATCH 013/263] Catch listSync errors. Prevent crash on unreadable directory. Fix #687, fix #671, fix #651 --- lib/directory.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index 73e0ed84..3fbbf158 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -266,7 +266,10 @@ class Directory squashDirectoryNames: (fullPath) -> squashedDirs = [@name] loop - contents = fs.listSync fullPath + try + contents = fs.listSync fullPath + catch error + break break if contents.length isnt 1 break if not fs.isDirectorySync(contents[0]) relativeDir = path.relative(fullPath, contents[0]) From fa47b685866666d704043673cd816f481ae220cb Mon Sep 17 00:00:00 2001 From: Yuichi Tanikawa Date: Tue, 15 Mar 2016 12:17:41 +0900 Subject: [PATCH 014/263] Add a failing test spec for #705 --- spec/tree-view-spec.coffee | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index b9ddc2dc..d2d70934 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2354,6 +2354,11 @@ describe "TreeView", -> iotaDirPath = path.join(lambdaDirPath, "iota") kappaDirPath = path.join(lambdaDirPath, "kappa") + muDirPath = path.join(rootDirPath, "mu") + nuDirPath = path.join(muDirPath, "nu") + xiDirPath1 = path.join(muDirPath, "xi") + xiDirPath2 = path.join(nuDirPath, "xi") + fs.makeTreeSync(zetaDirPath) fs.writeFileSync(zetaFilePath, "doesn't matter") @@ -2370,6 +2375,11 @@ describe "TreeView", -> fs.makeTreeSync(iotaDirPath) fs.makeTreeSync(kappaDirPath) + fs.makeTreeSync(muDirPath) + fs.makeTreeSync(nuDirPath) + fs.makeTreeSync(xiDirPath1) + fs.makeTreeSync(xiDirPath2) + atom.project.setPaths([rootDirPath]) it "defaults to disabled", -> @@ -2411,6 +2421,16 @@ describe "TreeView", -> expect(lambdaEntries).toEqual(["iota", "kappa"]) + describe "when a directory is reloaded", -> + it "squashes the directory names the last of which is same as an unsquashed directory", -> + muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') + muDir[0].expand() + muDir[0].reload() + muEntries = [].slice.call(muDir[0].children[1].children).map (element) -> + element.innerText + + expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) + describe "Git status decorations", -> [projectPath, modifiedFile, originalFileContent] = [] From 401acd041b18bfd04b2695897353d14d848852ff Mon Sep 17 00:00:00 2001 From: Yuichi Tanikawa Date: Tue, 15 Mar 2016 11:36:54 +0900 Subject: [PATCH 015/263] Fix squashed directories appearing duplicated when reloaded --- lib/directory-view.coffee | 17 ++++++++--------- lib/directory.coffee | 3 +-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 784c64f2..82c135f0 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -33,17 +33,16 @@ class DirectoryView extends HTMLElement @directoryName.title = @directory.name @directoryName.dataset.path = @directory.path - if @directory.squashedName? - @squashedDirectoryName = document.createElement('span') - @squashedDirectoryName.classList.add('squashed-dir') - @squashedDirectoryName.textContent = @directory.squashedName - - directoryNameTextNode = document.createTextNode(@directory.name) + if @directory.squashedNames? + squashedDirectoryNameNode = document.createElement('span') + squashedDirectoryNameNode.classList.add('squashed-dir') + squashedDirectoryNameNode.textContent = @directory.squashedNames[0] + @directoryName.appendChild(squashedDirectoryNameNode) + @directoryName.appendChild(document.createTextNode(@directory.squashedNames[1])) + else + @directoryName.textContent = @directory.name @appendChild(@header) - if @squashedDirectoryName? - @directoryName.appendChild(@squashedDirectoryName) - @directoryName.appendChild(directoryNameTextNode) @header.appendChild(@directoryName) @appendChild(@entries) diff --git a/lib/directory.coffee b/lib/directory.coffee index 73e0ed84..1eaad083 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -274,7 +274,6 @@ class Directory fullPath = path.join(fullPath, relativeDir) if squashedDirs.length > 1 - @squashedName = squashedDirs[0..squashedDirs.length - 2].join(path.sep) + path.sep - @name = squashedDirs[squashedDirs.length - 1] + @squashedNames = [squashedDirs[0..squashedDirs.length - 2].join(path.sep) + path.sep, _.last(squashedDirs)] return fullPath From d79dc4b82ad021860f9082cdd1b1ef8ee26dca0c Mon Sep 17 00:00:00 2001 From: Yuichi Tanikawa Date: Tue, 15 Mar 2016 19:56:31 +0900 Subject: [PATCH 016/263] Update data-name and title attributes of squashed directories --- lib/directory-view.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 82c135f0..ad1a8501 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -29,17 +29,19 @@ class DirectoryView extends HTMLElement else iconClass = 'icon-file-submodule' if @directory.submodule @directoryName.classList.add(iconClass) - @directoryName.dataset.name = @directory.name - @directoryName.title = @directory.name @directoryName.dataset.path = @directory.path if @directory.squashedNames? + @directoryName.dataset.name = @directory.squashedNames.join('') + @directoryName.title = @directory.squashedNames.join('') squashedDirectoryNameNode = document.createElement('span') squashedDirectoryNameNode.classList.add('squashed-dir') squashedDirectoryNameNode.textContent = @directory.squashedNames[0] @directoryName.appendChild(squashedDirectoryNameNode) @directoryName.appendChild(document.createTextNode(@directory.squashedNames[1])) else + @directoryName.dataset.name = @directory.name + @directoryName.title = @directory.name @directoryName.textContent = @directory.name @appendChild(@header) From b5bbb006acd48df3261da0d34fb65a13fea12a07 Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 30 Mar 2016 18:31:13 -0400 Subject: [PATCH 017/263] Fix symlinkSync not working on Windows --- spec/default-file-icons-spec.coffee | 4 ++-- spec/tree-view-spec.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/default-file-icons-spec.coffee b/spec/default-file-icons-spec.coffee index 1eef0354..83ea9fa2 100644 --- a/spec/default-file-icons-spec.coffee +++ b/spec/default-file-icons-spec.coffee @@ -41,7 +41,7 @@ describe 'DefaultFileIcons', -> filePath = path.join(tempDir, 'foo.bar') linkPath = path.join(tempDir, 'link.bar') fs.writeFileSync(filePath, '') - fs.symlinkSync(filePath, linkPath) + fs.symlinkSync(filePath, linkPath, 'junction') expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file') @@ -49,6 +49,6 @@ describe 'DefaultFileIcons', -> filePath = path.join(tempDir, 'foo.zip') linkPath = path.join(tempDir, 'link.zip') fs.writeFileSync(filePath, '') - fs.symlinkSync(filePath, linkPath) + fs.symlinkSync(filePath, linkPath, 'junction') expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file') diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index ac95bc6a..09a393fa 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2491,7 +2491,7 @@ describe "TreeView", -> describe "when the project is a symbolic link to the repository root", -> beforeEach -> symlinkPath = temp.path('tree-view-project') - fs.symlinkSync(projectPath, symlinkPath) + fs.symlinkSync(projectPath, symlinkPath, 'junction') atom.project.setPaths([symlinkPath]) $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand() From c7fd525909dd75b90d18826ed5a5a576a86db755 Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 30 Mar 2016 20:57:13 -0400 Subject: [PATCH 018/263] Fix backslash escaping issues --- spec/tree-view-spec.coffee | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 09a393fa..c1c65cde 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2388,7 +2388,11 @@ describe "TreeView", -> expect(zetaEntries).toEqual(["zeta.txt"]) it "squashes two dir names when the first only contains a single dir", -> - betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha#{path.sep}beta):first") + if path.sep is '\\' + # First escape the backslashes for Coffeescript, then escape them for jQuery + betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha\\\\beta):first") + else + betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha#{path.sep}beta):first") betaDir[0].expand() betaEntries = [].slice.call(betaDir[0].children[1].children).map (element) -> element.innerText @@ -2396,7 +2400,11 @@ describe "TreeView", -> expect(betaEntries).toEqual(["beta.txt"]) it "squashes three dir names when the first and second only contain single dirs", -> - epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma#{path.sep}delta#{path.sep}epsilon):first") + if path.sep is '\\' + # First escape the backslashes for Coffeescript, then escape them for jQuery + epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma\\\\delta\\\\epsilon):first") + else + epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma#{path.sep}delta#{path.sep}epsilon):first") epsilonDir[0].expand() epsilonEntries = [].slice.call(epsilonDir[0].children[1].children).map (element) -> element.innerText From 2d521dd2c76728aaa992c68df0a61f9dd0f8a7e8 Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 30 Mar 2016 21:25:54 -0400 Subject: [PATCH 019/263] Fix Windows error message when failing to open externally --- spec/tree-view-spec.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index c1c65cde..a4c5c46b 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2874,7 +2874,11 @@ describe "TreeView", -> runs -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in Finder failed' - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' + + if process.platform isnt 'win32' + expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' + else # Typical Windows + expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'The system cannot find the path specified.' describe "when reloading a directory with deletions and additions", -> it "does not throw an error (regression)", -> From 3d56064c5f2adf03d0fde4b2fa6f457fd96a00a0 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 31 Mar 2016 21:43:56 -0400 Subject: [PATCH 020/263] Wait for status change before checking modified state --- spec/tree-view-spec.coffee | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index a4c5c46b..6b79bdd0 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2515,11 +2515,19 @@ describe "TreeView", -> describe "when a file loses its modified status", -> it "updates its and its parent directories' styles", -> - fs.writeFileSync(modifiedFile, originalFileContent) - atom.project.getRepositories()[0].getPathStatus(modifiedFile) + runs -> + fs.writeFileSync(modifiedFile, originalFileContent) + + waitsFor (done) -> + disposable = atom.project.getRepositories()[0].onDidChangeStatuses -> + disposable.dispose() + done() + + runs -> + atom.project.getRepositories()[0].getPathStatus(modifiedFile) - expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified' - expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified' + expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified' + expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified' describe "when the resize handle is double clicked", -> beforeEach -> From 08d02d3213625336029a9f7ee5a9d9f842675cd9 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 31 Mar 2016 21:51:16 -0400 Subject: [PATCH 021/263] Revert "Wait for status change before checking modified state" This reverts commit 3d56064c5f2adf03d0fde4b2fa6f457fd96a00a0. --- spec/tree-view-spec.coffee | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6b79bdd0..a4c5c46b 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2515,19 +2515,11 @@ describe "TreeView", -> describe "when a file loses its modified status", -> it "updates its and its parent directories' styles", -> - runs -> - fs.writeFileSync(modifiedFile, originalFileContent) - - waitsFor (done) -> - disposable = atom.project.getRepositories()[0].onDidChangeStatuses -> - disposable.dispose() - done() - - runs -> - atom.project.getRepositories()[0].getPathStatus(modifiedFile) + fs.writeFileSync(modifiedFile, originalFileContent) + atom.project.getRepositories()[0].getPathStatus(modifiedFile) - expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified' - expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified' + expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified' + expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified' describe "when the resize handle is double clicked", -> beforeEach -> From 06f70307033ff220370ca4630ecad2e8a4f962fd Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sat, 30 Apr 2016 02:23:27 +1000 Subject: [PATCH 022/263] Allow packages to provide multiple file-icon classes --- lib/file-view.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index dc65a1c9..b3045c29 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,11 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - @fileName.classList.add(FileIcons.getService().iconClassForPath(@file.path)) + iconClass = FileIcons.getService().iconClassForPath(@file.path) + if iconClass + unless Array.isArray iconClass + iconClass = iconClass.toString().split(/\s+/g) + @fileName.classList.add(iconClass...) @subscriptions.add @file.onDidStatusChange => @updateStatus() @updateStatus() From 2c5801262744593695b99979085e3a494577bed8 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Tue, 10 May 2016 21:41:15 +1000 Subject: [PATCH 023/263] Pass file references as a secondary argument If the package providing the service needs additional information on the file, they can't do so without querying the filesystem. For instance, an author may need a link-icon shown for symlinked files. Without providing a direct file reference, this becomes much more cumbersome. --- lib/file-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index b3045c29..c3aa3412 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path) + iconClass = FileIcons.getService().iconClassForPath(@file.path, @file) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) From e36352a0a61f444608fd98cc329349804d3bc4f1 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Wed, 11 May 2016 11:20:08 +1000 Subject: [PATCH 024/263] Emit events when directories are toggled --- lib/directory.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/directory.coffee b/lib/directory.coffee index 279b64b1..add9c0cf 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -54,6 +54,12 @@ class Directory onDidRemoveEntries: (callback) -> @emitter.on('did-remove-entries', callback) + onDidCollapse: (callback) -> + @emitter.on('did-collapse', callback) + + onDidExpand: (callback) -> + @emitter.on('did-expand', callback) + loadRealPath: -> if @useSyncFS @realPath = fs.realpathSync(@path) @@ -251,6 +257,7 @@ class Directory @expansionState.isExpanded = false @expansionState = @serializeExpansionState() @unwatch() + @emitter.emit('did-collapse') # Public: Expand this directory, load its children, and start watching it for # changes. @@ -258,6 +265,7 @@ class Directory @expansionState.isExpanded = true @reload() @watch() + @emitter.emit('did-expand') serializeExpansionState: -> expansionState = {} From 457b26ec3ebb01ef9169fbd9af2cea7619192c8d Mon Sep 17 00:00:00 2001 From: Alhadis Date: Fri, 13 May 2016 16:00:38 +1000 Subject: [PATCH 025/263] Add specs for enhanced class-list handling --- spec/file-icons-spec.coffee | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/spec/file-icons-spec.coffee b/spec/file-icons-spec.coffee index 5ce8637d..e6ffd48a 100644 --- a/spec/file-icons-spec.coffee +++ b/spec/file-icons-spec.coffee @@ -1,3 +1,7 @@ +fs = require 'fs-plus' +temp = require('temp').track() +path = require 'path' + DefaultFileIcons = require '../lib/default-file-icons' FileIcons = require '../lib/file-icons' @@ -21,3 +25,45 @@ describe 'FileIcons', -> FileIcons.resetService() expect(FileIcons.getService()).not.toBe(service) + + + describe 'Class handling', -> + [workspaceElement, treeView, files] = [] + + beforeEach -> + rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) + + for i in [1..3] + filepath = path.join(rootDirPath, "file-#{i}.txt") + fs.writeFileSync(filepath, "Nah") + + atom.project.setPaths([rootDirPath]) + workspaceElement = atom.views.getView(atom.workspace) + jasmine.attachToDOM(workspaceElement) + + FileIcons.setService + iconClassForPath: (path, file) -> + [name, id] = path.match(/file-(\d+)\.txt$/) + switch id + when "1" then 'first second' + when "2" then ['first', 'second'] + when "3" then file.constructor.name + + waitsForPromise -> + atom.packages.activatePackage('tree-view') + + runs -> + treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() + files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') + + afterEach -> + temp.cleanup() + + it 'allows multiple classes to be passed', -> + expect(files[0].fileName.className).toBe('name icon first second') + + it 'allows an array of classes to be passed', -> + expect(files[1].fileName.className).toBe('name icon first second') + + it 'passes a file reference as iconClassForPath\'s second argument', -> + expect(files[2].fileName.className).toBe('name icon File') From 4ec7de345f15ceb69df9a35cf3c0007d30fcd53d Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sat, 14 May 2016 02:45:54 +1000 Subject: [PATCH 026/263] Pass the file's DOM element as a secondary argument This provides developers with much more information beyond a simple file reference, which can always be accessed through the DOM node anyway. --- lib/file-view.coffee | 2 +- spec/file-icons-spec.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index c3aa3412..0ff01b55 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path, @file) + iconClass = FileIcons.getService().iconClassForPath(@file.path, @) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/spec/file-icons-spec.coffee b/spec/file-icons-spec.coffee index e6ffd48a..dd56e997 100644 --- a/spec/file-icons-spec.coffee +++ b/spec/file-icons-spec.coffee @@ -65,5 +65,5 @@ describe 'FileIcons', -> it 'allows an array of classes to be passed', -> expect(files[1].fileName.className).toBe('name icon first second') - it 'passes a file reference as iconClassForPath\'s second argument', -> - expect(files[2].fileName.className).toBe('name icon File') + it 'passes a FileView reference as iconClassForPath\'s second argument', -> + expect(files[2].fileName.className).toBe('name icon tree-view-file') From 37b791eea8fcf9788c89520e3faf97e08f626e07 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sat, 14 May 2016 03:29:57 +1000 Subject: [PATCH 027/263] Silence Travis --- lib/file-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index 0ff01b55..61f1d144 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path, @) + iconClass = FileIcons.getService().iconClassForPath(@file.path, this) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) From da97a2683d3e5cd2247db288e862b2fd6d4dde57 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Fri, 3 Jun 2016 13:11:47 +1000 Subject: [PATCH 028/263] Pass filesystem stats to directory/file instances Developers monitoring the tree-view may need to access file-system stats for an entry. Having the stats available on the instance saves an author from making another filesystem call. For very large directories, this is a non-trivial boost to performance. Rather than pass a reference to the stat variable directly, a new object is created to give to the Directory or File instance. This might benefit garbage collection, although this is based on little more than theory. --- lib/directory.coffee | 7 ++++--- lib/file.coffee | 2 +- lib/tree-view.coffee | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index 279b64b1..be3ea9fc 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -9,7 +9,7 @@ realpathCache = {} module.exports = class Directory - constructor: ({@name, fullPath, @symlink, @expansionState, @isRoot, @ignoredPatterns, @useSyncFS}) -> + constructor: ({@name, fullPath, @symlink, @expansionState, @isRoot, @ignoredPatterns, @useSyncFS, @stats}) -> @destroyed = false @emitter = new Emitter() @subscriptions = new CompositeDisposable() @@ -177,6 +177,7 @@ class Directory stat = fs.lstatSyncNoException(fullPath) symlink = stat.isSymbolicLink?() stat = fs.statSyncNoException(fullPath) if symlink + statFlat = _.pick stat, _.keys(stat)... if stat.isDirectory?() if @entries.hasOwnProperty(name) @@ -185,14 +186,14 @@ class Directory directories.push(name) else expansionState = @expansionState.entries[name] - directories.push(new Directory({name, fullPath, symlink, expansionState, @ignoredPatterns, @useSyncFS})) + directories.push(new Directory({name, fullPath, symlink, expansionState, @ignoredPatterns, @useSyncFS, stats: statFlat})) else if stat.isFile?() if @entries.hasOwnProperty(name) # push a placeholder since this entry already exists but this helps # track the insertion index for the created views files.push(name) else - files.push(new File({name, fullPath, symlink, realpathCache, @useSyncFS})) + files.push(new File({name, fullPath, symlink, realpathCache, @useSyncFS, stats: statFlat})) @sortEntries(directories.concat(files)) diff --git a/lib/file.coffee b/lib/file.coffee index de898b06..b6c4a0a3 100644 --- a/lib/file.coffee +++ b/lib/file.coffee @@ -5,7 +5,7 @@ fs = require 'fs-plus' module.exports = class File - constructor: ({@name, fullPath, @symlink, realpathCache, useSyncFS}) -> + constructor: ({@name, fullPath, @symlink, realpathCache, useSyncFS, @stats}) -> @destroyed = false @emitter = new Emitter() @subscriptions = new CompositeDisposable() diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 5abf3031..50569a59 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -275,6 +275,9 @@ class TreeView extends View @loadIgnoredPatterns() @roots = for projectPath in atom.project.getPaths() + stats = fs.lstatSyncNoException(projectPath) + stats = _.pick stats, _.keys(stats)... + directory = new Directory({ name: path.basename(projectPath) fullPath: projectPath @@ -285,6 +288,7 @@ class TreeView extends View {isExpanded: true} @ignoredPatterns @useSyncFS + stats }) root = new DirectoryView() root.initialize(directory) From 8bde2883e19345002caf893eae9406051f7074b7 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Fri, 3 Jun 2016 15:36:24 +1000 Subject: [PATCH 029/263] Add specs for da97a26 --- spec/tree-view-spec.coffee | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f8c25ef3..462b00ff 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3325,3 +3325,39 @@ describe "TreeView", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') expect(atom.views.getView(atom.workspace.getPanes()[1])).toHaveFocus() + + + describe "provision of filesystem stats", -> + [file1Data, file2Data] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] + + beforeEach -> + rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) + subdirPath = path.join(rootDirPath, "subdir") + filePath1 = path.join(rootDirPath, "file1.txt") + filePath2 = path.join(subdirPath, "file2.txt") + + fs.makeTreeSync(subdirPath) + fs.writeFileSync(filePath1, file1Data) + fs.writeFileSync(filePath2, file2Data) + atom.project.setPaths([rootDirPath]) + + it "passes stats to File instances", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + expect(stats).toBeDefined() + expect(stats.mtime).toBeDefined() + expect(stats.size).toEqual(file1Data.length) + + it "passes stats to Directory instances", -> + stats = treeView.roots[0].directory.entries["subdir"].stats + expect(stats).toBeDefined() + expect(stats.mtime).toBeDefined() + + it "passes stats to a root directory when initialised", -> + expect(treeView.roots[0].directory.stats).toBeDefined() + + it "passes stats to File instances in subdirectories", -> + treeView.find('.entries > li:contains(subdir)').click() + subdir = treeView.roots[0].directory.entries["subdir"] + stats = subdir.entries["file2.txt"].stats + expect(stats).toBeDefined() + expect(stats.size).toEqual(file2Data.length) From ca9acf68d8298e979b2f4ce65065053709eb9574 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 5 Jun 2016 03:06:23 +1000 Subject: [PATCH 030/263] Use expand method to open directories in spec --- spec/tree-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 462b00ff..2979773f 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3356,7 +3356,7 @@ describe "TreeView", -> expect(treeView.roots[0].directory.stats).toBeDefined() it "passes stats to File instances in subdirectories", -> - treeView.find('.entries > li:contains(subdir)').click() + treeView.find('.entries > li:contains(subdir)')[0].expand() subdir = treeView.roots[0].directory.entries["subdir"] stats = subdir.entries["file2.txt"].stats expect(stats).toBeDefined() From 3aff76cfe31afc013dcbf798f99e0809deb1cb5b Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 5 Jun 2016 16:43:04 +1000 Subject: [PATCH 031/263] Convert date-based stats to Unix timestamps This allows easier comparison of times, and also stops Date objects from being serialised into strings when passed through Task handlers. --- lib/directory.coffee | 4 +++- lib/tree-view.coffee | 2 ++ spec/tree-view-spec.coffee | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index be3ea9fc..fb0f1874 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -178,7 +178,9 @@ class Directory symlink = stat.isSymbolicLink?() stat = fs.statSyncNoException(fullPath) if symlink statFlat = _.pick stat, _.keys(stat)... - + for i in ["atime", "birthtime", "ctime", "mtime"] + statFlat[i] = statFlat[i].getTime() + console.log statFlat if stat.isDirectory?() if @entries.hasOwnProperty(name) # push a placeholder since this entry already exists but this helps diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 50569a59..4b711891 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -277,6 +277,8 @@ class TreeView extends View @roots = for projectPath in atom.project.getPaths() stats = fs.lstatSyncNoException(projectPath) stats = _.pick stats, _.keys(stats)... + for i in ["atime", "birthtime", "ctime", "mtime"] + stats[i] = stats[i].getTime() directory = new Directory({ name: path.basename(projectPath) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 2979773f..f95228f0 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3328,9 +3328,10 @@ describe "TreeView", -> describe "provision of filesystem stats", -> - [file1Data, file2Data] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] + [file1Data, file2Data, timeStarted] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] beforeEach -> + timeStarted = Date.now() rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) subdirPath = path.join(rootDirPath, "subdir") filePath1 = path.join(rootDirPath, "file1.txt") @@ -3361,3 +3362,15 @@ describe "TreeView", -> stats = subdir.entries["file2.txt"].stats expect(stats).toBeDefined() expect(stats.size).toEqual(file2Data.length) + + it "converts date-stats to timestamps", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + stamp = stats.mtime + expect(_.isDate stamp).toBe(false) + expect(typeof stamp).toBe("number") + expect(Number.isNaN stamp).toBe(false) + + it "accurately converts timestamps", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + # Two minutes should be enough + expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000) From 023ac756131fad261a9f93ce9c5c0df045212eda Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 5 Jun 2016 17:11:16 +1000 Subject: [PATCH 032/263] Remove console.log call from Directory class --- lib/directory.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index fb0f1874..92d1b1a4 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -180,7 +180,7 @@ class Directory statFlat = _.pick stat, _.keys(stat)... for i in ["atime", "birthtime", "ctime", "mtime"] statFlat[i] = statFlat[i].getTime() - console.log statFlat + if stat.isDirectory?() if @entries.hasOwnProperty(name) # push a placeholder since this entry already exists but this helps From 6980dfc69617225dbc1088992b694f8828778969 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 5 Jun 2016 18:31:13 +1000 Subject: [PATCH 033/263] Use a more specific name for iterator variables Requested by @thomasjo: - https://github.com/atom/tree-view/pull/859#discussion-diff-65813251 --- lib/directory.coffee | 4 ++-- lib/tree-view.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index 92d1b1a4..ab2f45a3 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -178,8 +178,8 @@ class Directory symlink = stat.isSymbolicLink?() stat = fs.statSyncNoException(fullPath) if symlink statFlat = _.pick stat, _.keys(stat)... - for i in ["atime", "birthtime", "ctime", "mtime"] - statFlat[i] = statFlat[i].getTime() + for key in ["atime", "birthtime", "ctime", "mtime"] + statFlat[key] = statFlat[key].getTime() if stat.isDirectory?() if @entries.hasOwnProperty(name) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 4b711891..68042874 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -277,8 +277,8 @@ class TreeView extends View @roots = for projectPath in atom.project.getPaths() stats = fs.lstatSyncNoException(projectPath) stats = _.pick stats, _.keys(stats)... - for i in ["atime", "birthtime", "ctime", "mtime"] - stats[i] = stats[i].getTime() + for key in ["atime", "birthtime", "ctime", "mtime"] + stats[key] = stats[key].getTime() directory = new Directory({ name: path.basename(projectPath) From 61adcbce5613f8e0ea0571db75ab530985607008 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Mon, 6 Jun 2016 18:56:35 +1000 Subject: [PATCH 034/263] Avoid breaking on non-existent filesystem times If a date-typed stat is undefined, avoid throwing a TypeError. --- lib/directory.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index ab2f45a3..9c88fc18 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -179,7 +179,7 @@ class Directory stat = fs.statSyncNoException(fullPath) if symlink statFlat = _.pick stat, _.keys(stat)... for key in ["atime", "birthtime", "ctime", "mtime"] - statFlat[key] = statFlat[key].getTime() + statFlat[key] = statFlat[key]?.getTime() if stat.isDirectory?() if @entries.hasOwnProperty(name) From d82ce9585c5d2efd6094738d4f2b9ff3815cdf4e Mon Sep 17 00:00:00 2001 From: Alhadis Date: Tue, 7 Jun 2016 00:42:38 +1000 Subject: [PATCH 035/263] Isolate filesystem-stats spec to a new file --- spec/file-stats-spec.coffee | 63 +++++++++++++++++++++++++++++++++++++ spec/tree-view-spec.coffee | 49 ----------------------------- 2 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 spec/file-stats-spec.coffee diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee new file mode 100644 index 00000000..0942f262 --- /dev/null +++ b/spec/file-stats-spec.coffee @@ -0,0 +1,63 @@ +_ = require 'underscore-plus' +{$, $$} = require 'atom-space-pen-views' +fs = require 'fs-plus' +path = require 'path' +temp = require('temp').track() + +describe "FileStats", -> + [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] + + beforeEach -> + timeStarted = Date.now() + rootDirPath = fs.absolute(temp.mkdirSync("tree-view")) + subdirPath = path.join(rootDirPath, "subdir") + filePath1 = path.join(rootDirPath, "file1.txt") + filePath2 = path.join(subdirPath, "file2.txt") + + fs.makeTreeSync(subdirPath) + fs.writeFileSync(filePath1, file1Data) + fs.writeFileSync(filePath2, file2Data) + atom.project.setPaths([rootDirPath]) + + waitsForPromise -> + atom.packages.activatePackage("tree-view") + + runs -> + treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + + afterEach -> + temp.cleanup() + + describe "provision of filesystem stats", -> + it "passes stats to File instances", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + expect(stats).toBeDefined() + expect(stats.mtime).toBeDefined() + expect(stats.size).toEqual(file1Data.length) + + it "passes stats to Directory instances", -> + stats = treeView.roots[0].directory.entries["subdir"].stats + expect(stats).toBeDefined() + expect(stats.mtime).toBeDefined() + + it "passes stats to a root directory when initialised", -> + expect(treeView.roots[0].directory.stats).toBeDefined() + + it "passes stats to File instances in subdirectories", -> + treeView.find(".entries > li:contains(subdir)")[0].expand() + subdir = treeView.roots[0].directory.entries["subdir"] + stats = subdir.entries["file2.txt"].stats + expect(stats).toBeDefined() + expect(stats.size).toEqual(file2Data.length) + + it "converts date-stats to timestamps", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + stamp = stats.mtime + expect(_.isDate stamp).toBe(false) + expect(typeof stamp).toBe("number") + expect(Number.isNaN stamp).toBe(false) + + it "accurately converts timestamps", -> + stats = treeView.roots[0].directory.entries["file1.txt"].stats + # Two minutes should be enough + expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f95228f0..f8c25ef3 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3325,52 +3325,3 @@ describe "TreeView", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') expect(atom.views.getView(atom.workspace.getPanes()[1])).toHaveFocus() - - - describe "provision of filesystem stats", -> - [file1Data, file2Data, timeStarted] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] - - beforeEach -> - timeStarted = Date.now() - rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) - subdirPath = path.join(rootDirPath, "subdir") - filePath1 = path.join(rootDirPath, "file1.txt") - filePath2 = path.join(subdirPath, "file2.txt") - - fs.makeTreeSync(subdirPath) - fs.writeFileSync(filePath1, file1Data) - fs.writeFileSync(filePath2, file2Data) - atom.project.setPaths([rootDirPath]) - - it "passes stats to File instances", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats - expect(stats).toBeDefined() - expect(stats.mtime).toBeDefined() - expect(stats.size).toEqual(file1Data.length) - - it "passes stats to Directory instances", -> - stats = treeView.roots[0].directory.entries["subdir"].stats - expect(stats).toBeDefined() - expect(stats.mtime).toBeDefined() - - it "passes stats to a root directory when initialised", -> - expect(treeView.roots[0].directory.stats).toBeDefined() - - it "passes stats to File instances in subdirectories", -> - treeView.find('.entries > li:contains(subdir)')[0].expand() - subdir = treeView.roots[0].directory.entries["subdir"] - stats = subdir.entries["file2.txt"].stats - expect(stats).toBeDefined() - expect(stats.size).toEqual(file2Data.length) - - it "converts date-stats to timestamps", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats - stamp = stats.mtime - expect(_.isDate stamp).toBe(false) - expect(typeof stamp).toBe("number") - expect(Number.isNaN stamp).toBe(false) - - it "accurately converts timestamps", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats - # Two minutes should be enough - expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000) From 387cb6b79779b9e46417805903243f8382fabf90 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Tue, 12 Jul 2016 13:57:47 +0200 Subject: [PATCH 036/263] Prepare 0.208.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 86d7cb8d..52cd2897 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.208.0", + "version": "0.208.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 3ba57e02665805c0ddf253aea056967380c24138 Mon Sep 17 00:00:00 2001 From: liuderchi Date: Wed, 6 Jul 2016 13:47:41 +0800 Subject: [PATCH 037/263] :art: wrap some code in showSelectedEntryInFileManager() - wrap it into openInFileManager() for future feature --- lib/tree-view.coffee | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 5abf3031..f0d4a4f2 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -483,13 +483,7 @@ class TreeView extends View label: 'File Manager' args: [pathToOpen] - showSelectedEntryInFileManager: -> - entry = @selectedEntry() - return unless entry - - isFile = entry instanceof FileView - {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile) - + openInFileManager: (command, args, label, isFile) -> handleError = (errorMessage) -> atom.notifications.addError "Opening #{if isFile then 'file' else 'folder'} in #{label} failed", detail: errorMessage @@ -512,6 +506,14 @@ class TreeView extends View handle() handleError(error?.message) + showSelectedEntryInFileManager: -> + entry = @selectedEntry() + return unless entry + + isFile = entry instanceof FileView + {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile) + @openInFileManager(command, args, label, isFile) + openSelectedEntryInNewWindow: -> if pathToOpen = @selectedEntry()?.getPath() atom.open({pathsToOpen: [pathToOpen], newWindow: true}) From 6c6e5a786c3f40d4a992d895fc15d560b1129dc6 Mon Sep 17 00:00:00 2001 From: liuderchi Date: Wed, 6 Jul 2016 14:03:44 +0800 Subject: [PATCH 038/263] feat: open current file in file manager in editor view - add function showCurrentFileInFileManager() in tree-view.coffee - add menu-item in context-menu in tree-view.cson - register command in main.coffee --- lib/main.coffee | 1 + lib/tree-view.coffee | 6 ++++++ menus/tree-view.cson | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/lib/main.coffee b/lib/main.coffee index 9d2eb4a3..f5162aba 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -23,6 +23,7 @@ module.exports = 'tree-view:duplicate': => @createView().copySelectedEntry() 'tree-view:remove': => @createView().removeSelectedEntries() 'tree-view:rename': => @createView().moveSelectedEntry() + 'tree-view:show-current-file-in-file-manager': => @createView().showCurrentFileInFileManager() }) deactivate: -> diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f0d4a4f2..39686163 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -514,6 +514,12 @@ class TreeView extends View {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile) @openInFileManager(command, args, label, isFile) + showCurrentFileInFileManager: -> + return unless editor = atom.workspace.getActiveTextEditor() + return if editor.getPath() is undefined # handle untitled tab + {command, args, label} = @fileManagerCommandForPath(editor.getPath(), true) + @openInFileManager(command, args, label, true) + openSelectedEntryInNewWindow: -> if pathToOpen = @selectedEntry()?.getPath() atom.open({pathsToOpen: [pathToOpen], newWindow: true}) diff --git a/menus/tree-view.cson b/menus/tree-view.cson index 50a562b1..a4a643d7 100644 --- a/menus/tree-view.cson +++ b/menus/tree-view.cson @@ -97,4 +97,9 @@ 'atom-pane[data-active-item-path] .tab.active': [ {'label': 'Rename', 'command': 'tree-view:rename'} {'label': 'Reveal in Tree View', 'command': 'tree-view:reveal-active-file'} + {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} + ] + + 'atom-text-editor': [ + {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} ] From 9031ea3b0299985b7d5a6e6e8bc62939c064b5f2 Mon Sep 17 00:00:00 2001 From: liuderchi Date: Mon, 18 Jul 2016 16:13:22 +0800 Subject: [PATCH 039/263] :white_check_mark: add specs for function showCurrentFileInFileManager() - reuse spec of "showSelectedEntryInFileManager()" with some modification1 --- spec/tree-view-spec.coffee | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f8c25ef3..d11d12a7 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3007,6 +3007,70 @@ describe "TreeView", -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in Finder failed' expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' + describe "showCurrentFileInFileManager()", -> + it "does nothing when no file is opened", -> + expect(atom.workspace.getPaneItems().length).toBe(0) + expect(treeView.showCurrentFileInFileManager()).toBeUndefined() + + it "does nothing when only an untitled tab is opened", -> + waitsForPromise -> + atom.workspace.open() + runs -> + $(workspaceElement).focus() + expect(treeView.showCurrentFileInFileManager()).toBeUndefined() + + describe "showCurrentFileInFileManager() when some file is opened", -> + beforeEach -> + filePath = path.join(os.tmpdir(), 'non-project-file.txt') + fs.writeFileSync(filePath, 'test') + waitsForPromise -> + atom.workspace.open(filePath) + + it "displays the standard error output when the process fails", -> + atom.notifications.clear() + {BufferedProcess} = require 'atom' + spyOn(BufferedProcess.prototype, 'spawn').andCallFake -> + EventEmitter = require 'events' + fakeProcess = new EventEmitter() + fakeProcess.send = -> + fakeProcess.kill = -> + fakeProcess.stdout = new EventEmitter() + fakeProcess.stdout.setEncoding = -> + fakeProcess.stderr = new EventEmitter() + fakeProcess.stderr.setEncoding = -> + @process = fakeProcess + process.nextTick -> + fakeProcess.stderr.emit('data', 'bad process') + fakeProcess.stderr.emit('close') + fakeProcess.stdout.emit('close') + fakeProcess.emit('exit') + + treeView.showCurrentFileInFileManager() + + waitsFor -> + atom.notifications.getNotifications().length is 1 + + runs -> + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening file' + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'failed' + expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'bad process' + + it "handles errors thrown when spawning the OS file manager", -> + atom.notifications.clear() + spyOn(treeView, 'fileManagerCommandForPath').andReturn + command: '/this/command/does/not/exist' + label: 'Finder' + args: ['foo'] + + treeView.showCurrentFileInFileManager() + + waitsFor -> + atom.notifications.getNotifications().length is 1 + + runs -> + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening file in Finder failed' + expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' + describe "when reloading a directory with deletions and additions", -> it "does not throw an error (regression)", -> projectPath = temp.mkdirSync('atom-project') From 6b4de0a7ac1c0a8cb3039cc9154eda5b6a60a36b Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 18 Jul 2016 09:54:21 -0400 Subject: [PATCH 040/263] Update deprecated require --- spec/default-file-icons-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/default-file-icons-spec.coffee b/spec/default-file-icons-spec.coffee index ed23e656..5cf62817 100644 --- a/spec/default-file-icons-spec.coffee +++ b/spec/default-file-icons-spec.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -process = require 'process' +{process} = require 'electron' temp = require('temp').track() DefaultFileIcons = require '../lib/default-file-icons' From 824db03aba6c14753680fd6cb0f0ecfd4721dfad Mon Sep 17 00:00:00 2001 From: Wliu Date: Mon, 18 Jul 2016 10:02:47 -0400 Subject: [PATCH 041/263] Revert "Update deprecated require" This reverts commit 6b4de0a7ac1c0a8cb3039cc9154eda5b6a60a36b. --- spec/default-file-icons-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/default-file-icons-spec.coffee b/spec/default-file-icons-spec.coffee index 5cf62817..ed23e656 100644 --- a/spec/default-file-icons-spec.coffee +++ b/spec/default-file-icons-spec.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -{process} = require 'electron' +process = require 'process' temp = require('temp').track() DefaultFileIcons = require '../lib/default-file-icons' From b27dfb5320f9617681941dfd59182129d8e9e6aa Mon Sep 17 00:00:00 2001 From: liuderchi Date: Tue, 19 Jul 2016 10:34:46 +0800 Subject: [PATCH 042/263] :art: improve openInFileManager(), code/spec of showSelectedEntryInFileManager() - make openInFileManager() returns a BufferedProcess instance for spec to manipulate - refine spec by adding 'shows file in file manager when some file is opened' --- lib/tree-view.coffee | 4 +-- spec/tree-view-spec.coffee | 58 ++++++-------------------------------- 2 files changed, 11 insertions(+), 51 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 39686163..70fb2921 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -505,10 +505,10 @@ class TreeView extends View showProcess.onWillThrowError ({error, handle}) -> handle() handleError(error?.message) + showProcess showSelectedEntryInFileManager: -> - entry = @selectedEntry() - return unless entry + return unless entry = @selectedEntry() isFile = entry instanceof FileView {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index d11d12a7..c624220d 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3019,57 +3019,17 @@ describe "TreeView", -> $(workspaceElement).focus() expect(treeView.showCurrentFileInFileManager()).toBeUndefined() - describe "showCurrentFileInFileManager() when some file is opened", -> - beforeEach -> - filePath = path.join(os.tmpdir(), 'non-project-file.txt') - fs.writeFileSync(filePath, 'test') - waitsForPromise -> - atom.workspace.open(filePath) + it "shows file in file manager when some file is opened", -> + filePath = path.join(os.tmpdir(), 'non-project-file.txt') + fs.writeFileSync(filePath, 'test') + waitsForPromise -> + atom.workspace.open(filePath) - it "displays the standard error output when the process fails", -> - atom.notifications.clear() + runs -> + fileManagerProcess = treeView.showCurrentFileInFileManager() {BufferedProcess} = require 'atom' - spyOn(BufferedProcess.prototype, 'spawn').andCallFake -> - EventEmitter = require 'events' - fakeProcess = new EventEmitter() - fakeProcess.send = -> - fakeProcess.kill = -> - fakeProcess.stdout = new EventEmitter() - fakeProcess.stdout.setEncoding = -> - fakeProcess.stderr = new EventEmitter() - fakeProcess.stderr.setEncoding = -> - @process = fakeProcess - process.nextTick -> - fakeProcess.stderr.emit('data', 'bad process') - fakeProcess.stderr.emit('close') - fakeProcess.stdout.emit('close') - fakeProcess.emit('exit') - - treeView.showCurrentFileInFileManager() - - waitsFor -> - atom.notifications.getNotifications().length is 1 - - runs -> - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening file' - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'failed' - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'bad process' - - it "handles errors thrown when spawning the OS file manager", -> - atom.notifications.clear() - spyOn(treeView, 'fileManagerCommandForPath').andReturn - command: '/this/command/does/not/exist' - label: 'Finder' - args: ['foo'] - - treeView.showCurrentFileInFileManager() - - waitsFor -> - atom.notifications.getNotifications().length is 1 - - runs -> - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening file in Finder failed' - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' + expect(fileManagerProcess instanceof BufferedProcess).toBeTruthy() + fileManagerProcess.kill() describe "when reloading a directory with deletions and additions", -> it "does not throw an error (regression)", -> From 6b6feb3141750addb8bf366366bf052269a4383c Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 19 Jul 2016 10:57:52 -0700 Subject: [PATCH 043/263] Simplify showSelectedEntryInFileManager error handling --- lib/tree-view.coffee | 2 +- spec/tree-view-spec.coffee | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 5abf3031..0c74cd9f 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -507,7 +507,7 @@ class TreeView extends View handleError(errorMessage) if failed - showProcess = new BufferedProcess({command, args, stderr, exit}) + showProcess = new BufferedProcess({command, args, options:{shell:false}, stderr, exit}) showProcess.onWillThrowError ({error, handle}) -> handle() handleError(error?.message) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 54287839..4ff20a0c 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3002,22 +3002,19 @@ describe "TreeView", -> it "handle errors thrown when spawning the OS file manager", -> spyOn(treeView, 'fileManagerCommandForPath').andReturn - command: '/this/command/does/not/exist' - label: 'Finder' + command: path.normalize('/this/command/does/not/exist') + label: 'OS file manager' args: ['foo'] + debugger treeView.showSelectedEntryInFileManager() waitsFor -> atom.notifications.getNotifications().length is 1 runs -> - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in Finder failed' - - if process.platform isnt 'win32' - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' - else # Typical Windows - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'The system cannot find the path specified.' + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in OS file manager failed' + expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' describe "when reloading a directory with deletions and additions", -> it "does not throw an error (regression)", -> From 7adbc3330f40fb838bb32fcc2299a336e0ffcd18 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 19 Jul 2016 10:58:15 -0700 Subject: [PATCH 044/263] Simplify showSelectedEntryInFileManager error handling --- spec/tree-view-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 4ff20a0c..2f0acb8e 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3006,7 +3006,6 @@ describe "TreeView", -> label: 'OS file manager' args: ['foo'] - debugger treeView.showSelectedEntryInFileManager() waitsFor -> From 5e4a75a585774bfda62ad10e2d218c41cb7af4b7 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 19 Jul 2016 20:41:40 -0400 Subject: [PATCH 045/263] :shirt: --- lib/tree-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 0c74cd9f..1db72100 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -226,7 +226,7 @@ class TreeView extends View openAfterPromise: (uri, options) -> if promise = @currentlyOpening.get(uri) - promise.then => atom.workspace.open(uri, options) + promise.then -> atom.workspace.open(uri, options) else atom.workspace.open(uri, options) @@ -507,7 +507,7 @@ class TreeView extends View handleError(errorMessage) if failed - showProcess = new BufferedProcess({command, args, options:{shell:false}, stderr, exit}) + showProcess = new BufferedProcess({command, args, options: {shell: false}, stderr, exit}) showProcess.onWillThrowError ({error, handle}) -> handle() handleError(error?.message) From 317ff6b948bd079a5e1fb94886ecba52c4ce5b58 Mon Sep 17 00:00:00 2001 From: liuderchi Date: Wed, 20 Jul 2016 10:05:25 +0800 Subject: [PATCH 046/263] :art: use spyOn(), andCallFake() to avoid actually spawning file manager --- spec/tree-view-spec.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index c624220d..edb0cab3 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3026,8 +3026,9 @@ describe "TreeView", -> atom.workspace.open(filePath) runs -> - fileManagerProcess = treeView.showCurrentFileInFileManager() {BufferedProcess} = require 'atom' + spyOn(BufferedProcess.prototype, 'spawn').andCallFake -> + fileManagerProcess = treeView.showCurrentFileInFileManager() expect(fileManagerProcess instanceof BufferedProcess).toBeTruthy() fileManagerProcess.kill() From 06ae18f110865b453a40d91532cac3505de8caca Mon Sep 17 00:00:00 2001 From: liuderchi Date: Wed, 20 Jul 2016 23:08:07 +0800 Subject: [PATCH 047/263] :art: fix selector in menus/tree-view.cson - disable in mini-editor --- menus/tree-view.cson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menus/tree-view.cson b/menus/tree-view.cson index a4a643d7..90ad6aed 100644 --- a/menus/tree-view.cson +++ b/menus/tree-view.cson @@ -100,6 +100,6 @@ {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} ] - 'atom-text-editor': [ + 'atom-text-editor:not([mini])': [ {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} ] From a09b8844dc819a07417a6d51d06511a107cdfbc9 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Mon, 25 Jul 2016 22:00:20 +1000 Subject: [PATCH 048/263] Move icon-class specs to tree-view-spec --- spec/file-icons-spec.coffee | 46 ------------------------------------- spec/tree-view-spec.coffee | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/spec/file-icons-spec.coffee b/spec/file-icons-spec.coffee index dd56e997..5ce8637d 100644 --- a/spec/file-icons-spec.coffee +++ b/spec/file-icons-spec.coffee @@ -1,7 +1,3 @@ -fs = require 'fs-plus' -temp = require('temp').track() -path = require 'path' - DefaultFileIcons = require '../lib/default-file-icons' FileIcons = require '../lib/file-icons' @@ -25,45 +21,3 @@ describe 'FileIcons', -> FileIcons.resetService() expect(FileIcons.getService()).not.toBe(service) - - - describe 'Class handling', -> - [workspaceElement, treeView, files] = [] - - beforeEach -> - rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) - - for i in [1..3] - filepath = path.join(rootDirPath, "file-#{i}.txt") - fs.writeFileSync(filepath, "Nah") - - atom.project.setPaths([rootDirPath]) - workspaceElement = atom.views.getView(atom.workspace) - jasmine.attachToDOM(workspaceElement) - - FileIcons.setService - iconClassForPath: (path, file) -> - [name, id] = path.match(/file-(\d+)\.txt$/) - switch id - when "1" then 'first second' - when "2" then ['first', 'second'] - when "3" then file.constructor.name - - waitsForPromise -> - atom.packages.activatePackage('tree-view') - - runs -> - treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() - files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') - - afterEach -> - temp.cleanup() - - it 'allows multiple classes to be passed', -> - expect(files[0].fileName.className).toBe('name icon first second') - - it 'allows an array of classes to be passed', -> - expect(files[1].fileName.className).toBe('name icon first second') - - it 'passes a FileView reference as iconClassForPath\'s second argument', -> - expect(files[2].fileName.className).toBe('name icon tree-view-file') diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f8c25ef3..4fc8ff1f 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -6,6 +6,9 @@ temp = require('temp').track() os = require 'os' eventHelpers = require "./event-helpers" +DefaultFileIcons = require '../lib/default-file-icons' +FileIcons = require '../lib/file-icons' + waitsForFileToOpen = (causeFileToOpen) -> waitsFor (done) -> disposable = atom.workspace.onDidOpen -> @@ -3325,3 +3328,42 @@ describe "TreeView", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') expect(atom.views.getView(atom.workspace.getPanes()[1])).toHaveFocus() + + +describe 'Icon class handling', -> + [workspaceElement, treeView, files] = [] + + beforeEach -> + rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) + + for i in [1..3] + filepath = path.join(rootDirPath, "file-#{i}.txt") + fs.writeFileSync(filepath, "Nah") + + atom.project.setPaths([rootDirPath]) + workspaceElement = atom.views.getView(atom.workspace) + jasmine.attachToDOM(workspaceElement) + + FileIcons.setService + iconClassForPath: (path) -> + [name, id] = path.match(/file-(\d+)\.txt$/) + switch id + when "1" then 'first second' + when "2" then ['first', 'second'] + else "some-other-file" + + waitsForPromise -> + atom.packages.activatePackage('tree-view') + + runs -> + treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() + files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') + + afterEach -> + temp.cleanup() + + it 'allows multiple classes to be passed', -> + expect(files[0].fileName.className).toBe('name icon first second') + + it 'allows an array of classes to be passed', -> + expect(files[1].fileName.className).toBe('name icon first second') From 04b55c450b61a55fcaad6b4c903ab14df96ca189 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Mon, 25 Jul 2016 23:11:41 +1000 Subject: [PATCH 049/263] Define a File method for supplying basic metadata --- lib/file-view.coffee | 2 +- lib/file.coffee | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index 61f1d144..296dbe80 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path, this) + iconClass = FileIcons.getService().iconClassForPath(@file.path, @file.getMetadata()) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/lib/file.coffee b/lib/file.coffee index de898b06..d3e3979f 100644 --- a/lib/file.coffee +++ b/lib/file.coffee @@ -67,3 +67,6 @@ class File isPathEqual: (pathToCompare) -> @path is pathToCompare or @realPath is pathToCompare + + # Return a snapshot of the instance's filesystem properties for API consumption + getMetadata: -> {@symlink, @realPath, @status, @stats} From 38be0cba14d97bc5fd8c1e20855747b7d68521c1 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Mon, 25 Jul 2016 23:30:01 +1000 Subject: [PATCH 050/263] Modify spec to expect metadata to be passed --- spec/tree-view-spec.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 4fc8ff1f..5260b65e 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3345,7 +3345,11 @@ describe 'Icon class handling', -> jasmine.attachToDOM(workspaceElement) FileIcons.setService - iconClassForPath: (path) -> + iconClassForPath: (path, info) -> + expect(info).toBeDefined() + expect(info.hasOwnProperty "symlink").toBe true + expect(info.hasOwnProperty "stats").toBe true + [name, id] = path.match(/file-(\d+)\.txt$/) switch id when "1" then 'first second' From 97601a6753731233530a2770b07a41691052d12e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Jul 2016 14:25:33 -0600 Subject: [PATCH 051/263] Wait for promise triggered by clicks to resolve before running next test Hopefully this addresses our flaky test issues! --- spec/tree-view-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f8c25ef3..df5b5798 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -580,6 +580,10 @@ describe "TreeView", -> sampleJs.trigger clickEvent(originalEvent: {detail: 2}) .not.toThrow() + # Ensure we don't move on to the next test until the promise spawned click event resolves. + # (If it resolves in the middle of the next test we'll pollute that test) + waitsForPromise -> treeView.currentlyOpening.get(atom.workspace.getActivePaneItem().getPath()) + describe "when the file is pending", -> editor = null From d69e1026fd7176239fcb1f0e4c22bf14a3e07e1e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Jul 2016 14:46:07 -0600 Subject: [PATCH 052/263] Prepare 0.208.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 52cd2897..a9b3d179 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.208.1", + "version": "0.208.2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 6655c26af3e22c2016619909c2830d72cc008ae7 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sat, 30 Jul 2016 15:27:49 +1000 Subject: [PATCH 053/263] Delete second argument passed to iconClassForPath See: https://github.com/atom/tree-view/pull/825#discussion_r72758094 --- lib/file-view.coffee | 2 +- lib/file.coffee | 5 +---- spec/tree-view-spec.coffee | 6 +----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index 296dbe80..b3045c29 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path, @file.getMetadata()) + iconClass = FileIcons.getService().iconClassForPath(@file.path) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/lib/file.coffee b/lib/file.coffee index d3e3979f..ea891964 100644 --- a/lib/file.coffee +++ b/lib/file.coffee @@ -36,7 +36,7 @@ class File onDidStatusChange: (callback) -> @emitter.on('did-status-change', callback) - # Subscribe to the project' repo for changes to the Git status of this file. + # Subscribe to the project's repo for changes to the Git status of this file. subscribeToRepo: -> repo = repoForPath(@path) return unless repo? @@ -67,6 +67,3 @@ class File isPathEqual: (pathToCompare) -> @path is pathToCompare or @realPath is pathToCompare - - # Return a snapshot of the instance's filesystem properties for API consumption - getMetadata: -> {@symlink, @realPath, @status, @stats} diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 8a47aea2..02d7d459 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3349,11 +3349,7 @@ describe 'Icon class handling', -> jasmine.attachToDOM(workspaceElement) FileIcons.setService - iconClassForPath: (path, info) -> - expect(info).toBeDefined() - expect(info.hasOwnProperty "symlink").toBe true - expect(info.hasOwnProperty "stats").toBe true - + iconClassForPath: (path) -> [name, id] = path.match(/file-(\d+)\.txt$/) switch id when "1" then 'first second' From 03c74551bddac51c75e8cea92901bbe7decdabea Mon Sep 17 00:00:00 2001 From: Alhadis Date: Tue, 2 Aug 2016 03:26:44 +1000 Subject: [PATCH 054/263] Use package's name to identify service context --- lib/file-view.coffee | 2 +- spec/tree-view-spec.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index b3045c29..f4ec1f1e 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path) + iconClass = FileIcons.getService().iconClassForPath(@file.path, "tree-view") if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 02d7d459..4662ecbd 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3349,7 +3349,8 @@ describe 'Icon class handling', -> jasmine.attachToDOM(workspaceElement) FileIcons.setService - iconClassForPath: (path) -> + iconClassForPath: (path, context) -> + expect(context).toBe "tree-view" [name, id] = path.match(/file-(\d+)\.txt$/) switch id when "1" then 'first second' From 6127c9e9281ee7065cb92cc472b1e915a1c590ed Mon Sep 17 00:00:00 2001 From: Alhadis Date: Fri, 5 Aug 2016 19:07:49 +1000 Subject: [PATCH 055/263] Temporarily remove context from API call See: https://github.com/atom/tree-view/pull/825#issuecomment-237788504 --- lib/file-view.coffee | 2 +- spec/tree-view-spec.coffee | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index f4ec1f1e..b3045c29 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path, "tree-view") + iconClass = FileIcons.getService().iconClassForPath(@file.path) if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 4662ecbd..02d7d459 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3349,8 +3349,7 @@ describe 'Icon class handling', -> jasmine.attachToDOM(workspaceElement) FileIcons.setService - iconClassForPath: (path, context) -> - expect(context).toBe "tree-view" + iconClassForPath: (path) -> [name, id] = path.match(/file-(\d+)\.txt$/) switch id when "1" then 'first second' From 8a9fd3a6f8adaa74618bd9fb28a381123e95ebea Mon Sep 17 00:00:00 2001 From: Alhadis Date: Fri, 5 Aug 2016 19:27:00 +1000 Subject: [PATCH 056/263] Revert "Temporarily remove context from API call" This reverts commit 6127c9e9281ee7065cb92cc472b1e915a1c590ed. --- lib/file-view.coffee | 2 +- spec/tree-view-spec.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index b3045c29..f4ec1f1e 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -19,7 +19,7 @@ class FileView extends HTMLElement @fileName.dataset.name = @file.name @fileName.dataset.path = @file.path - iconClass = FileIcons.getService().iconClassForPath(@file.path) + iconClass = FileIcons.getService().iconClassForPath(@file.path, "tree-view") if iconClass unless Array.isArray iconClass iconClass = iconClass.toString().split(/\s+/g) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 02d7d459..4662ecbd 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3349,7 +3349,8 @@ describe 'Icon class handling', -> jasmine.attachToDOM(workspaceElement) FileIcons.setService - iconClassForPath: (path) -> + iconClassForPath: (path, context) -> + expect(context).toBe "tree-view" [name, id] = path.match(/file-(\d+)\.txt$/) switch id when "1" then 'first second' From 7eb5ff9c78391a3c4ef25d32da9c4bbb4ceac550 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 10 Aug 2016 15:30:53 -0600 Subject: [PATCH 057/263] Make as assertion about squashed names before reload --- spec/tree-view-spec.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index d2d70934..c7167440 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2425,10 +2425,12 @@ describe "TreeView", -> it "squashes the directory names the last of which is same as an unsquashed directory", -> muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') muDir[0].expand() - muDir[0].reload() - muEntries = [].slice.call(muDir[0].children[1].children).map (element) -> - element.innerText + muEntries = Array.from(muDir[0].children[1].children).map (element) -> element.innerText + expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) + muDir[0].expand() + muDir[0].reload() + muEntries = Array.from(muDir[0].children[1].children).map (element) -> element.innerText expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) describe "Git status decorations", -> From 329bf1a400817ae1d31719af55960d268dfb4ebe Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 10 Aug 2016 15:38:27 -0600 Subject: [PATCH 058/263] Prepare 0.208.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9b3d179..df86b977 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.208.2", + "version": "0.208.3", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 724815108d73b796d538419fbe445551a8f3f177 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 24 Jun 2016 14:08:36 -0700 Subject: [PATCH 059/263] Fix unnecessary fat arrow lint warning --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 68042874..a055c3af 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -226,7 +226,7 @@ class TreeView extends View openAfterPromise: (uri, options) -> if promise = @currentlyOpening.get(uri) - promise.then => atom.workspace.open(uri, options) + promise.then -> atom.workspace.open(uri, options) else atom.workspace.open(uri, options) From c1645f4da9ce7ad5580e2297b0a2e09b660cd42e Mon Sep 17 00:00:00 2001 From: liuderchi Date: Thu, 18 Aug 2016 11:23:42 +0800 Subject: [PATCH 060/263] :art: safer check when editor.getPath() is invalid - returns early when editor path is null, empty string or undefined --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 70fb2921..e581a80a 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -516,7 +516,7 @@ class TreeView extends View showCurrentFileInFileManager: -> return unless editor = atom.workspace.getActiveTextEditor() - return if editor.getPath() is undefined # handle untitled tab + return unless editor.getPath() {command, args, label} = @fileManagerCommandForPath(editor.getPath(), true) @openInFileManager(command, args, label, true) From b9b379c6de4dbfff1e90b7ac8a118073ca605a9e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 19 Aug 2016 14:40:32 +0200 Subject: [PATCH 061/263] Prepare 0.209.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index df86b977..dd1ab6f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.208.3", + "version": "0.209.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From e40b91d378f73b3b8776bba941d541d07f7c7b8f Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Tue, 23 Aug 2016 09:35:17 +0200 Subject: [PATCH 062/263] :bug: Guard against undefined path --- lib/main.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.coffee b/lib/main.coffee index 9d2eb4a3..f532fe20 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -51,7 +51,7 @@ module.exports = @treeView shouldAttach: -> - projectPath = atom.project.getPaths()[0] + projectPath = atom.project.getPaths()[0] ? '' if atom.workspace.getActivePaneItem() false else if path.basename(projectPath) is '.git' From 06bdfae4ba1e57cf6ebe1ac617457541cd5637ba Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 24 Aug 2016 12:42:58 -0700 Subject: [PATCH 063/263] Prepare 0.209.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd1ab6f2..ce1130fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.209.0", + "version": "0.209.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 409eca38d684a21e413d3f5cc81511153d2e7030 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 26 Aug 2016 16:03:22 +0200 Subject: [PATCH 064/263] Increase spec resiliency in tree-view-spec.coffee Sometimes `treeView.currentlyOpening` doesn't contain the expected `Promise`, thus causing failures like the one in https://circleci.com/gh/atom/atom/841. It's possible that such `Promise` finishes before the test has the chance to call `then` on it, thus causing it to be removed from that `Map` (see https://github.com/atom/tree-view/blob/06bdfae/lib/tree-view.coffee#L223). With this commit, we'll now simply wait until that `Promise` is removed from the `currentlyOpening` `Map`, which should be more resilient. --- spec/tree-view-spec.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 13d128b7..88e11afc 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -583,9 +583,10 @@ describe "TreeView", -> sampleJs.trigger clickEvent(originalEvent: {detail: 2}) .not.toThrow() - # Ensure we don't move on to the next test until the promise spawned click event resolves. - # (If it resolves in the middle of the next test we'll pollute that test) - waitsForPromise -> treeView.currentlyOpening.get(atom.workspace.getActivePaneItem().getPath()) + waitsFor -> + # Ensure we don't move on to the next test until the promise spawned click event resolves. + # (If it resolves in the middle of the next test we'll pollute that test). + not treeView.currentlyOpening.has(atom.workspace.getActivePaneItem().getPath()) describe "when the file is pending", -> editor = null From dc52f07da23383c0089e1f8d78f52eb2aaa3c861 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 26 Aug 2016 16:12:58 +0200 Subject: [PATCH 065/263] :art: --- spec/tree-view-spec.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 88e11afc..10470b6b 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3359,14 +3359,14 @@ describe "TreeView", -> describe 'Icon class handling', -> [workspaceElement, treeView, files] = [] - + beforeEach -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) - + for i in [1..3] filepath = path.join(rootDirPath, "file-#{i}.txt") fs.writeFileSync(filepath, "Nah") - + atom.project.setPaths([rootDirPath]) workspaceElement = atom.views.getView(atom.workspace) jasmine.attachToDOM(workspaceElement) @@ -3382,11 +3382,11 @@ describe 'Icon class handling', -> waitsForPromise -> atom.packages.activatePackage('tree-view') - + runs -> treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') - + afterEach -> temp.cleanup() From d72da0213d89f6c82bde09095e0b88e2079b60d8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 26 Aug 2016 16:27:34 +0200 Subject: [PATCH 066/263] Prepare 0.209.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce1130fb..ac9d7286 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.209.1", + "version": "0.209.2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From ddf3bd3afaccb0b9804fe929820334841f35390b Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 28 Aug 2016 05:04:17 +1000 Subject: [PATCH 067/263] Avoid breaking when calling lstat on virtual paths Resolves atom/tree-view#927. --- lib/tree-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index a055c3af..8cb3af13 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -276,6 +276,7 @@ class TreeView extends View @roots = for projectPath in atom.project.getPaths() stats = fs.lstatSyncNoException(projectPath) + continue unless stats stats = _.pick stats, _.keys(stats)... for key in ["atime", "birthtime", "ctime", "mtime"] stats[key] = stats[key].getTime() From 36a6961fa3d5b5c9aa38d55bdd8652cfdb1e4cb0 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Sun, 28 Aug 2016 17:15:49 +1000 Subject: [PATCH 068/263] Add spec for ddf3bd3 --- spec/file-stats-spec.coffee | 64 ++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee index 0942f262..172a4c7a 100644 --- a/spec/file-stats-spec.coffee +++ b/spec/file-stats-spec.coffee @@ -5,30 +5,31 @@ path = require 'path' temp = require('temp').track() describe "FileStats", -> - [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] - - beforeEach -> - timeStarted = Date.now() - rootDirPath = fs.absolute(temp.mkdirSync("tree-view")) - subdirPath = path.join(rootDirPath, "subdir") - filePath1 = path.join(rootDirPath, "file1.txt") - filePath2 = path.join(subdirPath, "file2.txt") - - fs.makeTreeSync(subdirPath) - fs.writeFileSync(filePath1, file1Data) - fs.writeFileSync(filePath2, file2Data) - atom.project.setPaths([rootDirPath]) - - waitsForPromise -> - atom.packages.activatePackage("tree-view") - - runs -> - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() - - afterEach -> - temp.cleanup() describe "provision of filesystem stats", -> + [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] + + beforeEach -> + timeStarted = Date.now() + rootDirPath = fs.absolute(temp.mkdirSync("tree-view")) + subdirPath = path.join(rootDirPath, "subdir") + filePath1 = path.join(rootDirPath, "file1.txt") + filePath2 = path.join(subdirPath, "file2.txt") + + fs.makeTreeSync(subdirPath) + fs.writeFileSync(filePath1, file1Data) + fs.writeFileSync(filePath2, file2Data) + atom.project.setPaths([rootDirPath]) + + waitsForPromise -> + atom.packages.activatePackage("tree-view") + + runs -> + treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + + afterEach -> + temp.cleanup() + it "passes stats to File instances", -> stats = treeView.roots[0].directory.entries["file1.txt"].stats expect(stats).toBeDefined() @@ -39,25 +40,36 @@ describe "FileStats", -> stats = treeView.roots[0].directory.entries["subdir"].stats expect(stats).toBeDefined() expect(stats.mtime).toBeDefined() - + it "passes stats to a root directory when initialised", -> expect(treeView.roots[0].directory.stats).toBeDefined() - + it "passes stats to File instances in subdirectories", -> treeView.find(".entries > li:contains(subdir)")[0].expand() subdir = treeView.roots[0].directory.entries["subdir"] stats = subdir.entries["file2.txt"].stats expect(stats).toBeDefined() expect(stats.size).toEqual(file2Data.length) - + it "converts date-stats to timestamps", -> stats = treeView.roots[0].directory.entries["file1.txt"].stats stamp = stats.mtime expect(_.isDate stamp).toBe(false) expect(typeof stamp).toBe("number") expect(Number.isNaN stamp).toBe(false) - + it "accurately converts timestamps", -> stats = treeView.roots[0].directory.entries["file1.txt"].stats # Two minutes should be enough expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000) + + describe "virtual filepaths", -> + beforeEach -> + atom.project.setPaths([]) + waitsForPromise -> Promise.all [ + atom.packages.activatePackage("tree-view") + atom.packages.activatePackage("about") + ] + + it "doesn't throw an exception when accessing virtual filepaths", -> + atom.project.setPaths(["atom://about"]) From fee03d024bd67736bba34f514cba92caf2e894e1 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Mon, 29 Aug 2016 13:46:03 +1000 Subject: [PATCH 069/263] Adjust formatting --- lib/tree-view.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 8cb3af13..3eade0df 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -275,8 +275,7 @@ class TreeView extends View @loadIgnoredPatterns() @roots = for projectPath in atom.project.getPaths() - stats = fs.lstatSyncNoException(projectPath) - continue unless stats + continue unless stats = fs.lstatSyncNoException(projectPath) stats = _.pick stats, _.keys(stats)... for key in ["atime", "birthtime", "ctime", "mtime"] stats[key] = stats[key].getTime() From 7ef8c72fd1f9479057477bc726e13cc5e89cc800 Mon Sep 17 00:00:00 2001 From: Wliu Date: Mon, 29 Aug 2016 11:57:21 -0400 Subject: [PATCH 070/263] Prepare 0.209.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac9d7286..66d7f105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.209.2", + "version": "0.209.3", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 5981f4738729b1a61b6da0e86c2d3109180af0e9 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Wed, 7 Sep 2016 16:44:36 +1000 Subject: [PATCH 071/263] Fire events when creating, moving or deleting items --- lib/copy-dialog.coffee | 4 +++- lib/move-dialog.coffee | 1 + lib/tree-view.coffee | 32 ++++++++++++++++++++++++-------- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/copy-dialog.coffee b/lib/copy-dialog.coffee index d80863f2..5509949e 100644 --- a/lib/copy-dialog.coffee +++ b/lib/copy-dialog.coffee @@ -32,8 +32,10 @@ class CopyDialog extends Dialog try if fs.isDirectorySync(@initialPath) fs.copySync(@initialPath, newPath) + @trigger 'entry-copied', [@initialPath, newPath] else - fs.copy @initialPath, newPath, -> + fs.copy @initialPath, newPath, => + @trigger 'entry-copied', [@initialPath, newPath] atom.workspace.open newPath, activatePane: true initialLine: activeEditor?.getLastCursor().getBufferRow() diff --git a/lib/move-dialog.coffee b/lib/move-dialog.coffee index 69d50d8e..d601db5c 100644 --- a/lib/move-dialog.coffee +++ b/lib/move-dialog.coffee @@ -36,6 +36,7 @@ class MoveDialog extends Dialog try fs.makeTreeSync(directoryPath) unless fs.existsSync(directoryPath) fs.moveSync(@initialPath, newPath) + @trigger 'entry-moved', [@initialPath, newPath] if repo = repoForPath(newPath) repo.getPathStatus(@initialPath) repo.getPathStatus(newPath) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 3eade0df..eb9b8519 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -2,7 +2,7 @@ path = require 'path' {shell} = require 'electron' _ = require 'underscore-plus' -{BufferedProcess, CompositeDisposable} = require 'atom' +{BufferedProcess, CompositeDisposable, Emitter} = require 'atom' {repoForPath, getStyleObject, getFullExtension} = require "./helpers" {$, View} = require 'atom-space-pen-views' fs = require 'fs-plus' @@ -32,6 +32,7 @@ class TreeView extends View initialize: (state) -> @disposables = new CompositeDisposable + @emitter = new Emitter @focusAfterAttach = false @roots = [] @scrollLeftAfterAttach = -1 @@ -453,6 +454,8 @@ class TreeView extends View if oldPath MoveDialog ?= require './move-dialog' dialog = new MoveDialog(oldPath) + dialog.on 'entry-moved', (event, oldPath, newPath) => + @emitter.emit 'entry-moved', {oldPath, newPath} dialog.attach() # Get the outline of a system call to the current platform's file manager. @@ -533,6 +536,8 @@ class TreeView extends View CopyDialog ?= require './copy-dialog' dialog = new CopyDialog(oldPath) + dialog.on 'entry-copied', (event, oldPath, newPath) => + @emitter.emit 'entry-copied', {oldPath, newPath} dialog.attach() removeSelectedEntries: -> @@ -554,10 +559,12 @@ class TreeView extends View message: "Are you sure you want to delete the selected #{if selectedPaths.length > 1 then 'items' else 'item'}?" detailedMessage: "You are deleting:\n#{selectedPaths.join('\n')}" buttons: - "Move to Trash": -> + "Move to Trash": => failedDeletions = [] for selectedPath in selectedPaths - if not shell.moveItemToTrash(selectedPath) + if shell.moveItemToTrash(selectedPath) + @emitter.emit 'entry-deleted', {path: selectedPath} + else failedDeletions.push "#{selectedPath}" if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) @@ -633,15 +640,21 @@ class TreeView extends View if fs.isDirectorySync(initialPath) # use fs.copy to copy directories since read/write will fail for directories - catchAndShowFileErrors -> fs.copySync(initialPath, newPath) + catchAndShowFileErrors => + fs.copySync(initialPath, newPath) + @emitter.emit 'entry-copied', {oldPath: initialPath, newPath} else # read the old file and write a new one at target location - catchAndShowFileErrors -> fs.writeFileSync(newPath, fs.readFileSync(initialPath)) + catchAndShowFileErrors => + fs.writeFileSync(newPath, fs.readFileSync(initialPath)) + @emitter.emit 'entry-copied', {oldPath: initialPath, newPath} else if cutPaths - # Only move the target if the cut target doesn't exists and if the newPath + # Only move the target if the cut target doesn't exist and if the newPath # is not within the initial path unless fs.existsSync(newPath) or newPath.startsWith(initialPath) - catchAndShowFileErrors -> fs.moveSync(initialPath, newPath) + catchAndShowFileErrors => + fs.moveSync(initialPath, newPath) + @emitter.emit 'entry-moved', {oldPath: initialPath, newPath} add: (isCreatingFile) -> selectedEntry = @selectedEntry() ? @roots[0] @@ -652,9 +665,11 @@ class TreeView extends View dialog.on 'directory-created', (event, createdPath) => @entryForPath(createdPath)?.reload() @selectEntryForPath(createdPath) + @emitter.emit 'directory-created', {path: createdPath} false - dialog.on 'file-created', (event, createdPath) -> + dialog.on 'file-created', (event, createdPath) => atom.workspace.open(createdPath) + @emitter.emit 'file-created', {path: createdPath} false dialog.attach() @@ -725,6 +740,7 @@ class TreeView extends View try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) + @emitter.emit 'entry-moved', {oldPath: initialPath, newPath} if repo = repoForPath(newPath) repo.getPathStatus(initialPath) From c0885505e57330a1626417273fe1b249fda36d58 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Wed, 7 Sep 2016 18:27:07 +1000 Subject: [PATCH 072/263] Add TreeView methods to attach file-event handlers --- lib/tree-view.coffee | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index eb9b8519..0c20f805 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -88,6 +88,21 @@ class TreeView extends View @disposables.dispose() @detach() if @panel? + onDirectoryCreated: (callback) -> + @emitter.on('directory-created', callback) + + onEntryCopied: (callback) -> + @emitter.on('entry-copied', callback) + + onEntryDeleted: (callback) -> + @emitter.on('entry-deleted', callback) + + onEntryMoved: (callback) -> + @emitter.on('entry-moved', callback) + + onFileCreated: (callback) -> + @emitter.on('file-created', callback) + handleEvents: -> @on 'dblclick', '.tree-view-resize-handle', => @resizeToFitContent() From 1488a665f3a2d9a2c9495f839a6e885646752055 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Wed, 7 Sep 2016 23:00:36 +1000 Subject: [PATCH 073/263] Add specs for 5981f47 and c088550 --- spec/tree-view-spec.coffee | 93 ++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 10470b6b..e7078ae4 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -1492,18 +1492,29 @@ describe "TreeView", -> LocalStorage.clear() describe "when attempting to paste a directory into itself", -> + handlers = null + describe "when copied", -> - it "makes a copy inside itself", -> + beforeEach -> LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) + handlers = treeView.emitter.handlersByEventName + it "makes a copy inside itself", -> dirView.click() newPath = path.join(dirPath, path.basename(dirPath)) expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() expect(fs.existsSync(newPath)).toBeTruthy() + it "dispatches an event to the tree-view", -> + treeView.onEntryCopied -> + spyOn(handlers['entry-copied'], '0') + + dirView.click() + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(handlers['entry-copied'][0].callCount).toEqual(1) + it 'does not keep copying recursively', -> - LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) dirView.click() newPath = path.join(dirPath, path.basename(dirPath)) @@ -1538,19 +1549,21 @@ describe "TreeView", -> describe "when a file has been copied", -> describe "when a file is selected", -> - it "creates a copy of the original file in the selected file's parent directory", -> + beforeEach -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) + it "creates a copy of the original file in the selected file's parent directory", -> + treeView.onEntryCopied -> + spyOn(treeView.emitter.handlersByEventName['entry-copied'], '0') fileView2.click() atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() expect(fs.existsSync(filePath)).toBeTruthy() + expect(treeView.emitter.handlersByEventName['entry-copied'][0].callCount).toEqual(1) describe "when the target already exists", -> it "appends a number to the destination name", -> - LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - fileView.click() atom.commands.dispatch(treeView.element, "tree-view:paste") atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1696,20 +1709,25 @@ describe "TreeView", -> expect(fs.existsSync(path.join(dirPath, "test-file30.txt"))).toBeTruthy() describe "when a file has been cut", -> + handlers = null + + beforeEach -> + LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) + handlers = treeView.emitter.handlersByEventName + treeView.onEntryMoved -> + spyOn(handlers['entry-moved'], '0') + describe "when a file is selected", -> it "creates a copy of the original file in the selected file's parent directory and removes the original", -> - LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - fileView2.click() atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() + expect(handlers['entry-moved'][0].callCount).toEqual(1) describe 'when the target destination file exists', -> it 'does not move the cut file', -> - LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - filePath3 = path.join(dirPath2, "test-file.txt") fs.writeFileSync(filePath3, "doesn't matter") @@ -1717,6 +1735,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(filePath)).toBeTruthy() + expect(handlers['entry-moved'][0].callCount).toEqual(0) describe "when a directory is selected", -> it "creates a copy of the original file in the selected directory and removes the original", -> @@ -1727,12 +1746,19 @@ describe "TreeView", -> expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() + expect(handlers['entry-moved'][0].callCount).toEqual(1) describe "when multiple files have been cut", -> describe "when a file is selected", -> - it "moves the selected files to the parent directory of the selected file", -> + handlers = null + + beforeEach -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath2, filePath3]) + handlers = treeView.emitter.handlersByEventName + treeView.onEntryMoved -> + spyOn(handlers['entry-moved'], '0') + it "moves the selected files to the parent directory of the selected file", -> fileView.click() atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1740,11 +1766,10 @@ describe "TreeView", -> expect(fs.existsSync(path.join(dirPath, path.basename(filePath3)))).toBeTruthy() expect(fs.existsSync(filePath2)).toBeFalsy() expect(fs.existsSync(filePath3)).toBeFalsy() + expect(handlers['entry-moved'][0].callCount).toEqual(2) describe 'when the target destination file exists', -> it 'does not move the cut file', -> - LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath2, filePath3]) - filePath4 = path.join(dirPath, "test-file2.txt") filePath5 = path.join(dirPath, "test-file3.txt") fs.writeFileSync(filePath4, "doesn't matter") @@ -1755,6 +1780,7 @@ describe "TreeView", -> expect(fs.existsSync(filePath2)).toBeTruthy() expect(fs.existsSync(filePath3)).toBeTruthy() + expect(handlers['entry-moved'][0].callCount).toEqual(0) describe "when a directory is selected", -> it "creates a copy of the original file in the selected directory and removes the original", -> @@ -1784,11 +1810,15 @@ describe "TreeView", -> expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT: no such file or directory' describe "tree-view:add-file", -> - [addPanel, addDialog] = [] + [addPanel, addDialog, handlers] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) + handlers = treeView.emitter.handlersByEventName + treeView.onFileCreated -> + spyOn(handlers['file-created'], '0') + waitsForFileToOpen -> fileView.click() @@ -1813,7 +1843,7 @@ describe "TreeView", -> describe "when the path without a trailing '#{path.sep}' 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", -> + it "adds a file, closes the dialog and selects the file in the tree-view", -> newPath = path.join(dirPath, "new-test-file.txt") waitsForFileToOpen -> @@ -1830,6 +1860,7 @@ describe "TreeView", -> runs -> expect(treeView.find('.selected').text()).toBe path.basename(newPath) + expect(handlers['file-created'][0]).toHaveBeenCalled() it "adds file in any project path", -> newPath = path.join(dirPath3, "new-test-file.txt") @@ -1854,6 +1885,7 @@ describe "TreeView", -> runs -> expect(treeView.find('.selected').text()).toBe path.basename(newPath) + expect(handlers['file-created'][0]).toHaveBeenCalled() describe "when a file already exists at that location", -> it "shows an error message and does not close the dialog", -> @@ -1865,9 +1897,10 @@ describe "TreeView", -> expect(addDialog.errorMessage.text()).toContain 'already exists' expect(addDialog).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel + expect(handlers['file-created'][0]).not.toHaveBeenCalled() describe "when the project has no path", -> - it "add a file and closes the dialog", -> + it "adds a file and closes the dialog", -> atom.project.setPaths([]) addDialog.close() atom.commands.dispatch(treeView.element, "tree-view:add-file") @@ -1884,6 +1917,7 @@ describe "TreeView", -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).toBe fs.realpathSync(newPath) + expect(handlers['file-created'][0]).toHaveBeenCalled() describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> it "shows an error message and does not close the dialog", -> @@ -1964,11 +1998,15 @@ describe "TreeView", -> expect(addDialog.text()).toContain("You must open a directory to create a file with a relative path") describe "tree-view:add-folder", -> - [addPanel, addDialog] = [] + [addPanel, addDialog, handlers] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) + handlers = treeView.emitter.handlersByEventName + treeView.onDirectoryCreated -> + spyOn(handlers['directory-created'], '0') + waitsForFileToOpen -> fileView.click() @@ -1997,6 +2035,7 @@ describe "TreeView", -> expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath expect(treeView.find(".tree-view")).toMatchSelector(':focus') expect(dirView.find('.directory.selected:contains(new)').length).toBe 1 + expect(handlers['directory-created'][0].callCount).toBe 1 describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> describe "when no directory exists at the given path", -> @@ -2009,6 +2048,7 @@ describe "TreeView", -> expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath expect(treeView.find(".tree-view")).toMatchSelector(':focus') expect(dirView.find('.directory.selected:contains(new)').length).toBe(1) + expect(handlers['directory-created'][0].callCount).toBe(1) it "selects the created directory and does not change the expansion state of existing directories", -> expandedPath = path.join(dirPath, 'expanded-dir') @@ -2026,6 +2066,7 @@ describe "TreeView", -> expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath expect(treeView.find(".tree-view")).toMatchSelector(':focus') expect(dirView.find('.directory.selected:contains(new2)').length).toBe(1) + expect(handlers['directory-created'][0].callCount).toBe(1) expect(treeView.entryForPath(expandedPath).isExpanded).toBeTruthy() describe "when the project has no path", -> @@ -2041,6 +2082,7 @@ describe "TreeView", -> addDialog.miniEditor.getModel().insertText(newPath) atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() + expect(handlers['directory-created'][0].callCount).toBe 1 expect(atom.workspace.getModalPanels().length).toBe 0 describe "when a directory already exists at the given path", -> @@ -2053,14 +2095,19 @@ describe "TreeView", -> expect(addDialog.errorMessage.text()).toContain 'already exists' expect(addDialog).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel + expect(handlers['directory-created'][0].callCount).toBe 0 describe "tree-view:move", -> describe "when a file is selected", -> - moveDialog = null + [moveDialog, handlers] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) + handlers = treeView.emitter.handlersByEventName + treeView.onEntryMoved -> + spyOn(handlers['entry-moved'], '0') + waitsForFileToOpen -> fileView.click() @@ -2099,6 +2146,7 @@ describe "TreeView", -> dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir)') dirView[0].expand() expect($(dirView[0].entries).children().length).toBe 0 + expect(handlers['entry-moved'][0].callCount).toBe 1 describe "when the directories along the new path don't exist", -> it "creates the target directory before moving the file", -> @@ -2113,6 +2161,7 @@ describe "TreeView", -> runs -> expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() + expect(handlers['entry-moved'][0].callCount).toBe 1 describe "when a file or directory already exists at the target path", -> it "shows an error message and does not close the dialog", -> @@ -2126,6 +2175,7 @@ describe "TreeView", -> expect(moveDialog.errorMessage.text()).toContain 'already exists' expect(moveDialog).toHaveClass('error') expect(moveDialog.hasParent()).toBeTruthy() + expect(handlers['entry-moved'][0].callCount).toBe 0 describe "when 'core:cancel' is triggered on the move dialog", -> it "removes the dialog and focuses the tree view", -> @@ -2648,6 +2698,10 @@ describe "TreeView", -> describe "when the file is deleted", -> it "updates the style of the directory", -> + handlers = treeView.emitter.handlersByEventName + treeView.onEntryDeleted -> + spyOn(handlers['entry-deleted'], '0') + expect(treeView.selectedEntry().getPath()).toContain(path.join('dir2', 'new2')) dirView = $(treeView.roots[0].entries).find('.directory:contains(dir2)') expect(dirView).not.toBeNull() @@ -2656,6 +2710,7 @@ describe "TreeView", -> dialog.buttons["Move to Trash"]() atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(dirView[0].directory.updateStatus).toHaveBeenCalled() + expect(handlers['entry-deleted'][0]).toHaveBeenCalled() describe "when the project is a symbolic link to the repository root", -> beforeEach -> @@ -2802,7 +2857,7 @@ describe "TreeView", -> Object.defineProperty(process, "platform", {__proto__: null, value: 'win32'}) afterEach -> - # Ensure that process.platform is set back to it's original value + # Ensure that process.platform is set back to its original value Object.defineProperty(process, "platform", {__proto__: null, value: originalPlatform}) describe 'using the ctrl key', -> @@ -2821,7 +2876,7 @@ describe "TreeView", -> Object.defineProperty(process, "platform", {__proto__: null, value: 'darwin'}) afterEach -> - # Ensure that process.platform is set back to it's original value + # Ensure that process.platform is set back to its original value Object.defineProperty(process, "platform", {__proto__: null, value: originalPlatform}) describe 'using the ctrl key', -> From 74b1b7ec00e572e757d1715ff1f25e73c987d259 Mon Sep 17 00:00:00 2001 From: Alhadis Date: Wed, 7 Sep 2016 23:24:19 +1000 Subject: [PATCH 074/263] Fix typos and polish punctuation of spec titles --- spec/tree-view-spec.coffee | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index e7078ae4..601ff759 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2820,7 +2820,7 @@ describe "TreeView", -> fileView3 = treeView.find('.file:contains(test-file3.txt)') describe 'selecting multiple items', -> - it 'switches the contextual menu to muli-select mode', -> + it 'switches the contextual menu to multi-select mode', -> fileView1.click() fileView2.trigger($.Event('mousedown', {shiftKey: true})) expect(treeView.find('.tree-view')).toHaveClass('multi-select') @@ -2828,13 +2828,13 @@ describe "TreeView", -> expect(treeView.find('.tree-view')).toHaveClass('full-menu') describe 'selecting multiple items', -> - it 'switches the contextual menu to muli-select mode', -> + it 'switches the contextual menu to multi-select mode', -> fileView1.click() fileView2.trigger($.Event('mousedown', {shiftKey: true})) expect(treeView.find('.tree-view')).toHaveClass('multi-select') describe 'using the shift key', -> - it 'selects the items between the already selected item and the shift clicked item', -> + it 'selects the items between the already selected item and the shift-clicked item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {shiftKey: true})) expect(fileView1).toHaveClass('selected') @@ -2842,7 +2842,7 @@ describe "TreeView", -> expect(fileView3).toHaveClass('selected') describe 'using the metakey(cmd) key', -> - it 'selects the cmd clicked item in addition to the original selected item', -> + it 'selects the cmd-clicked item in addition to the original selected item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {metaKey: true})) expect(fileView1).toHaveClass('selected') @@ -2861,7 +2861,7 @@ describe "TreeView", -> Object.defineProperty(process, "platform", {__proto__: null, value: originalPlatform}) describe 'using the ctrl key', -> - it 'selects the ctrl clicked item in addition to the original selected item', -> + it 'selects the ctrl-clicked item in addition to the original selected item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(fileView1).toHaveClass('selected') @@ -2880,7 +2880,7 @@ describe "TreeView", -> Object.defineProperty(process, "platform", {__proto__: null, value: originalPlatform}) describe 'using the ctrl key', -> - describe "previous item is selected but the ctrl clicked item is not", -> + describe "previous item is selected but the ctrl-clicked item is not", -> it 'selects the clicked item, but deselects the previous item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {ctrlKey: true})) @@ -2894,7 +2894,7 @@ describe "TreeView", -> expect(treeView.list).toHaveClass('full-menu') expect(treeView.list).not.toHaveClass('multi-select') - describe 'previous item is selected including the ctrl clicked', -> + describe 'previous item is selected including the ctrl-clicked', -> it 'displays the multi-select menu', -> fileView1.click() fileView3.trigger($.Event('mousedown', {metaKey: true})) @@ -2917,7 +2917,7 @@ describe "TreeView", -> expect(treeView.list).not.toHaveClass('multi-select') describe 'when no item is selected', -> - it 'selects the ctrl clicked item', -> + it 'selects the ctrl-clicked item', -> fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(fileView3).toHaveClass('selected') @@ -2944,18 +2944,18 @@ describe "TreeView", -> expect(treeView.list).toHaveClass('full-menu') expect(treeView.list).not.toHaveClass('multi-select') - it 'selects right clicked item', -> + it 'selects right-clicked item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView3).toHaveClass('selected') - it 'de-selects the previously selected item', -> + it 'deselects the previously selected item', -> fileView1.click() fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView1).not.toHaveClass('selected') describe 'when no item is selected', -> - it 'selects the right clicked item', -> + it 'selects the right-clicked item', -> fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView3).toHaveClass('selected') From 0ae4993f1c752d6358fb9e782dac39575d359556 Mon Sep 17 00:00:00 2001 From: Phoenix Eliot Date: Wed, 5 Oct 2016 16:27:17 -0700 Subject: [PATCH 075/263] Add description to Always Open Existing --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 66d7f105..0aa98647 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,8 @@ }, "alwaysOpenExisting": { "type": "boolean", - "default": false + "default": false, + "description": "When opening a file, always focus an already-existing view of the file even if it's in a another pane." } } } From 84bfbf09ebb304ee8dab2cd37bc4f77db7157007 Mon Sep 17 00:00:00 2001 From: liuderchi Date: Fri, 7 Oct 2016 00:17:22 +0800 Subject: [PATCH 076/263] :art: refine item name in context-menu - use platform specific name: Finder for darwin; Explorer for windows; File Manager for linux --- menus/tree-view.cson | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/menus/tree-view.cson b/menus/tree-view.cson index 90ad6aed..64d7a1be 100644 --- a/menus/tree-view.cson +++ b/menus/tree-view.cson @@ -97,9 +97,28 @@ 'atom-pane[data-active-item-path] .tab.active': [ {'label': 'Rename', 'command': 'tree-view:rename'} {'label': 'Reveal in Tree View', 'command': 'tree-view:reveal-active-file'} + ] + + '.platform-darwin atom-pane[data-active-item-path] .tab.active': [ + {'label': 'Show In Finder', 'command': 'tree-view:show-current-file-in-file-manager'} + ] + + '.platform-win32 atom-pane[data-active-item-path] .tab.active': [ + {'label': 'Show In Explorer', 'command': 'tree-view:show-current-file-in-file-manager'} + ] + + '.platform-linux atom-pane[data-active-item-path] .tab.active': [ {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} ] - 'atom-text-editor:not([mini])': [ + '.platform-darwin atom-text-editor:not([mini])': [ + {'label': 'Show In Finder', 'command': 'tree-view:show-current-file-in-file-manager'} + ] + + '.platform-win32 atom-text-editor:not([mini])': [ + {'label': 'Show In Explorer', 'command': 'tree-view:show-current-file-in-file-manager'} + ] + + '.platform-linux atom-text-editor:not([mini])': [ {'label': 'Show In File Manager', 'command': 'tree-view:show-current-file-in-file-manager'} ] From aae11b1a707d8bf63089bcdfe382372733746c63 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 6 Oct 2016 19:09:06 -0400 Subject: [PATCH 077/263] Prepare 0.210.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0aa98647..f48060ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.209.3", + "version": "0.210.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 32422496cafaa7832e13dc12f34859ddb2e0af63 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 10 Oct 2016 20:50:00 -0700 Subject: [PATCH 078/263] make dragging files and project roots mutually exclusive --- lib/project-folder-dnd-handler.coffee | 19 +++----------- lib/tree-view.coffee | 36 +++++++++++++++------------ spec/tree-view-spec.coffee | 18 -------------- 3 files changed, 24 insertions(+), 49 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 054d99bc..df562b1c 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -16,9 +16,6 @@ class ProjectFolderDragAndDropHandler RendererIpc.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) onDragStart: (event) => - unless $(event.target).closest('.project-root-header').size() - return - event.originalEvent.dataTransfer.setData 'atom-event', 'true' projectRoot = $(event.target).closest('.project-root') directory = projectRoot[0].directory @@ -147,19 +144,11 @@ class ProjectFolderDragAndDropHandler else projectRoots.index(projectRoot) + 1 - isMovingProjectFolders: (event) -> - target = $(event.target) - return null if @isPlaceholder(target) - - element = target.closest('.project-root-header') - return false unless element.length - - elementTop30 = element.offset().top + element.height() * 0.3 - return true if event.originalEvent.pageY < elementTop30 - elementBottom30 = element.offset().top + element.height() * 0.7 - return true if event.originalEvent.pageY > elementBottom30 + canDragStart: (event) -> + $(event.target).closest('.project-root-header').size() > 0 - false + isDragging: (event) -> + Boolean event.originalEvent.dataTransfer.getData 'from-root-path' getPlaceholder: -> @placeholderEl ?= $('
  • ', class: 'placeholder') diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f5b699c9..16d0e0eb 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -281,7 +281,7 @@ class TreeView extends View stats = _.pick stats, _.keys(stats)... for key in ["atime", "birthtime", "ctime", "mtime"] stats[key] = stats[key].getTime() - + directory = new Directory({ name: path.basename(projectPath) fullPath: projectPath @@ -828,6 +828,10 @@ class TreeView extends View onDragEnter: (e) => e.stopPropagation() + + if @projectFolderDragAndDropHandler.isDragging(e) + return + entry = e.currentTarget.parentNode @dragEventCounts.set(entry, 0) unless @dragEventCounts.get(entry) entry.classList.add('selected') if @dragEventCounts.get(entry) is 0 @@ -835,15 +839,21 @@ class TreeView extends View onDragLeave: (e) => e.stopPropagation() + + if @projectFolderDragAndDropHandler.isDragging(e) + return @projectFolderDragAndDropHandler.onDragLeave(e) + entry = e.currentTarget.parentNode @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) entry.classList.remove('selected') if @dragEventCounts.get(entry) is 0 - @projectFolderDragAndDropHandler.onDragLeave(e) # Handle entry name object dragstart event onDragStart: (e) -> e.stopPropagation() + if @projectFolderDragAndDropHandler.canDragStart(e) + return @projectFolderDragAndDropHandler.onDragStart(e) + target = $(e.currentTarget).find(".name") initialPath = target.data("path") @@ -865,36 +875,30 @@ class TreeView extends View window.requestAnimationFrame -> fileNameElement.remove() - @projectFolderDragAndDropHandler.onDragStart(e) - # Handle entry dragover event; reset default dragover actions onDragOver: (e) -> e.preventDefault() e.stopPropagation() + if @projectFolderDragAndDropHandler.isDragging(e) + return @projectFolderDragAndDropHandler.onDragOver(e) + entry = e.currentTarget - if @projectFolderDragAndDropHandler.isMovingProjectFolders(e) - entry.classList.remove('selected') - else if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') + if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') entry.classList.add('selected') - @projectFolderDragAndDropHandler.onDragOver(e) - # Handle entry drop event onDrop: (e) -> e.preventDefault() e.stopPropagation() - entry = e.currentTarget - unless entry instanceof DirectoryView - @projectFolderDragAndDropHandler.onDrop(e) - return + if @projectFolderDragAndDropHandler.isDragging(e) + return @projectFolderDragAndDropHandler.onDrop(e) + entry = e.currentTarget entry.classList.remove('selected') - if @projectFolderDragAndDropHandler.isMovingProjectFolders(e) - @projectFolderDragAndDropHandler.onDrop(e) - return + return unless entry instanceof DirectoryView newDirectoryPath = $(entry).find(".name").data("path") return false unless newDirectoryPath diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6c2315e7..5d375abb 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3410,24 +3410,6 @@ describe "TreeView", -> treeView.projectFolderDragAndDropHandler.onDragEnd() expect('.placeholder').not.toExist() - describe "when dragging on the middle part of the header", -> - it "should not add the placeholder", -> - # Dragging gammaDir onto alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - gammaDir = $(treeView).find('.project-root:contains(gamma):first') - [dragStartEvent, dragOverEvents] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) - - treeView.onDragStart(dragStartEvent) - treeView.onDragEnter(dragOverEvents.middle) - treeView.onDragOver(dragOverEvents.middle) - expect(alphaDir).toHaveClass('selected') - expect('.placeholder').not.toExist() - - # Is removed when drag ends - treeView.projectFolderDragAndDropHandler.onDragEnd() - expect('.placeholder').not.toExist() - describe "when dragging on the bottom part of the header", -> it "should add the placeholder below the directory", -> # Dragging gammaDir onto alphaDir From 1a317b597f90ad9ac814ff86f06a1c75adc68efb Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 17 Oct 2016 21:35:14 -0700 Subject: [PATCH 079/263] Allow drag / dropping project folders to empty space --- lib/tree-view.coffee | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 16d0e0eb..ab197e74 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -105,6 +105,8 @@ class TreeView extends View @on 'dragleave', '.entry.directory > .header', (e) => @onDragLeave(e) @on 'dragover', '.entry', (e) => @onDragOver(e) @on 'drop', '.entry', (e) => @onDrop(e) + @on 'dragover', '.tree-view', (e) => @onDragOverTree(e) + @on 'drop', '.tree-view', (e) => @onDropTree(e) atom.commands.add @element, 'core:move-up': @moveUp.bind(this) @@ -912,3 +914,18 @@ class TreeView extends View # Drop event from OS for file in e.originalEvent.dataTransfer.files @moveEntry(file.path, newDirectoryPath) + + # handle drag over and drop on the empty space in which there are no entries + onDragOverTree: (e) -> + e.preventDefault() + e.stopPropagation() + + if @projectFolderDragAndDropHandler.isDragging(e) + return @projectFolderDragAndDropHandler.onDragOver(e) + + onDropTree: (e) -> + e.preventDefault() + e.stopPropagation() + + if @projectFolderDragAndDropHandler.isDragging(e) + return @projectFolderDragAndDropHandler.onDrop(e) From 702ee4fe45515bf05fd3e9b67c91f6c1ad4406ec Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 17 Oct 2016 21:41:44 -0700 Subject: [PATCH 080/263] Ensure that placeholder doesn't disappear above the top of tree view --- styles/tree-view.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/styles/tree-view.less b/styles/tree-view.less index dfb492c7..474e01ff 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -114,6 +114,11 @@ border-radius: 4px; border: 1px solid transparent; } + + // ensure that placeholder doesn't disappear above the top of the view + &:first-child { + margin-top: 1px; + } } } From 93a6b81a75eb492337914d09a468c3fe451edddf Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Mon, 17 Oct 2016 22:07:45 -0700 Subject: [PATCH 081/263] add test for when dragging below all entries --- spec/tree-view-spec.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 5d375abb..4fc79b97 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3426,6 +3426,25 @@ describe "TreeView", -> treeView.projectFolderDragAndDropHandler.onDragEnd() expect('.placeholder').not.toExist() + describe "when below all entries", -> + it "should add the placeholder below the last directory", -> + # Dragging gammaDir onto alphaDir + alphaDir = $(treeView).find('.project-root:contains(alpha):first') + lastDir = $(treeView).find('.project-root:last') + [dragStartEvent, dragOverEvents] = + eventHelpers.buildPositionalDragEvents(alphaDir.find('.project-root-header')[0], treeView.find('.tree-view')[0]) + + expect(alphaDir[0]).not.toEqual(lastDir[0]) + + treeView.onDragStart(dragStartEvent) + treeView.onDragOver(dragOverEvents.bottom) + expect(lastDir[0].nextSibling).toHaveClass('placeholder') + + # Is removed when drag ends + treeView.projectFolderDragAndDropHandler.onDragEnd() + expect('.placeholder').not.toExist() + + describe "when dropping a project root's header onto a different project root's header", -> describe "when dropping on the top part of the header", -> it "should add the placeholder above the directory", -> From 34df5f49793e27ae08b52128ea6f36efcd82c329 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Tue, 18 Oct 2016 21:45:16 -0700 Subject: [PATCH 082/263] Fix dragging between windows for deprecated ipc --- lib/project-folder-dnd-handler.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index df562b1c..0d39b5af 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -1,5 +1,5 @@ BrowserWindow = null # Defer require until actually used -RendererIpc = require 'ipc' +{ipcRenderer} = require 'electron' {$, View} = require 'atom-space-pen-views' _ = require 'underscore-plus' @@ -9,11 +9,11 @@ class ProjectFolderDragAndDropHandler constructor: (@treeView) -> @treeView.on 'dragend', '.project-root-header', @onDragEnd - RendererIpc.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) + ipcRenderer.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) # unused unsubscribe: -> - RendererIpc.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) + ipcRenderer.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) onDragStart: (event) => event.originalEvent.dataTransfer.setData 'atom-event', 'true' @@ -70,7 +70,7 @@ class ProjectFolderDragAndDropHandler element = projectRoots.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' @getPlaceholder().insertAfter(element) - onDropOnOtherWindow: (fromItemIndex) => + onDropOnOtherWindow: (event, fromItemIndex) => paths = atom.project.getPaths() paths.splice(fromItemIndex, 1) atom.project.setPaths(paths) From 9d0f1585ea58de75796f4f59e01256cc79fbf970 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Wed, 19 Oct 2016 14:28:12 -0700 Subject: [PATCH 083/263] Fix test for deprecation fix --- spec/tree-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 4fc79b97..6612a475 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3521,7 +3521,7 @@ describe "TreeView", -> gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) treeView.onDragStart(dragStartEvent) - treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow(gammaDir.index()) + treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow({}, gammaDir.index()) expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] expect('.placeholder').not.toExist() From dff6350b48f3c367061aeb701fdba11219b51f3f Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Wed, 19 Oct 2016 14:41:22 -0700 Subject: [PATCH 084/263] remove deprecated use of remote.require('browser-window') --- lib/project-folder-dnd-handler.coffee | 13 +++++-------- spec/tree-view-spec.coffee | 3 ++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/project-folder-dnd-handler.coffee index 0d39b5af..8e24ac79 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/project-folder-dnd-handler.coffee @@ -1,5 +1,6 @@ -BrowserWindow = null # Defer require until actually used -{ipcRenderer} = require 'electron' +url = require 'url' + +{ipcRenderer, remote} = require 'electron' {$, View} = require 'atom-space-pen-views' _ = require 'underscore-plus' @@ -37,7 +38,7 @@ class ProjectFolderDragAndDropHandler uriHasProtocol: (uri) -> try - require('url').parse(uri).protocol? + url.parse(uri).protocol? catch error false @@ -114,7 +115,7 @@ class ProjectFolderDragAndDropHandler if not isNaN(fromWindowId) # Let the window where the drag started know that the tab was dropped - browserWindow = @browserWindowForId(fromWindowId) + browserWindow = remote.BrowserWindow.fromId(fromWindowId) browserWindow?.webContents.send('tree-view:project-folder-dropped', fromIndex) removeDropTargetClasses: -> @@ -162,7 +163,3 @@ class ProjectFolderDragAndDropHandler getWindowId: -> @processId ?= atom.getCurrentWindow().id - - browserWindowForId: (id) -> - BrowserWindow ?= require('remote').require('browser-window') - BrowserWindow.fromId id diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6612a475..0251a808 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -4,6 +4,7 @@ fs = require 'fs-plus' path = require 'path' temp = require('temp').track() os = require 'os' +{remote} = require 'electron' eventHelpers = require "./event-helpers" DefaultFileIcons = require '../lib/default-file-icons' @@ -3503,7 +3504,7 @@ describe "TreeView", -> # mock browserWindowForId browserWindowMock = {webContents: {send: ->}} - treeView.projectFolderDragAndDropHandler.browserWindowForId = -> browserWindowMock + spyOn(remote.BrowserWindow, 'fromId').andReturn(browserWindowMock) spyOn(browserWindowMock.webContents, 'send') treeView.onDrop(dropEvent) From 14c15582edaa8cf817b54791cd9662d9cd8e4b62 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Thu, 20 Oct 2016 22:01:19 -0700 Subject: [PATCH 085/263] Refactor to shorten name of drag and drop handler --- ...ndler.coffee => root-drag-and-drop.coffee} | 2 +- lib/tree-view.coffee | 30 +++++++++---------- spec/tree-view-spec.coffee | 10 +++---- 3 files changed, 21 insertions(+), 21 deletions(-) rename lib/{project-folder-dnd-handler.coffee => root-drag-and-drop.coffee} (99%) diff --git a/lib/project-folder-dnd-handler.coffee b/lib/root-drag-and-drop.coffee similarity index 99% rename from lib/project-folder-dnd-handler.coffee rename to lib/root-drag-and-drop.coffee index 8e24ac79..17fabed4 100644 --- a/lib/project-folder-dnd-handler.coffee +++ b/lib/root-drag-and-drop.coffee @@ -6,7 +6,7 @@ url = require 'url' _ = require 'underscore-plus' module.exports = -class ProjectFolderDragAndDropHandler +class RootDragAndDropHandler constructor: (@treeView) -> @treeView.on 'dragend', '.project-root-header', @onDragEnd diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index ab197e74..15a2a097 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -15,7 +15,7 @@ Minimatch = null # Defer requiring until actually needed Directory = require './directory' DirectoryView = require './directory-view' FileView = require './file-view' -ProjectFolderDragAndDropHandler = require './project-folder-dnd-handler' +RootDragAndDrop = require './root-drag-and-drop' LocalStorage = window.localStorage toggleConfig = (keyPath) -> @@ -43,7 +43,7 @@ class TreeView extends View @currentlyOpening = new Map @dragEventCounts = new WeakMap - @projectFolderDragAndDropHandler = new ProjectFolderDragAndDropHandler(this) + @rootDragAndDrop = new RootDragAndDrop(this) @handleEvents() @@ -831,7 +831,7 @@ class TreeView extends View onDragEnter: (e) => e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) + if @rootDragAndDrop.isDragging(e) return entry = e.currentTarget.parentNode @@ -842,8 +842,8 @@ class TreeView extends View onDragLeave: (e) => e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) - return @projectFolderDragAndDropHandler.onDragLeave(e) + if @rootDragAndDrop.isDragging(e) + return @rootDragAndDrop.onDragLeave(e) entry = e.currentTarget.parentNode @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) @@ -853,8 +853,8 @@ class TreeView extends View onDragStart: (e) -> e.stopPropagation() - if @projectFolderDragAndDropHandler.canDragStart(e) - return @projectFolderDragAndDropHandler.onDragStart(e) + if @rootDragAndDrop.canDragStart(e) + return @rootDragAndDrop.onDragStart(e) target = $(e.currentTarget).find(".name") initialPath = target.data("path") @@ -882,8 +882,8 @@ class TreeView extends View e.preventDefault() e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) - return @projectFolderDragAndDropHandler.onDragOver(e) + if @rootDragAndDrop.isDragging(e) + return @rootDragAndDrop.onDragOver(e) entry = e.currentTarget if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') @@ -894,8 +894,8 @@ class TreeView extends View e.preventDefault() e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) - return @projectFolderDragAndDropHandler.onDrop(e) + if @rootDragAndDrop.isDragging(e) + return @rootDragAndDrop.onDrop(e) entry = e.currentTarget entry.classList.remove('selected') @@ -920,12 +920,12 @@ class TreeView extends View e.preventDefault() e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) - return @projectFolderDragAndDropHandler.onDragOver(e) + if @rootDragAndDrop.isDragging(e) + return @rootDragAndDrop.onDragOver(e) onDropTree: (e) -> e.preventDefault() e.stopPropagation() - if @projectFolderDragAndDropHandler.isDragging(e) - return @projectFolderDragAndDropHandler.onDrop(e) + if @rootDragAndDrop.isDragging(e) + return @rootDragAndDrop.onDrop(e) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 0251a808..25a2e52f 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3408,7 +3408,7 @@ describe "TreeView", -> expect(alphaDir[0].previousSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.projectFolderDragAndDropHandler.onDragEnd() + treeView.rootDragAndDrop.onDragEnd() expect('.placeholder').not.toExist() describe "when dragging on the bottom part of the header", -> @@ -3424,7 +3424,7 @@ describe "TreeView", -> expect(alphaDir[0].nextSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.projectFolderDragAndDropHandler.onDragEnd() + treeView.rootDragAndDrop.onDragEnd() expect('.placeholder').not.toExist() describe "when below all entries", -> @@ -3442,7 +3442,7 @@ describe "TreeView", -> expect(lastDir[0].nextSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.projectFolderDragAndDropHandler.onDragEnd() + treeView.rootDragAndDrop.onDragEnd() expect('.placeholder').not.toExist() @@ -3499,7 +3499,7 @@ describe "TreeView", -> dropEvent = dragDropEvents.bottom dropEvent.originalEvent.dataTransfer.setData('atom-event', true) - dropEvent.originalEvent.dataTransfer.setData('from-window-id', treeView.projectFolderDragAndDropHandler.getWindowId() + 1) + dropEvent.originalEvent.dataTransfer.setData('from-window-id', treeView.rootDragAndDrop.getWindowId() + 1) dropEvent.originalEvent.dataTransfer.setData('from-root-path', etaDirPath) # mock browserWindowForId @@ -3522,7 +3522,7 @@ describe "TreeView", -> gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) treeView.onDragStart(dragStartEvent) - treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow({}, gammaDir.index()) + treeView.rootDragAndDrop.onDropOnOtherWindow({}, gammaDir.index()) expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] expect('.placeholder').not.toExist() From d5c39cef43573b0e6b50077f90178f85fd9995e5 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Thu, 20 Oct 2016 22:02:27 -0700 Subject: [PATCH 086/263] Properly remove rootDragAndDrop handler on deactivate --- lib/root-drag-and-drop.coffee | 14 ++++++++------ lib/tree-view.coffee | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 17fabed4..31f307f1 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -8,12 +8,12 @@ _ = require 'underscore-plus' module.exports = class RootDragAndDropHandler constructor: (@treeView) -> - @treeView.on 'dragend', '.project-root-header', @onDragEnd - ipcRenderer.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) - # unused - unsubscribe: -> + # will be cleaned up by tree view + @treeView.on 'dragend', '.project-root-header', @onDragEnd + + dispose: -> ipcRenderer.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) onDragStart: (event) => @@ -65,10 +65,12 @@ class RootDragAndDropHandler projectRoots = $(@treeView.roots) if newDropTargetIndex < projectRoots.length - element = projectRoots.eq(newDropTargetIndex).addClass 'is-drop-target' + element = projectRoots.eq(newDropTargetIndex) + element.addClass 'is-drop-target' @getPlaceholder().insertBefore(element) else - element = projectRoots.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' + element = projectRoots.eq(newDropTargetIndex - 1) + element.addClass 'drop-target-is-after' @getPlaceholder().insertAfter(element) onDropOnOtherWindow: (event, fromItemIndex) => diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 15a2a097..699dfba1 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -87,6 +87,7 @@ class TreeView extends View deactivate: -> root.directory.destroy() for root in @roots @disposables.dispose() + @rootDragAndDrop.dispose() @detach() if @panel? handleEvents: -> From f2e23fe0531219d1d02cf049c3c0bede192783fd Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sun, 23 Oct 2016 15:49:55 -0700 Subject: [PATCH 087/263] Improve performance of placeholder rendering --- styles/tree-view.less | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index 474e01ff..338e4002 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -50,6 +50,7 @@ flex: 1; width: 100%; overflow: auto; + will-change: transform; } .tree-view { @@ -93,21 +94,32 @@ .placeholder { position: absolute; left: @component-icon-padding; + padding: 0; z-index: 999; display: inline-block; - height: 2px; - margin: -1px; padding: 0; width: calc(~"100% -" @component-icon-padding); background: @background-color-info; list-style: none; + pointer-events: none; + + // bar + &:before { + content: ""; + position: absolute; + height: 2px; + margin: -1px; padding: 0; + width: inherit; + background: inherit; + } &:after { content: ""; position: absolute; left: 0; - margin: -1px; + margin-top: -2px; + margin-left: -1px; width: 4px; height: 4px; background: @background-color-info; From 8fb43e53ccc546866b3bac67c2bfeff0856d13ac Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sun, 23 Oct 2016 17:14:40 -0700 Subject: [PATCH 088/263] Clean up event handling --- lib/root-drag-and-drop.coffee | 84 +++++++++++++++++++++-------------- lib/tree-view.coffee | 37 ++++----------- 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 31f307f1..82ebce33 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -9,32 +9,39 @@ module.exports = class RootDragAndDropHandler constructor: (@treeView) -> ipcRenderer.on('tree-view:project-folder-dropped', @onDropOnOtherWindow) - - # will be cleaned up by tree view - @treeView.on 'dragend', '.project-root-header', @onDragEnd + @handleEvents() dispose: -> ipcRenderer.removeListener('tree-view:project-folder-dropped', @onDropOnOtherWindow) - onDragStart: (event) => - event.originalEvent.dataTransfer.setData 'atom-event', 'true' - projectRoot = $(event.target).closest('.project-root') + handleEvents: -> + # onDragStart is called directly by TreeView's onDragStart + # will be cleaned up by tree view, since they are tree-view's handlers + @treeView.on 'dragenter', '.tree-view', @onDragEnter + @treeView.on 'dragend', '.project-root-header', @onDragEnd + @treeView.on 'dragleave', '.tree-view', @onDragLeave + @treeView.on 'dragover', '.tree-view', @onDragOver + @treeView.on 'drop', '.tree-view', @onDrop + + onDragStart: (e) => + e.originalEvent.dataTransfer.setData 'atom-event', 'true' + projectRoot = $(e.target).closest('.project-root') directory = projectRoot[0].directory - event.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() + e.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() rootIndex = -1 (rootIndex = index; break) for root, index in @treeView.roots when root.directory is directory - event.originalEvent.dataTransfer.setData 'from-root-index', rootIndex - event.originalEvent.dataTransfer.setData 'from-root-path', directory.path - event.originalEvent.dataTransfer.setData 'from-window-id', @getWindowId() + e.originalEvent.dataTransfer.setData 'from-root-index', rootIndex + e.originalEvent.dataTransfer.setData 'from-root-path', directory.path + e.originalEvent.dataTransfer.setData 'from-window-id', @getWindowId() - event.originalEvent.dataTransfer.setData 'text/plain', directory.path + e.originalEvent.dataTransfer.setData 'text/plain', directory.path if process.platform in ['darwin', 'linux'] pathUri = "file://#{directory.path}" unless @uriHasProtocol(directory.path) - event.originalEvent.dataTransfer.setData 'text/uri-list', pathUri + e.originalEvent.dataTransfer.setData 'text/uri-list', pathUri uriHasProtocol: (uri) -> try @@ -42,22 +49,31 @@ class RootDragAndDropHandler catch error false - onDragLeave: (event) => - @removePlaceholder() + onDragEnter: (e) => + e.stopPropagation() - onDragEnd: (event) => + onDragLeave: (e) => + e.stopPropagation() + @removePlaceholder() if e.target is e.currentTarget + + onDragEnd: (e) => + e.stopPropagation() @clearDropTarget() - onDragOver: (event) => - unless event.originalEvent.dataTransfer.getData('atom-event') is 'true' + onDragOver: (e) => + unless e.originalEvent.dataTransfer.getData('atom-event') is 'true' return - entry = event.currentTarget - if entry.classList.contains('selected') - @clearDropTarget() + e.preventDefault() + e.stopPropagation() + + entry = e.currentTarget + + if @treeView.roots.length is 0 + @getPlaceholder().appendTo(@treeView.list) return - newDropTargetIndex = @getDropTargetIndex(event) + newDropTargetIndex = @getDropTargetIndex(e) return unless newDropTargetIndex? @removeDropTargetClasses() @@ -73,7 +89,7 @@ class RootDragAndDropHandler element.addClass 'drop-target-is-after' @getPlaceholder().insertAfter(element) - onDropOnOtherWindow: (event, fromItemIndex) => + onDropOnOtherWindow: (e, fromItemIndex) => paths = atom.project.getPaths() paths.splice(fromItemIndex, 1) atom.project.setPaths(paths) @@ -87,9 +103,11 @@ class RootDragAndDropHandler @removeDropTargetClasses() @removePlaceholder() - onDrop: (event) => - event.preventDefault() - {dataTransfer} = event.originalEvent + onDrop: (e) => + e.preventDefault() + e.stopPropagation() + + {dataTransfer} = e.originalEvent # TODO: support dragging folders from the filesystem -- electron needs to add support first return unless dataTransfer.getData('atom-event') is 'true' @@ -99,7 +117,7 @@ class RootDragAndDropHandler fromIndex = parseInt(dataTransfer.getData('project-root-index')) fromRootIndex = parseInt(dataTransfer.getData('from-root-index')) - toIndex = @getDropTargetIndex(event) + toIndex = @getDropTargetIndex(e) @clearDropTarget() @@ -125,8 +143,8 @@ class RootDragAndDropHandler workspaceElement.find('.tree-view .is-drop-target').removeClass 'is-drop-target' workspaceElement.find('.tree-view .drop-target-is-after').removeClass 'drop-target-is-after' - getDropTargetIndex: (event) -> - target = $(event.target) + getDropTargetIndex: (e) -> + target = $(e.target) return if @isPlaceholder(target) @@ -140,18 +158,18 @@ class RootDragAndDropHandler elementCenter = element.offset().top + element.height() / 2 - if event.originalEvent.pageY < elementCenter + if e.originalEvent.pageY < elementCenter projectRoots.index(projectRoot) else if projectRoot.next('.project-root').length > 0 projectRoots.index(projectRoot.next('.project-root')) else projectRoots.index(projectRoot) + 1 - canDragStart: (event) -> - $(event.target).closest('.project-root-header').size() > 0 + canDragStart: (e) -> + $(e.target).closest('.project-root-header').size() > 0 - isDragging: (event) -> - Boolean event.originalEvent.dataTransfer.getData 'from-root-path' + isDragging: (e) -> + Boolean e.originalEvent.dataTransfer.getData 'from-root-path' getPlaceholder: -> @placeholderEl ?= $('
  • ', class: 'placeholder') diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 699dfba1..7d2550d3 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -106,8 +106,6 @@ class TreeView extends View @on 'dragleave', '.entry.directory > .header', (e) => @onDragLeave(e) @on 'dragover', '.entry', (e) => @onDragOver(e) @on 'drop', '.entry', (e) => @onDrop(e) - @on 'dragover', '.tree-view', (e) => @onDragOverTree(e) - @on 'drop', '.tree-view', (e) => @onDropTree(e) atom.commands.add @element, 'core:move-up': @moveUp.bind(this) @@ -830,10 +828,9 @@ class TreeView extends View @list[0].classList.contains('multi-select') onDragEnter: (e) => - e.stopPropagation() + return if @rootDragAndDrop.isDragging(e) - if @rootDragAndDrop.isDragging(e) - return + e.stopPropagation() entry = e.currentTarget.parentNode @dragEventCounts.set(entry, 0) unless @dragEventCounts.get(entry) @@ -841,10 +838,9 @@ class TreeView extends View @dragEventCounts.set(entry, @dragEventCounts.get(entry) + 1) onDragLeave: (e) => - e.stopPropagation() + return if @rootDragAndDrop.isDragging(e) - if @rootDragAndDrop.isDragging(e) - return @rootDragAndDrop.onDragLeave(e) + e.stopPropagation() entry = e.currentTarget.parentNode @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) @@ -880,24 +876,22 @@ class TreeView extends View # Handle entry dragover event; reset default dragover actions onDragOver: (e) -> + return if @rootDragAndDrop.isDragging(e) + e.preventDefault() e.stopPropagation() - if @rootDragAndDrop.isDragging(e) - return @rootDragAndDrop.onDragOver(e) - entry = e.currentTarget if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') entry.classList.add('selected') # Handle entry drop event onDrop: (e) -> + return if @rootDragAndDrop.isDragging(e) + e.preventDefault() e.stopPropagation() - if @rootDragAndDrop.isDragging(e) - return @rootDragAndDrop.onDrop(e) - entry = e.currentTarget entry.classList.remove('selected') @@ -915,18 +909,3 @@ class TreeView extends View # Drop event from OS for file in e.originalEvent.dataTransfer.files @moveEntry(file.path, newDirectoryPath) - - # handle drag over and drop on the empty space in which there are no entries - onDragOverTree: (e) -> - e.preventDefault() - e.stopPropagation() - - if @rootDragAndDrop.isDragging(e) - return @rootDragAndDrop.onDragOver(e) - - onDropTree: (e) -> - e.preventDefault() - e.stopPropagation() - - if @rootDragAndDrop.isDragging(e) - return @rootDragAndDrop.onDrop(e) From aab2f7899557b8b7913c08c52cc81a067a13d07d Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sun, 23 Oct 2016 17:30:12 -0700 Subject: [PATCH 089/263] Fix specs for refactored event handling --- spec/event-helpers.coffee | 7 ++++++- spec/tree-view-spec.coffee | 38 +++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index c294b44c..96b7b6a8 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -75,4 +75,9 @@ module.exports.buildPositionalDragEvents = (dragged, target) -> dragStartEvent.currentTarget = dragged dragStartEvent.originalEvent = {dataTransfer} - [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer)] + dragEndEvent = $.Event() + dragEndEvent.target = dragged + dragEndEvent.currentTarget = dragged + dragEndEvent.originalEvent = {dataTransfer} + + [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer), dragEndEvent] diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 25a2e52f..5642b1cc 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3400,15 +3400,15 @@ describe "TreeView", -> # Dragging gammaDir onto alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') gammaDir = $(treeView).find('.project-root:contains(gamma):first') - [dragStartEvent, dragOverEvents] = + [dragStartEvent, dragOverEvents, dragEndEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) - treeView.onDragOver(dragOverEvents.top) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDragOver(dragOverEvents.top) expect(alphaDir[0].previousSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.rootDragAndDrop.onDragEnd() + treeView.rootDragAndDrop.onDragEnd(dragEndEvent) expect('.placeholder').not.toExist() describe "when dragging on the bottom part of the header", -> @@ -3416,15 +3416,15 @@ describe "TreeView", -> # Dragging gammaDir onto alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') gammaDir = $(treeView).find('.project-root:contains(gamma):first') - [dragStartEvent, dragOverEvents] = + [dragStartEvent, dragOverEvents, dragEndEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) - treeView.onDragOver(dragOverEvents.bottom) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom) expect(alphaDir[0].nextSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.rootDragAndDrop.onDragEnd() + treeView.rootDragAndDrop.onDragEnd(dragEndEvent) expect('.placeholder').not.toExist() describe "when below all entries", -> @@ -3432,17 +3432,17 @@ describe "TreeView", -> # Dragging gammaDir onto alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') lastDir = $(treeView).find('.project-root:last') - [dragStartEvent, dragOverEvents] = + [dragStartEvent, dragOverEvents, dragEndEvent] = eventHelpers.buildPositionalDragEvents(alphaDir.find('.project-root-header')[0], treeView.find('.tree-view')[0]) expect(alphaDir[0]).not.toEqual(lastDir[0]) - treeView.onDragStart(dragStartEvent) - treeView.onDragOver(dragOverEvents.bottom) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom) expect(lastDir[0].nextSibling).toHaveClass('placeholder') # Is removed when drag ends - treeView.rootDragAndDrop.onDragEnd() + treeView.rootDragAndDrop.onDragEnd(dragEndEvent) expect('.placeholder').not.toExist() @@ -3455,8 +3455,8 @@ describe "TreeView", -> [dragStartEvent, dragDropEvents] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) - treeView.onDrop(dragDropEvents.top) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDrop(dragDropEvents.top) projectPaths = atom.project.getPaths() expect(projectPaths[0]).toEqual(gammaDirPath) expect(projectPaths[1]).toEqual(alphaDirPath) @@ -3472,8 +3472,8 @@ describe "TreeView", -> [dragStartEvent, dragDropEvents] = eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) - treeView.onDrop(dragDropEvents.bottom) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDrop(dragDropEvents.bottom) projectPaths = atom.project.getPaths() expect(projectPaths[0]).toEqual(alphaDirPath) expect(projectPaths[1]).toEqual(thetaDirPath) @@ -3486,7 +3486,7 @@ describe "TreeView", -> it "should carry the folder's information", -> gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath if process.platform in ['darwin', 'linux'] @@ -3507,7 +3507,7 @@ describe "TreeView", -> spyOn(remote.BrowserWindow, 'fromId').andReturn(browserWindowMock) spyOn(browserWindowMock.webContents, 'send') - treeView.onDrop(dropEvent) + treeView.rootDragAndDrop.onDrop(dropEvent) waitsFor -> browserWindowMock.webContents.send.callCount > 0 @@ -3521,7 +3521,7 @@ describe "TreeView", -> it "removes the root folder from the first window", -> gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) - treeView.onDragStart(dragStartEvent) + treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDropOnOtherWindow({}, gammaDir.index()) expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] From 8b9a60747675d02b157047a2c725e22934932629 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sun, 23 Oct 2016 17:36:41 -0700 Subject: [PATCH 090/263] Remove unecessary will-change rule --- styles/tree-view.less | 1 - 1 file changed, 1 deletion(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index 338e4002..455fcfb0 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -50,7 +50,6 @@ flex: 1; width: 100%; overflow: auto; - will-change: transform; } .tree-view { From cb1470244309682524462f5aaf8237ffed5acede Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Sun, 23 Oct 2016 18:12:13 -0700 Subject: [PATCH 091/263] Fix lint warning --- lib/root-drag-and-drop.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 82ebce33..af68e01a 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -49,7 +49,7 @@ class RootDragAndDropHandler catch error false - onDragEnter: (e) => + onDragEnter: (e) -> e.stopPropagation() onDragLeave: (e) => From c6375214c46a856a56f87df088fad4d6e2a183d8 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Tue, 25 Oct 2016 12:34:50 -0700 Subject: [PATCH 092/263] Fix dragging jank by not placing placeholder if not needed Initial fix provided by @BinaryMuse --- lib/root-drag-and-drop.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index af68e01a..f2252e2f 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -24,6 +24,7 @@ class RootDragAndDropHandler @treeView.on 'drop', '.tree-view', @onDrop onDragStart: (e) => + @prevDropTargetIndex = null e.originalEvent.dataTransfer.setData 'atom-event', 'true' projectRoot = $(e.target).closest('.project-root') directory = projectRoot[0].directory @@ -75,6 +76,8 @@ class RootDragAndDropHandler newDropTargetIndex = @getDropTargetIndex(e) return unless newDropTargetIndex? + return if @prevDropTargetIndex is newDropTargetIndex + @prevDropTargetIndex = newDropTargetIndex @removeDropTargetClasses() From 0535752c51bfa4098c0405b1d9d97d1a5b7347d7 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Tue, 25 Oct 2016 12:40:27 -0700 Subject: [PATCH 093/263] Remove useless removeDropTargetClasses --- lib/root-drag-and-drop.coffee | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index f2252e2f..1e25546a 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -79,8 +79,6 @@ class RootDragAndDropHandler return if @prevDropTargetIndex is newDropTargetIndex @prevDropTargetIndex = newDropTargetIndex - @removeDropTargetClasses() - projectRoots = $(@treeView.roots) if newDropTargetIndex < projectRoots.length @@ -103,7 +101,6 @@ class RootDragAndDropHandler element = @treeView.find(".is-dragging") element.removeClass 'is-dragging' element[0]?.updateTooltip() - @removeDropTargetClasses() @removePlaceholder() onDrop: (e) => @@ -141,11 +138,6 @@ class RootDragAndDropHandler browserWindow = remote.BrowserWindow.fromId(fromWindowId) browserWindow?.webContents.send('tree-view:project-folder-dropped', fromIndex) - removeDropTargetClasses: -> - workspaceElement = $(atom.views.getView(atom.workspace)) - workspaceElement.find('.tree-view .is-drop-target').removeClass 'is-drop-target' - workspaceElement.find('.tree-view .drop-target-is-after').removeClass 'drop-target-is-after' - getDropTargetIndex: (e) -> target = $(e.target) From edf9e5bc953e8294f08eb30f1ede140a98e06d60 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Tue, 25 Oct 2016 13:07:33 -0700 Subject: [PATCH 094/263] Switch positioning of placeholder to use entire entry instead of header Based on patch from @BinaryMuse --- lib/root-drag-and-drop.coffee | 6 ++---- spec/event-helpers.coffee | 16 ++++++++++------ spec/tree-view-spec.coffee | 18 +++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 1e25546a..ef83c483 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -149,11 +149,9 @@ class RootDragAndDropHandler return 0 unless projectRoot.length - element = projectRoot.find('.project-root-header') + center = projectRoot.offset().top + projectRoot.height() / 2 - elementCenter = element.offset().top + element.height() / 2 - - if e.originalEvent.pageY < elementCenter + if e.originalEvent.pageY < center projectRoots.index(projectRoot) else if projectRoot.next('.project-root').length > 0 projectRoots.index(projectRoot.next('.project-root')) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 96b7b6a8..41283b20 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -41,29 +41,33 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> dropEvent -buildElementPositionalDragEvents = (el, dataTransfer) -> +buildElementPositionalDragEvents = (el, dataTransfer, currentTargetSelector) -> if not el? return {} $el = $(el) + + $currentTarget = if currentTargetSelector then $el.closest(currentTargetSelector) else $el + currentTarget = $currentTarget[0] + topEvent = $.Event() topEvent.target = el - topEvent.currentTarget = el + topEvent.currentTarget = currentTarget topEvent.originalEvent = {dataTransfer, pageY: $el.offset().top} middleEvent = $.Event() middleEvent.target = el - middleEvent.currentTarget = el + middleEvent.currentTarget = currentTarget middleEvent.originalEvent = {dataTransfer, pageY: $el.offset().top + $el.height() * 0.5} bottomEvent = $.Event() bottomEvent.target = el - bottomEvent.currentTarget = el + bottomEvent.currentTarget = currentTarget bottomEvent.originalEvent = {dataTransfer, pageY: $el.offset().bottom} {top: topEvent, middle: middleEvent, bottom: bottomEvent} -module.exports.buildPositionalDragEvents = (dragged, target) -> +module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelector) -> dataTransfer = data: {} setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values @@ -80,4 +84,4 @@ module.exports.buildPositionalDragEvents = (dragged, target) -> dragEndEvent.currentTarget = dragged dragEndEvent.originalEvent = {dataTransfer} - [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer), dragEndEvent] + [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent] diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 5642b1cc..e7533d0c 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3394,14 +3394,14 @@ describe "TreeView", -> afterEach -> [alphaDirPath, gammaDirPath, thetaDirPath, etaDirPath] = [] - describe "when dragging a project root's header onto a different project root's header", -> - describe "when dragging on the top part of the header", -> + describe "when dragging a project root's header onto a different project root", -> + describe "when dragging on the top part of the root", -> it "should add the placeholder above the directory", -> # Dragging gammaDir onto alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDragOver(dragOverEvents.top) @@ -3411,13 +3411,13 @@ describe "TreeView", -> treeView.rootDragAndDrop.onDragEnd(dragEndEvent) expect('.placeholder').not.toExist() - describe "when dragging on the bottom part of the header", -> + describe "when dragging on the bottom part of the root", -> it "should add the placeholder below the directory", -> # Dragging gammaDir onto alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom) @@ -3446,14 +3446,14 @@ describe "TreeView", -> expect('.placeholder').not.toExist() - describe "when dropping a project root's header onto a different project root's header", -> + describe "when dropping a project root's header onto a different project root", -> describe "when dropping on the top part of the header", -> it "should add the placeholder above the directory", -> # dropping gammaDir above alphaDir alphaDir = $(treeView).find('.project-root:contains(alpha):first') gammaDir = $(treeView).find('.project-root:contains(gamma):first') [dragStartEvent, dragDropEvents] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDrop(dragDropEvents.top) @@ -3470,7 +3470,7 @@ describe "TreeView", -> alphaDir = $(treeView).find('.project-root:contains(alpha):first') thetaDir = $(treeView).find('.project-root:contains(theta):first') [dragStartEvent, dragDropEvents] = - eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir.find('.project-root-header')[0]) + eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDrop(dragDropEvents.bottom) @@ -3495,7 +3495,7 @@ describe "TreeView", -> describe "when a root folder is dropped from another Atom window", -> it "adds the root folder to the window", -> alphaDir = $(treeView).find('.project-root:contains(alpha):first') - [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0]) + [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0], '.tree-view') dropEvent = dragDropEvents.bottom dropEvent.originalEvent.dataTransfer.setData('atom-event', true) From 7dbae5036477138c700bb79ce3e6086164b9b4d3 Mon Sep 17 00:00:00 2001 From: Jordan Klassen Date: Tue, 25 Oct 2016 13:10:19 -0700 Subject: [PATCH 095/263] Use a different signifier than tab events --- lib/root-drag-and-drop.coffee | 6 +++--- spec/tree-view-spec.coffee | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index ef83c483..2605d497 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -25,7 +25,7 @@ class RootDragAndDropHandler onDragStart: (e) => @prevDropTargetIndex = null - e.originalEvent.dataTransfer.setData 'atom-event', 'true' + e.originalEvent.dataTransfer.setData 'atom-tree-view-event', 'true' projectRoot = $(e.target).closest('.project-root') directory = projectRoot[0].directory @@ -62,7 +62,7 @@ class RootDragAndDropHandler @clearDropTarget() onDragOver: (e) => - unless e.originalEvent.dataTransfer.getData('atom-event') is 'true' + unless e.originalEvent.dataTransfer.getData('atom-tree-view-event') is 'true' return e.preventDefault() @@ -110,7 +110,7 @@ class RootDragAndDropHandler {dataTransfer} = e.originalEvent # TODO: support dragging folders from the filesystem -- electron needs to add support first - return unless dataTransfer.getData('atom-event') is 'true' + return unless dataTransfer.getData('atom-tree-view-event') is 'true' fromWindowId = parseInt(dataTransfer.getData('from-window-id')) fromRootPath = dataTransfer.getData('from-root-path') diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index e7533d0c..2850b6f8 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3498,7 +3498,7 @@ describe "TreeView", -> [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0], '.tree-view') dropEvent = dragDropEvents.bottom - dropEvent.originalEvent.dataTransfer.setData('atom-event', true) + dropEvent.originalEvent.dataTransfer.setData('atom-tree-view-event', true) dropEvent.originalEvent.dataTransfer.setData('from-window-id', treeView.rootDragAndDrop.getWindowId() + 1) dropEvent.originalEvent.dataTransfer.setData('from-root-path', etaDirPath) From d6f0a5ece5cbe053cd3b281c22d292bdce3cb393 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 26 Oct 2016 11:15:02 -0700 Subject: [PATCH 096/263] Prepare 0.211.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f48060ec..fbbe8932 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.210.0", + "version": "0.211.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 7f12c1356fe4c2ed5eb5245eacd759c19fda900d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 28 Oct 2016 11:24:34 -0700 Subject: [PATCH 097/263] Remove unnecessary onWillDeactivate method from file-icons service --- README.md | 1 - lib/main.coffee | 6 +++--- spec/tree-view-spec.coffee | 23 ++++++++++------------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 575770ff..ea07ab34 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,3 @@ The Tree View displays icons next to files. These icons are customizable by inst The `atom.file-icons` service must provide the following methods: * `iconClassForPath(path)` - Returns a CSS class name to add to the file view -* `onWillDeactivate` - An event that lets the tree view return to its default icon strategy diff --git a/lib/main.coffee b/lib/main.coffee index 0e03f1de..680abdd8 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -1,4 +1,4 @@ -{CompositeDisposable} = require 'event-kit' +{Disposable, CompositeDisposable} = require 'event-kit' path = require 'path' FileIcons = require './file-icons' @@ -34,10 +34,10 @@ module.exports = consumeFileIcons: (service) -> FileIcons.setService(service) - @fileIconsDisposable = service.onWillDeactivate -> + @treeView?.updateRoots() + new Disposable => FileIcons.resetService() @treeView?.updateRoots() - @treeView?.updateRoots() serialize: -> if @treeView? diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 8f22e42f..9e89a23b 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3554,9 +3554,7 @@ describe "TreeView", -> describe 'Icon class handling', -> - [workspaceElement, treeView, files] = [] - - beforeEach -> + it 'allows multiple classes to be passed', -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) for i in [1..3] @@ -3565,16 +3563,16 @@ describe 'Icon class handling', -> atom.project.setPaths([rootDirPath]) workspaceElement = atom.views.getView(atom.workspace) - jasmine.attachToDOM(workspaceElement) - FileIcons.setService + providerDisposable = atom.packages.serviceHub.provide 'atom.file-icons', '1.0.0', { iconClassForPath: (path, context) -> expect(context).toBe "tree-view" [name, id] = path.match(/file-(\d+)\.txt$/) switch id - when "1" then 'first second' - when "2" then ['first', 'second'] + when "1" then 'first-icon-class second-icon-class' + when "2" then ['third-icon-class', 'fourth-icon-class'] else "some-other-file" + } waitsForPromise -> atom.packages.activatePackage('tree-view') @@ -3583,11 +3581,10 @@ describe 'Icon class handling', -> treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') - afterEach -> - temp.cleanup() + expect(files[0].fileName.className).toBe('name icon first-icon-class second-icon-class') + expect(files[1].fileName.className).toBe('name icon third-icon-class fourth-icon-class') - it 'allows multiple classes to be passed', -> - expect(files[0].fileName.className).toBe('name icon first second') + providerDisposable.dispose() - it 'allows an array of classes to be passed', -> - expect(files[1].fileName.className).toBe('name icon first second') + files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') + expect(files[0].fileName.className).toBe('name icon icon-file-text') From 8a7f1c318289716fe8592baeadc7a62d0ca02978 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 28 Oct 2016 12:21:31 -0700 Subject: [PATCH 098/263] Prepare 0.211.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fbbe8932..5772ac5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.211.0", + "version": "0.211.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 1e23411f8a604f0d74151effeeeaea54b3be9aeb Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 18 Nov 2016 12:28:18 +0900 Subject: [PATCH 099/263] Remove min-height --- styles/tree-view.less | 1 - 1 file changed, 1 deletion(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index 455fcfb0..49af49b9 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -68,7 +68,6 @@ */ isolation: isolate; min-width: -webkit-min-content; - min-height: 100%; padding-left: @component-icon-padding; padding-right: @component-padding; position: relative; From bc997fef8508f0884ce5e35a9fed0d43a15bde5b Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 22 Dec 2016 11:04:44 -0800 Subject: [PATCH 100/263] :memo: Update issue and PR templates --- ISSUE_TEMPLATE.md | 40 ++++++++++++++++++++++++++++++++++++++++ PULL_REQUEST_TEMPLATE.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 ISSUE_TEMPLATE.md create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..b60bb86c --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,40 @@ + + +### Prerequisites + +* [ ] Put an X between the brackets on this line if you have done all of the following: + * Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode + * Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/ + * Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq + * Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom + * Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages + +### Description + +[Description of the issue] + +### Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expect to happen] + +**Actual behavior:** [What actually happens] + +**Reproduces how often:** [What percentage of the time does it reproduce?] + +### Versions + +You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running. + +### Additional Information + +Any additional information, configuration or data that might be necessary to reproduce the issue. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..2750afc8 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +### Requirements + +* Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* All new code requires tests to ensure against regressions + +### Description of the Change + + + +## Alternate Designs + + + +### Benefits + + + +### Possible Drawbacks + + + +### Applicable Issues + + From 0da839a681245fd54dbd27a04ff45208384269ea Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 26 Dec 2016 10:22:39 -0800 Subject: [PATCH 101/263] :memo: Update issue and PR templates --- PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 2750afc8..cdaa94a8 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,7 @@ We must be able to understand the design of your change from this description. I --> -## Alternate Designs +### Alternate Designs From 289771b3a75498ad240bfcab87b1fa8b8d141add Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Thu, 29 Dec 2016 02:26:02 +0000 Subject: [PATCH 102/263] :bug: Solve bug mentioned in issue #1005 --- lib/tree-view.coffee | 3 ++- spec/tree-view-spec.coffee | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f70ccbc6..e59605a7 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -565,7 +565,7 @@ class TreeView extends View message: "Are you sure you want to delete the selected #{if selectedPaths.length > 1 then 'items' else 'item'}?" detailedMessage: "You are deleting:\n#{selectedPaths.join('\n')}" buttons: - "Move to Trash": -> + "Move to Trash": => failedDeletions = [] for selectedPath in selectedPaths if not shell.moveItemToTrash(selectedPath) @@ -576,6 +576,7 @@ class TreeView extends View atom.notifications.addError "The following #{if failedDeletions.length > 1 then 'files' else 'file'} couldn't be moved to trash#{if process.platform is 'linux' then " (is `gvfs-trash` installed?)" else ""}", detail: "#{failedDeletions.join('\n')}" dismissable: true + @updateRoots() "Cancel": null # Public: Copy the path of the selected entry element. diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 9e89a23b..0206a429 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2507,6 +2507,9 @@ describe "TreeView", -> xiDirPath1 = path.join(muDirPath, "xi") xiDirPath2 = path.join(nuDirPath, "xi") + omicronDirPath = path.join(rootDirPath, "omicron") + piDirPath = path.join(omicronDirPath, "pi") + fs.makeTreeSync(zetaDirPath) fs.writeFileSync(zetaFilePath, "doesn't matter") @@ -2528,6 +2531,9 @@ describe "TreeView", -> fs.makeTreeSync(xiDirPath1) fs.makeTreeSync(xiDirPath2) + fs.makeTreeSync(omicronDirPath) + fs.makeTreeSync(piDirPath) + atom.project.setPaths([rootDirPath]) it "defaults to disabled", -> @@ -2569,6 +2575,19 @@ describe "TreeView", -> expect(lambdaEntries).toEqual(["iota", "kappa"]) + describe "when a squashed directory is deleted", -> + it "un-squashes the directories", -> + jasmine.attachToDOM(workspaceElement) + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + treeView.focus() + treeView.selectEntry(piDir) + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + atom.commands.dispatch(treeView.element, 'tree-view:remove') + + omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] + expect(omicronDir.title).toEqual("omicron") + describe "when a directory is reloaded", -> it "squashes the directory names the last of which is same as an unsquashed directory", -> muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') From 6fa05d37db7de2162a981830aad68acbef57033a Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 30 Dec 2016 21:26:30 -0500 Subject: [PATCH 103/263] Prepare 0.212.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5772ac5f..cf8821b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.211.1", + "version": "0.212.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 22c06eab4cc1e43bff8183c94de0949e827ce1ea Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sun, 1 Jan 2017 17:16:36 -0800 Subject: [PATCH 104/263] Cover trash failures for macOS and Windows with a message --- lib/tree-view.coffee | 13 ++++++++++++- spec/tree-view-spec.coffee | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index e59605a7..7306bd67 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -573,12 +573,23 @@ class TreeView extends View if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) if failedDeletions.length > 0 - atom.notifications.addError "The following #{if failedDeletions.length > 1 then 'files' else 'file'} couldn't be moved to trash#{if process.platform is 'linux' then " (is `gvfs-trash` installed?)" else ""}", + atom.notifications.addError @formatTrashFailureMessage(failedDeletions), detail: "#{failedDeletions.join('\n')}" dismissable: true @updateRoots() "Cancel": null + formatTrashFailureMessage: (failedDeletions) -> + fileText = if failedDeletions.length > 1 then 'files' else 'file' + + "The following #{fileText} couldn't be moved to the trash. #{@formatTrashEnabledMessage()}" + + formatTrashEnabledMessage: -> + switch process.platform + when 'linux' then 'Is `gvfs-trash` installed?' + when 'darwin' then 'Is Trash enabled on the volume where the files are stored?' + when 'win32' then 'Is RecycleBin support enabled on the drive where the files are stored?' + # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that copying from 2 different # instances of atom works as intended diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 0206a429..52dac734 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2351,7 +2351,7 @@ describe "TreeView", -> expect(notificationsNumber).toBe 1 if notificationsNumber is 1 notification = atom.notifications.getNotifications()[0] - expect(notification.getMessage()).toContain 'The following file couldn\'t be moved to trash' + expect(notification.getMessage()).toContain 'The following file couldn\'t be moved to the trash' expect(notification.getDetail()).toContain 'test-file.txt' it "does nothing when no file is selected", -> From 80a0a26809777ede890f4dea0157512f8b2cd183 Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Mon, 2 Jan 2017 01:56:22 +0000 Subject: [PATCH 105/263] :racehorse: Update roots after deletion only if `tree-view.squashDirectoryNames` config key is set to `true` --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index e59605a7..c4044440 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -576,7 +576,7 @@ class TreeView extends View atom.notifications.addError "The following #{if failedDeletions.length > 1 then 'files' else 'file'} couldn't be moved to trash#{if process.platform is 'linux' then " (is `gvfs-trash` installed?)" else ""}", detail: "#{failedDeletions.join('\n')}" dismissable: true - @updateRoots() + @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') "Cancel": null # Public: Copy the path of the selected entry element. From 9ad2d192f98a259bfb5a4ef9a9c5892d77c4e11f Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Mon, 2 Jan 2017 10:48:15 +0000 Subject: [PATCH 106/263] :bug: Solve bug mentioned in issue #702 --- lib/tree-view.coffee | 4 +++- spec/tree-view-spec.coffee | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index c4044440..677bc1c5 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -664,9 +664,11 @@ class TreeView extends View dialog.on 'directory-created', (event, createdPath) => @entryForPath(createdPath)?.reload() @selectEntryForPath(createdPath) + @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') false - dialog.on 'file-created', (event, createdPath) -> + dialog.on 'file-created', (event, createdPath) => atom.workspace.open(createdPath) + @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') false dialog.attach() diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 0206a429..743e9bbc 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2588,6 +2588,43 @@ describe "TreeView", -> omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] expect(omicronDir.title).toEqual("omicron") + describe "when a file is created within a directory with another squashed directory", -> + it "un-squashes the directories", -> + jasmine.attachToDOM(workspaceElement) + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + expect(piDir).not.toBeNull() + omicronPath = piDir.getPath().replace "/pi", "" + sigmaFilePath = path.join(omicronPath, "sigma.txt") + fs.writeFileSync(sigmaFilePath, "doesn't matter") + treeView.updateRoots() + + omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] + expect(omicronDir.title).toEqual("omicron") + omicronDir.click() + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] + expect(piDir.title).toEqual("pi") + sigmaFile = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .file:contains(sigma) span")[0] + expect(sigmaFile.title).toEqual("sigma.txt") + + describe "when a directory is created within a directory with another squashed directory", -> + it "un-squashes the directories", -> + jasmine.attachToDOM(workspaceElement) + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + expect(piDir).not.toBeNull() + omicronPath = piDir.getPath().replace "/pi", "" + rhoDirPath = path.join(omicronPath, "rho") + fs.makeTreeSync(rhoDirPath) + treeView.updateRoots() + + omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] + expect(omicronDir.title).toEqual("omicron") + omicronDir.click() + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] + expect(piDir.title).toEqual("pi") + rhoDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(rho) span")[0] + expect(rhoDir.title).toEqual("rho") + + describe "when a directory is reloaded", -> it "squashes the directory names the last of which is same as an unsquashed directory", -> muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') From 1657296ffc87b3f4e89af5eb52471c14f3350d22 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 2 Jan 2017 11:37:19 -0800 Subject: [PATCH 107/263] Improve the win32 message --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 7306bd67..2c026a44 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -588,7 +588,7 @@ class TreeView extends View switch process.platform when 'linux' then 'Is `gvfs-trash` installed?' when 'darwin' then 'Is Trash enabled on the volume where the files are stored?' - when 'win32' then 'Is RecycleBin support enabled on the drive where the files are stored?' + when 'win32' then 'Is there a Recycle Bin on the drive where the files are stored?' # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that copying from 2 different From eb4742176b7b3af28dab04aed4a568be3a34258f Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Mon, 2 Jan 2017 21:06:25 +0000 Subject: [PATCH 108/263] Make tests windows-friendly using #{path.sep} instead of "/" --- spec/tree-view-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 743e9bbc..4a50dc64 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2593,7 +2593,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() - omicronPath = piDir.getPath().replace "/pi", "" + omicronPath = piDir.getPath().replace "#{path.sep}pi", "" sigmaFilePath = path.join(omicronPath, "sigma.txt") fs.writeFileSync(sigmaFilePath, "doesn't matter") treeView.updateRoots() @@ -2611,7 +2611,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() - omicronPath = piDir.getPath().replace "/pi", "" + omicronPath = piDir.getPath().replace "#{path.sep}pi", "" rhoDirPath = path.join(omicronPath, "rho") fs.makeTreeSync(rhoDirPath) treeView.updateRoots() From 1b5c7d8284f3121678252ce0a90be6832c93d70a Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Mon, 2 Jan 2017 22:50:29 +0000 Subject: [PATCH 109/263] :bug: Prevent root directories to be squashed --- lib/directory.coffee | 2 +- spec/tree-view-spec.coffee | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index c26e4e57..538d88b4 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -14,7 +14,7 @@ class Directory @emitter = new Emitter() @subscriptions = new CompositeDisposable() - if atom.config.get('tree-view.squashDirectoryNames') + if atom.config.get('tree-view.squashDirectoryNames') and not @isRoot fullPath = @squashDirectoryNames(fullPath) @path = fullPath diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 0206a429..8e2fff9f 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2543,6 +2543,18 @@ describe "TreeView", -> beforeEach -> atom.config.set('tree-view.squashDirectoryNames', true) + it "it does not squash root directories", -> + rootDir = fs.absolute(temp.mkdirSync('tree-view')) + zetaDir = path.join(rootDir, "zeta") + fs.makeTreeSync(zetaDir) + atom.project.setPaths([rootDir]) + jasmine.attachToDOM(workspaceElement) + + rootDirPath = treeView.roots[0].getPath() + expect(rootDirPath).toBe(rootDir) + zetaDirPath = $(treeView.roots[0].entries).find('.directory:contains(zeta):first')[0].getPath() + expect(zetaDirPath).toBe(zetaDir) + it "does not squash a file in to a DirectoryViews", -> zetaDir = $(treeView.roots[0].entries).find('.directory:contains(zeta):first') zetaDir[0].expand() From b57e61074a9574a83b77b7d69aebca439cab3b3d Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Mon, 2 Jan 2017 23:04:33 +0000 Subject: [PATCH 110/263] Solve minor wording issue in test --- spec/tree-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 8e2fff9f..6a7b6e74 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2543,7 +2543,7 @@ describe "TreeView", -> beforeEach -> atom.config.set('tree-view.squashDirectoryNames', true) - it "it does not squash root directories", -> + it "does not squash root directories", -> rootDir = fs.absolute(temp.mkdirSync('tree-view')) zetaDir = path.join(rootDir, "zeta") fs.makeTreeSync(zetaDir) From cf00ccc034d918c8bd1a0ccbe949599d1c950700 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 3 Jan 2017 12:14:04 -0800 Subject: [PATCH 111/263] :art: Improve the no trash notification formatting --- lib/tree-view.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 2c026a44..9af92688 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -574,6 +574,7 @@ class TreeView extends View repo.getPathStatus(selectedPath) if failedDeletions.length > 0 atom.notifications.addError @formatTrashFailureMessage(failedDeletions), + description: @formatTrashEnabledMessage() detail: "#{failedDeletions.join('\n')}" dismissable: true @updateRoots() @@ -582,7 +583,7 @@ class TreeView extends View formatTrashFailureMessage: (failedDeletions) -> fileText = if failedDeletions.length > 1 then 'files' else 'file' - "The following #{fileText} couldn't be moved to the trash. #{@formatTrashEnabledMessage()}" + "The following #{fileText} couldn't be moved to the trash." formatTrashEnabledMessage: -> switch process.platform From d46f8e650e5574d57b36cb6a66e152d9264679f9 Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Wed, 4 Jan 2017 00:17:06 +0000 Subject: [PATCH 112/263] Remove unnecessary new-line --- spec/tree-view-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 4a50dc64..a40b3784 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2624,7 +2624,6 @@ describe "TreeView", -> rhoDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(rho) span")[0] expect(rhoDir.title).toEqual("rho") - describe "when a directory is reloaded", -> it "squashes the directory names the last of which is same as an unsquashed directory", -> muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') From 7b790162790c38586b827e6d34f439e363c57c60 Mon Sep 17 00:00:00 2001 From: MiguelCG Date: Wed, 4 Jan 2017 00:17:31 +0000 Subject: [PATCH 113/263] Add comments --- spec/tree-view-spec.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index a40b3784..642fdc38 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2593,6 +2593,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() + # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" sigmaFilePath = path.join(omicronPath, "sigma.txt") fs.writeFileSync(sigmaFilePath, "doesn't matter") @@ -2611,6 +2612,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() + # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" rhoDirPath = path.join(omicronPath, "rho") fs.makeTreeSync(rhoDirPath) From a61c77e4a55024ee1b673e62eb4915efa11daba0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 3 Jan 2017 19:31:18 -0800 Subject: [PATCH 114/263] Prepare 0.213.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf8821b6..24e1e40b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.212.0", + "version": "0.213.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 045925aa89a10f05f704b23b42a9cae5f6420aee Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 4 Jan 2017 16:04:20 -0800 Subject: [PATCH 115/263] Don't assume that buffers' paths are real paths in spec --- spec/tree-view-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index bb26441e..47e49609 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -1875,7 +1875,7 @@ describe "TreeView", -> [addPanel] = atom.workspace.getModalPanels() addDialog = $(addPanel.getItem()).view() - newPath = temp.path() + newPath = path.join(fs.realpathSync(temp.mkdirSync()), 'a-file') addDialog.miniEditor.getModel().insertText(newPath) waitsForFileToOpen -> @@ -1884,7 +1884,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).toBe fs.realpathSync(newPath) + expect(atom.workspace.getActivePaneItem().getPath()).toBe(newPath) describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> it "shows an error message and does not close the dialog", -> From 9d704e717e87a4f3cd5780916e2e5a61229d8135 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 4 Jan 2017 16:06:43 -0800 Subject: [PATCH 116/263] Prepare 0.213.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 24e1e40b..d261c6b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.213.0", + "version": "0.213.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 32a3b829595eec8d536fba4bbb2b457b9620c608 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 5 Jan 2017 10:17:21 -0500 Subject: [PATCH 117/263] Use Node 6 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2b0fde43..32ca55a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ environment: - ATOM_CHANNEL: beta install: - - ps: Install-Product node 4 + - ps: Install-Product node 6 build_script: - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/atom/ci/master/build-package.ps1')) From 6f27db96050c30489f7307cf2477947ed3f8de06 Mon Sep 17 00:00:00 2001 From: Ian Olsen Date: Tue, 17 Jan 2017 17:45:29 -0800 Subject: [PATCH 118/263] Manually close editors for files deleted from tree view --- lib/tree-view.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index a7421527..50c85e81 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -568,7 +568,11 @@ class TreeView extends View "Move to Trash": => failedDeletions = [] for selectedPath in selectedPaths - if not shell.moveItemToTrash(selectedPath) + if shell.moveItemToTrash(selectedPath) + for editor in atom.workspace.getTextEditors() + if editor?.getPath() is selectedPath + editor.destroy() + else failedDeletions.push "#{selectedPath}" if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) From dd4bb20bd4780fdb8bbe4edb6c790bfe8dea2a4a Mon Sep 17 00:00:00 2001 From: simurai Date: Tue, 24 Jan 2017 13:39:45 +0900 Subject: [PATCH 119/263] Prepare 0.213.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d261c6b7..2499c3cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.213.1", + "version": "0.213.2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From a876afbe5dd232da01fd0003bc9f523b8f0befbe Mon Sep 17 00:00:00 2001 From: Ian Olsen Date: Wed, 25 Jan 2017 15:03:57 -0800 Subject: [PATCH 120/263] Prepare 0.214.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2499c3cb..52d4423f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.213.2", + "version": "0.214.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 1611ee3816a3cf2d7707ca6e588b6911aa652c47 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 30 Jan 2017 14:01:38 +0100 Subject: [PATCH 121/263] Start removing jQuery from tests --- spec/tree-view-spec.coffee | 219 +++++++++++++++++++------------------ 1 file changed, 113 insertions(+), 106 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6f9941d1..f76081bc 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -57,40 +57,42 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() + files = treeView[0].querySelectorAll('.file') + root1 = treeView.roots[0] + root2 = treeView.roots[1] + sampleJs = files[0] + sampleTxt = files[1] - root1 = $(treeView.roots[0]) - root2 = $(treeView.roots[1]) - sampleJs = treeView.find('.file:contains(tree-view.js)') - sampleTxt = treeView.find('.file:contains(tree-view.txt)') - - expect(treeView.roots[0].directory.watchSubscription).toBeTruthy() + expect(root1.directory.watchSubscription).toBeTruthy() afterEach -> temp.cleanup() describe ".initialize(project)", -> it "renders the root directories of the project and their contents alphabetically with subdirectories first, in a collapsed state", -> - expect(root1.find('> .header .disclosure-arrow')).not.toHaveClass('expanded') - expect(root1.find('> .header .name')).toHaveText('root-dir1') + expect(root1.querySelector('.header .disclosure-arrow')).not.toHaveClass('expanded') + expect(root1.querySelector('.header .name')).toHaveText('root-dir1') - rootEntries = root1.find('.entries') - subdir0 = rootEntries.find('> li:eq(0)') + rootEntries = root1.querySelectorAll('.entries li') + subdir0 = rootEntries[0] expect(subdir0).not.toHaveClass('expanded') - expect(subdir0.find('.name')).toHaveText('dir1') + expect(subdir0.querySelector('.name')).toHaveText('dir1') - subdir2 = rootEntries.find('> li:eq(1)') + subdir2 = rootEntries[1] expect(subdir2).not.toHaveClass('expanded') - expect(subdir2.find('.name')).toHaveText('dir2') + expect(subdir2.querySelector('.name')).toHaveText('dir2') - expect(subdir0.find('[data-name="dir1"]')).toExist() - expect(subdir2.find('[data-name="dir2"]')).toExist() + expect(subdir0.querySelector('[data-name="dir1"]')).toExist() + expect(subdir2.querySelector('[data-name="dir2"]')).toExist() - expect(rootEntries.find('> .file:contains(tree-view.js)')).toExist() - expect(rootEntries.find('> .file:contains(tree-view.txt)')).toExist() + file1 = root1.querySelector('.file [data-name="tree-view.js"]') + expect(file1).toExist() + expect(file1).toHaveText('tree-view.js') - expect(rootEntries.find('> .file [data-name="tree-view.js"]')).toExist() - expect(rootEntries.find('> .file [data-name="tree-view.txt"]')).toExist() + file2 = root1.querySelector('.file [data-name="tree-view.txt"]') + expect(file2).toExist() + expect(file2).toHaveText('tree-view.txt') it "selects the root folder", -> expect(treeView.selectedEntry()).toEqual(treeView.roots[0]) @@ -208,10 +210,10 @@ describe "TreeView", -> expect(atom.workspace.getLeftPanels().length).toBe(0) it "restores expanded directories and selected file when deserialized", -> - root1.find('.directory:contains(dir1)').click() + root1.querySelectorAll('.directory')[0].dispatchEvent(new MouseEvent('click', {detail: 1, bubbles: true})) waitsForFileToOpen -> - sampleJs.click() + sampleJs.dispatchEvent(new MouseEvent('click', {detail: 1, bubbles: true})) runs -> atom.packages.deactivatePackage("tree-view") @@ -220,7 +222,7 @@ describe "TreeView", -> atom.packages.activatePackage("tree-view") runs -> - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() expect($(treeView.selectedEntry())).toMatchSelector(".file:contains(tree-view.js)") root1 = $(treeView.roots[0]) @@ -236,7 +238,7 @@ describe "TreeView", -> atom.packages.activatePackage("tree-view") runs -> - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView.list).toMatchSelector ':focus' it "restores the scroll top when toggled", -> @@ -322,7 +324,7 @@ describe "TreeView", -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') atom.commands.dispatch(workspaceElement, 'tree-view:toggle') expect(atom.workspace.getLeftPanels().length).toBe 0 - treeView = $(atom.workspace.getRightPanels()[0].getItem()).view() + treeView = atom.workspace.getRightPanels()[0].getItem() expect(treeView).toMatchSelector('[data-show-on-right-side="true"]') describe "when tree-view:toggle-focus is triggered on the root view", -> @@ -512,7 +514,7 @@ describe "TreeView", -> describe "when a directory's disclosure arrow is clicked", -> it "expands / collapses the associated directory", -> - subdir = root1.find('.entries > li:contains(dir1)') + subdir = root1.querySelector('.entries > li') expect(subdir).not.toHaveClass('expanded') @@ -524,10 +526,10 @@ describe "TreeView", -> expect(subdir).not.toHaveClass('expanded') it "restores the expansion state of descendant directories", -> - child = root1.find('.entries > li:contains(dir1)') + child = root1.querySelector('.entries > li') child.click() - grandchild = child.find('.entries > li:contains(sub-dir1)') + grandchild = child.querySelector('.entries > li') grandchild.click() root1.click() @@ -535,37 +537,37 @@ describe "TreeView", -> root1.click() # previously expanded descendants remain expanded - expect(root1.find('> .entries > li:contains(dir1) > .entries > li:contains(sub-dir1) > .entries').length).toBe 1 + expect(root1.querySelectorAll('.entries > li > .entries > li > .entries').length).toBe 1 # collapsed descendants remain collapsed - expect(root1.find('> .entries > li:contains(dir2) > .entries')).not.toHaveClass('expanded') + expect(root1.querySelectorAll('.entries > li')[1].querySelector('.entries')).not.toHaveClass('expanded') it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", -> - child = root1.find('li:contains(dir1)') + child = root1.querySelector('li') child.click() - grandchild = child.find('li:contains(sub-dir1)') + grandchild = child.querySelector('li') grandchild.click() - expect(treeView.roots[0].directory.watchSubscription).toBeTruthy() - expect(child[0].directory.watchSubscription).toBeTruthy() - expect(grandchild[0].directory.watchSubscription).toBeTruthy() + expect(root1.directory.watchSubscription).toBeTruthy() + expect(child.directory.watchSubscription).toBeTruthy() + expect(grandchild.directory.watchSubscription).toBeTruthy() root1.click() - expect(treeView.roots[0].directory.watchSubscription).toBeFalsy() - expect(child[0].directory.watchSubscription).toBeFalsy() - expect(grandchild[0].directory.watchSubscription).toBeFalsy() + expect(root1.directory.watchSubscription).toBeFalsy() + expect(child.directory.watchSubscription).toBeFalsy() + expect(grandchild.directory.watchSubscription).toBeFalsy() describe "when mouse down fires on a file or directory", -> it "selects the entry", -> - dir = root1.find('li:contains(dir1)') + dir = root1.querySelector('li') expect(dir).not.toHaveClass 'selected' - dir.mousedown() + dir.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, detail: 1})) expect(dir).toHaveClass 'selected' expect(sampleJs).not.toHaveClass 'selected' - sampleJs.mousedown() + sampleJs.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, detail: 1})) expect(sampleJs).toHaveClass 'selected' describe "when the package first activates and there is a file open (regression)", -> @@ -580,8 +582,8 @@ describe "TreeView", -> it "does not throw when the file is double clicked", -> expect -> - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) - sampleJs.trigger clickEvent(originalEvent: {detail: 2}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) .not.toThrow() waitsFor -> @@ -601,8 +603,8 @@ describe "TreeView", -> runs -> expect(atom.workspace.getActivePane().getActiveItem()).toBe editor expect(atom.workspace.getActivePane().getPendingItem()).toBe editor - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) - sampleJs.trigger clickEvent(originalEvent: {detail: 2}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) waitsFor -> atom.workspace.getActivePane().getPendingItem() is null @@ -638,7 +640,7 @@ describe "TreeView", -> spyOn(atom.workspace, 'open') treeView.focus() - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) it "selects the file and retains focus on tree-view", -> expect(sampleJs).toHaveClass 'selected' @@ -655,7 +657,7 @@ describe "TreeView", -> spyOn(atom.workspace, 'open').andCallFake (uri, options) -> originalOpen(uri, options).then -> openedCount++ - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) treeView.openSelectedEntry() waitsFor 'open to be called twice', -> @@ -672,8 +674,8 @@ describe "TreeView", -> it "opens the file and focuses it", -> waitsForFileToOpen -> - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) - sampleJs.trigger clickEvent(originalEvent: {detail: 2}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) waitsFor "next tick to avoid race condition", (done) -> setImmediate(done) @@ -690,8 +692,8 @@ describe "TreeView", -> spyOn(atom.workspace, 'open').andCallFake (uri, options) -> originalOpen(uri, options).then -> openedCount++ - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) - sampleJs.trigger clickEvent(originalEvent: {detail: 2}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) waitsFor 'open to be called twice', -> openedCount is 2 @@ -701,8 +703,8 @@ describe "TreeView", -> describe "when a directory is single-clicked", -> it "is selected", -> - subdir = root1.find('.directory:first') - subdir.trigger clickEvent(originalEvent: {detail: 1}) + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(subdir).toHaveClass 'selected' describe "when a directory is double-clicked", -> @@ -711,15 +713,15 @@ describe "TreeView", -> subdir = null waitsForFileToOpen -> - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> treeView.focus() - subdir = root1.find('.directory:first') - subdir.trigger clickEvent(originalEvent: {detail: 1}) + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(subdir).toHaveClass 'selected' expect(subdir).toHaveClass 'expanded' - subdir.trigger clickEvent(originalEvent: {detail: 2}) + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) expect(subdir).toHaveClass 'selected' expect(subdir).not.toHaveClass 'expanded' expect(treeView).toHaveFocus() @@ -731,36 +733,38 @@ describe "TreeView", -> treeView.roots[0].collapse() expect(treeView.roots[0]).not.toHaveClass 'expanded' - root1.trigger clickEvent({altKey: true}) + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1, altKey: true})) expect(treeView.roots[0]).toHaveClass 'expanded' - children = root1.find('.directory') + children = root1.querySelectorAll('.directory') expect(children.length).toBeGreaterThan 0 - children.each (index, child) -> expect(child).toHaveClass 'expanded' + for child in children + expect(child).toHaveClass 'expanded' describe "when the directory is expanded", -> parent = null children = null beforeEach -> - parent = root1.find('> .entries > .directory').eq(2) - parent[0].expand() - children = parent.find('.expanded.directory') - children.each (index, child) -> + parent = root1.querySelectorAll('.entries > .directory')[2] + parent.expand() + children = parent.querySelectorAll('.expanded.directory') + for child in children child.expand() it "recursively collapses the directory", -> - parent.click() - parent[0].expand() + parent.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + parent.expand() expect(parent).toHaveClass 'expanded' - children.each (index, child) -> - $(child).click().expand() - expect($(child)).toHaveClass 'expanded' + for child in children + child.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + child.expand() + expect(child).toHaveClass 'expanded' - parent.trigger clickEvent({altKey: true}) + parent.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1, altKey: true})) expect(parent).not.toHaveClass 'expanded' - children.each (index, child) -> + for child in children expect(child).not.toHaveClass 'expanded' expect(treeView.roots[0]).toHaveClass 'expanded' @@ -768,21 +772,21 @@ describe "TreeView", -> describe "when the item has a path", -> it "selects the entry with that path in the tree view if it is visible", -> waitsForFileToOpen -> - sampleJs.click() + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) waitsForPromise -> atom.workspace.open(atom.project.getDirectories()[0].resolve('tree-view.txt')) runs -> expect(sampleTxt).toHaveClass 'selected' - expect(treeView.find('.selected').length).toBe 1 + expect(treeView.querySelectorAll('.selected').length).toBe 1 it "selects the path's parent dir if its entry is not visible", -> waitsForPromise -> atom.workspace.open(path.join('dir1', 'sub-dir1', 'sub-file1')) runs -> - dirView = root1.find('.directory:contains(dir1)') + dirView = root1.querySelector('.directory') expect(dirView).toHaveClass 'selected' describe "when the tree-view.autoReveal config setting is true", -> @@ -794,20 +798,20 @@ describe "TreeView", -> atom.workspace.open(path.join('dir1', 'sub-dir1', 'sub-file1')) runs -> - dirView = root1.find('.directory:contains(dir1)') - fileView = root1.find('.file:contains(sub-file1)') + dirView = root1.querySelector('.directory') + fileView = root1.querySelector('.file') expect(dirView).not.toHaveClass 'selected' expect(fileView).toHaveClass 'selected' - expect(treeView.find('.selected').length).toBe 1 + expect(treeView[0].querySelectorAll('.selected').length).toBe 1 describe "when the item has no path", -> it "deselects the previously selected entry", -> waitsForFileToOpen -> - sampleJs.click() + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.workspace.getActivePane().activateItem(document.createElement("div")) - expect(treeView.find('.selected')).not.toExist() + expect(treeView[0].querySelector('.selected')).not.toExist() describe "when a different editor becomes active", -> beforeEach -> @@ -817,14 +821,14 @@ describe "TreeView", -> leftEditorPane = null waitsForFileToOpen -> - sampleJs.click() + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> leftEditorPane = atom.workspace.getActivePane() leftEditorPane.splitRight() waitsForFileToOpen -> - sampleTxt.click() + sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> expect(sampleTxt).toHaveClass('selected') @@ -833,64 +837,67 @@ describe "TreeView", -> describe "keyboard navigation", -> afterEach -> - expect(treeView.find('.selected').length).toBeLessThan 2 + expect(treeView[0].querySelectorAll('.selected').length).toBeLessThan 2 describe "core:move-down", -> describe "when a collapsed directory is selected", -> it "skips to the next directory", -> - root1.find('.directory:eq(0)').click() + root1.querySelector('.directory').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, 'core:move-down') - expect(root1.find('.directory:eq(1)')).toHaveClass 'selected' + expect(root1.querySelectorAll('.directory')[1]).toHaveClass 'selected' describe "when an expanded directory is selected", -> it "selects the first entry of the directory", -> - subdir = root1.find('.directory:eq(1)') - subdir.click() + subdir = root1.querySelectorAll('.directory')[1] + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, 'core:move-down') - expect($(subdir[0].entries).find('.entry:first')).toHaveClass 'selected' + expect(subdir.querySelector('.entry')).toHaveClass 'selected' describe "when the last entry of an expanded directory is selected", -> it "selects the entry after its parent directory", -> - subdir1 = root1.find('.directory:eq(1)') - subdir1[0].expand() + subdir1 = root1.querySelectorAll('.directory')[1] + subdir1.expand() waitsForFileToOpen -> - $(subdir1[0].entries).find('.entry:last').click() + entries = subdir1.querySelectorAll('.entry') + entries[entries.length - 1].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(root1.find('.directory:eq(2)')).toHaveClass 'selected' + expect(root1.querySelectorAll('.directory')[2]).toHaveClass 'selected' describe "when the last directory of another last directory is selected", -> [nested, nested2] = [] beforeEach -> - nested = root1.find('.directory:eq(2)') - expect(nested.find('.header').text()).toContain 'nested' - nested[0].expand() - nested2 = $(nested[0].entries).find('.entry:last') - nested2.click() - nested2[0].collapse() + nested = root1.querySelectorAll('.directory')[2] + expect(nested.querySelector('.header').textContent).toContain 'nested' + nested.expand() + entries = nested.querySelectorAll('.entry') + nested2 = entries[entries.length - 1] + nested2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + nested2.collapse() describe "when the directory is collapsed", -> it "selects the entry after its grandparent directory", -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(nested.next()).toHaveClass 'selected' + expect(nested.nextSibling).toHaveClass 'selected' describe "when the directory is expanded", -> it "selects the entry after its grandparent directory", -> - nested2[0].expand() - nested2.find('.file').remove() # kill the .gitkeep file, which has to be there but screws the test + nested2.expand() + nested2.querySelector('.file').remove() # kill the .gitkeep file, which has to be there but screws the test atom.commands.dispatch(treeView.element, 'core:move-down') - expect(nested.next()).toHaveClass 'selected' + expect(nested.nextSibling).toHaveClass 'selected' describe "when the last entry of the last directory is selected", -> it "does not change the selection", -> - lastEntry = root2.find('> .entries .entry:last') + entries = root2.querySelectorAll('.entries .entry') + lastEntry = entries[entries.length - 1] waitsForFileToOpen -> - lastEntry.click() + lastEntry.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'core:move-down') @@ -2395,7 +2402,7 @@ describe "TreeView", -> describe "project changes", -> beforeEach -> atom.project.setPaths([path1]) - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() root1 = $(treeView.roots[0]) describe "when a root folder is added", -> @@ -2403,7 +2410,7 @@ describe "TreeView", -> root1.find('.directory:contains(dir1)').click() atom.project.setPaths([path1, path2]) - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() root1 = $(treeView.roots[0]) expect(root1.find(".directory:contains(dir1)")).toHaveClass("expanded") @@ -2412,7 +2419,7 @@ describe "TreeView", -> root1.click() atom.project.setPaths([path1, path2]) - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() root1 = $(treeView.roots[0]) expect(root1).toHaveClass("collapsed") From e61ffab06bac3606fca08610fe9248a607570507 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 30 Jan 2017 15:31:06 +0100 Subject: [PATCH 122/263] Remove jQuery from more specs --- lib/dialog.coffee | 2 +- spec/tree-view-spec.coffee | 522 ++++++++++++++++++------------------- 2 files changed, 254 insertions(+), 270 deletions(-) diff --git a/lib/dialog.coffee b/lib/dialog.coffee index 715d3c83..9931f4f1 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -29,7 +29,7 @@ class Dialog extends View @miniEditor.getModel().setSelectedBufferRange(range) attach: -> - @panel = atom.workspace.addModalPanel(item: this.element) + @panel = atom.workspace.addModalPanel(item: this) @miniEditor.focus() @miniEditor.getModel().scrollToCursorPosition() diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index f76081bc..04f5621e 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -1,5 +1,4 @@ _ = require 'underscore-plus' -{$, $$} = require 'atom-space-pen-views' fs = require 'fs-plus' path = require 'path' temp = require('temp').track() @@ -17,11 +16,6 @@ waitsForFileToOpen = (causeFileToOpen) -> done() causeFileToOpen() -clickEvent = (properties) -> - event = $.Event('click') - _.extend(event, properties) if properties? - event - setupPaneFiles = -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) @@ -58,7 +52,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') treeView = atom.workspace.getLeftPanels()[0].getItem() - files = treeView[0].querySelectorAll('.file') + files = treeView.element.querySelectorAll('.file') root1 = treeView.roots[0] root2 = treeView.roots[1] sampleJs = files[0] @@ -224,9 +218,9 @@ describe "TreeView", -> runs -> treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() - expect($(treeView.selectedEntry())).toMatchSelector(".file:contains(tree-view.js)") - root1 = $(treeView.roots[0]) - expect(root1.find(".directory:contains(dir1)")).toHaveClass("expanded") + expect(treeView.selectedEntry().textContent).toBe('tree-view.js') + root1 = treeView.roots[0] + expect(root1.querySelector(".directory")).toHaveClass("expanded") it "restores the focus state of the tree view", -> jasmine.attachToDOM(workspaceElement) @@ -291,7 +285,7 @@ describe "TreeView", -> describe "when the tree view is not focused", -> it "hides the tree view", -> - $(workspaceElement).focus() + workspaceElement.focus() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') expect(treeView).toBeHidden() @@ -344,7 +338,7 @@ describe "TreeView", -> atom.workspace.open() # When we call focus below, we want an editor to become focused runs -> - $(workspaceElement).focus() + workspaceElement.focus() expect(treeView).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView).toBeVisible() @@ -518,23 +512,23 @@ describe "TreeView", -> expect(subdir).not.toHaveClass('expanded') - subdir.click() + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(subdir).toHaveClass('expanded') - subdir.click() + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(subdir).not.toHaveClass('expanded') it "restores the expansion state of descendant directories", -> child = root1.querySelector('.entries > li') - child.click() + child.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) grandchild = child.querySelector('.entries > li') - grandchild.click() + grandchild.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(treeView.roots[0]).not.toHaveClass('expanded') - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) # previously expanded descendants remain expanded expect(root1.querySelectorAll('.entries > li > .entries > li > .entries').length).toBe 1 @@ -544,16 +538,16 @@ describe "TreeView", -> it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", -> child = root1.querySelector('li') - child.click() + child.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) grandchild = child.querySelector('li') - grandchild.click() + grandchild.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(root1.directory.watchSubscription).toBeTruthy() expect(child.directory.watchSubscription).toBeTruthy() expect(grandchild.directory.watchSubscription).toBeTruthy() - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(root1.directory.watchSubscription).toBeFalsy() expect(child.directory.watchSubscription).toBeFalsy() @@ -621,7 +615,7 @@ describe "TreeView", -> treeView.focus() waitsForFileToOpen -> - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> activePaneItem = atom.workspace.getActivePaneItem() @@ -729,7 +723,7 @@ describe "TreeView", -> describe "when an directory is alt-clicked", -> describe "when the directory is collapsed", -> it "recursively expands the directory", -> - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) treeView.roots[0].collapse() expect(treeView.roots[0]).not.toHaveClass 'expanded' @@ -779,7 +773,7 @@ describe "TreeView", -> runs -> expect(sampleTxt).toHaveClass 'selected' - expect(treeView.querySelectorAll('.selected').length).toBe 1 + expect(treeView.element.querySelectorAll('.selected').length).toBe 1 it "selects the path's parent dir if its entry is not visible", -> waitsForPromise -> @@ -802,7 +796,7 @@ describe "TreeView", -> fileView = root1.querySelector('.file') expect(dirView).not.toHaveClass 'selected' expect(fileView).toHaveClass 'selected' - expect(treeView[0].querySelectorAll('.selected').length).toBe 1 + expect(treeView.element.querySelectorAll('.selected').length).toBe 1 describe "when the item has no path", -> it "deselects the previously selected entry", -> @@ -811,7 +805,7 @@ describe "TreeView", -> runs -> atom.workspace.getActivePane().activateItem(document.createElement("div")) - expect(treeView[0].querySelector('.selected')).not.toExist() + expect(treeView.element.querySelector('.selected')).not.toExist() describe "when a different editor becomes active", -> beforeEach -> @@ -837,7 +831,7 @@ describe "TreeView", -> describe "keyboard navigation", -> afterEach -> - expect(treeView[0].querySelectorAll('.selected').length).toBeLessThan 2 + expect(treeView.element.querySelectorAll('.selected').length).toBeLessThan 2 describe "core:move-down", -> describe "when a collapsed directory is selected", -> @@ -906,31 +900,34 @@ describe "TreeView", -> 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 = root1.find('.directory:last') - fileAfterDir = lastDir.next() - lastDir[0].expand() + directories = root1.querySelectorAll('.directory') + lastDir = directories[directories.length - 1] + fileAfterDir = lastDir.nextSibling + lastDir.expand() waitsForFileToOpen -> - fileAfterDir.click() + fileAfterDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'core:move-up') - expect(lastDir.find('.entry:last')).toHaveClass 'selected' + entries = lastDir.querySelectorAll('.entry') + expect(entries[entries.length - 1]).toHaveClass 'selected' describe "when there is an entry before the currently selected entry", -> it "selects the previous entry", -> - lastEntry = root1.find('.entry:last') + entries = root1.querySelectorAll('.entry') + lastEntry = entries[entries.length - 1] waitsForFileToOpen -> - lastEntry.click() + lastEntry.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'core:move-up') - expect(lastEntry.prev()).toHaveClass 'selected' + expect(lastEntry.previousSibling).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 = root1.find('.directory:first') - subdir[0].expand() - subdir.find('> .entries > .entry:first').click() + subdir = root1.querySelector('.directory') + subdir.expand() + subdir.querySelector('.entries .entry').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, 'core:move-up') @@ -938,7 +935,7 @@ describe "TreeView", -> describe "when there is no parent directory or previous entry", -> it "does not change the selection", -> - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, 'core:move-up') expect(treeView.roots[0]).toHaveClass 'selected' @@ -954,19 +951,18 @@ describe "TreeView", -> describe "core:move-to-top", -> it "scrolls to the top", -> - treeView.height(100) + treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) - element.expand() for element in treeView.find('.directory') - expect(treeView.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight() - - expect(treeView.scrollTop()).toBe 0 + element.expand() for element in treeView.element.querySelectorAll('.directory') + expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) - entryCount = treeView.find(".entry").length + entryCount = treeView.element.querySelectorAll(".entry").length _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scrollTop()).toBeGreaterThan 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:move-to-top') - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) it "selects the root entry", -> entryCount = treeView.find(".entry").length @@ -978,76 +974,77 @@ describe "TreeView", -> describe "core:move-to-bottom", -> it "scrolls to the bottom", -> - treeView.height(100) + treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) - element.expand() for element in treeView.find('.directory') - expect(treeView.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight() + element.expand() for element in treeView.element.querySelectorAll('.directory') + expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) - expect(treeView.scrollTop()).toBe 0 atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.scrollBottom()).toBe root1.outerHeight() + root2.outerHeight() + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBeGreaterThan(0) treeView.roots[0].collapse() treeView.roots[1].collapse() atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) it "selects the last entry", -> expect(treeView.roots[0]).toHaveClass 'selected' atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(root2.find('.entry:last')).toHaveClass 'selected' + entries = root2.querySelectorAll('.entry') + expect(entries[entries.length - 1]).toHaveClass 'selected' describe "core:page-up", -> it "scrolls up a page", -> - treeView.height(5) + treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) - element.expand() for element in treeView.find('.directory') - expect(treeView.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight() + element.expand() for element in treeView.element.querySelectorAll('.directory') + expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) treeView.scrollToBottom() - scrollTop = treeView.scrollTop() + scrollTop = treeView.element.querySelector('.tree-view-scroller').scrollTop expect(scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:page-up') - expect(treeView.scrollTop()).toBe scrollTop - treeView.height() + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe scrollTop - treeView.element.offsetHeight describe "core:page-down", -> it "scrolls down a page", -> - treeView.height(5) + treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) - element.expand() for element in treeView.find('.directory') - expect(treeView.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight() + element.expand() for element in treeView.element.querySelectorAll('.directory') + expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) atom.commands.dispatch(treeView.element, 'core:page-down') - expect(treeView.scrollTop()).toBe treeView.height() + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe treeView.element.offsetHeight describe "movement outside of viewable region", -> it "scrolls the tree view to the selected item", -> - treeView.height(100) + treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) - element.expand() for element in treeView.find('.directory') - expect(treeView.list.outerHeight()).toBeGreaterThan treeView.scroller.outerHeight() + element.expand() for element in treeView.element.querySelectorAll('.directory') + expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) - entryCount = treeView.find(".entry").length - entryHeight = treeView.find('.file').height() + entryCount = treeView.element.querySelectorAll(".entry").length + entryHeight = treeView.element.querySelector('.file').offsetHeight _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scrollBottom()).toBeGreaterThan (entryCount * entryHeight) - 1 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop + treeView.element.offsetHeight).toBeGreaterThan((entryCount * entryHeight) - 1) _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-up') - expect(treeView.scrollTop()).toBe 0 + expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe 0 describe "tree-view:expand-directory", -> describe "when a directory entry is selected", -> it "expands the current directory", -> - subdir = root1.find('.directory:first') - subdir.click() - subdir[0].collapse() + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.collapse() expect(subdir).not.toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:expand-item') @@ -1059,11 +1056,11 @@ describe "TreeView", -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) fs.mkdirSync(path.join(rootDirPath, "empty-dir")) atom.project.setPaths([rootDirPath]) - rootView = $(treeView.roots[0]) + rootView = treeView.roots[0] - subdir = rootView.find('.directory:first') - subdir.click() - subdir[0].expand() + subdir = rootView.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.expand() expect(subdir).toHaveClass('expanded') expect(subdir).toHaveClass('selected') @@ -1073,17 +1070,17 @@ describe "TreeView", -> describe "when the directory has entries", -> it "moves the cursor down to the first sub-entry", -> - subdir = root1.find('.directory:first') - subdir.click() - subdir[0].expand() + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.expand() atom.commands.dispatch(treeView.element, 'tree-view:expand-item') - expect(subdir.find('.entry:first')).toHaveClass('selected') + expect(subdir.querySelector('.entry')).toHaveClass('selected') describe "when a file entry is selected", -> it "does nothing", -> waitsForFileToOpen -> - root1.find('.file').click() + root1.querySelector('.file').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'tree-view:expand-directory') @@ -1091,29 +1088,29 @@ describe "TreeView", -> describe "tree-view:recursive-expand-directory", -> describe "when an collapsed root is recursively expanded", -> it "expands the root and all subdirectories", -> - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) treeView.roots[0].collapse() expect(treeView.roots[0]).not.toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:recursive-expand-directory') expect(treeView.roots[0]).toHaveClass 'expanded' - children = root1.find('.directory') + children = root1.querySelectorAll('.directory') expect(children.length).toBeGreaterThan 0 - children.each (index, child) -> + for child in children expect(child).toHaveClass 'expanded' describe "tree-view:collapse-directory", -> subdir = null beforeEach -> - subdir = root1.find('> .entries > .directory').eq(0) - subdir[0].expand() + subdir = root1.querySelector('.entries > .directory') + subdir.expand() describe "when an expanded directory is selected", -> it "collapses the selected directory", -> - subdir.click() - subdir[0].expand() + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.expand() expect(subdir).toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:collapse-directory') @@ -1123,9 +1120,9 @@ describe "TreeView", -> describe "when a collapsed directory is selected", -> it "collapses and selects the selected directory's parent directory", -> - directories = subdir.find('.directory') - directories.click() - directories[0].collapse() + directories = subdir.querySelector('.directory') + directories.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + directories.collapse() atom.commands.dispatch(treeView.element, 'tree-view:collapse-directory') expect(subdir).not.toHaveClass 'expanded' @@ -1142,7 +1139,7 @@ describe "TreeView", -> describe "when a file is selected", -> it "collapses and selects the selected file's parent directory", -> waitsForFileToOpen -> - subdir.find('.file').click() + subdir.querySelector('.file').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'tree-view:collapse-directory') @@ -1155,26 +1152,26 @@ describe "TreeView", -> children = null beforeEach -> - parent = root1.find('> .entries > .directory').eq(2) - parent[0].expand() - children = parent.find('.expanded.directory') - children.each (index, child) -> + parent = root1.querySelectorAll('.entries > .directory')[2] + parent.expand() + children = parent.querySelectorAll('.expanded.directory') + for child in children child.expand() describe "when an expanded directory is recursively collapsed", -> it "collapses the directory and all its child directories", -> - parent.click() - parent[0].expand() + parent.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + parent.expand() expect(parent).toHaveClass 'expanded' - children.each (index, child) -> - $(child).click() + for child in children + child.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) child.expand() expect(child).toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:recursive-collapse-directory') expect(parent).not.toHaveClass 'expanded' - children.each (index, child) -> + for child in children expect(child).not.toHaveClass 'expanded' expect(treeView.roots[0]).toHaveClass 'expanded' @@ -1183,8 +1180,7 @@ describe "TreeView", -> it "opens the file in the editor and focuses it", -> jasmine.attachToDOM(workspaceElement) - file = root1.find('.file:contains(tree-view.js)')[0] - treeView.selectEntry(file) + treeView.selectEntry(sampleJs) waitsForFileToOpen -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') @@ -1198,8 +1194,7 @@ describe "TreeView", -> it "opens pending items in a permanent state", -> jasmine.attachToDOM(workspaceElement) - file = root1.find('.file:contains(tree-view.js)')[0] - treeView.selectEntry(file) + treeView.selectEntry(sampleJs) waitsForFileToOpen -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') @@ -1210,8 +1205,7 @@ describe "TreeView", -> expect(atom.workspace.getActivePane().getPendingItem()).toEqual item expect(atom.views.getView(item)).toHaveFocus() - file = root1.find('.file:contains(tree-view.js)')[0] - treeView.selectEntry(file) + treeView.selectEntry(sampleJs) waitsForFileToOpen -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') @@ -1224,9 +1218,9 @@ describe "TreeView", -> describe "when a directory is selected", -> it "expands or collapses the directory", -> - subdir = root1.find('.directory').first() - subdir.click() - subdir[0].collapse() + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.collapse() expect(subdir).not.toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') @@ -1257,7 +1251,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) waitsForFileToOpen -> - root1.find('.file:contains(tree-view.js)').click() + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> previousPane = atom.workspace.getActivePane() @@ -1292,8 +1286,7 @@ describe "TreeView", -> it "opens the file in the editor in pending state and focuses it", -> jasmine.attachToDOM(workspaceElement) - file = root1.find('.file:contains(tree-view.js)')[0] - treeView.selectEntry(file) + treeView.selectEntry(sampleJs) waitsForFileToOpen -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') @@ -1306,9 +1299,9 @@ describe "TreeView", -> describe "when a directory is selected", -> it "expands the directory", -> - subdir = root1.find('.directory').first() - subdir.click() - subdir[0].collapse() + subdir = root1.querySelector('.directory') + subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + subdir.collapse() expect(subdir).not.toHaveClass 'expanded' atom.commands.dispatch(treeView.element, 'tree-view:expand-item') @@ -1416,18 +1409,15 @@ describe "TreeView", -> atom.project.setPaths([rootDirPath, rootDirPath2]) - root1 = $(treeView.roots[0]) - dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir):first') - dirView[0].expand() - fileView = treeView.find('.file:contains(test-file.txt)') - dirView2 = $(treeView.roots[0].entries).find('.directory:contains(test-dir2):last') - dirView2[0].expand() - fileView2 = treeView.find('.file:contains(test-file2.txt)') - fileView3 = treeView.find('.file:contains(test-file3.txt)') - dirView3 = $(treeView.roots[1].entries).find('.directory:contains(test-dir3):first') - dirView3[0].expand() - fileView4 = treeView.find('.file:contains(test-file4.txt)') - fileView5 = treeView.find('.file:contains(test-file5.txt)') + root1 = treeView.roots[0] + root2 = treeView.roots[1] + [dirView, dirView2] = root1.querySelectorAll('.directory') + dirView.expand() + dirView2.expand() + dirView3 = root2.querySelector('.directory') + dirView3.expand() + [fileView, fileView2, fileView3] = root1.querySelectorAll('.file') + fileView4 = root2.querySelector('.file') describe "tree-view:copy", -> LocalStorage = window.localStorage @@ -1435,7 +1425,7 @@ describe "TreeView", -> LocalStorage.clear() waitsForFileToOpen -> - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:copy") @@ -1452,13 +1442,13 @@ describe "TreeView", -> describe 'when multiple files are selected', -> it 'saves the selected item paths in localStorage', -> - fileView3.addClass('selected') + fileView3.classList.add('selected') atom.commands.dispatch(treeView.element, "tree-view:copy") storedPaths = JSON.parse(LocalStorage['tree-view:copyPath']) expect(storedPaths.length).toBe 2 - expect(storedPaths[0]).toBe fileView2[0].getPath() - expect(storedPaths[1]).toBe fileView3[0].getPath() + expect(storedPaths[0]).toBe fileView2.getPath() + expect(storedPaths[1]).toBe fileView3.getPath() describe "tree-view:cut", -> LocalStorage = window.localStorage @@ -1467,7 +1457,7 @@ describe "TreeView", -> LocalStorage.clear() waitsForFileToOpen -> - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:cut") @@ -1485,13 +1475,13 @@ describe "TreeView", -> describe 'when multiple files are selected', -> it 'saves the selected item paths in localStorage', -> LocalStorage.clear() - fileView3.addClass('selected') + fileView3.classList.add('selected') atom.commands.dispatch(treeView.element, "tree-view:cut") storedPaths = JSON.parse(LocalStorage['tree-view:cutPath']) expect(storedPaths.length).toBe 2 - expect(storedPaths[0]).toBe fileView2[0].getPath() - expect(storedPaths[1]).toBe fileView3[0].getPath() + expect(storedPaths[0]).toBe fileView2.getPath() + expect(storedPaths[1]).toBe fileView3.getPath() describe "tree-view:paste", -> LocalStorage = window.localStorage @@ -1504,7 +1494,7 @@ describe "TreeView", -> it "makes a copy inside itself", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) - dirView.click() + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) newPath = path.join(dirPath, path.basename(dirPath)) expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() @@ -1512,7 +1502,7 @@ describe "TreeView", -> it 'does not keep copying recursively', -> LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) - dirView.click() + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) newPath = path.join(dirPath, path.basename(dirPath)) expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() @@ -1522,7 +1512,7 @@ describe "TreeView", -> describe "when cut", -> it "does nothing", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([dirPath]) - dirView.click() + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(fs.existsSync(dirPath)).toBeTruthy() expect(fs.existsSync(path.join(dirPath, path.basename(dirPath)))).toBeFalsy() @@ -1534,7 +1524,7 @@ describe "TreeView", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath2, filePathDoesntExist1, filePath3, filePathDoesntExist2]) - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath, path.basename(filePath2)))).toBeTruthy() @@ -1549,7 +1539,7 @@ describe "TreeView", -> it "creates a copy of the original file in the selected file's parent directory", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() @@ -1559,7 +1549,7 @@ describe "TreeView", -> it "appends a number to the destination name", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1574,10 +1564,9 @@ describe "TreeView", -> fs.writeFileSync(dotFilePath, "doesn't matter .") LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath]) - treeView.find('.file:contains(test.file.txt)').click() atom.commands.dispatch(treeView.element, "tree-view:paste") - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath, path.basename(dotFilePath)))).toBeTruthy() expect(fs.existsSync(dotFilePath)).toBeTruthy() @@ -1588,7 +1577,7 @@ describe "TreeView", -> fs.writeFileSync(dotFilePath, "doesn't matter .") LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath]) - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1600,7 +1589,7 @@ describe "TreeView", -> it "creates a copy of the original file in the selected directory", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - dirView2.click() + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() @@ -1610,7 +1599,7 @@ describe "TreeView", -> it "appends a number to the destination file name", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - dirView.click() + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1630,8 +1619,9 @@ describe "TreeView", -> it "creates a copy of the original file in the selected directory", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - dotDirView = $(treeView.roots[0].entries).find('.directory:contains(test\\.dir)') - dotDirView.click() + directories = treeView.roots[0].entries.querySelectorAll('.directory') + dotDirView = directories[directories.length - 1] + dotDirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dotDirPath, path.basename(filePath)))).toBeTruthy() @@ -1643,8 +1633,9 @@ describe "TreeView", -> fs.writeFileSync(dotFilePath, "doesn't matter .") LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath]) - dotDirView = $(treeView.roots[0].entries).find('.directory:contains(test\\.dir)') - dotDirView.click() + directories = treeView.roots[0].entries.querySelectorAll('.directory') + dotDirView = directories[directories.length - 1] + dotDirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1655,7 +1646,7 @@ describe "TreeView", -> describe "when pasting into a different root directory", -> it "creates the file", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath4]) - dirView2.click() + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath4)))).toBeTruthy() @@ -1667,7 +1658,7 @@ describe "TreeView", -> asteriskFilePath = path.join(dirPath, "test-file-**.txt") fs.writeFileSync(asteriskFilePath, "doesn't matter *") LocalStorage['tree-view:copyPath'] = JSON.stringify([asteriskFilePath]) - dirView2.click() + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(asteriskFilePath)))).toBeTruthy() @@ -1680,7 +1671,7 @@ describe "TreeView", -> it "copies the selected files to the parent directory of the selected file", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath2, filePath3]) - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath, path.basename(filePath2)))).toBeTruthy() @@ -1697,7 +1688,7 @@ describe "TreeView", -> fs.writeFileSync(filePath4, "doesn't matter") fs.writeFileSync(filePath5, "doesn't matter") - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath, "test-file20.txt"))).toBeTruthy() @@ -1708,7 +1699,7 @@ describe "TreeView", -> it "creates a copy of the original file in the selected file's parent directory and removes the original", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() @@ -1721,7 +1712,7 @@ describe "TreeView", -> filePath3 = path.join(dirPath2, "test-file.txt") fs.writeFileSync(filePath3, "doesn't matter") - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(filePath)).toBeTruthy() @@ -1730,7 +1721,7 @@ describe "TreeView", -> it "creates a copy of the original file in the selected directory and removes the original", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - dirView2.click() + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() @@ -1741,7 +1732,7 @@ describe "TreeView", -> it "moves the selected files to the parent directory of the selected file", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath2, filePath3]) - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath, path.basename(filePath2)))).toBeTruthy() @@ -1758,7 +1749,7 @@ describe "TreeView", -> fs.writeFileSync(filePath4, "doesn't matter") fs.writeFileSync(filePath5, "doesn't matter") - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(filePath2)).toBeTruthy() @@ -1768,7 +1759,7 @@ describe "TreeView", -> it "creates a copy of the original file in the selected directory and removes the original", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - dirView2.click() + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() @@ -1784,7 +1775,7 @@ describe "TreeView", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) - fileView2.click() + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.notifications.clear() atom.commands.dispatch(treeView.element, "tree-view:paste") @@ -1798,12 +1789,12 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() describe "when a file is selected", -> it "opens an add dialog with the file's current directory path populated", -> @@ -1816,8 +1807,8 @@ describe "TreeView", -> describe "when the parent directory of the selected file changes", -> it "still shows the active file as selected", -> - dirView[0].directory.emitter.emit 'did-remove-entries', {'deleted.txt': {}} - expect(treeView.find('.selected').text()).toBe path.basename(filePath) + dirView.directory.emitter.emit 'did-remove-entries', {'deleted.txt': {}} + expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(filePath) describe "when the path without a trailing '#{path.sep}' is changed and confirmed", -> describe "when no file exists at that location", -> @@ -1834,21 +1825,21 @@ describe "TreeView", -> expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath waitsFor "tree view to be updated", -> - $(dirView[0].entries).find("> .file").length > 1 + dirView.entries.querySelectorAll(".file").length > 1 runs -> - expect(treeView.find('.selected').text()).toBe path.basename(newPath) + expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) it "adds file in any project path", -> newPath = path.join(dirPath3, "new-test-file.txt") waitsForFileToOpen -> - fileView4.click() + fileView4.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) waitsForFileToOpen -> atom.commands.dispatch(treeView.element, "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() addDialog.miniEditor.getModel().insertText(path.basename(newPath)) atom.commands.dispatch addDialog.element, 'core:confirm' @@ -1858,10 +1849,10 @@ describe "TreeView", -> expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath waitsFor "tree view to be updated", -> - $(dirView3[0].entries).find("> .file").length > 1 + dirView3.entries.querySelectorAll(".file").length > 1 runs -> - expect(treeView.find('.selected').text()).toBe path.basename(newPath) + expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) describe "when a file already exists at that location", -> it "shows an error message and does not close the dialog", -> @@ -1880,7 +1871,7 @@ describe "TreeView", -> addDialog.close() atom.commands.dispatch(treeView.element, "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() newPath = path.join(fs.realpathSync(temp.mkdirSync()), 'a-file') addDialog.miniEditor.getModel().insertText(newPath) @@ -1906,7 +1897,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch addDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.find(".tree-view")).toMatchSelector(':focus') + expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) describe "when the add dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -1929,9 +1920,9 @@ describe "TreeView", -> describe "when a directory is selected", -> it "opens an add dialog with the directory's path populated", -> addDialog.cancel() - dirView.click() + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:add-file") - addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + addDialog = atom.workspace.getModalPanels()[0].getItem() expect(addDialog).toExist() expect(addDialog.promptText.text()).toBeTruthy() @@ -1943,20 +1934,20 @@ describe "TreeView", -> describe "when the root directory is selected", -> it "opens an add dialog with no path populated", -> addDialog.cancel() - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:add-file") - addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + addDialog = atom.workspace.getModalPanels()[0].getItem() expect(addDialog.miniEditor.getText()).toBe "" describe "when there is no entry selected", -> it "opens an add dialog with no path populated", -> addDialog.cancel() - root1.click() - root1.removeClass('selected') + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + root1.classList.remove('selected') expect(treeView.selectedEntry()).toBeNull() atom.commands.dispatch(treeView.element, "tree-view:add-file") - addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + addDialog = atom.workspace.getModalPanels()[0].getItem() expect(addDialog.miniEditor.getText()).toBe "" @@ -1966,10 +1957,10 @@ describe "TreeView", -> atom.project.setPaths([]) atom.commands.dispatch(workspaceElement, "tree-view:add-folder") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() addDialog.miniEditor.getModel().insertText("a-file") atom.commands.dispatch(addDialog.element, 'core:confirm') - expect(addDialog.text()).toContain("You must open a directory to create a file with a relative path") + expect(addDialog.element.textContent).toContain("You must open a directory to create a file with a relative path") describe "tree-view:add-folder", -> [addPanel, addDialog] = [] @@ -1978,12 +1969,12 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:add-folder") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() describe "when a file is selected", -> it "opens an add dialog with the file's current directory path populated", -> @@ -2003,8 +1994,8 @@ describe "TreeView", -> expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(treeView.find(".tree-view")).toMatchSelector(':focus') - expect(dirView.find('.directory.selected:contains(new)').length).toBe 1 + expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(dirView.querySelector('.directory.selected').textContent).toBe('new') describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> describe "when no directory exists at the given path", -> @@ -2015,8 +2006,8 @@ describe "TreeView", -> expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(treeView.find(".tree-view")).toMatchSelector(':focus') - expect(dirView.find('.directory.selected:contains(new)').length).toBe(1) + expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(dirView.querySelector('.directory.selected').textContent).toBe('new') it "selects the created directory and does not change the expansion state of existing directories", -> expandedPath = path.join(dirPath, 'expanded-dir') @@ -2032,8 +2023,8 @@ describe "TreeView", -> expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(treeView.find(".tree-view")).toMatchSelector(':focus') - expect(dirView.find('.directory.selected:contains(new2)').length).toBe(1) + expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(dirView.querySelector('.directory.selected').textContent).toBe('new2') expect(treeView.entryForPath(expandedPath).isExpanded).toBeTruthy() describe "when the project has no path", -> @@ -2042,7 +2033,7 @@ describe "TreeView", -> atom.project.setPaths([]) atom.commands.dispatch(treeView.element, "tree-view:add-folder") [addPanel] = atom.workspace.getModalPanels() - addDialog = $(addPanel.getItem()).view() + addDialog = addPanel.getItem() expect(addDialog.miniEditor.getModel().getText()).toBe '' newPath = temp.path() @@ -2070,11 +2061,11 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:move") - moveDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + moveDialog = atom.workspace.getModalPanels()[0].getItem() afterEach -> waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup @@ -2101,12 +2092,13 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 waitsFor "tree view to update", -> - root1.find('> .entries > .file:contains(renamed-test-file.txt)').length > 0 + files = Array.from(root1.querySelectorAll('.entries .file')) + files.filter((f) -> f.textContent is 'renamed-test-file.txt').length > 0 runs -> - dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir)') - dirView[0].expand() - expect($(dirView[0].entries).children().length).toBe 0 + dirView = treeView.roots[0].querySelector('.directory') + 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", -> @@ -2116,7 +2108,8 @@ describe "TreeView", -> atom.commands.dispatch moveDialog.element, 'core:confirm' waitsFor "tree view to update", -> - root1.find('> .entries > .directory:contains(new)').length > 0 + directories = Array.from(root1.querySelectorAll('.entries .directory')) + directories.filter((f) -> f.textContent is 'new').length > 0 runs -> expect(fs.existsSync(newPath)).toBeTruthy() @@ -2143,7 +2136,7 @@ describe "TreeView", -> describe "when the move dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> - $(workspaceElement).focus() + workspaceElement.focus() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.views.getView(atom.workspace.getActivePane())).toHaveFocus() @@ -2153,16 +2146,16 @@ describe "TreeView", -> beforeEach -> dotFilePath = path.join(dirPath, ".dotfile") fs.writeFileSync(dotFilePath, "dot") - dirView[0].collapse() - dirView[0].expand() - dotFileView = treeView.find('.file:contains(.dotfile)') + dirView.collapse() + dirView.expand() + dotFileView = treeView.entryForPath(dotFilePath) waitsForFileToOpen -> - dotFileView.click() + dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:move") - moveDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + moveDialog = atom.workspace.getModalPanels()[0].getItem() it "selects the entire file name", -> expect(moveDialog).toExist() @@ -2171,7 +2164,7 @@ describe "TreeView", -> describe "when the project is selected", -> it "doesn't display the move dialog", -> - treeView.roots[0].click() + treeView.roots[0].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:move") expect(atom.workspace.getModalPanels().length).toBe(0) @@ -2183,11 +2176,11 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:duplicate") - copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + copyDialog = atom.workspace.getModalPanels()[0].getItem() afterEach -> waits 50 # The copy specs cause too many false positives because of their async nature, so wait a little bit before we cleanup @@ -2211,15 +2204,15 @@ describe "TreeView", -> atom.commands.dispatch copyDialog.element, 'core:confirm' waitsFor "tree view to update", -> - root1.find('> .entries > .file:contains(duplicated-test-file.txt)').length > 0 + treeView.entryForPath(newPath) runs -> expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir)') - dirView[0].expand() - expect($(dirView[0].entries).children().length).toBe 1 + dirView = treeView.roots[0].entries.querySelector('.directory') + dirView.expand() + expect(dirView.entries.children.length).toBe 1 expect(atom.workspace.getActiveTextEditor().getPath()).toBe(newPath) describe "when the directories along the new path don't exist", -> @@ -2231,7 +2224,7 @@ describe "TreeView", -> atom.commands.dispatch copyDialog.element, 'core:confirm' waitsFor "tree view to update", -> - root1.find('> .entries > .directory:contains(new)').length > 0 + treeView.entryForPath(newPath) waitsFor "new path to exist", -> fs.existsSync(newPath) @@ -2271,16 +2264,16 @@ describe "TreeView", -> beforeEach -> dotFilePath = path.join(dirPath, ".dotfile") fs.writeFileSync(dotFilePath, "dot") - dirView[0].collapse() - dirView[0].expand() - dotFileView = treeView.find('.file:contains(.dotfile)') + dirView.collapse() + dirView.expand() + dotFileView = treeView.entryForPath(dotFilePath) waitsForFileToOpen -> - dotFileView.click() + dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, "tree-view:duplicate") - copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + copyDialog = atom.workspace.getModalPanels()[0].getItem() it "selects the entire file name", -> expect(copyDialog).toExist() @@ -2289,7 +2282,7 @@ describe "TreeView", -> describe "when the project is selected", -> it "doesn't display the copy dialog", -> - treeView.roots[0].click() + treeView.roots[0].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:duplicate") expect(atom.workspace.getModalPanels().length).toBe(0) @@ -2303,7 +2296,7 @@ describe "TreeView", -> runs -> editorElement = atom.views.getView(atom.workspace.getActivePaneItem()) atom.commands.dispatch(editorElement, "tree-view:duplicate") - copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view() + copyDialog = atom.workspace.getModalPanels()[0].getItem() it "duplicates the current file", -> expect(copyDialog.miniEditor.getText()).toBe('tree-view.js') @@ -2321,7 +2314,7 @@ describe "TreeView", -> spyOn(atom, 'confirm') jasmine.attachToDOM(workspaceElement) treeView.focus() - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, 'tree-view:remove') args = atom.confirm.mostRecentCall.args[0] @@ -2331,7 +2324,7 @@ describe "TreeView", -> spyOn(atom, 'confirm') waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> atom.commands.dispatch(treeView.element, 'tree-view:remove') @@ -2344,7 +2337,7 @@ describe "TreeView", -> spyOn(atom, 'confirm') waitsForFileToOpen -> - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> repeat = 2 @@ -2385,44 +2378,41 @@ describe "TreeView", -> runs -> expect(fs.existsSync(temporaryFilePath)).toBeFalsy() - entriesCountBefore = $(treeView.roots[0].entries).find('.entry').length + entriesCountBefore = treeView.roots[0].querySelectorAll('.entry').length fs.writeFileSync temporaryFilePath, 'hi' waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.entry').length is entriesCountBefore + 1 + treeView.roots[0].querySelectorAll('.entry').length is entriesCountBefore + 1 runs -> - expect($(treeView.roots[0].entries).find('.entry').length).toBe entriesCountBefore + 1 - expect($(treeView.roots[0].entries).find('.file:contains(temporary)')).toExist() + expect(treeView.entryForPath(temporaryFilePath)).toExist() fs.removeSync(temporaryFilePath) waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.entry').length is entriesCountBefore + treeView.roots[0].querySelectorAll('.entry').length is entriesCountBefore describe "project changes", -> beforeEach -> atom.project.setPaths([path1]) treeView = atom.workspace.getLeftPanels()[0].getItem() - root1 = $(treeView.roots[0]) + root1 = treeView.roots[0] describe "when a root folder is added", -> it "maintains expanded folders", -> - root1.find('.directory:contains(dir1)').click() + root1.querySelector('.directory').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.project.setPaths([path1, path2]) treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() - root1 = $(treeView.roots[0]) - expect(root1.find(".directory:contains(dir1)")).toHaveClass("expanded") + expect(treeView.roots[0].querySelector(".directory")).toHaveClass("expanded") it "maintains collapsed (root) folders", -> - root1.click() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.project.setPaths([path1, path2]) treeView = atom.workspace.getLeftPanels()[0].getItem() expect(treeView).toExist() - root1 = $(treeView.roots[0]) - expect(root1).toHaveClass("collapsed") + expect(treeView.roots[0]).toHaveClass("collapsed") describe "the hideVcsIgnoredFiles config option", -> describe "when the project's path is the repository's working directory", -> @@ -2440,13 +2430,13 @@ describe "TreeView", -> atom.config.set "tree-view.hideVcsIgnoredFiles", false it "hides git-ignored files if the option is set, but otherwise shows them", -> - expect(treeView.find('.file:contains(ignored.txt)').length).toBe 1 + expect(Array.from(treeView.element.querySelectorAll('.file')).map((f) -> f.textContent)).toEqual(['.gitignore', 'ignored.txt']) atom.config.set("tree-view.hideVcsIgnoredFiles", true) - expect(treeView.find('.file:contains(ignored.txt)').length).toBe 0 + expect(Array.from(treeView.element.querySelectorAll('.file')).map((f) -> f.textContent)).toEqual(['.gitignore']) atom.config.set("tree-view.hideVcsIgnoredFiles", false) - expect(treeView.find('.file:contains(ignored.txt)').length).toBe 1 + expect(Array.from(treeView.element.querySelectorAll('.file')).map((f) -> f.textContent)).toEqual(['.gitignore', 'ignored.txt']) describe "when the project's path is a subfolder of the repository's working directory", -> beforeEach -> @@ -2460,7 +2450,7 @@ describe "TreeView", -> atom.config.set("tree-view.hideVcsIgnoredFiles", true) it "does not hide git ignored files", -> - expect(treeView.find('.file:contains(tree-view.js)').length).toBe 1 + expect(Array.from(treeView.element.querySelectorAll('.file')).map((f) -> f.textContent)).toEqual(['.gitignore', 'tree-view.js', 'tree-view.txt']) describe "the hideIgnoredNames config option", -> beforeEach -> @@ -2475,19 +2465,13 @@ describe "TreeView", -> atom.config.set "tree-view.hideIgnoredNames", false it "hides ignored files if the option is set, but otherwise shows them", -> - expect(treeView.find('.directory .name:contains(.git)').length).toBe 1 - expect(treeView.find('.directory .name:contains(test.js)').length).toBe 1 - expect(treeView.find('.directory .name:contains(test.txt)').length).toBe 1 + expect(Array.from(treeView.roots[0].querySelectorAll('.entry')).map((e) -> e.textContent)).toEqual(['.git', 'test.js', 'test.txt']) atom.config.set("tree-view.hideIgnoredNames", true) - expect(treeView.find('.directory .name:contains(.git)').length).toBe 0 - expect(treeView.find('.directory .name:contains(test.js)').length).toBe 0 - expect(treeView.find('.directory .name:contains(test.txt)').length).toBe 1 + expect(Array.from(treeView.roots[0].querySelectorAll('.entry')).map((e) -> e.textContent)).toEqual(['test.txt']) atom.config.set("core.ignoredNames", []) - expect(treeView.find('.directory .name:contains(.git)').length).toBe 1 - expect(treeView.find('.directory .name:contains(test.js)').length).toBe 1 - expect(treeView.find('.directory .name:contains(test.txt)').length).toBe 1 + expect(Array.from(treeView.roots[0].querySelectorAll('.entry')).map((e) -> e.textContent)).toEqual(['.git', 'test.js', 'test.txt']) describe "the squashedDirectoryName config option", -> beforeEach -> @@ -2628,7 +2612,7 @@ describe "TreeView", -> omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] expect(omicronDir.title).toEqual("omicron") - omicronDir.click() + omicronDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] expect(piDir.title).toEqual("pi") sigmaFile = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .file:contains(sigma) span")[0] @@ -2647,7 +2631,7 @@ describe "TreeView", -> omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] expect(omicronDir.title).toEqual("omicron") - omicronDir.click() + omicronDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] expect(piDir.title).toEqual("pi") rhoDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(rho) span")[0] @@ -2729,7 +2713,7 @@ describe "TreeView", -> element.expand() for element in treeView.find('.directory') fileView = treeView.find('.file:contains(new2)') expect(fileView).not.toBeNull() - fileView.click() + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) describe "when the file is deleted", -> it "updates the style of the directory", -> @@ -2851,7 +2835,7 @@ describe "TreeView", -> describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView2.trigger($.Event('mousedown', {shiftKey: true})) expect(treeView.find('.tree-view')).toHaveClass('multi-select') fileView3.trigger($.Event('mousedown')) @@ -2859,13 +2843,13 @@ describe "TreeView", -> describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView2.trigger($.Event('mousedown', {shiftKey: true})) expect(treeView.find('.tree-view')).toHaveClass('multi-select') describe 'using the shift key', -> it 'selects the items between the already selected item and the shift clicked item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {shiftKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView2).toHaveClass('selected') @@ -2873,7 +2857,7 @@ describe "TreeView", -> describe 'using the metakey(cmd) key', -> it 'selects the cmd clicked item in addition to the original selected item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {metaKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') @@ -2892,7 +2876,7 @@ describe "TreeView", -> describe 'using the ctrl key', -> it 'selects the ctrl clicked item in addition to the original selected item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') @@ -2912,28 +2896,28 @@ describe "TreeView", -> describe 'using the ctrl key', -> describe "previous item is selected but the ctrl clicked item is not", -> it 'selects the clicked item, but deselects the previous item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(fileView1).not.toHaveClass('selected') expect(fileView3).toHaveClass('selected') expect(fileView2).not.toHaveClass('selected') it 'displays the full contextual menu', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(treeView.list).toHaveClass('full-menu') expect(treeView.list).not.toHaveClass('multi-select') describe 'previous item is selected including the ctrl clicked', -> it 'displays the multi-select menu', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {metaKey: true})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(treeView.list).not.toHaveClass('full-menu') expect(treeView.list).toHaveClass('multi-select') it 'does not deselect any of the items', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {metaKey: true})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(fileView1).toHaveClass('selected') @@ -2941,7 +2925,7 @@ describe "TreeView", -> describe 'when clicked item is the only item selected', -> it 'displays the full contextual menu', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {ctrlKey: true})) expect(treeView.list).toHaveClass('full-menu') expect(treeView.list).not.toHaveClass('multi-select') @@ -2959,7 +2943,7 @@ describe "TreeView", -> describe "right-clicking", -> describe 'when multiple items are selected', -> it 'displays the multi-select context menu', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {metaKey: true})) fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView1).toHaveClass('selected') @@ -2969,18 +2953,18 @@ describe "TreeView", -> describe 'when a single item is selected', -> it 'displays the full context menu', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {button: 2})) expect(treeView.list).toHaveClass('full-menu') expect(treeView.list).not.toHaveClass('multi-select') it 'selects right clicked item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView3).toHaveClass('selected') it 'de-selects the previously selected item', -> - fileView1.click() + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.trigger($.Event('mousedown', {button: 2})) expect(fileView1).not.toHaveClass('selected') From 7b892dfd8a7995a3adb4c48b44c2f8bfc391f0d7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 09:01:54 +0100 Subject: [PATCH 123/263] Remove jQuery from tests exercising the `squashedDirectoryName` option --- spec/tree-view-spec.coffee | 84 +++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 04f5621e..811b568d 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2543,13 +2543,13 @@ describe "TreeView", -> rootDirPath = treeView.roots[0].getPath() expect(rootDirPath).toBe(rootDir) - zetaDirPath = $(treeView.roots[0].entries).find('.directory:contains(zeta):first')[0].getPath() + zetaDirPath = findDirectoryContainingText(treeView.roots[0], 'zeta').getPath() expect(zetaDirPath).toBe(zetaDir) it "does not squash a file in to a DirectoryViews", -> - zetaDir = $(treeView.roots[0].entries).find('.directory:contains(zeta):first') - zetaDir[0].expand() - zetaEntries = [].slice.call(zetaDir[0].children[1].children).map (element) -> + zetaDir = findDirectoryContainingText(treeView.roots[0], 'zeta') + zetaDir.expand() + zetaEntries = [].slice.call(zetaDir.children[1].children).map (element) -> element.innerText expect(zetaEntries).toEqual(["zeta.txt"]) @@ -2557,11 +2557,11 @@ describe "TreeView", -> it "squashes two dir names when the first only contains a single dir", -> if path.sep is '\\' # First escape the backslashes for Coffeescript, then escape them for jQuery - betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha\\\\beta):first") + betaDir = findDirectoryContainingText(treeView.roots[0], "alpha\\\\beta") else - betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha#{path.sep}beta):first") - betaDir[0].expand() - betaEntries = [].slice.call(betaDir[0].children[1].children).map (element) -> + betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta") + betaDir.expand() + betaEntries = [].slice.call(betaDir.children[1].children).map (element) -> element.innerText expect(betaEntries).toEqual(["beta.txt"]) @@ -2569,19 +2569,19 @@ describe "TreeView", -> it "squashes three dir names when the first and second only contain single dirs", -> if path.sep is '\\' # First escape the backslashes for Coffeescript, then escape them for jQuery - epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma\\\\delta\\\\epsilon):first") + epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma\\\\delta\\\\epsilon") else - epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma#{path.sep}delta#{path.sep}epsilon):first") - epsilonDir[0].expand() - epsilonEntries = [].slice.call(epsilonDir[0].children[1].children).map (element) -> + epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon") + epsilonDir.expand() + epsilonEntries = [].slice.call(epsilonDir.children[1].children).map (element) -> element.innerText expect(epsilonEntries).toEqual(["theta.txt"]) it "does not squash a dir name when there are two child dirs ", -> - lambdaDir = $(treeView.roots[0].entries).find('.directory:contains(lambda):first') - lambdaDir[0].expand() - lambdaEntries = [].slice.call(lambdaDir[0].children[1].children).map (element) -> + lambdaDir = findDirectoryContainingText(treeView.roots[0], "lambda") + lambdaDir.expand() + lambdaEntries = [].slice.call(lambdaDir.children[1].children).map (element) -> element.innerText expect(lambdaEntries).toEqual(["iota", "kappa"]) @@ -2589,20 +2589,20 @@ describe "TreeView", -> describe "when a squashed directory is deleted", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + piDir = findDirectoryContainingText(treeView.roots[0], "omicron#{path.sep}pi") treeView.focus() treeView.selectEntry(piDir) spyOn(atom, 'confirm').andCallFake (dialog) -> dialog.buttons["Move to Trash"]() atom.commands.dispatch(treeView.element, 'tree-view:remove') - omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] - expect(omicronDir.title).toEqual("omicron") + omicronDir = findDirectoryContainingText(treeView.roots[0], "omicron") + expect(omicronDir.header.textContent).toEqual("omicron") describe "when a file is created within a directory with another squashed directory", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + piDir = findDirectoryContainingText(treeView.roots[0], "omicron#{path.sep}pi") expect(piDir).not.toBeNull() # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" @@ -2610,18 +2610,18 @@ describe "TreeView", -> fs.writeFileSync(sigmaFilePath, "doesn't matter") treeView.updateRoots() - omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] - expect(omicronDir.title).toEqual("omicron") + omicronDir = findDirectoryContainingText(treeView.roots[0], "omicron") + expect(omicronDir.header.textContent).toEqual("omicron") omicronDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] - expect(piDir.title).toEqual("pi") - sigmaFile = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .file:contains(sigma) span")[0] - expect(sigmaFile.title).toEqual("sigma.txt") + piDir = findDirectoryContainingText(omicronDir, "pi") + expect(piDir.header.textContent).toEqual("pi") + sigmaFile = findFileContainingText(omicronDir, "sigma.txt") + expect(sigmaFile.fileName.textContent).toEqual("sigma.txt") describe "when a directory is created within a directory with another squashed directory", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + piDir = findDirectoryContainingText(treeView.roots[0], "omicron#{path.sep}pi") expect(piDir).not.toBeNull() # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" @@ -2629,26 +2629,34 @@ describe "TreeView", -> fs.makeTreeSync(rhoDirPath) treeView.updateRoots() - omicronDir = $(treeView.roots[0].entries).find(".directory:contains(omicron):first span")[0] - expect(omicronDir.title).toEqual("omicron") + omicronDir = findDirectoryContainingText(treeView.roots[0], "omicron") + expect(omicronDir.header.textContent).toEqual("omicron") omicronDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(pi) span")[0] - expect(piDir.title).toEqual("pi") - rhoDir = $(treeView.roots[0].entries).find(".directory:contains(omicron) .entries .directory:contains(rho) span")[0] - expect(rhoDir.title).toEqual("rho") + piDir = findDirectoryContainingText(omicronDir, "pi") + expect(piDir.header.textContent).toEqual("pi") + rhoDir = findDirectoryContainingText(omicronDir, "rho") + expect(rhoDir.header.textContent).toEqual("rho") describe "when a directory is reloaded", -> it "squashes the directory names the last of which is same as an unsquashed directory", -> - muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first') - muDir[0].expand() - muEntries = Array.from(muDir[0].children[1].children).map (element) -> element.innerText + muDir = findDirectoryContainingText(treeView.roots[0], "mu") + muDir.expand() + muEntries = Array.from(muDir.children[1].children).map (element) -> element.innerText expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) - muDir[0].expand() - muDir[0].reload() - muEntries = Array.from(muDir[0].children[1].children).map (element) -> element.innerText + muDir.expand() + muDir.reload() + muEntries = Array.from(muDir.children[1].children).map (element) -> element.innerText expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) + findDirectoryContainingText = (element, text) -> + directories = Array.from(element.querySelectorAll('.entries .directory')) + directories.find((directory) -> directory.header.textContent is text) + + findFileContainingText = (element, text) -> + files = Array.from(element.querySelectorAll('.entries .file')) + files.find((file) -> file.fileName.textContent is text) + describe "Git status decorations", -> [projectPath, modifiedFile, originalFileContent] = [] From 86caaef6eca9785034cbfe178f1eded9d61a221f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 09:57:54 +0100 Subject: [PATCH 124/263] Finish removing jQuery from tree-view-spec.coffee --- spec/tree-view-spec.coffee | 367 ++++++++++++++++++------------------- 1 file changed, 183 insertions(+), 184 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 811b568d..022b2a4e 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -225,7 +225,7 @@ describe "TreeView", -> it "restores the focus state of the tree view", -> jasmine.attachToDOM(workspaceElement) treeView.focus() - expect(treeView.list).toMatchSelector ':focus' + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() atom.packages.deactivatePackage("tree-view") waitsForPromise -> @@ -233,7 +233,7 @@ describe "TreeView", -> runs -> treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView.list).toMatchSelector ':focus' + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() it "restores the scroll top when toggled", -> workspaceElement.style.height = '5px' @@ -294,7 +294,7 @@ describe "TreeView", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') expect(treeView.hasParent()).toBeTruthy() - expect(treeView.list).toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() describe "when tree-view:toggle-side is triggered on the root view", -> describe "when the tree view is on the left", -> @@ -330,7 +330,7 @@ describe "TreeView", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView.hasParent()).toBeTruthy() - expect(treeView.list).toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() describe "when the tree view is shown", -> it "focuses the tree view", -> @@ -342,7 +342,7 @@ describe "TreeView", -> expect(treeView).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView).toBeVisible() - expect(treeView.list).toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() describe "when the tree view is focused", -> it "unfocuses the tree view", -> @@ -354,7 +354,7 @@ describe "TreeView", -> expect(treeView).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView).toBeVisible() - expect(treeView.list).not.toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus() describe "when tree-view:reveal-active-file is triggered on the root view", -> beforeEach -> @@ -464,10 +464,10 @@ describe "TreeView", -> runs -> jasmine.attachToDOM(workspaceElement) treeView.focus() - expect(treeView.list).toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') expect(treeView).toBeVisible() - expect(treeView.list).not.toMatchSelector(':focus') + expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus() expect(atom.workspace.getActivePane().isActive()).toBe(true) describe "copy path commands", -> @@ -2132,7 +2132,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch moveDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.find(".tree-view")).toMatchSelector(':focus') + expect(treeView.find(".tree-view")).toHaveFocus() describe "when the move dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2250,7 +2250,7 @@ describe "TreeView", -> jasmine.attachToDOM(treeView.element) atom.commands.dispatch copyDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.find(".tree-view")).toMatchSelector(':focus') + expect(treeView.find(".tree-view")).toHaveFocus() describe "when the duplicate dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2649,14 +2649,6 @@ describe "TreeView", -> muEntries = Array.from(muDir.children[1].children).map (element) -> element.innerText expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"]) - findDirectoryContainingText = (element, text) -> - directories = Array.from(element.querySelectorAll('.entries .directory')) - directories.find((directory) -> directory.header.textContent is text) - - findFileContainingText = (element, text) -> - files = Array.from(element.querySelectorAll('.entries .file')) - files.find((file) -> file.fileName.textContent is text) - describe "Git status decorations", -> [projectPath, modifiedFile, originalFileContent] = [] @@ -2686,60 +2678,59 @@ describe "TreeView", -> treeView.useSyncFS = true treeView.updateRoots() - $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand() + treeView.roots[0].entries.querySelectorAll('.directory')[1].expand() describe "when the project is the repository root", -> it "adds a custom style", -> - expect(treeView.find('.icon-repo').length).toBe 1 + expect(treeView.element.querySelectorAll('.icon-repo').length).toBe 1 describe "when a file is modified", -> it "adds a custom style", -> - $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand() - expect(treeView.find('.file:contains(b.txt)')).toHaveClass 'status-modified' + expect(treeView.element.querySelector('.file.status-modified')).toHaveText('b.txt') describe "when a directory if modified", -> it "adds a custom style", -> - expect(treeView.find('.directory:contains(dir)')).toHaveClass 'status-modified' + expect(treeView.element.querySelector('.directory.status-modified').header).toHaveText('dir') describe "when a file is new", -> it "adds a custom style", -> - $(treeView.roots[0].entries).find('.directory:contains(dir2)')[0].expand() - expect(treeView.find('.file:contains(new2)')).toHaveClass 'status-added' + treeView.roots[0].entries.querySelectorAll('.directory')[2].expand() + expect(treeView.element.querySelector('.file.status-added')).toHaveText('new2') describe "when a directory is new", -> it "adds a custom style", -> - expect(treeView.find('.directory:contains(dir2)')).toHaveClass 'status-added' + expect(treeView.element.querySelector('.directory.status-added').header).toHaveText('dir2') describe "when a file is ignored", -> it "adds a custom style", -> - expect(treeView.find('.file:contains(ignored.txt)')).toHaveClass 'status-ignored' + expect(treeView.element.querySelector('.file.status-ignored')).toHaveText('ignored.txt') describe "when a file is selected in a directory", -> beforeEach -> jasmine.attachToDOM(workspaceElement) treeView.focus() - element.expand() for element in treeView.find('.directory') - fileView = treeView.find('.file:contains(new2)') + element.expand() for element in treeView.element.querySelectorAll('.directory') + fileView = treeView.element.querySelector('.file.status-added') expect(fileView).not.toBeNull() fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) describe "when the file is deleted", -> it "updates the style of the directory", -> expect(treeView.selectedEntry().getPath()).toContain(path.join('dir2', 'new2')) - dirView = $(treeView.roots[0].entries).find('.directory:contains(dir2)') + dirView = findDirectoryContainingText(treeView.roots[0], 'dir2') expect(dirView).not.toBeNull() - spyOn(dirView[0].directory, 'updateStatus') + spyOn(dirView.directory, 'updateStatus') spyOn(atom, 'confirm').andCallFake (dialog) -> dialog.buttons["Move to Trash"]() atom.commands.dispatch(treeView.element, 'tree-view:remove') - expect(dirView[0].directory.updateStatus).toHaveBeenCalled() + expect(dirView.directory.updateStatus).toHaveBeenCalled() describe "when the project is a symbolic link to the repository root", -> beforeEach -> symlinkPath = temp.path('tree-view-project') fs.symlinkSync(projectPath, symlinkPath, 'junction') atom.project.setPaths([symlinkPath]) - $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand() + treeView.roots[0].entries.querySelectorAll('.directory')[1].expand() waitsFor (done) -> disposable = atom.project.getRepositories()[0].onDidChangeStatuses -> @@ -2748,29 +2739,31 @@ describe "TreeView", -> describe "when a file is modified", -> it "updates its and its parent directories' styles", -> - expect(treeView.find('.file:contains(b.txt)')).toHaveClass 'status-modified' - expect(treeView.find('.directory:contains(dir)')).toHaveClass 'status-modified' + expect(treeView.element.querySelector('.file.status-modified')).toHaveText('b.txt') + expect(treeView.element.querySelector('.directory.status-modified').header).toHaveText('dir') describe "when a file loses its modified status", -> it "updates its and its parent directories' styles", -> fs.writeFileSync(modifiedFile, originalFileContent) atom.project.getRepositories()[0].getPathStatus(modifiedFile) - expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified' - expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified' + expect(treeView.element.querySelector('.file.status-modified')).not.toExist() + expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() describe "when the resize handle is double clicked", -> beforeEach -> - treeView.width(10).find('.list-tree').width 100 + treeView.element.style.width = '10px' + treeView.element.querySelector('.list-tree').style.width = '100px' + jasmine.attachToDOM(workspaceElement) it "sets the width of the tree to be the width of the list", -> - expect(treeView.width()).toBe 10 - treeView.find('.tree-view-resize-handle').trigger 'dblclick' - expect(treeView.width()).toBeGreaterThan 10 + expect(parseInt(treeView.element.style.width)).toBe 10 + treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true})) + expect(parseInt(treeView.element.style.width)).toBeGreaterThan 10 - treeView.width(1000) - treeView.find('.tree-view-resize-handle').trigger 'dblclick' - expect(treeView.width()).toBeLessThan 1000 + treeView.element.style.width = '1000px' + treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true})) + expect(parseInt(treeView.element.style.width)).toBeLessThan 1000 describe "when other panels are added", -> beforeEach -> @@ -2780,20 +2773,20 @@ describe "TreeView", -> expect(treeView).toBeVisible() expect(atom.workspace.getLeftPanels().length).toBe(1) - treeView.width(100) + treeView.element.style.width = '100px' - expect(treeView.width()).toBe(100) + expect(parseInt(treeView.element.style.width)).toBe(100) panel = document.createElement('div') panel.style.width = '100px' atom.workspace.addLeftPanel({item: panel, priority: 10}) expect(atom.workspace.getLeftPanels().length).toBe(2) - expect(treeView.width()).toBe(100) + expect(parseInt(treeView.element.style.width)).toBe(100) treeView.resizeTreeView({pageX: 250, which: 1}) - expect(treeView.width()).toBe(150) + expect(parseInt(treeView.element.style.width)).toBe(150) it "should resize normally on the right side", -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') @@ -2802,20 +2795,20 @@ describe "TreeView", -> expect(treeView).toBeVisible() expect(atom.workspace.getRightPanels().length).toBe(1) - treeView.width(100) + treeView.element.style.width = '100px' - expect(treeView.width()).toBe(100) + expect(parseInt(treeView.element.style.width)).toBe(100) panel = document.createElement('div') panel.style.width = '100px' atom.workspace.addRightPanel({item: panel, priority: 10}) expect(atom.workspace.getRightPanels().length).toBe(2) - expect(treeView.width()).toBe(100) + expect(parseInt(treeView.element.style.width)).toBe(100) - treeView.resizeTreeView({pageX: $(document.body).width() - 250, which: 1}) + treeView.resizeTreeView({pageX: document.body.offsetWidth - 250, which: 1}) - expect(treeView.width()).toBe(150) + expect(parseInt(treeView.element.style.width)).toBe(150) describe "selecting items", -> [dirView, fileView1, fileView2, fileView3, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3] = [] @@ -2835,30 +2828,28 @@ describe "TreeView", -> atom.project.setPaths([rootDirPath]) - dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir)') - dirView[0].expand() - fileView1 = treeView.find('.file:contains(test-file1.txt)') - fileView2 = treeView.find('.file:contains(test-file2.txt)') - fileView3 = treeView.find('.file:contains(test-file3.txt)') + dirView = treeView.entryForPath(dirPath) + dirView.expand() + [fileView1, fileView2, fileView3] = dirView.querySelectorAll('.file') describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView2.trigger($.Event('mousedown', {shiftKey: true})) - expect(treeView.find('.tree-view')).toHaveClass('multi-select') - fileView3.trigger($.Event('mousedown')) - expect(treeView.find('.tree-view')).toHaveClass('full-menu') + fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView2.trigger($.Event('mousedown', {shiftKey: true})) + fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) expect(treeView.find('.tree-view')).toHaveClass('multi-select') describe 'using the shift key', -> it 'selects the items between the already selected item and the shift clicked item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {shiftKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView2).toHaveClass('selected') expect(fileView3).toHaveClass('selected') @@ -2866,7 +2857,7 @@ describe "TreeView", -> describe 'using the metakey(cmd) key', -> it 'selects the cmd clicked item in addition to the original selected item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {metaKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') expect(fileView2).not.toHaveClass('selected') @@ -2885,7 +2876,7 @@ describe "TreeView", -> describe 'using the ctrl key', -> it 'selects the ctrl clicked item in addition to the original selected item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') expect(fileView2).not.toHaveClass('selected') @@ -2905,87 +2896,87 @@ describe "TreeView", -> describe "previous item is selected but the ctrl clicked item is not", -> it 'selects the clicked item, but deselects the previous item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) expect(fileView1).not.toHaveClass('selected') expect(fileView3).toHaveClass('selected') expect(fileView2).not.toHaveClass('selected') it 'displays the full contextual menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) - expect(treeView.list).toHaveClass('full-menu') - expect(treeView.list).not.toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') describe 'previous item is selected including the ctrl clicked', -> it 'displays the multi-select menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {metaKey: true})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) - expect(treeView.list).not.toHaveClass('full-menu') - expect(treeView.list).toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') it 'does not deselect any of the items', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {metaKey: true})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') describe 'when clicked item is the only item selected', -> it 'displays the full contextual menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) - expect(treeView.list).toHaveClass('full-menu') - expect(treeView.list).not.toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') describe 'when no item is selected', -> it 'selects the ctrl clicked item', -> - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) expect(fileView3).toHaveClass('selected') it 'displays the full context menu', -> - fileView3.trigger($.Event('mousedown', {ctrlKey: true})) - expect(treeView.list).toHaveClass('full-menu') - expect(treeView.list).not.toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') describe "right-clicking", -> describe 'when multiple items are selected', -> it 'displays the multi-select context menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {metaKey: true})) - fileView3.trigger($.Event('mousedown', {button: 2})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') - expect(treeView.list).not.toHaveClass('full-menu') - expect(treeView.list).toHaveClass('multi-select') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') describe 'when a single item is selected', -> it 'displays the full context menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {button: 2})) - expect(treeView.list).toHaveClass('full-menu') - expect(treeView.list).not.toHaveClass('multi-select') + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') it 'selects right clicked item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {button: 2})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView3).toHaveClass('selected') it 'de-selects the previously selected item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView3.trigger($.Event('mousedown', {button: 2})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView1).not.toHaveClass('selected') describe 'when no item is selected', -> it 'selects the right clicked item', -> - fileView3.trigger($.Event('mousedown', {button: 2})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView3).toHaveClass('selected') it 'shows the full context menu', -> - fileView3.trigger($.Event('mousedown', {button: 2})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView3).toHaveClass('selected') - expect(treeView.list).toHaveClass('full-menu') - expect(treeView.list).not.toHaveClass('multi-select') + expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') describe "the sortFoldersBeforeFiles config option", -> [dirView, fileView, dirView2, fileView2, fileView3, rootDirPath, dirPath, filePath, dirPath2, filePath2, filePath3] = [] @@ -3031,16 +3022,16 @@ describe "TreeView", -> expect(topLevelEntries).toEqual(["alpha", "gamma", "alpha.txt", "zeta.txt"]) - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() - alphaEntries = [].slice.call(alphaDir[0].children[1].children).map (element) -> + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + alphaEntries = [].slice.call(alphaDir.children[1].children).map (element) -> element.innerText expect(alphaEntries).toEqual(["eta", "beta.txt"]) - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - gammaEntries = [].slice.call(gammaDir[0].children[1].children).map (element) -> + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + gammaEntries = [].slice.call(gammaDir.children[1].children).map (element) -> element.innerText expect(gammaEntries).toEqual(["theta", "delta.txt", "epsilon.txt"]) @@ -3053,16 +3044,16 @@ describe "TreeView", -> expect(topLevelEntries).toEqual(["alpha", "alpha.txt", "gamma", "zeta.txt"]) - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() - alphaEntries = [].slice.call(alphaDir[0].children[1].children).map (element) -> + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + alphaEntries = [].slice.call(alphaDir.children[1].children).map (element) -> element.innerText expect(alphaEntries).toEqual(["beta.txt", "eta"]) - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - gammaEntries = [].slice.call(gammaDir[0].children[1].children).map (element) -> + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + gammaEntries = [].slice.call(gammaDir.children[1].children).map (element) -> element.innerText expect(gammaEntries).toEqual(["delta.txt", "epsilon.txt", "theta"]) @@ -3123,7 +3114,7 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open() runs -> - $(workspaceElement).focus() + workspaceElement.focus() expect(treeView.showCurrentFileInFileManager()).toBeUndefined() it "shows file in file manager when some file is opened", -> @@ -3205,14 +3196,15 @@ describe "TreeView", -> describe "when dragging a FileView onto a DirectoryView's header", -> it "should add the selected class to the DirectoryView", -> # Dragging theta onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - deltaFile = gammaDir[0].entries.children[2] + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + deltaFile = gammaDir.entries.children[2] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0]) + eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header')) treeView.onDragStart(dragStartEvent) treeView.onDragEnter(dragEnterEvent) expect(alphaDir).toHaveClass('selected') @@ -3228,104 +3220,104 @@ describe "TreeView", -> describe "when dropping a FileView onto a DirectoryView's header", -> it "should move the file to the hovered directory", -> # Dragging delta.txt onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - deltaFile = gammaDir[0].entries.children[2] + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + deltaFile = gammaDir.entries.children[2] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0], alphaDir[0]) + eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header'), alphaDir) runs -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) - expect(alphaDir[0].children.length).toBe 2 + expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 runs -> - expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 describe "when dropping a DirectoryView onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> # Dragging thetaDir onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - thetaDir = gammaDir[0].entries.children[0] + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + thetaDir = gammaDir.entries.children[0] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.find('.header')[0], alphaDir[0]) + eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.querySelector('.header'), alphaDir) runs -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) - expect(alphaDir[0].children.length).toBe 2 + expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 runs -> - expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 describe "when dragging a file from the OS onto a DirectoryView's header", -> it "should move the file to the hovered directory", -> # Dragging delta.txt from OS file explorer onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir[0]) + dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir) runs -> treeView.onDrop(dropEvent) - expect(alphaDir[0].children.length).toBe 2 + expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 runs -> - expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 describe "when dragging a directory from the OS onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> # Dragging gammaDir from OS file explorer onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - dropEvent = eventHelpers.buildExternalDropEvent([gammaDirPath], alphaDir[0]) + dropEvent = eventHelpers.buildExternalDropEvent([gammaDirPath], alphaDir) runs -> treeView.onDrop(dropEvent) - expect(alphaDir[0].children.length).toBe 2 + expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 runs -> - expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 describe "when dragging a file and directory from the OS onto a DirectoryView's header", -> it "should move the file and directory to the hovered directory", -> # Dragging delta.txt and gammaDir from OS file explorer onto alphaDir - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir[0]) + dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir) runs -> treeView.onDrop(dropEvent) - expect(alphaDir[0].children.length).toBe 2 + expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 3 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 3 runs -> - expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4 describe "the alwaysOpenExisting config option", -> it "defaults to unset", -> @@ -3340,21 +3332,21 @@ describe "TreeView", -> treeView.focus() waitsForFileToOpen -> - sampleJs.trigger clickEvent(originalEvent: {detail: 1}) + sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> expect(sampleJs).toHaveClass 'selected' expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(treeView.list).toHaveFocus() + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() waitsForFileToOpen -> - sampleTxt.trigger clickEvent(originalEvent: {detail: 1}) + sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> expect(sampleTxt).toHaveClass 'selected' - expect(treeView.find('.selected').length).toBe 1 + expect(treeView.element.querySelectorAll('.selected').length).toBe 1 expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') - expect(treeView.list).toHaveFocus() + expect(treeView.element.querySelector('.tree-view')).toHaveFocus() describe "opening existing opened files in existing split panes", -> beforeEach -> @@ -3420,7 +3412,7 @@ describe "TreeView", -> treeView.focus() waitsForFileToOpen -> - sampleTxt.trigger clickEvent(originalEvent: {detail: 1}) + sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> activePaneItem = atom.workspace.getActivePaneItem() @@ -3445,8 +3437,8 @@ describe "TreeView", -> treeView.focus() waitsForFileToOpen -> - sampleTxt.trigger clickEvent(originalEvent: {detail: 1}) - sampleTxt.trigger clickEvent(originalEvent: {detail: 2}) + sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) waits 100 @@ -3499,14 +3491,14 @@ describe "TreeView", -> describe "when dragging on the top part of the root", -> it "should add the placeholder above the directory", -> # Dragging gammaDir onto alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - gammaDir = $(treeView).find('.project-root:contains(gamma):first') + alphaDir = treeView.roots[0] + gammaDir = treeView.roots[1] [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') + eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDragOver(dragOverEvents.top) - expect(alphaDir[0].previousSibling).toHaveClass('placeholder') + expect(alphaDir.previousSibling).toHaveClass('placeholder') # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) @@ -3515,14 +3507,14 @@ describe "TreeView", -> describe "when dragging on the bottom part of the root", -> it "should add the placeholder below the directory", -> # Dragging gammaDir onto alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - gammaDir = $(treeView).find('.project-root:contains(gamma):first') + alphaDir = treeView.roots[0] + gammaDir = treeView.roots[1] [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') + eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom) - expect(alphaDir[0].nextSibling).toHaveClass('placeholder') + expect(alphaDir.nextSibling).toHaveClass('placeholder') # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) @@ -3531,16 +3523,16 @@ describe "TreeView", -> describe "when below all entries", -> it "should add the placeholder below the last directory", -> # Dragging gammaDir onto alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - lastDir = $(treeView).find('.project-root:last') + alphaDir = treeView.roots[0] + lastDir = treeView.roots[treeView.roots.length - 1] [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(alphaDir.find('.project-root-header')[0], treeView.find('.tree-view')[0]) + eventHelpers.buildPositionalDragEvents(alphaDir.querySelector('.project-root-header'), treeView.element.querySelector('.tree-view')) - expect(alphaDir[0]).not.toEqual(lastDir[0]) + expect(alphaDir).not.toEqual(lastDir) treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom) - expect(lastDir[0].nextSibling).toHaveClass('placeholder') + expect(lastDir.nextSibling).toHaveClass('placeholder') # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) @@ -3551,10 +3543,10 @@ describe "TreeView", -> describe "when dropping on the top part of the header", -> it "should add the placeholder above the directory", -> # dropping gammaDir above alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - gammaDir = $(treeView).find('.project-root:contains(gamma):first') + alphaDir = treeView.roots[0] + gammaDir = treeView.roots[1] [dragStartEvent, dragDropEvents] = - eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') + eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDrop(dragDropEvents.top) @@ -3568,10 +3560,10 @@ describe "TreeView", -> describe "when dropping on the bottom part of the header", -> it "should add the placeholder below the directory", -> # dropping thetaDir below alphaDir - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - thetaDir = $(treeView).find('.project-root:contains(theta):first') + alphaDir = treeView.roots[0] + thetaDir = treeView.roots[2] [dragStartEvent, dragDropEvents] = - eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view') + eventHelpers.buildPositionalDragEvents(thetaDir.querySelector('.project-root-header'), alphaDir, '.tree-view') treeView.rootDragAndDrop.onDragStart(dragStartEvent) treeView.rootDragAndDrop.onDrop(dragDropEvents.bottom) @@ -3585,8 +3577,8 @@ describe "TreeView", -> describe "when a root folder is dragged out of application", -> it "should carry the folder's information", -> - gammaDir = $(treeView).find('.project-root:contains(gamma):first') - [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) + gammaDir = treeView.roots[1] + [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header')) treeView.rootDragAndDrop.onDragStart(dragStartEvent) expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath @@ -3595,8 +3587,8 @@ describe "TreeView", -> describe "when a root folder is dropped from another Atom window", -> it "adds the root folder to the window", -> - alphaDir = $(treeView).find('.project-root:contains(alpha):first') - [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0], '.tree-view') + alphaDir = treeView.roots[0] + [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.querySelector('.project-root-header'), '.tree-view') dropEvent = dragDropEvents.bottom dropEvent.originalEvent.dataTransfer.setData('atom-tree-view-event', true) @@ -3620,14 +3612,21 @@ describe "TreeView", -> describe "when a root folder is dropped to another Atom window", -> it "removes the root folder from the first window", -> - gammaDir = $(treeView).find('.project-root:contains(gamma):first') - [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0]) + gammaDir = treeView.roots[1] + [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header')) treeView.rootDragAndDrop.onDragStart(dragStartEvent) - treeView.rootDragAndDrop.onDropOnOtherWindow({}, gammaDir.index()) + treeView.rootDragAndDrop.onDropOnOtherWindow({}, Array.from(gammaDir.parentElement.children).indexOf(gammaDir)) expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] expect('.placeholder').not.toExist() + findDirectoryContainingText = (element, text) -> + directories = Array.from(element.querySelectorAll('.entries .directory')) + directories.find((directory) -> directory.header.textContent is text) + + findFileContainingText = (element, text) -> + files = Array.from(element.querySelectorAll('.entries .file')) + files.find((file) -> file.fileName.textContent is text) describe 'Icon class handling', -> it 'allows multiple classes to be passed', -> From 6bb458d95272e6022b7111482d349c8f27d96a8c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 10:18:55 +0100 Subject: [PATCH 125/263] :art: --- spec/tree-view-spec.coffee | 58 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 022b2a4e..0e712ac8 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -225,7 +225,7 @@ describe "TreeView", -> it "restores the focus state of the tree view", -> jasmine.attachToDOM(workspaceElement) treeView.focus() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") atom.packages.deactivatePackage("tree-view") waitsForPromise -> @@ -233,7 +233,7 @@ describe "TreeView", -> runs -> treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") it "restores the scroll top when toggled", -> workspaceElement.style.height = '5px' @@ -294,7 +294,7 @@ describe "TreeView", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') expect(treeView.hasParent()).toBeTruthy() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "when tree-view:toggle-side is triggered on the root view", -> describe "when the tree view is on the left", -> @@ -330,7 +330,7 @@ describe "TreeView", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView.hasParent()).toBeTruthy() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "when the tree view is shown", -> it "focuses the tree view", -> @@ -342,7 +342,7 @@ describe "TreeView", -> expect(treeView).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView).toBeVisible() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "when the tree view is focused", -> it "unfocuses the tree view", -> @@ -354,7 +354,7 @@ describe "TreeView", -> expect(treeView).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') expect(treeView).toBeVisible() - expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus() + expect(treeView.list).not.toMatchSelector(":focus") describe "when tree-view:reveal-active-file is triggered on the root view", -> beforeEach -> @@ -464,10 +464,10 @@ describe "TreeView", -> runs -> jasmine.attachToDOM(workspaceElement) treeView.focus() - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') expect(treeView).toBeVisible() - expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus() + expect(treeView.list).not.toMatchSelector(":focus") expect(atom.workspace.getActivePane().isActive()).toBe(true) describe "copy path commands", -> @@ -2132,7 +2132,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch moveDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.find(".tree-view")).toHaveFocus() + expect(treeView.querySelector(".tree-view")).toHaveFocus() describe "when the move dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2250,7 +2250,7 @@ describe "TreeView", -> jasmine.attachToDOM(treeView.element) atom.commands.dispatch copyDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.find(".tree-view")).toHaveFocus() + expect(treeView.querySelector(".tree-view")).toHaveFocus() describe "when the duplicate dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2836,9 +2836,9 @@ describe "TreeView", -> it 'switches the contextual menu to muli-select mode', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') + expect(treeView.list).toHaveClass('multi-select') fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') + expect(treeView.list).toHaveClass('full-menu') describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> @@ -2904,16 +2904,16 @@ describe "TreeView", -> it 'displays the full contextual menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') describe 'previous item is selected including the ctrl clicked', -> it 'displays the multi-select menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') + expect(treeView.list).not.toHaveClass('full-menu') + expect(treeView.list).toHaveClass('multi-select') it 'does not deselect any of the items', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -2926,8 +2926,8 @@ describe "TreeView", -> it 'displays the full contextual menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') describe 'when no item is selected', -> it 'selects the ctrl clicked item', -> @@ -2936,8 +2936,8 @@ describe "TreeView", -> it 'displays the full context menu', -> fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') describe "right-clicking", -> describe 'when multiple items are selected', -> @@ -2947,15 +2947,15 @@ describe "TreeView", -> fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView1).toHaveClass('selected') expect(fileView3).toHaveClass('selected') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select') + expect(treeView.list).not.toHaveClass('full-menu') + expect(treeView.list).toHaveClass('multi-select') describe 'when a single item is selected', -> it 'displays the full context menu', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') it 'selects right clicked item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -2975,8 +2975,8 @@ describe "TreeView", -> it 'shows the full context menu', -> fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2})) expect(fileView3).toHaveClass('selected') - expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu') - expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select') + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') describe "the sortFoldersBeforeFiles config option", -> [dirView, fileView, dirView2, fileView2, fileView3, rootDirPath, dirPath, filePath, dirPath2, filePath2, filePath3] = [] @@ -3337,7 +3337,7 @@ describe "TreeView", -> runs -> expect(sampleJs).toHaveClass 'selected' expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") waitsForFileToOpen -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -3346,7 +3346,7 @@ describe "TreeView", -> expect(sampleTxt).toHaveClass 'selected' expect(treeView.element.querySelectorAll('.selected').length).toBe 1 expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') - expect(treeView.element.querySelector('.tree-view')).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "opening existing opened files in existing split panes", -> beforeEach -> @@ -3422,7 +3422,7 @@ describe "TreeView", -> expect(treeView).toHaveFocus() it "doesn't open the file in the active pane", -> - expect(atom.views.getView(treeView)).toHaveFocus() + expect(atom.views.getView(treeView).element).toHaveFocus() expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') describe "when a file is double-clicked", -> From e61908f484c02dbcf6a067cb3b1aff812ffa0311 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 10:41:38 +0100 Subject: [PATCH 126/263] Start fixing actual failures on Windows --- spec/tree-view-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 0e712ac8..c492b20f 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -1852,7 +1852,7 @@ describe "TreeView", -> dirView3.entries.querySelectorAll(".file").length > 1 runs -> - expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) + expect(treeView.element.querySelector('.file.selected').textContent).toBe path.basename(newPath) describe "when a file already exists at that location", -> it "shows an error message and does not close the dialog", -> @@ -2132,7 +2132,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch moveDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.querySelector(".tree-view")).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "when the move dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2250,7 +2250,7 @@ describe "TreeView", -> jasmine.attachToDOM(treeView.element) atom.commands.dispatch copyDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.querySelector(".tree-view")).toHaveFocus() + expect(treeView.list).toMatchSelector(":focus") describe "when the duplicate dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> From 8e75f7af9ce344f0bcd6046aa70415b4da367531 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 10:57:36 +0100 Subject: [PATCH 127/263] Fix more failing tests on Windows --- spec/tree-view-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index c492b20f..d996fcd6 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2557,7 +2557,7 @@ describe "TreeView", -> it "squashes two dir names when the first only contains a single dir", -> if path.sep is '\\' # First escape the backslashes for Coffeescript, then escape them for jQuery - betaDir = findDirectoryContainingText(treeView.roots[0], "alpha\\\\beta") + betaDir = findDirectoryContainingText(treeView.roots[0], "alpha\\beta") else betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta") betaDir.expand() @@ -2569,7 +2569,7 @@ describe "TreeView", -> it "squashes three dir names when the first and second only contain single dirs", -> if path.sep is '\\' # First escape the backslashes for Coffeescript, then escape them for jQuery - epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma\\\\delta\\\\epsilon") + epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma\\delta\\epsilon") else epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon") epsilonDir.expand() From d281e643c398bd713d1cf754124e0196b7e1d315 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 10:59:03 +0100 Subject: [PATCH 128/263] :art: --- spec/tree-view-spec.coffee | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index d996fcd6..086e9997 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2555,11 +2555,7 @@ describe "TreeView", -> expect(zetaEntries).toEqual(["zeta.txt"]) it "squashes two dir names when the first only contains a single dir", -> - if path.sep is '\\' - # First escape the backslashes for Coffeescript, then escape them for jQuery - betaDir = findDirectoryContainingText(treeView.roots[0], "alpha\\beta") - else - betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta") + betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta") betaDir.expand() betaEntries = [].slice.call(betaDir.children[1].children).map (element) -> element.innerText @@ -2567,11 +2563,7 @@ describe "TreeView", -> expect(betaEntries).toEqual(["beta.txt"]) it "squashes three dir names when the first and second only contain single dirs", -> - if path.sep is '\\' - # First escape the backslashes for Coffeescript, then escape them for jQuery - epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma\\delta\\epsilon") - else - epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon") + epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon") epsilonDir.expand() epsilonEntries = [].slice.call(epsilonDir.children[1].children).map (element) -> element.innerText From 4a66cf56658c724c2bcec103bf9ee43c59761b25 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 11:09:23 +0100 Subject: [PATCH 129/263] Fix last failing tests on Windows --- spec/tree-view-spec.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 086e9997..2f27ec08 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2739,8 +2739,12 @@ describe "TreeView", -> fs.writeFileSync(modifiedFile, originalFileContent) atom.project.getRepositories()[0].getPathStatus(modifiedFile) - expect(treeView.element.querySelector('.file.status-modified')).not.toExist() - expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() + waitsFor -> + not treeView.element.querySelector('.file.status-modified') + + runs -> + expect(treeView.element.querySelector('.file.status-modified')).not.toExist() + expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() describe "when the resize handle is double clicked", -> beforeEach -> @@ -3414,7 +3418,7 @@ describe "TreeView", -> expect(treeView).toHaveFocus() it "doesn't open the file in the active pane", -> - expect(atom.views.getView(treeView).element).toHaveFocus() + expect(atom.views.getView(treeView)).toHaveFocus() expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') describe "when a file is double-clicked", -> From 7db1fc621d2c97dcff4a6862e336a8abbf7268fc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Jan 2017 11:12:43 +0100 Subject: [PATCH 130/263] Run symlink test only on Darwin --- spec/tree-view-spec.coffee | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 2f27ec08..7c260d86 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2717,7 +2717,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(dirView.directory.updateStatus).toHaveBeenCalled() - describe "when the project is a symbolic link to the repository root", -> + describe "on #darwin, when the project is a symbolic link to the repository root", -> beforeEach -> symlinkPath = temp.path('tree-view-project') fs.symlinkSync(projectPath, symlinkPath, 'junction') @@ -2739,12 +2739,8 @@ describe "TreeView", -> fs.writeFileSync(modifiedFile, originalFileContent) atom.project.getRepositories()[0].getPathStatus(modifiedFile) - waitsFor -> - not treeView.element.querySelector('.file.status-modified') - - runs -> - expect(treeView.element.querySelector('.file.status-modified')).not.toExist() - expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() + expect(treeView.element.querySelector('.file.status-modified')).not.toExist() + expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() describe "when the resize handle is double clicked", -> beforeEach -> From ad4e36607269c1056ad5247ed0d04bd741fb13c6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 1 Feb 2017 14:04:30 +0100 Subject: [PATCH 131/263] :arrow_up: pathwatcher 6.8.0-1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 52d4423f..717cdf3f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "event-kit": "^1.0.0", "fs-plus": "^2.3.0", "minimatch": "~0.3.0", - "pathwatcher": "^6.2", + "pathwatcher": "6.8.0-1", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From 620b2d17d85c4551deea2acb6fe12c8d058e394a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 1 Feb 2017 14:25:10 +0100 Subject: [PATCH 132/263] Fix tests on Windows --- spec/tree-view-spec.coffee | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6f9941d1..cac633d0 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2598,7 +2598,12 @@ describe "TreeView", -> describe "when a squashed directory is deleted", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + if path.sep is '\\' + # First escape the backslashes for Coffeescript, then escape them for jQuery + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron\\\\pi):first")[0] + else + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + treeView.focus() treeView.selectEntry(piDir) spyOn(atom, 'confirm').andCallFake (dialog) -> @@ -2611,7 +2616,11 @@ describe "TreeView", -> describe "when a file is created within a directory with another squashed directory", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + if path.sep is '\\' + # First escape the backslashes for Coffeescript, then escape them for jQuery + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron\\\\pi):first")[0] + else + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" @@ -2630,7 +2639,11 @@ describe "TreeView", -> describe "when a directory is created within a directory with another squashed directory", -> it "un-squashes the directories", -> jasmine.attachToDOM(workspaceElement) - piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] + if path.sep is '\\' + # First escape the backslashes for Coffeescript, then escape them for jQuery + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron\\\\pi):first")[0] + else + piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0] expect(piDir).not.toBeNull() # omicron is a squashed dir, so searching for omicron would give us omicron/pi instead omicronPath = piDir.getPath().replace "#{path.sep}pi", "" @@ -2735,7 +2748,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(dirView[0].directory.updateStatus).toHaveBeenCalled() - describe "when the project is a symbolic link to the repository root", -> + describe "on #darwin, when the project is a symbolic link to the repository root", -> beforeEach -> symlinkPath = temp.path('tree-view-project') fs.symlinkSync(projectPath, symlinkPath, 'junction') @@ -3113,7 +3126,6 @@ describe "TreeView", -> runs -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in OS file manager failed' - expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT' describe "showCurrentFileInFileManager()", -> it "does nothing when no file is opened", -> From 48d10d8e53706ca78b3f22fa53c42e4b261b15be Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Feb 2017 09:59:42 +0100 Subject: [PATCH 133/263] :arrow_up: pathwatcher 6.8.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 717cdf3f..9b90367b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "event-kit": "^1.0.0", "fs-plus": "^2.3.0", "minimatch": "~0.3.0", - "pathwatcher": "6.8.0-1", + "pathwatcher": "6.8.1", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From 21133885c8c3a03034e1221a616a1b3e3e44908b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Feb 2017 13:51:26 +0100 Subject: [PATCH 134/263] Stop using space-pen in dialogs --- lib/add-dialog.coffee | 10 +++- lib/dialog.coffee | 61 ++++++++++++------- lib/tree-view.coffee | 15 ++--- spec/tree-view-spec.coffee | 118 ++++++++++++++++++------------------- 4 files changed, 113 insertions(+), 91 deletions(-) diff --git a/lib/add-dialog.coffee b/lib/add-dialog.coffee index d29ad748..c8fdbb12 100644 --- a/lib/add-dialog.coffee +++ b/lib/add-dialog.coffee @@ -23,6 +23,12 @@ class AddDialog extends Dialog select: false iconClass: if isCreatingFile then 'icon-file-add' else 'icon-file-directory-create' + onDidCreateFile: (callback) -> + @emitter.on('did-create-file', callback) + + onDidCreateDirectory: (callback) -> + @emitter.on('did-create-directory', callback) + onConfirm: (newPath) -> newPath = newPath.replace(/\s+$/, '') # Remove trailing whitespace endsWithDirectorySeparator = newPath[newPath.length - 1] is path.sep @@ -44,11 +50,11 @@ class AddDialog extends Dialog else fs.writeFileSync(newPath, '') repoForPath(newPath)?.getPathStatus(newPath) - @trigger 'file-created', [newPath] + @emitter.emit('did-create-file', newPath) @close() else fs.makeTreeSync(newPath) - @trigger 'directory-created', [newPath] + @emitter.emit('did-create-directory', newPath) @cancel() catch error @showError("#{error.message}.") diff --git a/lib/dialog.coffee b/lib/dialog.coffee index 9931f4f1..1ab74a85 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -1,48 +1,69 @@ -{$, TextEditorView, View} = require 'atom-space-pen-views' +{TextEditor, CompositeDisposable, Disposable, Emitter, Range, Point} = require 'atom' path = require 'path' module.exports = -class Dialog extends View - @content: ({prompt} = {}) -> - @div class: 'tree-view-dialog', => - @label prompt, class: 'icon', outlet: 'promptText' - @subview 'miniEditor', new TextEditorView(mini: true) - @div class: 'error-message', outlet: 'errorMessage' - - initialize: ({initialPath, select, iconClass} = {}) -> - @promptText.addClass(iconClass) if iconClass +class Dialog + constructor: ({initialPath, select, iconClass, prompt} = {}) -> + @emitter = new Emitter() + @disposables = new CompositeDisposable() + + @element = document.createElement('div') + @element.classList.add('tree-view-dialog') + + @promptText = document.createElement('label') + @promptText.classList.add('icon') + @promptText.classList.add(iconClass) if iconClass + @promptText.textContent = prompt + @element.appendChild(@promptText) + + @miniEditor = new TextEditor({mini: true}) + blurHandler = => + @close() if document.hasFocus() + @miniEditor.element.addEventListener('blur', blurHandler) + @disposables.add(new Disposable(=> @miniEditor.element.removeEventListener('blur', blurHandler))) + @disposables.add(@miniEditor.onDidChange => @showError()) + @element.appendChild(@miniEditor.element) + + @errorMessage = document.createElement('div') + @errorMessage.classList.add('error-message') + @element.appendChild(@errorMessage) + atom.commands.add @element, 'core:confirm': => @onConfirm(@miniEditor.getText()) 'core:cancel': => @cancel() - @miniEditor.on 'blur', => @close() if document.hasFocus() - @miniEditor.getModel().onDidChange => @showError() - @miniEditor.getModel().setText(initialPath) + + @miniEditor.setText(initialPath) if select extension = path.extname(initialPath) baseName = path.basename(initialPath) + selectionStart = initialPath.length - baseName.length if baseName is extension selectionEnd = initialPath.length else selectionEnd = initialPath.length - extension.length - range = [[0, initialPath.length - baseName.length], [0, selectionEnd]] - @miniEditor.getModel().setSelectedBufferRange(range) + @miniEditor.setSelectedBufferRange(Range(Point(0, selectionStart), Point(0, selectionEnd))) attach: -> @panel = atom.workspace.addModalPanel(item: this) - @miniEditor.focus() - @miniEditor.getModel().scrollToCursorPosition() + @miniEditor.element.focus() + @miniEditor.scrollToCursorPosition() close: -> panelToDestroy = @panel @panel = null panelToDestroy?.destroy() + @emitter.dispose() + @disposables.dispose() + @miniEditor.destroy() atom.workspace.getActivePane().activate() cancel: -> @close() - $('.tree-view').focus() + document.querySelector('.tree-view').focus() showError: (message='') -> - @errorMessage.text(message) - @flashError() if message + @errorMessage.textContent = message + if message + @element.classList.add('error') + window.setTimeout((=> @element.classList.remove('error')), 300) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 50c85e81..347c2e49 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -7,9 +7,9 @@ _ = require 'underscore-plus' {$, View} = require 'atom-space-pen-views' fs = require 'fs-plus' -AddDialog = null # Defer requiring until actually needed -MoveDialog = null # Defer requiring until actually needed -CopyDialog = null # Defer requiring until actually needed +AddDialog = require './add-dialog' +MoveDialog = require './move-dialog' +CopyDialog = require './copy-dialog' Minimatch = null # Defer requiring until actually needed Directory = require './directory' @@ -454,7 +454,6 @@ class TreeView extends View oldPath = @getActivePath() if oldPath - MoveDialog ?= require './move-dialog' dialog = new MoveDialog(oldPath) dialog.attach() @@ -542,7 +541,6 @@ class TreeView extends View oldPath = @getActivePath() return unless oldPath - CopyDialog ?= require './copy-dialog' dialog = new CopyDialog(oldPath) dialog.attach() @@ -675,17 +673,14 @@ class TreeView extends View selectedEntry = @selectedEntry() ? @roots[0] selectedPath = selectedEntry?.getPath() ? '' - AddDialog ?= require './add-dialog' dialog = new AddDialog(selectedPath, isCreatingFile) - dialog.on 'directory-created', (event, createdPath) => + dialog.onDidCreateDirectory (createdPath) => @entryForPath(createdPath)?.reload() @selectEntryForPath(createdPath) @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') - false - dialog.on 'file-created', (event, createdPath) => + dialog.onDidCreateFile (createdPath) => atom.workspace.open(createdPath) @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') - false dialog.attach() removeProjectFolder: (e) -> diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 84b9e314..66103499 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -217,7 +217,7 @@ describe "TreeView", -> runs -> treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView).toExist() + expect(treeView.element).toExist() expect(treeView.selectedEntry().textContent).toBe('tree-view.js') root1 = treeView.roots[0] expect(root1.querySelector(".directory")).toHaveClass("expanded") @@ -1798,12 +1798,12 @@ describe "TreeView", -> describe "when a file is selected", -> it "opens an add dialog with the file's current directory path populated", -> - expect(addDialog).toExist() - expect(addDialog.promptText.text()).toBeTruthy() + expect(addDialog.element).toExist() + expect(addDialog.promptText.textContent).toBeTruthy() expect(atom.project.relativize(dirPath)).toMatch(/[^\\\/]$/) expect(addDialog.miniEditor.getText()).toBe(atom.project.relativize(dirPath) + path.sep) - expect(addDialog.miniEditor.getModel().getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length - expect(addDialog.miniEditor).toHaveFocus() + expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length + expect(addDialog.miniEditor.element).toHaveFocus() describe "when the parent directory of the selected file changes", -> it "still shows the active file as selected", -> @@ -1816,7 +1816,7 @@ describe "TreeView", -> newPath = path.join(dirPath, "new-test-file.txt") waitsForFileToOpen -> - addDialog.miniEditor.getModel().insertText(path.basename(newPath)) + addDialog.miniEditor.insertText(path.basename(newPath)) atom.commands.dispatch addDialog.element, 'core:confirm' runs -> @@ -1840,7 +1840,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() - addDialog.miniEditor.getModel().insertText(path.basename(newPath)) + addDialog.miniEditor.insertText(path.basename(newPath)) atom.commands.dispatch addDialog.element, 'core:confirm' runs -> @@ -1858,11 +1858,11 @@ describe "TreeView", -> it "shows an error message and does not close the dialog", -> newPath = path.join(dirPath, "new-test-file.txt") fs.writeFileSync(newPath, '') - addDialog.miniEditor.getModel().insertText(path.basename(newPath)) + addDialog.miniEditor.insertText(path.basename(newPath)) atom.commands.dispatch addDialog.element, 'core:confirm' - expect(addDialog.errorMessage.text()).toContain 'already exists' - expect(addDialog).toHaveClass('error') + expect(addDialog.errorMessage.textContent).toContain 'already exists' + expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel describe "when the project has no path", -> @@ -1874,7 +1874,7 @@ describe "TreeView", -> addDialog = addPanel.getItem() newPath = path.join(fs.realpathSync(temp.mkdirSync()), 'a-file') - addDialog.miniEditor.getModel().insertText(newPath) + addDialog.miniEditor.insertText(newPath) waitsForFileToOpen -> atom.commands.dispatch addDialog.element, 'core:confirm' @@ -1886,11 +1886,11 @@ describe "TreeView", -> describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> it "shows an error message and does not close the dialog", -> - addDialog.miniEditor.getModel().insertText("new-test-file" + path.sep) + addDialog.miniEditor.insertText("new-test-file" + path.sep) atom.commands.dispatch addDialog.element, 'core:confirm' - expect(addDialog.errorMessage.text()).toContain 'names must not end with' - expect(addDialog).toHaveClass('error') + expect(addDialog.errorMessage.textContent).toContain 'names must not end with' + expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel describe "when 'core:cancel' is triggered on the add dialog", -> @@ -1908,7 +1908,7 @@ describe "TreeView", -> describe "when the path ends with whitespace", -> it "removes the trailing whitespace before creating the file", -> newPath = path.join(dirPath, "new-test-file.txt") - addDialog.miniEditor.getModel().insertText(path.basename(newPath) + " ") + addDialog.miniEditor.insertText(path.basename(newPath) + " ") waitsForFileToOpen -> atom.commands.dispatch addDialog.element, 'core:confirm' @@ -1924,12 +1924,12 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, "tree-view:add-file") addDialog = atom.workspace.getModalPanels()[0].getItem() - expect(addDialog).toExist() - expect(addDialog.promptText.text()).toBeTruthy() + expect(addDialog.element).toExist() + expect(addDialog.promptText.textContent).toBeTruthy() expect(atom.project.relativize(dirPath)).toMatch(/[^\\\/]$/) expect(addDialog.miniEditor.getText()).toBe(atom.project.relativize(dirPath) + path.sep) - expect(addDialog.miniEditor.getModel().getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length - expect(addDialog.miniEditor).toHaveFocus() + expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length + expect(addDialog.miniEditor.element).toHaveFocus() describe "when the root directory is selected", -> it "opens an add dialog with no path populated", -> @@ -1958,7 +1958,7 @@ describe "TreeView", -> atom.commands.dispatch(workspaceElement, "tree-view:add-folder") [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() - addDialog.miniEditor.getModel().insertText("a-file") + addDialog.miniEditor.insertText("a-file") atom.commands.dispatch(addDialog.element, 'core:confirm') expect(addDialog.element.textContent).toContain("You must open a directory to create a file with a relative path") @@ -1978,18 +1978,18 @@ describe "TreeView", -> describe "when a file is selected", -> it "opens an add dialog with the file's current directory path populated", -> - expect(addDialog).toExist() - expect(addDialog.promptText.text()).toBeTruthy() + expect(addDialog.element).toExist() + expect(addDialog.promptText.textContent).toBeTruthy() expect(atom.project.relativize(dirPath)).toMatch(/[^\\\/]$/) expect(addDialog.miniEditor.getText()).toBe(atom.project.relativize(dirPath) + path.sep) - expect(addDialog.miniEditor.getModel().getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length - expect(addDialog.miniEditor).toHaveFocus() + expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length + expect(addDialog.miniEditor.element).toHaveFocus() describe "when the path without a trailing '#{path.sep}' is changed and confirmed", -> describe "when no directory exists at the given path", -> it "adds a directory and closes the dialog", -> newPath = path.join(dirPath, 'new', 'dir') - addDialog.miniEditor.getModel().insertText("new#{path.sep}dir") + addDialog.miniEditor.insertText("new#{path.sep}dir") atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 @@ -2001,7 +2001,7 @@ describe "TreeView", -> describe "when no directory exists at the given path", -> it "adds a directory and closes the dialog", -> newPath = path.join(dirPath, 'new', 'dir') - addDialog.miniEditor.getModel().insertText("new#{path.sep}dir#{path.sep}") + addDialog.miniEditor.insertText("new#{path.sep}dir#{path.sep}") atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 @@ -2018,7 +2018,7 @@ describe "TreeView", -> expandedView.expand() newPath = path.join(dirPath, "new2") + path.sep - addDialog.miniEditor.getModel().insertText("new2#{path.sep}") + addDialog.miniEditor.insertText("new2#{path.sep}") atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 @@ -2035,9 +2035,9 @@ describe "TreeView", -> [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() - expect(addDialog.miniEditor.getModel().getText()).toBe '' + expect(addDialog.miniEditor.getText()).toBe '' newPath = temp.path() - addDialog.miniEditor.getModel().insertText(newPath) + addDialog.miniEditor.insertText(newPath) atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 @@ -2046,11 +2046,11 @@ describe "TreeView", -> it "shows an error message and does not close the dialog", -> newPath = path.join(dirPath, "new-dir") fs.makeTreeSync(newPath) - addDialog.miniEditor.getModel().insertText("new-dir#{path.sep}") + addDialog.miniEditor.insertText("new-dir#{path.sep}") atom.commands.dispatch addDialog.element, 'core:confirm' - expect(addDialog.errorMessage.text()).toContain 'already exists' - expect(addDialog).toHaveClass('error') + expect(addDialog.errorMessage.textContent).toContain 'already exists' + expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel describe "tree-view:move", -> @@ -2073,11 +2073,11 @@ describe "TreeView", -> 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.promptText.text()).toBe "Enter the new path for the file." + expect(moveDialog.element).toExist() + expect(moveDialog.promptText.textContent).toBe "Enter the new path for the file." expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(filePath)) - expect(moveDialog.miniEditor.getModel().getSelectedText()).toBe path.basename(fileNameWithoutExtension) - expect(moveDialog.miniEditor).toHaveFocus() + expect(moveDialog.miniEditor.getSelectedText()).toBe path.basename(fileNameWithoutExtension) + expect(moveDialog.miniEditor.element).toHaveFocus() describe "when the path is changed and confirmed", -> describe "when all the directories along the new path exist", -> @@ -2124,9 +2124,9 @@ describe "TreeView", -> atom.commands.dispatch moveDialog.element, 'core:confirm' - expect(moveDialog.errorMessage.text()).toContain 'already exists' - expect(moveDialog).toHaveClass('error') - expect(moveDialog.hasParent()).toBeTruthy() + expect(moveDialog.errorMessage.textContent).toContain 'already exists' + expect(moveDialog.element).toHaveClass('error') + expect(moveDialog.element.parentElement).toBeTruthy() describe "when 'core:cancel' is triggered on the move dialog", -> it "removes the dialog and focuses the tree view", -> @@ -2158,9 +2158,9 @@ describe "TreeView", -> moveDialog = atom.workspace.getModalPanels()[0].getItem() it "selects the entire file name", -> - expect(moveDialog).toExist() + expect(moveDialog.element).toExist() expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) - expect(moveDialog.miniEditor.getModel().getSelectedText()).toBe '.dotfile' + expect(moveDialog.miniEditor.getSelectedText()).toBe '.dotfile' describe "when the project is selected", -> it "doesn't display the move dialog", -> @@ -2188,11 +2188,11 @@ describe "TreeView", -> it "opens a copy dialog to duplicate with the file's current path populated", -> extension = path.extname(filePath) fileNameWithoutExtension = path.basename(filePath, extension) - expect(copyDialog).toExist() - expect(copyDialog.promptText.text()).toBe "Enter the new path for the duplicate." + expect(copyDialog.element).toExist() + expect(copyDialog.promptText.textContent).toBe "Enter the new path for the duplicate." expect(copyDialog.miniEditor.getText()).toBe(atom.project.relativize(filePath)) - expect(copyDialog.miniEditor.getModel().getSelectedText()).toBe path.basename(fileNameWithoutExtension) - expect(copyDialog.miniEditor).toHaveFocus() + expect(copyDialog.miniEditor.getSelectedText()).toBe path.basename(fileNameWithoutExtension) + expect(copyDialog.miniEditor.element).toHaveFocus() describe "when the path is changed and confirmed", -> describe "when all the directories along the new path exist", -> @@ -2241,9 +2241,9 @@ describe "TreeView", -> atom.commands.dispatch copyDialog.element, 'core:confirm' - expect(copyDialog.errorMessage.text()).toContain 'already exists' - expect(copyDialog).toHaveClass('error') - expect(copyDialog.hasParent()).toBeTruthy() + expect(copyDialog.errorMessage.textContent).toContain 'already exists' + expect(copyDialog.element).toHaveClass('error') + expect(copyDialog.element.parentElement).toBeTruthy() describe "when 'core:cancel' is triggered on the copy dialog", -> it "removes the dialog and focuses the tree view", -> @@ -2276,9 +2276,9 @@ describe "TreeView", -> copyDialog = atom.workspace.getModalPanels()[0].getItem() it "selects the entire file name", -> - expect(copyDialog).toExist() + expect(copyDialog.element).toExist() expect(copyDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) - expect(copyDialog.miniEditor.getModel().getSelectedText()).toBe '.dotfile' + expect(copyDialog.miniEditor.getSelectedText()).toBe '.dotfile' describe "when the project is selected", -> it "doesn't display the copy dialog", -> @@ -2403,7 +2403,7 @@ describe "TreeView", -> atom.project.setPaths([path1, path2]) treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView).toExist() + expect(treeView.element).toExist() expect(treeView.roots[0].querySelector(".directory")).toHaveClass("expanded") it "maintains collapsed (root) folders", -> @@ -2411,7 +2411,7 @@ describe "TreeView", -> atom.project.setPaths([path1, path2]) treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView).toExist() + expect(treeView.element).toExist() expect(treeView.roots[0]).toHaveClass("collapsed") describe "the hideVcsIgnoredFiles config option", -> @@ -3493,7 +3493,7 @@ describe "TreeView", -> # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when dragging on the bottom part of the root", -> it "should add the placeholder below the directory", -> @@ -3509,7 +3509,7 @@ describe "TreeView", -> # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when below all entries", -> it "should add the placeholder below the last directory", -> @@ -3527,7 +3527,7 @@ describe "TreeView", -> # Is removed when drag ends treeView.rootDragAndDrop.onDragEnd(dragEndEvent) - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when dropping a project root's header onto a different project root", -> @@ -3546,7 +3546,7 @@ describe "TreeView", -> expect(projectPaths[1]).toEqual(alphaDirPath) # Is removed when drag ends - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when dropping on the bottom part of the header", -> it "should add the placeholder below the directory", -> @@ -3564,7 +3564,7 @@ describe "TreeView", -> expect(projectPaths[2]).toEqual(gammaDirPath) # Is removed when drag ends - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when a root folder is dragged out of application", -> it "should carry the folder's information", -> @@ -3598,7 +3598,7 @@ describe "TreeView", -> runs -> expect(atom.project.getPaths()).toContain etaDirPath - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() describe "when a root folder is dropped to another Atom window", -> @@ -3609,7 +3609,7 @@ describe "TreeView", -> treeView.rootDragAndDrop.onDropOnOtherWindow({}, Array.from(gammaDir.parentElement.children).indexOf(gammaDir)) expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] - expect('.placeholder').not.toExist() + expect(document.querySelector('.placeholder')).not.toExist() findDirectoryContainingText = (element, text) -> directories = Array.from(element.querySelectorAll('.entries .directory')) From 77883b01cc9368c77415cdcb69dec2b4b205bcd1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Feb 2017 17:14:22 +0100 Subject: [PATCH 135/263] Stop using space-pen in TreeView --- lib/root-drag-and-drop.coffee | 100 ++++++++------ lib/tree-view.coffee | 251 ++++++++++++++++++++-------------- spec/event-helpers.coffee | 83 ++++++----- spec/file-stats-spec.coffee | 5 +- spec/tree-view-spec.coffee | 136 +++++++++--------- 5 files changed, 312 insertions(+), 263 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 2605d497..433d9046 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -2,7 +2,6 @@ url = require 'url' {ipcRenderer, remote} = require 'electron' -{$, View} = require 'atom-space-pen-views' _ = require 'underscore-plus' module.exports = @@ -17,32 +16,34 @@ class RootDragAndDropHandler handleEvents: -> # onDragStart is called directly by TreeView's onDragStart # will be cleaned up by tree view, since they are tree-view's handlers - @treeView.on 'dragenter', '.tree-view', @onDragEnter - @treeView.on 'dragend', '.project-root-header', @onDragEnd - @treeView.on 'dragleave', '.tree-view', @onDragLeave - @treeView.on 'dragover', '.tree-view', @onDragOver - @treeView.on 'drop', '.tree-view', @onDrop + @treeView.element.addEventListener 'dragenter', @onDragEnter + @treeView.element.addEventListener 'dragend', @onDragEnd + @treeView.element.addEventListener 'dragleave', @onDragLeave + @treeView.element.addEventListener 'dragover', @onDragOver + @treeView.element.addEventListener 'drop', @onDrop onDragStart: (e) => + return unless @treeView.list.contains(e.target) + @prevDropTargetIndex = null - e.originalEvent.dataTransfer.setData 'atom-tree-view-event', 'true' - projectRoot = $(e.target).closest('.project-root') - directory = projectRoot[0].directory + e.dataTransfer.setData 'atom-tree-view-event', 'true' + projectRoot = e.target.closest('.project-root') + directory = projectRoot.directory - e.originalEvent.dataTransfer.setData 'project-root-index', projectRoot.index() + e.dataTransfer.setData 'project-root-index', Array.from(projectRoot.parentElement.children).indexOf(projectRoot) rootIndex = -1 (rootIndex = index; break) for root, index in @treeView.roots when root.directory is directory - e.originalEvent.dataTransfer.setData 'from-root-index', rootIndex - e.originalEvent.dataTransfer.setData 'from-root-path', directory.path - e.originalEvent.dataTransfer.setData 'from-window-id', @getWindowId() + e.dataTransfer.setData 'from-root-index', rootIndex + e.dataTransfer.setData 'from-root-path', directory.path + e.dataTransfer.setData 'from-window-id', @getWindowId() - e.originalEvent.dataTransfer.setData 'text/plain', directory.path + e.dataTransfer.setData 'text/plain', directory.path if process.platform in ['darwin', 'linux'] pathUri = "file://#{directory.path}" unless @uriHasProtocol(directory.path) - e.originalEvent.dataTransfer.setData 'text/uri-list', pathUri + e.dataTransfer.setData 'text/uri-list', pathUri uriHasProtocol: (uri) -> try @@ -51,18 +52,26 @@ class RootDragAndDropHandler false onDragEnter: (e) -> + return unless @treeView.list.contains(e.target) + e.stopPropagation() onDragLeave: (e) => + return unless @treeView.list.contains(e.target) + e.stopPropagation() @removePlaceholder() if e.target is e.currentTarget onDragEnd: (e) => + return unless e.target.matches('.project-root-header') + e.stopPropagation() @clearDropTarget() onDragOver: (e) => - unless e.originalEvent.dataTransfer.getData('atom-tree-view-event') is 'true' + return unless @treeView.list.contains(e.target) + + unless e.dataTransfer.getData('atom-tree-view-event') is 'true' return e.preventDefault() @@ -71,7 +80,7 @@ class RootDragAndDropHandler entry = e.currentTarget if @treeView.roots.length is 0 - @getPlaceholder().appendTo(@treeView.list) + @treeView.list.appendChild(@getPlaceholder()) return newDropTargetIndex = @getDropTargetIndex(e) @@ -79,16 +88,16 @@ class RootDragAndDropHandler return if @prevDropTargetIndex is newDropTargetIndex @prevDropTargetIndex = newDropTargetIndex - projectRoots = $(@treeView.roots) + projectRoots = @treeView.roots if newDropTargetIndex < projectRoots.length - element = projectRoots.eq(newDropTargetIndex) - element.addClass 'is-drop-target' - @getPlaceholder().insertBefore(element) + element = projectRoots[newDropTargetIndex] + element.classList.add('is-drop-target') + element.parentElement.insertBefore(@getPlaceholder(), element) else - element = projectRoots.eq(newDropTargetIndex - 1) - element.addClass 'drop-target-is-after' - @getPlaceholder().insertAfter(element) + element = projectRoots[newDropTargetIndex - 1] + element.classList.add('drop-target-is-after') + element.parentElement.insertBefore(@getPlaceholder(), element.nextSibling) onDropOnOtherWindow: (e, fromItemIndex) => paths = atom.project.getPaths() @@ -98,16 +107,18 @@ class RootDragAndDropHandler @clearDropTarget() clearDropTarget: -> - element = @treeView.find(".is-dragging") - element.removeClass 'is-dragging' - element[0]?.updateTooltip() + element = @treeView.element.querySelector(".is-dragging") + element?.classList.remove('is-dragging') + element?.updateTooltip() @removePlaceholder() onDrop: (e) => + return unless @treeView.list.contains(e.target) + e.preventDefault() e.stopPropagation() - {dataTransfer} = e.originalEvent + {dataTransfer} = e # TODO: support dragging folders from the filesystem -- electron needs to add support first return unless dataTransfer.getData('atom-tree-view-event') is 'true' @@ -139,40 +150,41 @@ class RootDragAndDropHandler browserWindow?.webContents.send('tree-view:project-folder-dropped', fromIndex) getDropTargetIndex: (e) -> - target = $(e.target) + return if @isPlaceholder(e.target) - return if @isPlaceholder(target) + projectRoots = @treeView.roots + projectRoot = e.target.closest('.project-root') + projectRoot = projectRoots[projectRoots.length - 1] unless projectRoot - projectRoots = $(@treeView.roots) - projectRoot = target.closest('.project-root') - projectRoot = projectRoots.last() if projectRoot.length is 0 + return 0 unless projectRoot - return 0 unless projectRoot.length + projectRootIndex = @treeView.roots.indexOf(projectRoot) - center = projectRoot.offset().top + projectRoot.height() / 2 + center = projectRoot.getBoundingClientRect().top + projectRoot.offsetHeight / 2 - if e.originalEvent.pageY < center - projectRoots.index(projectRoot) - else if projectRoot.next('.project-root').length > 0 - projectRoots.index(projectRoot.next('.project-root')) + if e.pageY < center + projectRootIndex else - projectRoots.index(projectRoot) + 1 + projectRootIndex + 1 canDragStart: (e) -> - $(e.target).closest('.project-root-header').size() > 0 + e.target.closest('.project-root-header') isDragging: (e) -> - Boolean e.originalEvent.dataTransfer.getData 'from-root-path' + Boolean e.dataTransfer.getData 'from-root-path' getPlaceholder: -> - @placeholderEl ?= $('
  • ', class: 'placeholder') + unless @placeholderEl + @placeholderEl = document.createElement('li') + @placeholderEl.classList.add('placeholder') + @placeholderEl removePlaceholder: -> @placeholderEl?.remove() @placeholderEl = null isPlaceholder: (element) -> - element.is('.placeholder') + element.classList.contains('.placeholder') getWindowId: -> @processId ?= atom.getCurrentWindow().id diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 347c2e49..30a4db5f 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -4,7 +4,6 @@ path = require 'path' _ = require 'underscore-plus' {BufferedProcess, CompositeDisposable} = require 'atom' {repoForPath, getStyleObject, getFullExtension} = require "./helpers" -{$, View} = require 'atom-space-pen-views' fs = require 'fs-plus' AddDialog = require './add-dialog' @@ -16,22 +15,32 @@ Directory = require './directory' DirectoryView = require './directory-view' FileView = require './file-view' RootDragAndDrop = require './root-drag-and-drop' -LocalStorage = window.localStorage toggleConfig = (keyPath) -> atom.config.set(keyPath, not atom.config.get(keyPath)) module.exports = -class TreeView extends View +class TreeView panel: null - @content: -> - @div class: 'tree-view-resizer tool-panel', 'data-show-on-right-side': atom.config.get('tree-view.showOnRightSide'), => - @div class: 'tree-view-scroller order--center', outlet: 'scroller', => - @ol class: 'tree-view full-menu list-tree has-collapsable-children focusable-panel', tabindex: -1, outlet: 'list' - @div class: 'tree-view-resize-handle', outlet: 'resizeHandle' + constructor: (state) -> + @element = document.createElement('div') + @element.classList.add('tree-view-resizer', 'tool-panel') + @element.dataset.showOnRightSide = atom.config.get('tree-view.showOnRightSide') + + @scroller = document.createElement('div') + @scroller.classList.add('tree-view-scroller', 'order--center') + @element.appendChild(@scroller) + + @list = document.createElement('ol') + @list.classList.add('tree-view', 'full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') + @list.tabIndex = -1 + @scroller.appendChild(@list) + + @resizeHandle = document.createElement('div') + @resizeHandle.classList.add('tree-view-resize-handle') + @element.appendChild(@resizeHandle) - initialize: (state) -> @disposables = new CompositeDisposable @focusAfterAttach = false @roots = [] @@ -62,17 +71,9 @@ class TreeView extends View @scrollTopAfterAttach = state.scrollTop if state.scrollTop @scrollLeftAfterAttach = state.scrollLeft if state.scrollLeft @attachAfterProjectPathSet = state.attached and _.isEmpty(atom.project.getPaths()) - @width(state.width) if state.width > 0 + @element.style.width = "#{state.width}px" if state.width > 0 @attach() if state.attached - attached: -> - @focus() if @focusAfterAttach - @scroller.scrollLeft(@scrollLeftAfterAttach) if @scrollLeftAfterAttach > 0 - @scrollTop(@scrollTopAfterAttach) if @scrollTopAfterAttach > 0 - - detached: -> - @resizeStopped() - serialize: -> directoryExpansionStates: new ((roots) -> @[root.directory.path] = root.directory.serializeExpansionState() for root in roots @@ -80,9 +81,9 @@ class TreeView extends View selectedPath: @selectedEntry()?.getPath() hasFocus: @hasFocus() attached: @panel? - scrollLeft: @scroller.scrollLeft() - scrollTop: @scrollTop() - width: @width() + scrollLeft: @scroller.scrollLeft + scrollTop: @scroller.scrollTop + width: parseInt(@element.style.width or 0) deactivate: -> root.directory.destroy() for root in @roots @@ -91,21 +92,19 @@ class TreeView extends View @detach() if @panel? handleEvents: -> - @on 'dblclick', '.tree-view-resize-handle', => - @resizeToFitContent() - @on 'click', '.entry', (e) => + @resizeHandle.addEventListener 'dblclick', => @resizeToFitContent() + @resizeHandle.addEventListener 'mousedown', (e) => @resizeStarted(e) + @element.addEventListener 'click', (e) => # This prevents accidental collapsing when a .entries element is the event target return if e.target.classList.contains('entries') @entryClicked(e) unless e.shiftKey or e.metaKey or e.ctrlKey - @on 'mousedown', '.entry', (e) => - @onMouseDown(e) - @on 'mousedown', '.tree-view-resize-handle', (e) => @resizeStarted(e) - @on 'dragstart', '.entry', (e) => @onDragStart(e) - @on 'dragenter', '.entry.directory > .header', (e) => @onDragEnter(e) - @on 'dragleave', '.entry.directory > .header', (e) => @onDragLeave(e) - @on 'dragover', '.entry', (e) => @onDragOver(e) - @on 'drop', '.entry', (e) => @onDrop(e) + @element.addEventListener 'mousedown', (e) => @onMouseDown(e) + @element.addEventListener 'dragstart', '.entry', (e) => @onDragStart(e) + @element.addEventListener 'dragenter', (e) => @onDragEnter(e) + @element.addEventListener 'dragleave', (e) => @onDragLeave(e) + @element.addEventListener 'dragover', (e) => @onDragOver(e) + @element.addEventListener 'drop', (e) => @onDrop(e) atom.commands.add @element, 'core:move-up': @moveUp.bind(this) @@ -168,6 +167,17 @@ class TreeView extends View @attach() @focus() + isVisible: -> + not @isHidden() + + isHidden: -> + if @element.style.display is 'none' or not document.body.contains(@element) + true + else if @element.style.display + false + else + getComputedStyle(@element).display is 'none' + attach: -> return if _.isEmpty(atom.project.getPaths()) @@ -177,17 +187,22 @@ class TreeView extends View else atom.workspace.addLeftPanel(item: this) + @focus() if @focusAfterAttach + @scroller.scrollLeft = @scrollLeftAfterAttach if @scrollLeftAfterAttach > 0 + @scroller.scrollTop = @scrollTopAfterAttach if @scrollTopAfterAttach > 0 + detach: -> - @scrollLeftAfterAttach = @scroller.scrollLeft() - @scrollTopAfterAttach = @scrollTop() + @scrollLeftAfterAttach = @scroller.scrollLeft + @scrollTopAfterAttach = @scroller.scrollTop # Clean up copy and cut localStorage Variables - LocalStorage['tree-view:cutPath'] = null - LocalStorage['tree-view:copyPath'] = null + window.localStorage['tree-view:cutPath'] = null + window.localStorage['tree-view:copyPath'] = null @panel.destroy() @panel = null @unfocus() + @resizeStopped() focus: -> @list.focus() @@ -196,7 +211,7 @@ class TreeView extends View atom.workspace.getActivePane().activate() hasFocus: -> - @list.is(':focus') or document.activeElement is @list[0] + @element.contains(document.activeElement) toggleFocus: -> if @hasFocus() @@ -205,7 +220,7 @@ class TreeView extends View @show() entryClicked: (e) -> - entry = e.currentTarget + entry = e.target.closest('.entry') isRecursive = e.altKey or false @selectEntry(entry) if entry instanceof DirectoryView @@ -216,8 +231,8 @@ class TreeView extends View false fileViewEntryClicked: (e) -> - filePath = e.currentTarget.getPath() - detail = e.originalEvent?.detail ? 1 + filePath = e.target.closest('.entry').getPath() + detail = e.detail ? 1 alwaysOpenExisting = atom.config.get('tree-view.alwaysOpenExisting') if detail is 1 if atom.config.get('core.allowPendingPaneItems') @@ -234,25 +249,26 @@ class TreeView extends View atom.workspace.open(uri, options) resizeStarted: => - $(document).on('mousemove', @resizeTreeView) - $(document).on('mouseup', @resizeStopped) + document.addEventListener('mousemove', @resizeTreeView) + document.addEventListener('mouseup', @resizeStopped) resizeStopped: => - $(document).off('mousemove', @resizeTreeView) - $(document).off('mouseup', @resizeStopped) + document.removeEventListener('mousemove', @resizeTreeView) + document.removeEventListener('mouseup', @resizeStopped) resizeTreeView: ({pageX, which}) => return @resizeStopped() unless which is 1 if atom.config.get('tree-view.showOnRightSide') - width = @outerWidth() + @offset().left - pageX + width = @element.offsetWidth + @element.getBoundingClientRect().left - pageX else - width = pageX - @offset().left - @width(width) + width = pageX - @element.getBoundingClientRect().left + + @element.style.width = "#{width}px" resizeToFitContent: -> - @width(1) # Shrink to measure the minimum width of list - @width(@list.outerWidth()) + @element.style.width = '1px' # Shrink to measure the minimum width of list + @element.style.width = "#{@list.offsetWidth}px" loadIgnoredPatterns: -> @ignoredPatterns.length = 0 @@ -297,7 +313,7 @@ class TreeView extends View }) root = new DirectoryView() root.initialize(directory) - @list[0].appendChild(root) + @list.appendChild(root) root if @attachAfterProjectPathSet @@ -343,7 +359,7 @@ class TreeView extends View bestMatchEntry = null bestMatchLength = 0 - for entry in @list[0].querySelectorAll('.entry') + for entry in @list.querySelectorAll('.entry') if entry.isPathEqual(entryPath) return entry @@ -366,10 +382,8 @@ class TreeView extends View @scrollToEntry(@selectedEntry()) return - selectedEntry = $(selectedEntry) - until @selectEntry(selectedEntry.next('.entry')[0]) - selectedEntry = selectedEntry.parents('.entry:first') - break unless selectedEntry.length + if nextEntry = @nextEntry(selectedEntry) + @selectEntry(nextEntry) else @selectEntry(@roots[0]) @@ -379,17 +393,38 @@ class TreeView extends View event.stopImmediatePropagation() selectedEntry = @selectedEntry() if selectedEntry? - selectedEntry = $(selectedEntry) - if previousEntry = @selectEntry(selectedEntry.prev('.entry')[0]) + if previousEntry = @previousEntry(selectedEntry) + @selectEntry(previousEntry) if previousEntry instanceof DirectoryView @selectEntry(_.last(previousEntry.entries.children)) else - @selectEntry(selectedEntry.parents('.directory').first()?[0]) + @selectEntry(selectedEntry.parentElement.closest('.directory')) else - @selectEntry(@list.find('.entry').last()?[0]) + entries = @list.querySelectorAll('.entry') + @selectEntry(entries[entries.length - 1]) @scrollToEntry(@selectedEntry()) + nextEntry: (entry) -> + currentEntry = entry + while currentEntry? + if currentEntry.nextSibling? + currentEntry = currentEntry.nextSibling + if currentEntry.matches('.entry') + return currentEntry + else + currentEntry = currentEntry.parentElement.closest('.directory') + + return null + + previousEntry: (entry) -> + currentEntry = entry + while currentEntry? + currentEntry = currentEntry.previousSibling + if currentEntry?.matches('.entry') + return currentEntry + return null + expandDirectory: (isRecursive=false) -> selectedEntry = @selectedEntry() if isRecursive is false and selectedEntry.isExpanded @@ -401,7 +436,7 @@ class TreeView extends View selectedEntry = @selectedEntry() return unless selectedEntry? - if directory = $(selectedEntry).closest('.expanded.directory')[0] + if directory = selectedEntry.closest('.expanded.directory') directory.collapse(isRecursive) @selectEntry(directory) @@ -603,8 +638,8 @@ class TreeView extends View selectedPaths = @selectedPaths() return unless selectedPaths and selectedPaths.length > 0 # save to localStorage so we can paste across multiple open apps - LocalStorage.removeItem('tree-view:cutPath') - LocalStorage['tree-view:copyPath'] = JSON.stringify(selectedPaths) + window.localStorage.removeItem('tree-view:cutPath') + window.localStorage['tree-view:copyPath'] = JSON.stringify(selectedPaths) # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that cutting from 2 different @@ -616,8 +651,8 @@ class TreeView extends View selectedPaths = @selectedPaths() return unless selectedPaths and selectedPaths.length > 0 # save to localStorage so we can paste across multiple open apps - LocalStorage.removeItem('tree-view:copyPath') - LocalStorage['tree-view:cutPath'] = JSON.stringify(selectedPaths) + window.localStorage.removeItem('tree-view:copyPath') + window.localStorage['tree-view:cutPath'] = JSON.stringify(selectedPaths) # Public: Paste a copied or cut item. # If a file is selected, the file's parent directory is used as the @@ -627,8 +662,8 @@ class TreeView extends View # Returns `destination newPath`. pasteEntries: -> selectedEntry = @selectedEntry() - cutPaths = if LocalStorage['tree-view:cutPath'] then JSON.parse(LocalStorage['tree-view:cutPath']) else null - copiedPaths = if LocalStorage['tree-view:copyPath'] then JSON.parse(LocalStorage['tree-view:copyPath']) else null + cutPaths = if window.localStorage['tree-view:cutPath'] then JSON.parse(window.localStorage['tree-view:cutPath']) else null + copiedPaths = if window.localStorage['tree-view:copyPath'] then JSON.parse(window.localStorage['tree-view:copyPath']) else null initialPaths = copiedPaths or cutPaths catchAndShowFileErrors = (operation) -> @@ -684,7 +719,7 @@ class TreeView extends View dialog.attach() removeProjectFolder: (e) -> - pathToRemove = $(e.target).closest(".project-root > .header").find(".name").data("path") + pathToRemove = e.target.closest(".project-root > .header")?.querySelector(".name")?.dataset.path # TODO: remove this conditional once the addition of Project::removePath # is released. @@ -692,7 +727,7 @@ class TreeView extends View atom.project.removePath(pathToRemove) if pathToRemove? selectedEntry: -> - @list[0].querySelector('.selected') + @list.querySelector('.selected') selectEntry: (entry) -> return unless entry? @@ -706,7 +741,7 @@ class TreeView extends View entry getSelectedEntries: -> - @list[0].querySelectorAll('.selected') + @list.querySelectorAll('.selected') deselect: (elementsToDeselect=@getSelectedEntries()) -> selected.classList.remove('selected') for selected in elementsToDeselect @@ -714,28 +749,34 @@ class TreeView extends View scrollTop: (top) -> if top? - @scroller.scrollTop(top) + @scroller.scrollTop = top else - @scroller.scrollTop() + @scroller.scrollTop scrollBottom: (bottom) -> if bottom? - @scroller.scrollBottom(bottom) + @scroller.scrollTop = bottom - @scroller.offsetHeight else - @scroller.scrollBottom() + @scroller.scrollTop + @scroller.offsetHeight scrollToEntry: (entry) -> element = if entry instanceof DirectoryView then entry.header else entry element?.scrollIntoViewIfNeeded(true) # true = center around item if possible scrollToBottom: -> - if lastEntry = _.last(@list[0].querySelectorAll('.entry')) + if lastEntry = _.last(@list.querySelectorAll('.entry')) @selectEntry(lastEntry) @scrollToEntry(lastEntry) scrollToTop: -> @selectEntry(@roots[0]) if @roots[0]? - @scrollTop(0) + @scroller.scrollTop = 0 + + pageUp: -> + @scroller.scrollTop -= @element.offsetHeight + + pageDown: -> + @scroller.scrollTop += @element.offsetHeight toggleSide: -> toggleConfig('tree-view.showOnRightSide') @@ -771,12 +812,12 @@ class TreeView extends View # return early if we're opening a contextual menu (right click) during multi-select mode if @multiSelectEnabled() and - e.currentTarget.classList.contains('selected') and + e.target.classList.contains('selected') and # mouse right click or ctrl click as right click on darwin platforms (e.button is 2 or e.ctrlKey and process.platform is 'darwin') return - entryToSelect = e.currentTarget + entryToSelect = e.target.closest('.entry') if e.shiftKey @selectContinuousEntries(entryToSelect) @@ -811,9 +852,9 @@ class TreeView extends View # Returns array of selected elements selectContinuousEntries: (entry) -> currentSelectedEntry = @selectedEntry() - parentContainer = $(entry).parent() - if $.contains(parentContainer[0], currentSelectedEntry) - entries = parentContainer.find('.entry').toArray() + parentContainer = entry.parentElement + if parentContainer.contains(currentSelectedEntry) + entries = Array.from(parentContainer.querySelectorAll('.entry')) entryIndex = entries.indexOf(entry) selectedIndex = entries.indexOf(currentSelectedEntry) elements = (entries[i] for i in [entryIndex..selectedIndex]) @@ -834,27 +875,27 @@ class TreeView extends View # Public: Toggle full-menu class on the main list element to display the full context # menu. showFullMenu: -> - @list[0].classList.remove('multi-select') - @list[0].classList.add('full-menu') + @list.classList.remove('multi-select') + @list.classList.add('full-menu') # Public: Toggle multi-select class on the main list element to display the the # menu with only items that make sense for multi select functionality showMultiSelectMenu: -> - @list[0].classList.remove('full-menu') - @list[0].classList.add('multi-select') + @list.classList.remove('full-menu') + @list.classList.add('multi-select') # Public: Check for multi-select class on the main list # # Returns boolean multiSelectEnabled: -> - @list[0].classList.contains('multi-select') + @list.classList.contains('multi-select') onDragEnter: (e) => return if @rootDragAndDrop.isDragging(e) e.stopPropagation() - entry = e.currentTarget.parentNode + entry = e.target.closest('.entry.directory > .header').parentNode @dragEventCounts.set(entry, 0) unless @dragEventCounts.get(entry) entry.classList.add('selected') if @dragEventCounts.get(entry) is 0 @dragEventCounts.set(entry, @dragEventCounts.get(entry) + 1) @@ -864,7 +905,7 @@ class TreeView extends View e.stopPropagation() - entry = e.currentTarget.parentNode + entry = e.target.closest('.entry.directory > .header').parentNode @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) entry.classList.remove('selected') if @dragEventCounts.get(entry) is 0 @@ -875,23 +916,21 @@ class TreeView extends View if @rootDragAndDrop.canDragStart(e) return @rootDragAndDrop.onDragStart(e) - target = $(e.currentTarget).find(".name") - initialPath = target.data("path") + target = e.target.closest('.entry').querySelector(".name") + initialPath = target.dataset.path - style = getStyleObject(target[0]) + fileNameElement = target.cloneNode(true) + for key, value of getStyleObject(target) + fileNameElement.style[key] = value + fileNameElement.style.position = 'absolute' + fileNameElement.style.top = 0 + fileNameElement.style.left = 0 - fileNameElement = target.clone() - .css(style) - .css( - position: 'absolute' - top: 0 - left: 0 - ) - fileNameElement.appendTo(document.body) + document.body.appendChild(fileNameElement) - e.originalEvent.dataTransfer.effectAllowed = "move" - e.originalEvent.dataTransfer.setDragImage(fileNameElement[0], 0, 0) - e.originalEvent.dataTransfer.setData("initialPath", initialPath) + e.dataTransfer.effectAllowed = "move" + e.dataTransfer.setDragImage(fileNameElement, 0, 0) + e.dataTransfer.setData("initialPath", initialPath) window.requestAnimationFrame -> fileNameElement.remove() @@ -903,7 +942,7 @@ class TreeView extends View e.preventDefault() e.stopPropagation() - entry = e.currentTarget + entry = e.target.closest('.entry') if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') entry.classList.add('selected') @@ -914,20 +953,20 @@ class TreeView extends View e.preventDefault() e.stopPropagation() - entry = e.currentTarget + entry = e.target.closest('.entry') entry.classList.remove('selected') return unless entry instanceof DirectoryView - newDirectoryPath = $(entry).find(".name").data("path") + newDirectoryPath = entry.querySelector('.name')?.dataset.path return false unless newDirectoryPath - initialPath = e.originalEvent.dataTransfer.getData("initialPath") + initialPath = e.dataTransfer.getData("initialPath") if initialPath # Drop event from Atom @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS - for file in e.originalEvent.dataTransfer.files + for file in e.dataTransfer.files @moveEntry(file.path, newDirectoryPath) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 41283b20..c3a30323 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -1,5 +1,3 @@ -{$} = require 'atom-space-pen-views' - module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) -> dataTransfer = data: {} @@ -7,20 +5,20 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) -> getData: (key) -> @data[key] setDragImage: (@image) -> return - dragStartEvent = $.Event() - dragStartEvent.target = dragged - dragStartEvent.currentTarget = dragged - dragStartEvent.originalEvent = {dataTransfer} + dragStartEvent = new DragEvent('dragstart') + Object.defineProperty(dragStartEvent, 'target', value: dragged) + Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged) + Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer) - dropEvent = $.Event() - dropEvent.target = dropTarget - dropEvent.currentTarget = dropTarget - dropEvent.originalEvent = {dataTransfer} + dropEvent = new DragEvent('drop') + Object.defineProperty(dropEvent, 'target', value: dropTarget) + Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget) + Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer) - dragEnterEvent = $.Event() - dragEnterEvent.target = enterTarget - dragEnterEvent.currentTarget = enterTarget - dragEnterEvent.originalEvent = {dataTransfer} + dragEnterEvent = new DragEvent('dragenter') + Object.defineProperty(dragEnterEvent, 'target', value: enterTarget) + Object.defineProperty(dragEnterEvent, 'currentTarget', value: enterTarget) + Object.defineProperty(dragEnterEvent, 'dataTransfer', value: dataTransfer) [dragStartEvent, dragEnterEvent, dropEvent] @@ -31,38 +29,39 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> getData: (key) -> @data[key] files: [] - dropEvent = $.Event() - dropEvent.target = dropTarget - dropEvent.currentTarget = dropTarget - dropEvent.originalEvent = {dataTransfer} + dropEvent = new DragEvent('drop') + Object.defineProperty(dropEvent, 'target', value: dropTarget) + Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget) + Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer) for filePath in filePaths - dropEvent.originalEvent.dataTransfer.files.push({path: filePath}) + dropEvent.dataTransfer.files.push({path: filePath}) dropEvent buildElementPositionalDragEvents = (el, dataTransfer, currentTargetSelector) -> if not el? return {} - $el = $(el) - $currentTarget = if currentTargetSelector then $el.closest(currentTargetSelector) else $el - currentTarget = $currentTarget[0] + currentTarget = if currentTargetSelector then el.closest(currentTargetSelector) else el - topEvent = $.Event() - topEvent.target = el - topEvent.currentTarget = currentTarget - topEvent.originalEvent = {dataTransfer, pageY: $el.offset().top} + topEvent = new DragEvent('dragstart') + Object.defineProperty(topEvent, 'target', value: el) + Object.defineProperty(topEvent, 'currentTarget', value: currentTarget) + Object.defineProperty(topEvent, 'dataTransfer', value: dataTransfer) + Object.defineProperty(topEvent, 'pageY', value: el.getBoundingClientRect().top) - middleEvent = $.Event() - middleEvent.target = el - middleEvent.currentTarget = currentTarget - middleEvent.originalEvent = {dataTransfer, pageY: $el.offset().top + $el.height() * 0.5} + middleEvent = new DragEvent('dragover') + Object.defineProperty(middleEvent, 'target', value: el) + Object.defineProperty(middleEvent, 'currentTarget', value: currentTarget) + Object.defineProperty(middleEvent, 'dataTransfer', value: dataTransfer) + Object.defineProperty(middleEvent, 'pageY', value: el.getBoundingClientRect().top + el.offsetHeight * 0.5) - bottomEvent = $.Event() - bottomEvent.target = el - bottomEvent.currentTarget = currentTarget - bottomEvent.originalEvent = {dataTransfer, pageY: $el.offset().bottom} + bottomEvent = new DragEvent('dragend') + Object.defineProperty(bottomEvent, 'target', value: el) + Object.defineProperty(bottomEvent, 'currentTarget', value: currentTarget) + Object.defineProperty(bottomEvent, 'dataTransfer', value: dataTransfer) + Object.defineProperty(bottomEvent, 'pageY', value: el.getBoundingClientRect().bottom) {top: topEvent, middle: middleEvent, bottom: bottomEvent} @@ -74,14 +73,14 @@ module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelect getData: (key) -> @data[key] setDragImage: (@image) -> return - dragStartEvent = $.Event() - dragStartEvent.target = dragged - dragStartEvent.currentTarget = dragged - dragStartEvent.originalEvent = {dataTransfer} + dragStartEvent = new DragEvent('dragstart') + Object.defineProperty(dragStartEvent, 'target', value: dragged) + Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged) + Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer) - dragEndEvent = $.Event() - dragEndEvent.target = dragged - dragEndEvent.currentTarget = dragged - dragEndEvent.originalEvent = {dataTransfer} + dragEndEvent = new DragEvent('dragend') + Object.defineProperty(dragEndEvent, 'target', value: dragged) + Object.defineProperty(dragEndEvent, 'currentTarget', value: dragged) + Object.defineProperty(dragEndEvent, 'dataTransfer', value: dataTransfer) [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent] diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee index 172a4c7a..1bbc9fe9 100644 --- a/spec/file-stats-spec.coffee +++ b/spec/file-stats-spec.coffee @@ -1,5 +1,4 @@ _ = require 'underscore-plus' -{$, $$} = require 'atom-space-pen-views' fs = require 'fs-plus' path = require 'path' temp = require('temp').track() @@ -25,7 +24,7 @@ describe "FileStats", -> atom.packages.activatePackage("tree-view") runs -> - treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view() + treeView = atom.workspace.getLeftPanels()[0].getItem() afterEach -> temp.cleanup() @@ -45,7 +44,7 @@ describe "FileStats", -> expect(treeView.roots[0].directory.stats).toBeDefined() it "passes stats to File instances in subdirectories", -> - treeView.find(".entries > li:contains(subdir)")[0].expand() + treeView.element.querySelector(".entries > li").expand() subdir = treeView.roots[0].directory.entries["subdir"] stats = subdir.entries["file2.txt"].stats expect(stats).toBeDefined() diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 66103499..99952262 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -106,12 +106,12 @@ describe "TreeView", -> treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() it "does not attach to the workspace or create a root node when initialized", -> - expect(treeView.hasParent()).toBeFalsy() + expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(0) it "does not attach to the workspace or create a root node when attach() is called", -> treeView.attach() - expect(treeView.hasParent()).toBeFalsy() + expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(0) it "serializes without throwing an exception", -> @@ -133,7 +133,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeFalsy() + expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(0) describe "when the project is assigned a path because a new buffer is saved", -> @@ -144,7 +144,7 @@ describe "TreeView", -> runs -> projectPath = temp.mkdirSync('atom-project') atom.workspace.getActivePaneItem().saveAs(path.join(projectPath, 'test.txt')) - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.roots).toHaveLength(1) expect(fs.absolute(treeView.roots[0].getPath())).toBe fs.absolute(projectPath) @@ -161,7 +161,7 @@ describe "TreeView", -> runs -> treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() - expect(treeView.hasParent()).toBeFalsy() + expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(2) describe "when the root view is opened to a directory", -> @@ -171,7 +171,7 @@ describe "TreeView", -> runs -> treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.roots).toHaveLength(2) describe "when the project is a .git folder", -> @@ -238,36 +238,36 @@ describe "TreeView", -> it "restores the scroll top when toggled", -> workspaceElement.style.height = '5px' jasmine.attachToDOM(workspaceElement) - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() treeView.focus() treeView.scrollTop(10) expect(treeView.scrollTop()).toBe(10) runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.is(':hidden') + waitsFor -> treeView.element.offsetHeight is 0 runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.is(':visible') + waitsFor -> treeView.element.offsetHeight > 0 runs -> expect(treeView.scrollTop()).toBe(10) it "restores the scroll left when toggled", -> - treeView.width(5) + treeView.element.style.width = '5px' jasmine.attachToDOM(workspaceElement) - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() treeView.focus() - treeView.scroller.scrollLeft(5) - expect(treeView.scroller.scrollLeft()).toBe(5) + treeView.scroller.scrollLeft = 5 + expect(treeView.scroller.scrollLeft).toBe(5) runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.is(':hidden') + waitsFor -> treeView.element.offsetHeight is 0 runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.is(':visible') + waitsFor -> treeView.element.offsetHeight > 0 - runs -> expect(treeView.scroller.scrollLeft()).toBe(5) + runs -> expect(treeView.scroller.scrollLeft).toBe(5) describe "when tree-view:toggle is triggered on the root view", -> beforeEach -> @@ -275,31 +275,31 @@ describe "TreeView", -> describe "when the tree view is visible", -> beforeEach -> - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() describe "when the tree view is focused", -> it "hides the tree view", -> treeView.focus() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView).toBeHidden() + expect(treeView.element).toBeHidden() describe "when the tree view is not focused", -> it "hides the tree view", -> workspaceElement.focus() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView).toBeHidden() + expect(treeView.element).toBeHidden() describe "when the tree view is hidden", -> it "shows and focuses the tree view", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.list).toHaveFocus() describe "when tree-view:toggle-side is triggered on the root view", -> describe "when the tree view is on the left", -> it "moves the tree view to the right", -> - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') expect(treeView.element.dataset.showOnRightSide).toBe('true') @@ -308,7 +308,7 @@ describe "TreeView", -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') it "moves the tree view to the left", -> - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') expect(treeView.element.dataset.showOnRightSide).toBe('false') @@ -329,7 +329,7 @@ describe "TreeView", -> it "shows and focuses the tree view", -> treeView.detach() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.list).toHaveFocus() describe "when the tree view is shown", -> @@ -339,9 +339,9 @@ describe "TreeView", -> runs -> workspaceElement.focus() - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() expect(treeView.list).toHaveFocus() describe "when the tree view is focused", -> @@ -351,9 +351,9 @@ describe "TreeView", -> runs -> treeView.focus() - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() expect(treeView.list).not.toHaveFocus() describe "when tree-view:reveal-active-file is triggered on the root view", -> @@ -371,7 +371,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).toHaveBeenCalled() waitsForPromise -> @@ -380,7 +380,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).toHaveBeenCalled() describe "if the tree-view.focusOnReveal config option is false", -> @@ -392,7 +392,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).not.toHaveBeenCalled() waitsForPromise -> @@ -401,7 +401,7 @@ describe "TreeView", -> runs -> atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).not.toHaveBeenCalled() describe "if the current file has no path", -> @@ -412,14 +412,14 @@ describe "TreeView", -> runs -> expect(atom.workspace.getActivePaneItem().getPath()).toBeUndefined() atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).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(atom.workspace.getActivePaneItem()).toBeUndefined() atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.hasParent()).toBeTruthy() + expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).toHaveBeenCalled() describe 'if there are more items than can be visible in the viewport', -> @@ -433,7 +433,7 @@ describe "TreeView", -> fs.writeFileSync(filepath, "doesn't matter") atom.project.setPaths([rootDirPath]) - treeView.height(100) + treeView.element.style.height = '100px' jasmine.attachToDOM(workspaceElement) it 'scrolls the selected file into the visible view', -> @@ -466,7 +466,7 @@ describe "TreeView", -> treeView.focus() expect(treeView.list).toHaveFocus() atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() expect(treeView.list).not.toHaveFocus() expect(atom.workspace.getActivePane().isActive()).toBe(true) @@ -622,7 +622,7 @@ describe "TreeView", -> it "selects the file and retains focus on tree-view", -> expect(sampleJs).toHaveClass 'selected' - expect(treeView).toHaveFocus() + expect(treeView.element).toHaveFocus() it "opens the file in a pending state", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') @@ -638,7 +638,7 @@ describe "TreeView", -> it "selects the file and retains focus on tree-view", -> expect(sampleJs).toHaveClass 'selected' - expect(treeView).toHaveFocus() + expect(treeView.element).toHaveFocus() it "does not open the file", -> expect(atom.workspace.open).not.toHaveBeenCalled() @@ -718,7 +718,7 @@ describe "TreeView", -> subdir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) expect(subdir).toHaveClass 'selected' expect(subdir).not.toHaveClass 'expanded' - expect(treeView).toHaveFocus() + expect(treeView.element).toHaveFocus() describe "when an directory is alt-clicked", -> describe "when the directory is collapsed", -> @@ -954,18 +954,18 @@ describe "TreeView", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight + expect(treeView.scroller.scrollTop).toBe(0) entryCount = treeView.element.querySelectorAll(".entry").length _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBeGreaterThan 0 + expect(treeView.scroller.scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:move-to-top') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.scroller.scrollTop).toBe(0) it "selects the root entry", -> - entryCount = treeView.find(".entry").length + entryCount = treeView.element.querySelectorAll(".entry").length _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') expect(treeView.roots[0]).not.toHaveClass 'selected' @@ -977,16 +977,16 @@ describe "TreeView", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight + expect(treeView.scroller.scrollTop).toBe(0) atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBeGreaterThan(0) + expect(treeView.scroller.scrollTop).toBeGreaterThan(0) treeView.roots[0].collapse() treeView.roots[1].collapse() atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.scroller.scrollTop).toBe(0) it "selects the last entry", -> expect(treeView.roots[0]).toHaveClass 'selected' @@ -999,45 +999,45 @@ describe "TreeView", -> treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight + expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.scroller.scrollTop).toBe(0) treeView.scrollToBottom() - scrollTop = treeView.element.querySelector('.tree-view-scroller').scrollTop + scrollTop = treeView.scroller.scrollTop expect(scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:page-up') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe scrollTop - treeView.element.offsetHeight + expect(treeView.scroller.scrollTop).toBe scrollTop - treeView.element.offsetHeight describe "core:page-down", -> it "scrolls down a page", -> treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight + expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.scroller.scrollTop).toBe(0) atom.commands.dispatch(treeView.element, 'core:page-down') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe treeView.element.offsetHeight + expect(treeView.scroller.scrollTop).toBe treeView.element.offsetHeight describe "movement outside of viewable region", -> it "scrolls the tree view to the selected item", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.element.querySelector('.tree-view').offsetHeight).toBeGreaterThan treeView.element.querySelector('.tree-view-scroller').offsetHeight + expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe(0) + expect(treeView.scroller.scrollTop).toBe(0) entryCount = treeView.element.querySelectorAll(".entry").length entryHeight = treeView.element.querySelector('.file').offsetHeight _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop + treeView.element.offsetHeight).toBeGreaterThan((entryCount * entryHeight) - 1) + expect(treeView.scroller.scrollTop + treeView.element.offsetHeight).toBeGreaterThan((entryCount * entryHeight) - 1) _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-up') - expect(treeView.element.querySelector('.tree-view-scroller').scrollTop).toBe 0 + expect(treeView.scroller.scrollTop).toBe 0 describe "tree-view:expand-directory", -> describe "when a directory entry is selected", -> @@ -2762,7 +2762,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) it "should resize normally", -> - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() expect(atom.workspace.getLeftPanels().length).toBe(1) treeView.element.style.width = '100px' @@ -2784,7 +2784,7 @@ describe "TreeView", -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') expect(treeView.element.dataset.showOnRightSide).toBe('true') - expect(treeView).toBeVisible() + expect(treeView.element).toBeVisible() expect(atom.workspace.getRightPanels().length).toBe(1) treeView.element.style.width = '100px' @@ -2836,7 +2836,7 @@ describe "TreeView", -> it 'switches the contextual menu to muli-select mode', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) - expect(treeView.find('.tree-view')).toHaveClass('multi-select') + expect(treeView.list).toHaveClass('multi-select') describe 'using the shift key', -> it 'selects the items between the already selected item and the shift clicked item', -> @@ -3410,7 +3410,7 @@ describe "TreeView", -> it "selects the file and retains focus on tree-view", -> expect(sampleTxt).toHaveClass 'selected' - expect(treeView).toHaveFocus() + expect(treeView.element).toHaveFocus() it "doesn't open the file in the active pane", -> expect(atom.views.getView(treeView)).toHaveFocus() @@ -3517,7 +3517,7 @@ describe "TreeView", -> alphaDir = treeView.roots[0] lastDir = treeView.roots[treeView.roots.length - 1] [dragStartEvent, dragOverEvents, dragEndEvent] = - eventHelpers.buildPositionalDragEvents(alphaDir.querySelector('.project-root-header'), treeView.element.querySelector('.tree-view')) + eventHelpers.buildPositionalDragEvents(alphaDir.querySelector('.project-root-header'), treeView.list) expect(alphaDir).not.toEqual(lastDir) @@ -3572,9 +3572,9 @@ describe "TreeView", -> [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header')) treeView.rootDragAndDrop.onDragStart(dragStartEvent) - expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath + expect(dragStartEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath if process.platform in ['darwin', 'linux'] - expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual "file://#{gammaDirPath}" + expect(dragStartEvent.dataTransfer.getData("text/uri-list")).toEqual "file://#{gammaDirPath}" describe "when a root folder is dropped from another Atom window", -> it "adds the root folder to the window", -> @@ -3582,9 +3582,9 @@ describe "TreeView", -> [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.querySelector('.project-root-header'), '.tree-view') dropEvent = dragDropEvents.bottom - dropEvent.originalEvent.dataTransfer.setData('atom-tree-view-event', true) - dropEvent.originalEvent.dataTransfer.setData('from-window-id', treeView.rootDragAndDrop.getWindowId() + 1) - dropEvent.originalEvent.dataTransfer.setData('from-root-path', etaDirPath) + dropEvent.dataTransfer.setData('atom-tree-view-event', true) + dropEvent.dataTransfer.setData('from-window-id', treeView.rootDragAndDrop.getWindowId() + 1) + dropEvent.dataTransfer.setData('from-root-path', etaDirPath) # mock browserWindowForId browserWindowMock = {webContents: {send: ->}} From baaaf116be92cc99312dca343767e2d7d27933e8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Feb 2017 18:37:08 +0100 Subject: [PATCH 136/263] Remove custom elements --- lib/directory-view.coffee | 60 ++++++++++++++++++++++++--------------- lib/file-view.coffee | 24 +++++++++------- lib/tree-view.coffee | 32 ++++++++++----------- 3 files changed, 66 insertions(+), 50 deletions(-) diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 3cef9717..70533d1d 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -3,13 +3,16 @@ Directory = require './directory' FileView = require './file-view' {repoForPath} = require './helpers' -class DirectoryView extends HTMLElement - initialize: (@directory) -> +module.exports = +class DirectoryView + constructor: (@directory) -> @subscriptions = new CompositeDisposable() @subscriptions.add @directory.onDidDestroy => @subscriptions.dispose() @subscribeToDirectory() - @classList.add('directory', 'entry', 'list-nested-item', 'collapsed') + @element = document.createElement('li') + @element.setAttribute('is', 'tree-view-directory') + @element.classList.add('directory', 'entry', 'list-nested-item', 'collapsed') @header = document.createElement('div') @header.classList.add('header', 'list-item') @@ -44,23 +47,36 @@ class DirectoryView extends HTMLElement @directoryName.title = @directory.name @directoryName.textContent = @directory.name - @appendChild(@header) + @element.appendChild(@header) @header.appendChild(@directoryName) - @appendChild(@entries) + @element.appendChild(@entries) if @directory.isRoot - @classList.add('project-root') + @element.classList.add('project-root') @header.classList.add('project-root-header') else - @draggable = true + @element.draggable = true @subscriptions.add @directory.onDidStatusChange => @updateStatus() @updateStatus() @expand() if @directory.expansionState.isExpanded + @element.collapse = @collapse.bind(this) + @element.expand = @expand.bind(this) + @element.toggleExpansion = @toggleExpansion.bind(this) + @element.reload = @reload.bind(this) + @element.isExpanded = @isExpanded + @element.updateStatus = @updateStatus.bind(this) + @element.isPathEqual = @isPathEqual.bind(this) + @element.getPath = @getPath.bind(this) + @element.directory = @directory + @element.header = @header + @element.entries = @entries + @element.directoryName = @directoryName + updateStatus: -> - @classList.remove('status-ignored', 'status-modified', 'status-added') - @classList.add("status-#{@directory.status}") if @directory.status? + @element.classList.remove('status-ignored', 'status-modified', 'status-added') + @element.classList.add("status-#{@directory.status}") if @directory.status? subscribeToDirectory: -> @subscriptions.add @directory.onDidAddEntries (addedEntries) => @@ -73,9 +89,9 @@ class DirectoryView extends HTMLElement insertionIndex = entry.indexInParentDirectory if insertionIndex < numberOfEntries - @entries.insertBefore(view, @entries.children[insertionIndex]) + @entries.insertBefore(view.element, @entries.children[insertionIndex]) else - @entries.appendChild(view) + @entries.appendChild(view.element) numberOfEntries++ @@ -87,14 +103,13 @@ class DirectoryView extends HTMLElement createViewForEntry: (entry) -> if entry instanceof Directory - view = new DirectoryElement() + view = new DirectoryView(entry) else - view = new FileView() - view.initialize(entry) + view = new FileView(entry) subscription = @directory.onDidRemoveEntries (removedEntries) -> for removedName, removedEntry of removedEntries when entry is removedEntry - view.remove() + view.element.remove() subscription.dispose() break @subscriptions.add(subscription) @@ -110,27 +125,26 @@ class DirectoryView extends HTMLElement expand: (isRecursive=false) -> unless @isExpanded @isExpanded = true - @classList.add('expanded') - @classList.remove('collapsed') + @element.isExpanded = @isExpanded + @element.classList.add('expanded') + @element.classList.remove('collapsed') @directory.expand() if isRecursive - for entry in @entries.children when entry instanceof DirectoryView + for entry in @entries.children when entry.classList.contains('directory') entry.expand(true) false collapse: (isRecursive=false) -> @isExpanded = false + @element.isExpanded = false if isRecursive for entry in @entries.children when entry.isExpanded entry.collapse(true) - @classList.remove('expanded') - @classList.add('collapsed') + @element.classList.remove('expanded') + @element.classList.add('collapsed') @directory.collapse() @entries.innerHTML = '' - -DirectoryElement = document.registerElement('tree-view-directory', prototype: DirectoryView.prototype, extends: 'li') -module.exports = DirectoryElement diff --git a/lib/file-view.coffee b/lib/file-view.coffee index f4ec1f1e..65d27360 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -2,18 +2,19 @@ FileIcons = require './file-icons' module.exports = -class FileView extends HTMLElement - initialize: (@file) -> +class FileView + constructor: (@file) -> @subscriptions = new CompositeDisposable() @subscriptions.add @file.onDidDestroy => @subscriptions.dispose() - @draggable = true - - @classList.add('file', 'entry', 'list-item') + @element = document.createElement('li') + @element.setAttribute('is', 'tree-view-file') + @element.draggable = true + @element.classList.add('file', 'entry', 'list-item') @fileName = document.createElement('span') @fileName.classList.add('name', 'icon') - @appendChild(@fileName) + @element.appendChild(@fileName) @fileName.textContent = @file.name @fileName.title = @file.name @fileName.dataset.name = @file.name @@ -28,14 +29,17 @@ class FileView extends HTMLElement @subscriptions.add @file.onDidStatusChange => @updateStatus() @updateStatus() + @element.getPath = @getPath.bind(this) + @element.isPathEqual = @isPathEqual.bind(this) + @element.file = @file + @element.fileName = @fileName + updateStatus: -> - @classList.remove('status-ignored', 'status-modified', 'status-added') - @classList.add("status-#{@file.status}") if @file.status? + @element.classList.remove('status-ignored', 'status-modified', 'status-added') + @element.classList.add("status-#{@file.status}") if @file.status? getPath: -> @fileName.dataset.path isPathEqual: (pathToCompare) -> @file.isPathEqual(pathToCompare) - -module.exports = document.registerElement('tree-view-file', prototype: FileView.prototype, extends: 'li') diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 30a4db5f..f30574d4 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -13,7 +13,6 @@ Minimatch = null # Defer requiring until actually needed Directory = require './directory' DirectoryView = require './directory-view' -FileView = require './file-view' RootDragAndDrop = require './root-drag-and-drop' toggleConfig = (keyPath) -> @@ -100,7 +99,7 @@ class TreeView @entryClicked(e) unless e.shiftKey or e.metaKey or e.ctrlKey @element.addEventListener 'mousedown', (e) => @onMouseDown(e) - @element.addEventListener 'dragstart', '.entry', (e) => @onDragStart(e) + @element.addEventListener 'dragstart', (e) => @onDragStart(e) @element.addEventListener 'dragenter', (e) => @onDragEnter(e) @element.addEventListener 'dragleave', (e) => @onDragLeave(e) @element.addEventListener 'dragover', (e) => @onDragOver(e) @@ -223,9 +222,9 @@ class TreeView entry = e.target.closest('.entry') isRecursive = e.altKey or false @selectEntry(entry) - if entry instanceof DirectoryView + if entry.classList.contains('directory') entry.toggleExpansion(isRecursive) - else if entry instanceof FileView + else if entry.classList.contains('file') @fileViewEntryClicked(e) false @@ -311,8 +310,7 @@ class TreeView @useSyncFS stats }) - root = new DirectoryView() - root.initialize(directory) + root = new DirectoryView(directory).element @list.appendChild(root) root @@ -344,7 +342,7 @@ class TreeView for pathComponent in activePathComponents currentPath += path.sep + pathComponent entry = @entryForPath(currentPath) - if entry instanceof DirectoryView + if entry.classList.contains('directory') entry.expand() else @selectEntry(entry) @@ -377,7 +375,7 @@ class TreeView event?.stopImmediatePropagation() selectedEntry = @selectedEntry() if selectedEntry? - if selectedEntry instanceof DirectoryView + if selectedEntry.classList.contains('directory') if @selectEntry(selectedEntry.entries.children[0]) @scrollToEntry(@selectedEntry()) return @@ -395,7 +393,7 @@ class TreeView if selectedEntry? if previousEntry = @previousEntry(selectedEntry) @selectEntry(previousEntry) - if previousEntry instanceof DirectoryView + if previousEntry.classList.contains('directory') @selectEntry(_.last(previousEntry.entries.children)) else @selectEntry(selectedEntry.parentElement.closest('.directory')) @@ -442,12 +440,12 @@ class TreeView openSelectedEntry: (options={}, expandDirectory=false) -> selectedEntry = @selectedEntry() - if selectedEntry instanceof DirectoryView + if selectedEntry.classList.contains('directory') if expandDirectory @expandDirectory(false) else selectedEntry.toggleExpansion() - else if selectedEntry instanceof FileView + else if selectedEntry.classList.contains('file') if atom.config.get('tree-view.alwaysOpenExisting') options = Object.assign searchAllPanes: true, options @openAfterPromise(selectedEntry.getPath(), options) @@ -455,7 +453,7 @@ class TreeView openSelectedEntrySplit: (orientation, side) -> selectedEntry = @selectedEntry() pane = atom.workspace.getActivePane() - if pane and selectedEntry instanceof FileView + if pane and selectedEntry.classList.contains('file') if atom.workspace.getActivePaneItem() split = pane.split orientation, side atom.workspace.openURIInPane selectedEntry.getPath(), split @@ -477,7 +475,7 @@ class TreeView openSelectedEntryInPane: (index) -> selectedEntry = @selectedEntry() pane = atom.workspace.getPanes()[index] - if pane and selectedEntry instanceof FileView + if pane and selectedEntry.classList.contains('file') atom.workspace.openURIInPane selectedEntry.getPath(), pane moveSelectedEntry: -> @@ -553,7 +551,7 @@ class TreeView showSelectedEntryInFileManager: -> return unless entry = @selectedEntry() - isFile = entry instanceof FileView + isFile = entry.classList.contains('file') {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile) @openInFileManager(command, args, label, isFile) @@ -676,7 +674,7 @@ class TreeView initialPathIsDirectory = fs.isDirectorySync(initialPath) if selectedEntry and initialPath and fs.existsSync(initialPath) basePath = selectedEntry.getPath() - basePath = path.dirname(basePath) if selectedEntry instanceof FileView + basePath = path.dirname(basePath) if selectedEntry.classList.contains('file') newPath = path.join(basePath, path.basename(initialPath)) if copiedPaths @@ -760,7 +758,7 @@ class TreeView @scroller.scrollTop + @scroller.offsetHeight scrollToEntry: (entry) -> - element = if entry instanceof DirectoryView then entry.header else entry + element = if entry?.classList.contains('directory') then entry.header else entry element?.scrollIntoViewIfNeeded(true) # true = center around item if possible scrollToBottom: -> @@ -956,7 +954,7 @@ class TreeView entry = e.target.closest('.entry') entry.classList.remove('selected') - return unless entry instanceof DirectoryView + return unless entry.classList.contains('directory') newDirectoryPath = entry.querySelector('.name')?.dataset.path return false unless newDirectoryPath From 3dd711be4c824f07041a1d29a62d55a9d2fb0d44 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Feb 2017 14:16:17 +0100 Subject: [PATCH 137/263] Add missing `updateStatus` method on FileView elements --- lib/file-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/file-view.coffee b/lib/file-view.coffee index 65d27360..0a729def 100644 --- a/lib/file-view.coffee +++ b/lib/file-view.coffee @@ -33,6 +33,7 @@ class FileView @element.isPathEqual = @isPathEqual.bind(this) @element.file = @file @element.fileName = @fileName + @element.updateStatus = @updateStatus.bind(this) updateStatus: -> @element.classList.remove('status-ignored', 'status-modified', 'status-added') From db34b854a59691546bd59ea7bf5d39e48d473170 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Feb 2017 14:29:37 +0100 Subject: [PATCH 138/263] Remove atom-space-pen-views from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 9b90367b..e3f54511 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ }, "private": true, "dependencies": { - "atom-space-pen-views": "^2.1.1", "event-kit": "^1.0.0", "fs-plus": "^2.3.0", "minimatch": "~0.3.0", From 31c472d5fc6d6ada2c843adc402682e6b5f0e60f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Feb 2017 15:15:36 +0100 Subject: [PATCH 139/263] Ignore user clicks on the tree-view's background --- lib/root-drag-and-drop.coffee | 10 +-- lib/tree-view.coffee | 160 +++++++++++++++++----------------- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index 433d9046..f15b20e1 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -16,11 +16,11 @@ class RootDragAndDropHandler handleEvents: -> # onDragStart is called directly by TreeView's onDragStart # will be cleaned up by tree view, since they are tree-view's handlers - @treeView.element.addEventListener 'dragenter', @onDragEnter - @treeView.element.addEventListener 'dragend', @onDragEnd - @treeView.element.addEventListener 'dragleave', @onDragLeave - @treeView.element.addEventListener 'dragover', @onDragOver - @treeView.element.addEventListener 'drop', @onDrop + @treeView.element.addEventListener 'dragenter', @onDragEnter.bind(this) + @treeView.element.addEventListener 'dragend', @onDragEnd.bind(this) + @treeView.element.addEventListener 'dragleave', @onDragLeave.bind(this) + @treeView.element.addEventListener 'dragover', @onDragOver.bind(this) + @treeView.element.addEventListener 'drop', @onDrop.bind(this) onDragStart: (e) => return unless @treeView.list.contains(e.target) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f30574d4..6196e157 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -219,15 +219,13 @@ class TreeView @show() entryClicked: (e) -> - entry = e.target.closest('.entry') - isRecursive = e.altKey or false - @selectEntry(entry) - if entry.classList.contains('directory') - entry.toggleExpansion(isRecursive) - else if entry.classList.contains('file') - @fileViewEntryClicked(e) - - false + if entry = e.target.closest('.entry') + isRecursive = e.altKey or false + @selectEntry(entry) + if entry.classList.contains('directory') + entry.toggleExpansion(isRecursive) + else if entry.classList.contains('file') + @fileViewEntryClicked(e) fileViewEntryClicked: (e) -> filePath = e.target.closest('.entry').getPath() @@ -806,29 +804,28 @@ class TreeView @element.style.display = '' onMouseDown: (e) -> - e.stopPropagation() - - # return early if we're opening a contextual menu (right click) during multi-select mode - if @multiSelectEnabled() and - e.target.classList.contains('selected') and - # mouse right click or ctrl click as right click on darwin platforms - (e.button is 2 or e.ctrlKey and process.platform is 'darwin') - return - - entryToSelect = e.target.closest('.entry') + if entryToSelect = e.target.closest('.entry') + e.stopPropagation() + + # return early if we're opening a contextual menu (right click) during multi-select mode + if @multiSelectEnabled() and + e.target.classList.contains('selected') and + # mouse right click or ctrl click as right click on darwin platforms + (e.button is 2 or e.ctrlKey and process.platform is 'darwin') + return - if e.shiftKey - @selectContinuousEntries(entryToSelect) - @showMultiSelectMenu() - # only allow ctrl click for multi selection on non darwin systems - else if e.metaKey or (e.ctrlKey and process.platform isnt 'darwin') - @selectMultipleEntries(entryToSelect) + if e.shiftKey + @selectContinuousEntries(entryToSelect) + @showMultiSelectMenu() + # only allow ctrl click for multi selection on non darwin systems + else if e.metaKey or (e.ctrlKey and process.platform isnt 'darwin') + @selectMultipleEntries(entryToSelect) - # only show the multi select menu if more then one file/directory is selected - @showMultiSelectMenu() if @selectedPaths().length > 1 - else - @selectEntry(entryToSelect) - @showFullMenu() + # only show the multi select menu if more then one file/directory is selected + @showMultiSelectMenu() if @selectedPaths().length > 1 + else + @selectEntry(entryToSelect) + @showFullMenu() onSideToggled: (newValue) -> @element.dataset.showOnRightSide = newValue @@ -889,82 +886,85 @@ class TreeView @list.classList.contains('multi-select') onDragEnter: (e) => - return if @rootDragAndDrop.isDragging(e) + if header = e.target.closest('.entry.directory > .header') + return if @rootDragAndDrop.isDragging(e) - e.stopPropagation() + e.stopPropagation() - entry = e.target.closest('.entry.directory > .header').parentNode - @dragEventCounts.set(entry, 0) unless @dragEventCounts.get(entry) - entry.classList.add('selected') if @dragEventCounts.get(entry) is 0 - @dragEventCounts.set(entry, @dragEventCounts.get(entry) + 1) + entry = header.parentNode + @dragEventCounts.set(entry, 0) unless @dragEventCounts.get(entry) + entry.classList.add('selected') if @dragEventCounts.get(entry) is 0 + @dragEventCounts.set(entry, @dragEventCounts.get(entry) + 1) onDragLeave: (e) => - return if @rootDragAndDrop.isDragging(e) + if header = e.target.closest('.entry.directory > .header') + return if @rootDragAndDrop.isDragging(e) - e.stopPropagation() + e.stopPropagation() - entry = e.target.closest('.entry.directory > .header').parentNode - @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) - entry.classList.remove('selected') if @dragEventCounts.get(entry) is 0 + entry = header.parentNode + @dragEventCounts.set(entry, @dragEventCounts.get(entry) - 1) + entry.classList.remove('selected') if @dragEventCounts.get(entry) is 0 # Handle entry name object dragstart event onDragStart: (e) -> - e.stopPropagation() + if entry = e.target.closest('.entry') + e.stopPropagation() - if @rootDragAndDrop.canDragStart(e) - return @rootDragAndDrop.onDragStart(e) + if @rootDragAndDrop.canDragStart(e) + return @rootDragAndDrop.onDragStart(e) - target = e.target.closest('.entry').querySelector(".name") - initialPath = target.dataset.path + target = entry.querySelector(".name") + initialPath = target.dataset.path - fileNameElement = target.cloneNode(true) - for key, value of getStyleObject(target) - fileNameElement.style[key] = value - fileNameElement.style.position = 'absolute' - fileNameElement.style.top = 0 - fileNameElement.style.left = 0 + fileNameElement = target.cloneNode(true) + for key, value of getStyleObject(target) + fileNameElement.style[key] = value + fileNameElement.style.position = 'absolute' + fileNameElement.style.top = 0 + fileNameElement.style.left = 0 - document.body.appendChild(fileNameElement) + document.body.appendChild(fileNameElement) - e.dataTransfer.effectAllowed = "move" - e.dataTransfer.setDragImage(fileNameElement, 0, 0) - e.dataTransfer.setData("initialPath", initialPath) + e.dataTransfer.effectAllowed = "move" + e.dataTransfer.setDragImage(fileNameElement, 0, 0) + e.dataTransfer.setData("initialPath", initialPath) - window.requestAnimationFrame -> - fileNameElement.remove() + window.requestAnimationFrame -> + fileNameElement.remove() # Handle entry dragover event; reset default dragover actions onDragOver: (e) -> - return if @rootDragAndDrop.isDragging(e) + if entry = e.target.closest('.entry') + return if @rootDragAndDrop.isDragging(e) - e.preventDefault() - e.stopPropagation() + e.preventDefault() + e.stopPropagation() - entry = e.target.closest('.entry') - if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') - entry.classList.add('selected') + if @dragEventCounts.get(entry) > 0 and not entry.classList.contains('selected') + entry.classList.add('selected') # Handle entry drop event onDrop: (e) -> - return if @rootDragAndDrop.isDragging(e) + if entry = e.target.closest('.entry') + return if @rootDragAndDrop.isDragging(e) - e.preventDefault() - e.stopPropagation() + e.preventDefault() + e.stopPropagation() - entry = e.target.closest('.entry') - entry.classList.remove('selected') + entry.classList.remove('selected') - return unless entry.classList.contains('directory') + return unless entry.classList.contains('directory') - newDirectoryPath = entry.querySelector('.name')?.dataset.path - return false unless newDirectoryPath + newDirectoryPath = entry.querySelector('.name')?.dataset.path + return false unless newDirectoryPath - initialPath = e.dataTransfer.getData("initialPath") + initialPath = e.dataTransfer.getData("initialPath") - if initialPath - # Drop event from Atom - @moveEntry(initialPath, newDirectoryPath) - else - # Drop event from OS - for file in e.dataTransfer.files - @moveEntry(file.path, newDirectoryPath) + if initialPath + # Drop event from Atom + @moveEntry(initialPath, newDirectoryPath) + else + # Drop event from OS + for file in e.dataTransfer.files + @moveEntry(file.path, newDirectoryPath) From 5c65c5b49e1d2df12b193a7101886ec6473ad843 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Feb 2017 18:40:38 +0100 Subject: [PATCH 140/263] Don't throw an error if canceling a dialog when the tree-view is hidden --- lib/dialog.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialog.coffee b/lib/dialog.coffee index 1ab74a85..db6b6fa9 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -60,7 +60,7 @@ class Dialog cancel: -> @close() - document.querySelector('.tree-view').focus() + document.querySelector('.tree-view')?.focus() showError: (message='') -> @errorMessage.textContent = message From 4573a61d9d35f0aba0fb4446a01f49e80ebdaba7 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 8 Feb 2017 10:48:02 -0800 Subject: [PATCH 141/263] Revert ":arrow_up: pathwatcher 6.8.1" This reverts commit 48d10d8e53706ca78b3f22fa53c42e4b261b15be. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9b90367b..717cdf3f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "event-kit": "^1.0.0", "fs-plus": "^2.3.0", "minimatch": "~0.3.0", - "pathwatcher": "6.8.1", + "pathwatcher": "6.8.0-1", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From c3454573210c753a5e82043ada664f6024cf123f Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 8 Feb 2017 10:50:37 -0800 Subject: [PATCH 142/263] Ensure open in file explorer works, fixes #1034 --- lib/tree-view.coffee | 2 +- spec/tree-view-spec.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 50c85e81..8ecc5175 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -510,7 +510,7 @@ class TreeView extends View handleError(errorMessage) if failed - showProcess = new BufferedProcess({command, args, options: {shell: false}, stderr, exit}) + showProcess = new BufferedProcess({command, args, stderr, exit}) showProcess.onWillThrowError ({error, handle}) -> handle() handleError(error?.message) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index cac633d0..6682b372 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3113,7 +3113,7 @@ describe "TreeView", -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'failed' expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'bad process' - it "handle errors thrown when spawning the OS file manager", -> + ffit "handle errors thrown when spawning the OS file manager", -> spyOn(treeView, 'fileManagerCommandForPath').andReturn command: path.normalize('/this/command/does/not/exist') label: 'OS file manager' @@ -3126,6 +3126,7 @@ describe "TreeView", -> runs -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Opening folder in OS file manager failed' + expect(atom.notifications.getNotifications()[0].getDetail()).toContain if process.platform is 'win32' then 'cannot find the path' else 'ENOENT' describe "showCurrentFileInFileManager()", -> it "does nothing when no file is opened", -> From b2c609f39ccce3af13eca91bf3371f9d644728b0 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 8 Feb 2017 11:00:47 -0800 Subject: [PATCH 143/263] Focused tests... we should add a linter rule --- spec/tree-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 6682b372..093a238d 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3113,7 +3113,7 @@ describe "TreeView", -> expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'failed' expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'bad process' - ffit "handle errors thrown when spawning the OS file manager", -> + it "handle errors thrown when spawning the OS file manager", -> spyOn(treeView, 'fileManagerCommandForPath').andReturn command: path.normalize('/this/command/does/not/exist') label: 'OS file manager' From a9f01d4b27fdcf54bb8da0d61fc55d95d4d275c5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 8 Feb 2017 12:52:14 -0700 Subject: [PATCH 144/263] Prepare 0.214.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 717cdf3f..0c3871c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.214.0", + "version": "0.214.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From a7e7be1800b066911890f3b4deeb87a394d7b74f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Feb 2017 16:40:10 +0100 Subject: [PATCH 145/263] Ensure an entry is selected before performing certain actions --- lib/tree-view.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 6196e157..ceb93f5a 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -423,6 +423,8 @@ class TreeView expandDirectory: (isRecursive=false) -> selectedEntry = @selectedEntry() + return unless selectedEntry? + if isRecursive is false and selectedEntry.isExpanded @moveDown() if selectedEntry.directory.getEntries().length > 0 else @@ -438,6 +440,8 @@ class TreeView openSelectedEntry: (options={}, expandDirectory=false) -> selectedEntry = @selectedEntry() + return unless selectedEntry? + if selectedEntry.classList.contains('directory') if expandDirectory @expandDirectory(false) @@ -450,6 +454,8 @@ class TreeView openSelectedEntrySplit: (orientation, side) -> selectedEntry = @selectedEntry() + return unless selectedEntry? + pane = atom.workspace.getActivePane() if pane and selectedEntry.classList.contains('file') if atom.workspace.getActivePaneItem() @@ -472,6 +478,8 @@ class TreeView openSelectedEntryInPane: (index) -> selectedEntry = @selectedEntry() + return unless selectedEntry? + pane = atom.workspace.getPanes()[index] if pane and selectedEntry.classList.contains('file') atom.workspace.openURIInPane selectedEntry.getPath(), pane From a13053b733ab820bd7943f64e7b76428e3a4b411 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 2 Mar 2017 11:51:49 +0100 Subject: [PATCH 146/263] Require tree-view eagerly --- lib/main.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.coffee b/lib/main.coffee index 680abdd8..eca79d80 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -2,6 +2,7 @@ path = require 'path' FileIcons = require './file-icons' +TreeView = require './tree-view' module.exports = treeView: null @@ -47,7 +48,6 @@ module.exports = createView: -> unless @treeView? - TreeView = require './tree-view' @treeView = new TreeView(@state) @treeView From 4a489c6a73e988a81d802cc572996be02ebb86e3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Mar 2017 12:07:16 +0100 Subject: [PATCH 147/263] Prepare 0.215.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44745806..d6142460 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.214.1", + "version": "0.215.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 4663e3c00cd65be6966adba20be09674de08d24e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Mar 2017 12:21:22 +0100 Subject: [PATCH 148/263] :arrow_up: pathwatcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6142460..35111b8b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "event-kit": "^1.0.0", "fs-plus": "^2.3.0", "minimatch": "~0.3.0", - "pathwatcher": "6.8.0-1", + "pathwatcher": "6.9.0", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From 461d8b08960fcc9afb8f0d27e97a3640c22ccc80 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Mar 2017 12:21:40 +0100 Subject: [PATCH 149/263] Prepare 0.215.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35111b8b..a13fef28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.215.0", + "version": "0.215.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From a7251ffb812223210d8927d72ac841f3af1f7261 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Mar 2017 12:33:56 +0100 Subject: [PATCH 150/263] :arrow_up: fs-plus and pathwatcher --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a13fef28..0ce71642 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "private": true, "dependencies": { "event-kit": "^1.0.0", - "fs-plus": "^2.3.0", + "fs-plus": "^3.0.0", "minimatch": "~0.3.0", - "pathwatcher": "6.9.0", + "pathwatcher": "^7.0.0", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From 3beaa0b1404a63b091fb0f5f141ab28acd558c98 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 6 Mar 2017 12:35:04 +0100 Subject: [PATCH 151/263] Prepare 0.215.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ce71642..c53fed3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.215.1", + "version": "0.215.2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 6d07f77b4735c91cd324d974a72b69aa2027b462 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 9 Mar 2017 19:09:35 -0800 Subject: [PATCH 152/263] Update editor paths when containing directory is renamed via move dialog --- lib/move-dialog.coffee | 8 ++++++++ spec/tree-view-spec.coffee | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/move-dialog.coffee b/lib/move-dialog.coffee index 69d50d8e..1498bf67 100644 --- a/lib/move-dialog.coffee +++ b/lib/move-dialog.coffee @@ -36,6 +36,7 @@ class MoveDialog extends Dialog try fs.makeTreeSync(directoryPath) unless fs.existsSync(directoryPath) fs.moveSync(@initialPath, newPath) + @updateEditorForPath(@initialPath, newPath) if repo = repoForPath(newPath) repo.getPathStatus(@initialPath) repo.getPathStatus(newPath) @@ -56,3 +57,10 @@ class MoveDialog extends Dialog oldStat.ino is newStat.ino catch true # new path does not exist so it is valid + + updateEditorForPath: (oldPath, newPath) -> + editors = atom.workspace.getTextEditors() + for editor in editors + filePath = editor.getPath() + if filePath.startsWith(oldPath) + editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index afac93fa..07569f1b 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2162,6 +2162,42 @@ describe "TreeView", -> expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) expect(moveDialog.miniEditor.getSelectedText()).toBe '.dotfile' + describe "when a subdirectory is selected", -> + moveDialog = null + + beforeEach -> + jasmine.attachToDOM(workspaceElement) + + waitsForFileToOpen -> + atom.workspace.open(filePath) + + runs -> + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.toggleFocus() + atom.commands.dispatch(treeView.element, "tree-view:move") + moveDialog = atom.workspace.getModalPanels()[0].getItem() + + 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 folder's current path populated", -> + extension = path.extname(dirPath) + expect(moveDialog.element).toExist() + expect(moveDialog.promptText.textContent).toBe "Enter the new path for the directory." + expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(dirPath)) + expect(moveDialog.miniEditor.element).toHaveFocus() + + describe "when the path is changed and confirmed", -> + it "updates text editor paths accordingly", -> + editor = atom.workspace.getActiveTextEditor() + expect(editor.getPath()).toBe(filePath) + + newPath = path.join(rootDirPath, 'renamed-dir') + moveDialog.miniEditor.setText(newPath) + + atom.commands.dispatch moveDialog.element, 'core:confirm' + expect(editor.getPath()).toBe(filePath.replace('test-dir', 'renamed-dir')) + describe "when the project is selected", -> it "doesn't display the move dialog", -> treeView.roots[0].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) From 081ead99d1ffc7b181a43f0274607c621c0ddcd0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 9 Mar 2017 19:35:01 -0800 Subject: [PATCH 153/263] Update editor paths when containing directory is renamed via drag'n'drop --- lib/tree-view.coffee | 8 ++++++++ spec/tree-view-spec.coffee | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 5b7040aa..7a92aa6e 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -795,6 +795,7 @@ class TreeView try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) + @updateEditorForPath(initialPath, newPath) if repo = repoForPath(newPath) repo.getPathStatus(initialPath) @@ -803,6 +804,13 @@ class TreeView catch error atom.notifications.addWarning("Failed to move entry #{initialPath} to #{newDirectoryPath}", detail: error.message) + updateEditorForPath: (oldPath, newPath) -> + editors = atom.workspace.getTextEditors() + for editor in editors + filePath = editor.getPath() + if filePath.startsWith(oldPath) + editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) + onStylesheetsChanged: => return unless @isVisible() diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 07569f1b..2288829d 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -3191,6 +3191,7 @@ describe "TreeView", -> describe "Dragging and dropping files", -> deltaFilePath = null gammaDirPath = null + thetaFilePath = null beforeEach -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) @@ -3206,6 +3207,7 @@ describe "TreeView", -> deltaFilePath = path.join(gammaDirPath, "delta.txt") epsilonFilePath = path.join(gammaDirPath, "epsilon.txt") thetaDirPath = path.join(gammaDirPath, "theta") + thetaFilePath = path.join(thetaDirPath, "theta.txt") fs.writeFileSync(alphaFilePath, "doesn't matter") fs.writeFileSync(zetaFilePath, "doesn't matter") @@ -3218,6 +3220,7 @@ describe "TreeView", -> fs.writeFileSync(deltaFilePath, "doesn't matter") fs.writeFileSync(epsilonFilePath, "doesn't matter") fs.makeTreeSync(thetaDirPath) + fs.writeFileSync(thetaFilePath, "doesn't matter") atom.project.setPaths([rootDirPath]) @@ -3278,11 +3281,14 @@ describe "TreeView", -> gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') gammaDir.expand() thetaDir = gammaDir.entries.children[0] + thetaDir.expand() - [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.querySelector('.header'), alphaDir) + waitsForFileToOpen -> + atom.workspace.open(thetaFilePath) runs -> + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.querySelector('.header'), alphaDir) treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) expect(alphaDir.children.length).toBe 2 @@ -3292,6 +3298,8 @@ describe "TreeView", -> runs -> expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + editor = atom.workspace.getActiveTextEditor() + expect(editor.getPath()).toBe(thetaFilePath.replace('gamma', 'alpha')) describe "when dragging a file from the OS onto a DirectoryView's header", -> it "should move the file to the hovered directory", -> From 49c6893d7a8d8c6a7a0212f6c4ef51c3a60efdca Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 9 Mar 2017 19:37:47 -0800 Subject: [PATCH 154/263] Extract helper method updateEditorsForPath --- lib/helpers.coffee | 7 +++++++ lib/move-dialog.coffee | 11 ++--------- lib/tree-view.coffee | 11 ++--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/helpers.coffee b/lib/helpers.coffee index c67e0d97..1df45dd0 100644 --- a/lib/helpers.coffee +++ b/lib/helpers.coffee @@ -22,3 +22,10 @@ module.exports = fullExtension = extension + fullExtension filePath = path.basename(filePath, extension) fullExtension + + updateEditorsForPath: (oldPath, newPath) -> + editors = atom.workspace.getTextEditors() + for editor in editors + filePath = editor.getPath() + if filePath.startsWith(oldPath) + editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) diff --git a/lib/move-dialog.coffee b/lib/move-dialog.coffee index 1498bf67..0be65527 100644 --- a/lib/move-dialog.coffee +++ b/lib/move-dialog.coffee @@ -1,7 +1,7 @@ path = require 'path' fs = require 'fs-plus' Dialog = require './dialog' -{repoForPath} = require "./helpers" +{repoForPath, updateEditorsForPath} = require "./helpers" module.exports = class MoveDialog extends Dialog @@ -36,7 +36,7 @@ class MoveDialog extends Dialog try fs.makeTreeSync(directoryPath) unless fs.existsSync(directoryPath) fs.moveSync(@initialPath, newPath) - @updateEditorForPath(@initialPath, newPath) + updateEditorsForPath(@initialPath, newPath) if repo = repoForPath(newPath) repo.getPathStatus(@initialPath) repo.getPathStatus(newPath) @@ -57,10 +57,3 @@ class MoveDialog extends Dialog oldStat.ino is newStat.ino catch true # new path does not exist so it is valid - - updateEditorForPath: (oldPath, newPath) -> - editors = atom.workspace.getTextEditors() - for editor in editors - filePath = editor.getPath() - if filePath.startsWith(oldPath) - editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 7a92aa6e..8f494e7f 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -3,7 +3,7 @@ path = require 'path' _ = require 'underscore-plus' {BufferedProcess, CompositeDisposable} = require 'atom' -{repoForPath, getStyleObject, getFullExtension} = require "./helpers" +{repoForPath, getStyleObject, getFullExtension, updateEditorsForPath} = require "./helpers" fs = require 'fs-plus' AddDialog = require './add-dialog' @@ -795,7 +795,7 @@ class TreeView try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) - @updateEditorForPath(initialPath, newPath) + updateEditorsForPath(initialPath, newPath) if repo = repoForPath(newPath) repo.getPathStatus(initialPath) @@ -804,13 +804,6 @@ class TreeView catch error atom.notifications.addWarning("Failed to move entry #{initialPath} to #{newDirectoryPath}", detail: error.message) - updateEditorForPath: (oldPath, newPath) -> - editors = atom.workspace.getTextEditors() - for editor in editors - filePath = editor.getPath() - if filePath.startsWith(oldPath) - editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) - onStylesheetsChanged: => return unless @isVisible() From 06d227f27a59f19dcc31e2ae9b0f67d296d8f8ba Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 9 Mar 2017 22:20:24 -0800 Subject: [PATCH 155/263] Close editors when containing directory is removed --- lib/helpers.coffee | 2 +- lib/tree-view.coffee | 2 +- spec/tree-view-spec.coffee | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/helpers.coffee b/lib/helpers.coffee index 1df45dd0..6f31695d 100644 --- a/lib/helpers.coffee +++ b/lib/helpers.coffee @@ -27,5 +27,5 @@ module.exports = editors = atom.workspace.getTextEditors() for editor in editors filePath = editor.getPath() - if filePath.startsWith(oldPath) + if filePath?.startsWith(oldPath) editor.getBuffer().setPath(filePath.replace(oldPath, newPath)) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 8f494e7f..9536f427 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -607,7 +607,7 @@ class TreeView for selectedPath in selectedPaths if shell.moveItemToTrash(selectedPath) for editor in atom.workspace.getTextEditors() - if editor?.getPath() is selectedPath + if editor?.getPath()?.startsWith(selectedPath) editor.destroy() else failedDeletions.push "#{selectedPath}" diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index 2288829d..049a2068 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -2401,6 +2401,29 @@ describe "TreeView", -> expect(atom.confirm.mostRecentCall).not.toExist expect(atom.notifications.getNotifications().length).toBe 0 + describe "when a directory is removed", -> + it "closes editors with files belonging to the removed folder", -> + jasmine.attachToDOM(workspaceElement) + + waitsForFileToOpen -> + atom.workspace.open(filePath2) + + waitsForFileToOpen -> + atom.workspace.open(filePath3) + + runs -> + openFilePaths = atom.workspace.getTextEditors().map((e) -> e.getPath()) + expect(openFilePaths).toEqual([filePath2, filePath3]) + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.toggleFocus() + + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + openFilePaths = (editor.getPath() for editor in atom.workspace.getTextEditors()) + expect(openFilePaths).toEqual([]) + describe "file system events", -> temporaryFilePath = null From 9b17cabbfea8d8c83716b5d359f2ab8c9f4a89ef Mon Sep 17 00:00:00 2001 From: Matthew Dapena-Tretter Date: Thu, 16 Mar 2017 10:17:33 -0700 Subject: [PATCH 156/263] Only check panes in center atom/atom#13977 adds multiple pane locations and updates the `atom.workspace.getPanes()` API to return the panes from all of them. This fixes the resultant test failures by only checking the panes in the center. --- spec/tree-view-spec.coffee | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index afac93fa..a5367f1c 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -30,6 +30,9 @@ setupPaneFiles = -> getPaneFileName = (index) -> "test-file-#{index}.txt" +# TODO: Remove this after atom/atom#13977 lands in favor of unguarded `getCenter()` calls +getCenter = -> atom.workspace.getCenter?() ? atom.workspace + describe "TreeView", -> [treeView, path1, path2, root1, root2, sampleJs, sampleTxt, workspaceElement] = [] @@ -1321,7 +1324,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened all windows", -> - expect(atom.workspace.getPanes().length).toBe 9 + expect(getCenter().getPanes().length).toBe 9 [0..8].forEach (index) -> paneNumber = index + 1 @@ -1335,7 +1338,7 @@ describe "TreeView", -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> - pane = atom.workspace.getPanes()[index] + pane = getCenter().getPanes()[index] item = atom.workspace.getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -1352,7 +1355,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened all windows", -> - expect(atom.workspace.getPanes().length).toBe 9 + expect(getCenter().getPanes().length).toBe 9 [0..8].forEach (index) -> paneNumber = index + 1 @@ -1368,7 +1371,7 @@ describe "TreeView", -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> - pane = atom.workspace.getPanes()[index] + pane = getCenter().getPanes()[index] item = atom.workspace.getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve(fileName) @@ -3353,21 +3356,21 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened both panes", -> - expect(atom.workspace.getPanes().length).toBe 2 + expect(getCenter().getPanes().length).toBe 2 describe "tree-view:open-selected-entry", -> beforeEach -> atom.config.set "tree-view.alwaysOpenExisting", true describe "when the first pane is focused, a file is opened that is already open in the second pane", -> beforeEach -> - firstPane = atom.workspace.getPanes()[0] + firstPane = getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' waitsForFileToOpen -> atom.commands.dispatch treeView.element, "tree-view:open-selected-entry" it "opens the file in the second pane and focuses it", -> - pane = atom.workspace.getPanes()[1] + pane = getCenter().getPanes()[1] item = atom.workspace.getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -3380,7 +3383,7 @@ describe "TreeView", -> describe "when the first pane is focused, a file is opened that is already open in the second pane", -> firstPane = null beforeEach -> - firstPane = atom.workspace.getPanes()[0] + firstPane = getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' waitsForFileToOpen -> @@ -3398,7 +3401,7 @@ describe "TreeView", -> describe "when core.allowPendingPaneItems is set to true (default)", -> firstPane = activePaneItem = null beforeEach -> - firstPane = atom.workspace.getPanes()[0] + firstPane = getCenter().getPanes()[0] firstPane.activate() treeView.focus() @@ -3423,7 +3426,7 @@ describe "TreeView", -> activePaneItem = null beforeEach -> - firstPane = atom.workspace.getPanes()[0] + firstPane = getCenter().getPanes()[0] firstPane.activate() treeView.focus() @@ -3440,7 +3443,7 @@ describe "TreeView", -> it "opens the file and focuses it", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') - expect(atom.views.getView(atom.workspace.getPanes()[1])).toHaveFocus() + expect(atom.views.getView(getCenter().getPanes()[1])).toHaveFocus() describe "Dragging and dropping root folders", -> [alphaDirPath, gammaDirPath, thetaDirPath, etaDirPath] = [] From d90a0c2ddd934d0af7c6fc8f49d58cf836104991 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 20 Mar 2017 11:22:18 -0700 Subject: [PATCH 157/263] Prepare 0.215.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c53fed3e..0c974ce7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.215.2", + "version": "0.215.3", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 15f87c7ec3e13b0ffe3355fdb01f36ecd192228a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 11:50:12 -0700 Subject: [PATCH 158/263] Update Move and CopyDialog to take callbacks --- lib/copy-dialog.coffee | 6 +++--- lib/move-dialog.coffee | 4 ++-- lib/tree-view.coffee | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/copy-dialog.coffee b/lib/copy-dialog.coffee index 3c260c61..bd484b9b 100644 --- a/lib/copy-dialog.coffee +++ b/lib/copy-dialog.coffee @@ -5,7 +5,7 @@ Dialog = require './dialog' module.exports = class CopyDialog extends Dialog - constructor: (@initialPath) -> + constructor: (@initialPath, {@onCopyCallback}) -> super prompt: 'Enter the new path for the duplicate.' initialPath: atom.project.relativize(@initialPath) @@ -32,10 +32,10 @@ class CopyDialog extends Dialog try if fs.isDirectorySync(@initialPath) fs.copySync(@initialPath, newPath) - @emitter.emit 'entry-copied', [@initialPath, newPath] + @onCopyCallback?({initialPath: @initialPath, newPath: newPath}) else fs.copy @initialPath, newPath, => - @emitter.emit 'entry-copied', [@initialPath, newPath] + @onCopyCallback?({initialPath: @initialPath, newPath: newPath}) atom.workspace.open newPath, activatePane: true initialLine: activeEditor?.getLastCursor().getBufferRow() diff --git a/lib/move-dialog.coffee b/lib/move-dialog.coffee index 9d53ac6d..4a68e9f1 100644 --- a/lib/move-dialog.coffee +++ b/lib/move-dialog.coffee @@ -5,7 +5,7 @@ Dialog = require './dialog' module.exports = class MoveDialog extends Dialog - constructor: (@initialPath) -> + constructor: (@initialPath, {@onMoveCallback}) -> if fs.isDirectorySync(@initialPath) prompt = 'Enter the new path for the directory.' else @@ -36,7 +36,7 @@ class MoveDialog extends Dialog try fs.makeTreeSync(directoryPath) unless fs.existsSync(directoryPath) fs.moveSync(@initialPath, newPath) - @emitter.emit 'entry-moved', [@initialPath, newPath] + @onMoveCallback?(initialPath: @initialPath, newPath: newPath) if repo = repoForPath(newPath) repo.getPathStatus(@initialPath) repo.getPathStatus(newPath) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 9234cf9e..1b8f41ac 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -509,9 +509,9 @@ class TreeView oldPath = @getActivePath() if oldPath - dialog = new MoveDialog(oldPath) - dialog.emitter.on 'entry-moved', ([oldPath, newPath]) => - @emitter.emit 'entry-moved', {oldPath, newPath} + dialog = new MoveDialog oldPath, + onMoveCallback: ({initialPath, newPath}) => + @emitter.emit 'entry-moved', {initialPath, newPath} dialog.attach() # Get the outline of a system call to the current platform's file manager. @@ -598,9 +598,9 @@ class TreeView oldPath = @getActivePath() return unless oldPath - dialog = new CopyDialog(oldPath) - dialog.emitter.on 'entry-copied', (oldPath, newPath) => - @emitter.emit 'entry-copied', {oldPath, newPath} + dialog = new CopyDialog oldPath, + onCopyCallback: ({initialPath, newPath}) => + @emitter.emit 'entry-copied', {initialPath, newPath} dialog.attach() removeSelectedEntries: -> From 2642b825720a2e6ef0b1ffcb2f3bb5aec324953d Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 11:50:29 -0700 Subject: [PATCH 159/263] Standardize event params --- lib/tree-view.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 1b8f41ac..a5610844 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -721,19 +721,19 @@ class TreeView # use fs.copy to copy directories since read/write will fail for directories catchAndShowFileErrors => fs.copySync(initialPath, newPath) - @emitter.emit 'entry-copied', {oldPath: initialPath, newPath} + @emitter.emit 'entry-copied', {initialPath, newPath} else # read the old file and write a new one at target location catchAndShowFileErrors => fs.writeFileSync(newPath, fs.readFileSync(initialPath)) - @emitter.emit 'entry-copied', {oldPath: initialPath, newPath} + @emitter.emit 'entry-copied', {initialPath, newPath} else if cutPaths # Only move the target if the cut target doesn't exist and if the newPath # is not within the initial path unless fs.existsSync(newPath) or newPath.startsWith(initialPath) catchAndShowFileErrors => fs.moveSync(initialPath, newPath) - @emitter.emit 'entry-moved', {oldPath: initialPath, newPath} + @emitter.emit 'entry-moved', {initialPath, newPath} add: (isCreatingFile) -> selectedEntry = @selectedEntry() ? @roots[0] @@ -824,7 +824,8 @@ class TreeView try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) - @emitter.emit 'entry-moved', {oldPath: initialPath, newPath} + console.log "MOVED!", initialPath, newPath + @emitter.emit 'entry-moved', {initialPath, newPath} if repo = repoForPath(newPath) repo.getPathStatus(initialPath) From b86a3287df628a5b67172a5112dcd98125dcbfa5 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 11:50:38 -0700 Subject: [PATCH 160/263] Update tests --- spec/tree-view-spec.coffee | 156 +++++++++++++++++++++---------------- 1 file changed, 87 insertions(+), 69 deletions(-) diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee index bf30c582..c7dc3af3 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-spec.coffee @@ -1493,26 +1493,24 @@ describe "TreeView", -> LocalStorage.clear() describe "when attempting to paste a directory into itself", -> - handlers = null - describe "when copied", -> beforeEach -> LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) - handlers = treeView.emitter.handlersByEventName - - dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + it "makes a copy inside itself", -> newPath = path.join(dirPath, path.basename(dirPath)) + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() expect(fs.existsSync(newPath)).toBeTruthy() it "dispatches an event to the tree-view", -> - treeView.onEntryCopied -> - spyOn(handlers['entry-copied'], '0') + newPath = path.join(dirPath, path.basename(dirPath)) + callback = jasmine.createSpy("onEntryCopied") + treeView.onEntryCopied(callback) dirView.click() - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(handlers['entry-copied'][0].callCount).toEqual(1) + atom.commands.dispatch(treeView.element, "tree-view:paste") + expect(callback).toHaveBeenCalledWith(initialPath: dirPath, newPath: newPath) it 'does not keep copying recursively', -> LocalStorage['tree-view:copyPath'] = JSON.stringify([dirPath]) @@ -1554,14 +1552,21 @@ describe "TreeView", -> LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath]) it "creates a copy of the original file in the selected file's parent directory", -> - treeView.onEntryCopied -> - spyOn(treeView.emitter.handlersByEventName['entry-copied'], '0') fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() + newPath = path.join(dirPath2, path.basename(filePath)) + expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeTruthy() - expect(treeView.emitter.handlersByEventName['entry-copied'][0].callCount).toEqual(1) + + it "emits an event", -> + callback = jasmine.createSpy("onEntryCopied") + treeView.onEntryCopied(callback) + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + newPath = path.join(dirPath2, path.basename(filePath)) + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath: newPath}) describe "when the target already exists", -> it "appends a number to the destination name", -> @@ -1713,27 +1718,33 @@ describe "TreeView", -> expect(fs.existsSync(path.join(dirPath, "test-file30.txt"))).toBeTruthy() describe "when a file has been cut", -> - handlers = null - beforeEach -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - handlers = treeView.emitter.handlersByEventName - treeView.onEntryMoved -> - spyOn(handlers['entry-moved'], '0') describe "when a file is selected", -> it "creates a copy of the original file in the selected file's parent directory and removes the original", -> - LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) - fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() + newPath = path.join(dirPath2, path.basename(filePath)) + expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() - expect(handlers['entry-moved'][0].callCount).toEqual(1) + + it "emits an event", -> + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) + + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + newPath = path.join(dirPath2, path.basename(filePath)) + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath}) describe 'when the target destination file exists', -> it 'does not move the cut file', -> + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) + filePath3 = path.join(dirPath2, "test-file.txt") fs.writeFileSync(filePath3, "doesn't matter") @@ -1741,39 +1752,49 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, "tree-view:paste") expect(fs.existsSync(filePath)).toBeTruthy() - expect(handlers['entry-moved'][0].callCount).toEqual(0) + expect(callback).not.toHaveBeenCalled() describe "when a directory is selected", -> it "creates a copy of the original file in the selected directory and removes the original", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath]) + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(path.join(dirPath2, path.basename(filePath)))).toBeTruthy() + newPath = path.join(dirPath2, path.basename(filePath)) + expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() - expect(handlers['entry-moved'][0].callCount).toEqual(1) + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath}) describe "when multiple files have been cut", -> describe "when a file is selected", -> - handlers = null - beforeEach -> - handlers = treeView.emitter.handlersByEventName - treeView.onEntryMoved -> - spyOn(handlers['entry-moved'], '0') - - it "moves the selected files to the parent directory of the selected file", -> LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath2, filePath3]) + it "moves the selected files to the parent directory of the selected file", -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(path.join(dirPath, path.basename(filePath2)))).toBeTruthy() - expect(fs.existsSync(path.join(dirPath, path.basename(filePath3)))).toBeTruthy() + newPath2 = path.join(dirPath, path.basename(filePath2)) + newPath3 = path.join(dirPath, path.basename(filePath3)) + expect(fs.existsSync(newPath2)).toBeTruthy() + expect(fs.existsSync(newPath3)).toBeTruthy() expect(fs.existsSync(filePath2)).toBeFalsy() expect(fs.existsSync(filePath3)).toBeFalsy() - expect(handlers['entry-moved'][0].callCount).toEqual(2) + + it "emits events", -> + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + newPath2 = path.join(dirPath, path.basename(filePath2)) + newPath3 = path.join(dirPath, path.basename(filePath3)) + expect(callback.callCount).toEqual(2) + expect(callback).toHaveBeenCalledWith({initialPath: filePath2, newPath: newPath2}) + expect(callback).toHaveBeenCalledWith({initialPath: filePath3, newPath: newPath3}) describe 'when the target destination file exists', -> it 'does not move the cut file', -> @@ -1818,14 +1839,12 @@ describe "TreeView", -> expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT: no such file or directory' describe "tree-view:add-file", -> - [addPanel, addDialog, handlers] = [] + [addPanel, addDialog, callback] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) - - handlers = treeView.emitter.handlersByEventName - treeView.onFileCreated -> - spyOn(handlers['file-created'], '0') + callback = jasmine.createSpy("onFileCreated") + treeView.onFileCreated(callback) waitsForFileToOpen -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -1851,7 +1870,7 @@ describe "TreeView", -> describe "when the path without a trailing '#{path.sep}' is changed and confirmed", -> describe "when no file exists at that location", -> - it "adds a file, closes the dialog and selects the file in the tree-view", -> + it "adds a file, closes the dialog, selects the file in the tree-view, and emits an event", -> newPath = path.join(dirPath, "new-test-file.txt") waitsForFileToOpen -> @@ -1867,8 +1886,8 @@ describe "TreeView", -> dirView.entries.querySelectorAll(".file").length > 1 runs -> - expect(handlers['file-created'][0]).toHaveBeenCalled() expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) + expect(callback).toHaveBeenCalledWith({path: newPath}) it "adds file in any project path", -> newPath = path.join(dirPath3, "new-test-file.txt") @@ -1892,8 +1911,8 @@ describe "TreeView", -> dirView3.entries.querySelectorAll(".file").length > 1 runs -> - expect(handlers['file-created'][0]).toHaveBeenCalled() expect(treeView.element.querySelector('.file.selected').textContent).toBe path.basename(newPath) + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when a file already exists at that location", -> it "shows an error message and does not close the dialog", -> @@ -1905,7 +1924,7 @@ describe "TreeView", -> expect(addDialog.errorMessage.textContent).toContain 'already exists' expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel - expect(handlers['file-created'][0]).not.toHaveBeenCalled() + expect(callback).not.toHaveBeenCalled() describe "when the project has no path", -> it "adds a file and closes the dialog", -> @@ -1925,7 +1944,7 @@ describe "TreeView", -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).toBe(newPath) - expect(handlers['file-created'][0]).toHaveBeenCalled() + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> it "shows an error message and does not close the dialog", -> @@ -1935,12 +1954,14 @@ describe "TreeView", -> expect(addDialog.errorMessage.textContent).toContain 'names must not end with' expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel + expect(callback).not.toHaveBeenCalled() describe "when 'core:cancel' is triggered on the add dialog", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch addDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(callback).not.toHaveBeenCalled() describe "when the add dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -1959,6 +1980,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when a directory is selected", -> it "opens an add dialog with the directory's path populated", -> @@ -2006,14 +2028,12 @@ describe "TreeView", -> expect(addDialog.element.textContent).toContain("You must open a directory to create a file with a relative path") describe "tree-view:add-folder", -> - [addPanel, addDialog, handlers] = [] + [addPanel, addDialog, callback] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) - - handlers = treeView.emitter.handlersByEventName - treeView.onDirectoryCreated -> - spyOn(handlers['directory-created'], '0') + callback = jasmine.createSpy("onDirectoryCreated") + treeView.onDirectoryCreated(callback) waitsForFileToOpen -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -2044,11 +2064,11 @@ describe "TreeView", -> expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') - expect(handlers['directory-created'][0].callCount).toBe 1 + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> describe "when no directory exists at the given path", -> - it "adds a directory and closes the dialog", -> + it "adds a directory, closes the dialog, and emits an event", -> newPath = path.join(dirPath, 'new', 'dir') addDialog.miniEditor.insertText("new#{path.sep}dir#{path.sep}") atom.commands.dispatch addDialog.element, 'core:confirm' @@ -2058,7 +2078,7 @@ describe "TreeView", -> expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') - expect(handlers['directory-created'][0].callCount).toBe(1) + expect(callback).toHaveBeenCalledWith({path: newPath + path.sep}) it "selects the created directory and does not change the expansion state of existing directories", -> expandedPath = path.join(dirPath, 'expanded-dir') @@ -2077,8 +2097,8 @@ describe "TreeView", -> expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) expect(dirView.querySelector('.directory.selected').textContent).toBe('new2') - expect(handlers['directory-created'][0].callCount).toBe(1) expect(treeView.entryForPath(expandedPath).isExpanded).toBeTruthy() + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when the project has no path", -> it "adds a directory and closes the dialog", -> @@ -2093,8 +2113,8 @@ describe "TreeView", -> addDialog.miniEditor.insertText(newPath) atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() - expect(handlers['directory-created'][0].callCount).toBe 1 expect(atom.workspace.getModalPanels().length).toBe 0 + expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when a directory already exists at the given path", -> it "shows an error message and does not close the dialog", -> @@ -2106,18 +2126,16 @@ describe "TreeView", -> expect(addDialog.errorMessage.textContent).toContain 'already exists' expect(addDialog.element).toHaveClass('error') expect(atom.workspace.getModalPanels()[0]).toBe addPanel - expect(handlers['directory-created'][0].callCount).toBe 0 + expect(callback).not.toHaveBeenCalled() describe "tree-view:move", -> describe "when a file is selected", -> - [moveDialog, handlers] = [] + [moveDialog, callback] = [] beforeEach -> jasmine.attachToDOM(workspaceElement) - - handlers = treeView.emitter.handlersByEventName - treeView.onEntryMoved -> - spyOn(handlers['entry-moved'], '0') + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) waitsForFileToOpen -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -2140,7 +2158,7 @@ describe "TreeView", -> 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", -> + it "moves the file, updates the tree view, closes the dialog, and emits an event", -> newPath = path.join(rootDirPath, 'renamed-test-file.txt') moveDialog.miniEditor.setText(path.basename(newPath)) @@ -2158,7 +2176,7 @@ describe "TreeView", -> dirView = treeView.roots[0].querySelector('.directory') dirView.expand() expect(dirView.entries.children.length).toBe 0 - expect(handlers['entry-moved'][0].callCount).toBe 1 + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath}) describe "when the directories along the new path don't exist", -> it "creates the target directory before moving the file", -> @@ -2174,7 +2192,7 @@ describe "TreeView", -> runs -> expect(fs.existsSync(newPath)).toBeTruthy() expect(fs.existsSync(filePath)).toBeFalsy() - expect(handlers['entry-moved'][0].callCount).toBe 1 + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath}) describe "when a file or directory already exists at the target path", -> it "shows an error message and does not close the dialog", -> @@ -2188,7 +2206,7 @@ describe "TreeView", -> expect(moveDialog.errorMessage.textContent).toContain 'already exists' expect(moveDialog.element).toHaveClass('error') expect(moveDialog.element.parentElement).toBeTruthy() - expect(handlers['entry-moved'][0].callCount).toBe 0 + expect(callback).not.toHaveBeenCalled() describe "when 'core:cancel' is triggered on the move dialog", -> it "removes the dialog and focuses the tree view", -> @@ -2770,10 +2788,10 @@ describe "TreeView", -> describe "when the file is deleted", -> it "updates the style of the directory", -> - handlers = treeView.emitter.handlersByEventName - treeView.onEntryDeleted -> - spyOn(handlers['entry-deleted'], '0') + callback = jasmine.createSpy("onEntryDeleted") + treeView.onEntryDeleted(callback) + deletedPath = treeView.selectedEntry().getPath() expect(treeView.selectedEntry().getPath()).toContain(path.join('dir2', 'new2')) dirView = findDirectoryContainingText(treeView.roots[0], 'dir2') expect(dirView).not.toBeNull() @@ -2782,7 +2800,7 @@ describe "TreeView", -> dialog.buttons["Move to Trash"]() atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(dirView.directory.updateStatus).toHaveBeenCalled() - expect(handlers['entry-deleted'][0]).toHaveBeenCalled() + expect(callback).toHaveBeenCalledWith({path: deletedPath}) describe "on #darwin, when the project is a symbolic link to the repository root", -> beforeEach -> From 425f3965371a7f01a1af1ee709b2a3122cc5a42b Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 11:54:12 -0700 Subject: [PATCH 161/263] :fire: console.log --- lib/tree-view.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index a5610844..9f2d62f9 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -824,7 +824,6 @@ class TreeView try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) - console.log "MOVED!", initialPath, newPath @emitter.emit 'entry-moved', {initialPath, newPath} if repo = repoForPath(newPath) From 9a5e04b78b077b178c3ce87e12547ef934bff9b8 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 11:59:10 -0700 Subject: [PATCH 162/263] Betterer names --- lib/copy-dialog.coffee | 6 +++--- lib/move-dialog.coffee | 4 ++-- lib/tree-view.coffee | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/copy-dialog.coffee b/lib/copy-dialog.coffee index bd484b9b..aa510d84 100644 --- a/lib/copy-dialog.coffee +++ b/lib/copy-dialog.coffee @@ -5,7 +5,7 @@ Dialog = require './dialog' module.exports = class CopyDialog extends Dialog - constructor: (@initialPath, {@onCopyCallback}) -> + constructor: (@initialPath, {@onCopy}) -> super prompt: 'Enter the new path for the duplicate.' initialPath: atom.project.relativize(@initialPath) @@ -32,10 +32,10 @@ class CopyDialog extends Dialog try if fs.isDirectorySync(@initialPath) fs.copySync(@initialPath, newPath) - @onCopyCallback?({initialPath: @initialPath, newPath: newPath}) + @onCopy?({initialPath: @initialPath, newPath: newPath}) else fs.copy @initialPath, newPath, => - @onCopyCallback?({initialPath: @initialPath, newPath: newPath}) + @onCopy?({initialPath: @initialPath, newPath: newPath}) atom.workspace.open newPath, activatePane: true initialLine: activeEditor?.getLastCursor().getBufferRow() diff --git a/lib/move-dialog.coffee b/lib/move-dialog.coffee index 4a68e9f1..e10d5c85 100644 --- a/lib/move-dialog.coffee +++ b/lib/move-dialog.coffee @@ -5,7 +5,7 @@ Dialog = require './dialog' module.exports = class MoveDialog extends Dialog - constructor: (@initialPath, {@onMoveCallback}) -> + constructor: (@initialPath, {@onMove}) -> if fs.isDirectorySync(@initialPath) prompt = 'Enter the new path for the directory.' else @@ -36,7 +36,7 @@ class MoveDialog extends Dialog try fs.makeTreeSync(directoryPath) unless fs.existsSync(directoryPath) fs.moveSync(@initialPath, newPath) - @onMoveCallback?(initialPath: @initialPath, newPath: newPath) + @onMove?(initialPath: @initialPath, newPath: newPath) if repo = repoForPath(newPath) repo.getPathStatus(@initialPath) repo.getPathStatus(newPath) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 9f2d62f9..cd866402 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -510,7 +510,7 @@ class TreeView if oldPath dialog = new MoveDialog oldPath, - onMoveCallback: ({initialPath, newPath}) => + onMove: ({initialPath, newPath}) => @emitter.emit 'entry-moved', {initialPath, newPath} dialog.attach() @@ -599,7 +599,7 @@ class TreeView return unless oldPath dialog = new CopyDialog oldPath, - onCopyCallback: ({initialPath, newPath}) => + onCopy: ({initialPath, newPath}) => @emitter.emit 'entry-copied', {initialPath, newPath} dialog.attach() From b5d228dc1ffd8f44ca2856526532150a6eb95fad Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 25 Mar 2017 12:42:56 -0700 Subject: [PATCH 163/263] Use new hooks for updating editors --- lib/tree-view.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index d4b5a16a..5dc34ada 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -77,6 +77,11 @@ class TreeView @disposables.add @onEntryMoved ({initialPath, newPath}) -> updateEditorsForPath(initialPath, newPath) + @disposables.add @onEntryDeleted ({path}) -> + for editor in atom.workspace.getTextEditors() + if editor?.getPath()?.startsWith(path) + editor.destroy() + serialize: -> directoryExpansionStates: new ((roots) -> @[root.directory.path] = root.directory.serializeExpansionState() for root in roots @@ -630,9 +635,6 @@ class TreeView for selectedPath in selectedPaths if shell.moveItemToTrash(selectedPath) @emitter.emit 'entry-deleted', {path: selectedPath} - for editor in atom.workspace.getTextEditors() - if editor?.getPath()?.startsWith(selectedPath) - editor.destroy() else failedDeletions.push "#{selectedPath}" if repo = repoForPath(selectedPath) From 0dbc6c9d8a0129ef81db25a7dba77ff9cdc2c053 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 27 Mar 2017 11:39:43 -0700 Subject: [PATCH 164/263] Prepare 0.216.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c974ce7..bd5a261d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.215.3", + "version": "0.216.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 771f76e56a2eac95ad3851417fad672e329cc609 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 29 Mar 2017 14:39:37 +0200 Subject: [PATCH 165/263] Display the correct drag image on Electron >= 1.14 This commit ensures that the cloned file name element (i.e. the element that is shown next to the cursor while dragging) is rendered on a separate GPU layer. This will prevent overlapping elements located at (0px, 0px) from being used as the drag image. --- lib/tree-view.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 5b7040aa..747392e6 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -931,6 +931,10 @@ class TreeView fileNameElement.style.position = 'absolute' fileNameElement.style.top = 0 fileNameElement.style.left = 0 + # Ensure the cloned file name element is rendered on a separate GPU layer + # to prevent overlapping elements located at (0px, 0px) from being used as + # the drag image. + fileNameElement.style.willChange = 'transform' document.body.appendChild(fileNameElement) From c3725a6364676f21eaef95f0b0c58aa97d76ca4a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 29 Mar 2017 16:19:24 +0200 Subject: [PATCH 166/263] Fix missing drop indicator on Electron >= 1.14 The security model of new Chromium versions forbids all event listeners, except for `onDragStart` and `onDrop`, to inspect the data values that have been previously set on the event's `dataTransfer` object. To circumvent this problem we can exploit the fact that all we need to do is checking whether a boolean has been set or not. To do so, we will simply list all the items contained in the `dataTransfer` object and read their `kind` property (that we use as a dictionary key), verifying that at least one of them contains the key we are searching for. --- lib/root-drag-and-drop.coffee | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/root-drag-and-drop.coffee b/lib/root-drag-and-drop.coffee index f15b20e1..afdfb2c3 100644 --- a/lib/root-drag-and-drop.coffee +++ b/lib/root-drag-and-drop.coffee @@ -71,7 +71,7 @@ class RootDragAndDropHandler onDragOver: (e) => return unless @treeView.list.contains(e.target) - unless e.dataTransfer.getData('atom-tree-view-event') is 'true' + unless @isAtomTreeViewEvent(e) return e.preventDefault() @@ -121,7 +121,7 @@ class RootDragAndDropHandler {dataTransfer} = e # TODO: support dragging folders from the filesystem -- electron needs to add support first - return unless dataTransfer.getData('atom-tree-view-event') is 'true' + return unless @isAtomTreeViewEvent(e) fromWindowId = parseInt(dataTransfer.getData('from-window-id')) fromRootPath = dataTransfer.getData('from-root-path') @@ -171,7 +171,18 @@ class RootDragAndDropHandler e.target.closest('.project-root-header') isDragging: (e) -> - Boolean e.dataTransfer.getData 'from-root-path' + for item in e.dataTransfer.items + if item.type is 'from-root-path' + return true + + return false + + isAtomTreeViewEvent: (e) -> + for item in e.dataTransfer.items + if item.type is 'atom-tree-view-event' + return true + + return false getPlaceholder: -> unless @placeholderEl From c6b2269fd88e1b1494cd9f2d873e14e30adecc4a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 29 Mar 2017 16:54:02 +0200 Subject: [PATCH 167/263] Fix tests --- spec/event-helpers.coffee | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index c3a30323..2d1051ed 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -5,6 +5,13 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) -> getData: (key) -> @data[key] setDragImage: (@image) -> return + Object.defineProperty( + dataTransfer, + 'items', + get: -> + Object.keys(dataTransfer.data).map((key) -> {type: key}) + ) + dragStartEvent = new DragEvent('dragstart') Object.defineProperty(dragStartEvent, 'target', value: dragged) Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged) @@ -29,6 +36,13 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> getData: (key) -> @data[key] files: [] + Object.defineProperty( + dataTransfer, + 'items', + get: -> + Object.keys(dataTransfer.data).map((key) -> {type: key}) + ) + dropEvent = new DragEvent('drop') Object.defineProperty(dropEvent, 'target', value: dropTarget) Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget) @@ -73,6 +87,13 @@ module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelect getData: (key) -> @data[key] setDragImage: (@image) -> return + Object.defineProperty( + dataTransfer, + 'items', + get: -> + Object.keys(dataTransfer.data).map((key) -> {type: key}) + ) + dragStartEvent = new DragEvent('dragstart') Object.defineProperty(dragStartEvent, 'target', value: dragged) Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged) From e7b94519f68fd724ab1f9613f3e6d72302ccd313 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 30 Mar 2017 15:43:48 +0200 Subject: [PATCH 168/263] Prepare 0.216.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd5a261d..3c312096 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.216.0", + "version": "0.216.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 98f08efef6511e7866702c6fa8e3cd79129b2970 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 31 Mar 2017 17:39:17 -0600 Subject: [PATCH 169/263] Convert lib/main.coffee to JS --- lib/main.coffee | 64 ------------------------------------- lib/main.js | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 64 deletions(-) delete mode 100644 lib/main.coffee create mode 100644 lib/main.js diff --git a/lib/main.coffee b/lib/main.coffee deleted file mode 100644 index eca79d80..00000000 --- a/lib/main.coffee +++ /dev/null @@ -1,64 +0,0 @@ -{Disposable, CompositeDisposable} = require 'event-kit' -path = require 'path' - -FileIcons = require './file-icons' -TreeView = require './tree-view' - -module.exports = - treeView: null - - activate: (@state) -> - @disposables = new CompositeDisposable - @state.attached ?= true if @shouldAttach() - - @createView() if @state.attached - - @disposables.add atom.commands.add('atom-workspace', { - 'tree-view:show': => @createView().show() - 'tree-view:toggle': => @createView().toggle() - 'tree-view:toggle-focus': => @createView().toggleFocus() - 'tree-view:reveal-active-file': => @createView().revealActiveFile() - 'tree-view:toggle-side': => @createView().toggleSide() - 'tree-view:add-file': => @createView().add(true) - 'tree-view:add-folder': => @createView().add(false) - 'tree-view:duplicate': => @createView().copySelectedEntry() - 'tree-view:remove': => @createView().removeSelectedEntries() - 'tree-view:rename': => @createView().moveSelectedEntry() - 'tree-view:show-current-file-in-file-manager': => @createView().showCurrentFileInFileManager() - }) - - deactivate: -> - @disposables.dispose() - @fileIconsDisposable?.dispose() - @treeView?.deactivate() - @treeView = null - - consumeFileIcons: (service) -> - FileIcons.setService(service) - @treeView?.updateRoots() - new Disposable => - FileIcons.resetService() - @treeView?.updateRoots() - - serialize: -> - if @treeView? - @treeView.serialize() - else - @state - - createView: -> - unless @treeView? - @treeView = new TreeView(@state) - @treeView - - shouldAttach: -> - projectPath = atom.project.getPaths()[0] ? '' - if atom.workspace.getActivePaneItem() - false - else if path.basename(projectPath) is '.git' - # Only attach when the project path matches the path to open signifying - # the .git folder was opened explicitly and not by using Atom as the Git - # editor. - projectPath is atom.getLoadSettings().pathToOpen - else - true diff --git a/lib/main.js b/lib/main.js new file mode 100644 index 00000000..f685cfa3 --- /dev/null +++ b/lib/main.js @@ -0,0 +1,85 @@ +const {Disposable, CompositeDisposable} = require('event-kit') +const path = require('path') + +const FileIcons = require('./file-icons') +const TreeView = require('./tree-view') + +module.exports = { + treeView: null, + + activate (state) { + this.state = state + this.disposables = new CompositeDisposable() + if (this.shouldAttach()) { + if (this.state.attached == null) { + this.state.attached = true + } + } + + if (this.state.attached) { + this.createView() + } + + return this.disposables.add(atom.commands.add('atom-workspace', { + 'tree-view:show': () => this.createView().show(), + 'tree-view:toggle': () => this.createView().toggle(), + 'tree-view:toggle-focus': () => this.createView().toggleFocus(), + 'tree-view:reveal-active-file': () => this.createView().revealActiveFile(), + 'tree-view:toggle-side': () => this.createView().toggleSide(), + 'tree-view:add-file': () => this.createView().add(true), + 'tree-view:add-folder': () => this.createView().add(false), + 'tree-view:duplicate': () => this.createView().copySelectedEntry(), + 'tree-view:remove': () => this.createView().removeSelectedEntries(), + 'tree-view:rename': () => this.createView().moveSelectedEntry(), + 'tree-view:show-current-file-in-file-manager': () => this.createView().showCurrentFileInFileManager() + }) + ) + }, + + deactivate () { + this.disposables.dispose() + if (this.fileIconsDisposable) this.fileIconsDisposable.dispose() + if (this.treeView) this.treeView.deactivate() + this.treeView = null + }, + + consumeFileIcons (service) { + FileIcons.setService(service) + if (this.treeView) this.treeView.updateRoots() + return new Disposable(() => { + FileIcons.resetService() + if (this.treeView) this.treeView.updateRoots() + } + ) + }, + + serialize () { + if (this.treeView != null) { + return this.treeView.serialize() + } else { + return this.state + } + }, + + createView () { + if (this.treeView == null) { + this.treeView = new TreeView(this.state) + } + return this.treeView + }, + + shouldAttach () { + let left + const projectPath = (left = atom.project.getPaths()[0]) != null ? left : '' + if (atom.workspace.getActivePaneItem()) { + return false + } else if (path.basename(projectPath) === '.git') { + // Only attach when the project path matches the path to open signifying + // the .git folder was opened explicitly and not by using Atom as the Git + // editor. + return projectPath === atom.getLoadSettings().pathToOpen + } else { + return true + } + } +} From 8caafbf7c81354dccf65c50633e1295c4ede2808 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 31 Mar 2017 17:39:22 -0600 Subject: [PATCH 170/263] Extract package singleton class for easier testing --- lib/main.js | 86 +-------------------------------------- lib/tree-view-package.js | 88 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 84 deletions(-) create mode 100644 lib/tree-view-package.js diff --git a/lib/main.js b/lib/main.js index f685cfa3..88b8d199 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,85 +1,3 @@ -const {Disposable, CompositeDisposable} = require('event-kit') -const path = require('path') +const TreeViewPackage = require('./tree-view-package') -const FileIcons = require('./file-icons') -const TreeView = require('./tree-view') - -module.exports = { - treeView: null, - - activate (state) { - this.state = state - this.disposables = new CompositeDisposable() - if (this.shouldAttach()) { - if (this.state.attached == null) { - this.state.attached = true - } - } - - if (this.state.attached) { - this.createView() - } - - return this.disposables.add(atom.commands.add('atom-workspace', { - 'tree-view:show': () => this.createView().show(), - 'tree-view:toggle': () => this.createView().toggle(), - 'tree-view:toggle-focus': () => this.createView().toggleFocus(), - 'tree-view:reveal-active-file': () => this.createView().revealActiveFile(), - 'tree-view:toggle-side': () => this.createView().toggleSide(), - 'tree-view:add-file': () => this.createView().add(true), - 'tree-view:add-folder': () => this.createView().add(false), - 'tree-view:duplicate': () => this.createView().copySelectedEntry(), - 'tree-view:remove': () => this.createView().removeSelectedEntries(), - 'tree-view:rename': () => this.createView().moveSelectedEntry(), - 'tree-view:show-current-file-in-file-manager': () => this.createView().showCurrentFileInFileManager() - }) - ) - }, - - deactivate () { - this.disposables.dispose() - if (this.fileIconsDisposable) this.fileIconsDisposable.dispose() - if (this.treeView) this.treeView.deactivate() - this.treeView = null - }, - - consumeFileIcons (service) { - FileIcons.setService(service) - if (this.treeView) this.treeView.updateRoots() - return new Disposable(() => { - FileIcons.resetService() - if (this.treeView) this.treeView.updateRoots() - } - ) - }, - - serialize () { - if (this.treeView != null) { - return this.treeView.serialize() - } else { - return this.state - } - }, - - createView () { - if (this.treeView == null) { - this.treeView = new TreeView(this.state) - } - return this.treeView - }, - - shouldAttach () { - let left - const projectPath = (left = atom.project.getPaths()[0]) != null ? left : '' - if (atom.workspace.getActivePaneItem()) { - return false - } else if (path.basename(projectPath) === '.git') { - // Only attach when the project path matches the path to open signifying - // the .git folder was opened explicitly and not by using Atom as the Git - // editor. - return projectPath === atom.getLoadSettings().pathToOpen - } else { - return true - } - } -} +module.exports = new TreeViewPackage() diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js new file mode 100644 index 00000000..c6894df2 --- /dev/null +++ b/lib/tree-view-package.js @@ -0,0 +1,88 @@ +const {Disposable, CompositeDisposable} = require('event-kit') +const path = require('path') + +const FileIcons = require('./file-icons') +const TreeView = require('./tree-view') + +module.exports = +class TreeViewPackage { + constructor () { + this.treeView = null + } + + activate (state) { + this.state = state + this.disposables = new CompositeDisposable() + if (this.shouldAttach()) { + if (this.state.attached == null) { + this.state.attached = true + } + } + + if (this.state.attached) { + this.createView() + } + + return this.disposables.add(atom.commands.add('atom-workspace', { + 'tree-view:show': () => this.createView().show(), + 'tree-view:toggle': () => this.createView().toggle(), + 'tree-view:toggle-focus': () => this.createView().toggleFocus(), + 'tree-view:reveal-active-file': () => this.createView().revealActiveFile(), + 'tree-view:toggle-side': () => this.createView().toggleSide(), + 'tree-view:add-file': () => this.createView().add(true), + 'tree-view:add-folder': () => this.createView().add(false), + 'tree-view:duplicate': () => this.createView().copySelectedEntry(), + 'tree-view:remove': () => this.createView().removeSelectedEntries(), + 'tree-view:rename': () => this.createView().moveSelectedEntry(), + 'tree-view:show-current-file-in-file-manager': () => this.createView().showCurrentFileInFileManager() + }) + ) + } + + deactivate () { + this.disposables.dispose() + if (this.fileIconsDisposable) this.fileIconsDisposable.dispose() + if (this.treeView) this.treeView.deactivate() + this.treeView = null + } + + consumeFileIcons (service) { + FileIcons.setService(service) + if (this.treeView) this.treeView.updateRoots() + return new Disposable(() => { + FileIcons.resetService() + if (this.treeView) this.treeView.updateRoots() + } + ) + } + + serialize () { + if (this.treeView != null) { + return this.treeView.serialize() + } else { + return this.state + } + } + + createView () { + if (this.treeView == null) { + this.treeView = new TreeView(this.state) + } + return this.treeView + } + + shouldAttach () { + let left + const projectPath = (left = atom.project.getPaths()[0]) != null ? left : '' + if (atom.workspace.getActivePaneItem()) { + return false + } else if (path.basename(projectPath) === '.git') { + // Only attach when the project path matches the path to open signifying + // the .git folder was opened explicitly and not by using Atom as the Git + // editor. + return projectPath === atom.getLoadSettings().pathToOpen + } else { + return true + } + } +} From eebc217ed7d5a192fcb8142f1bdb1740e9f17140 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 31 Mar 2017 15:41:02 -0600 Subject: [PATCH 171/263] :art: --- lib/tree-view-package.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index c6894df2..2c2d8f86 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -72,8 +72,8 @@ class TreeViewPackage { } shouldAttach () { - let left - const projectPath = (left = atom.project.getPaths()[0]) != null ? left : '' + const projectPath = atom.project.getPaths()[0] || '' + if (atom.workspace.getActivePaneItem()) { return false } else if (path.basename(projectPath) === '.git') { From 9a70d1ea19c18c15bc3ecc539ab25c8436fbb4fa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 31 Mar 2017 17:39:37 -0600 Subject: [PATCH 172/263] WIP: Start on making tree view a dock item --- lib/constants.js | 1 + lib/tree-view-package.js | 92 ++++++++++++------------ lib/tree-view.coffee | 152 +++++++++++---------------------------- package.json | 8 +-- styles/tree-view.less | 61 +++------------- 5 files changed, 96 insertions(+), 218 deletions(-) create mode 100644 lib/constants.js diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 00000000..a0bcbf2b --- /dev/null +++ b/lib/constants.js @@ -0,0 +1 @@ +exports.TREE_VIEW_URI = 'atom://tree-view' diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index 2c2d8f86..f4bb9a38 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -3,40 +3,33 @@ const path = require('path') const FileIcons = require('./file-icons') const TreeView = require('./tree-view') +const {TREE_VIEW_URI} = require('./constants') module.exports = class TreeViewPackage { - constructor () { - this.treeView = null - } - - activate (state) { - this.state = state + activate () { this.disposables = new CompositeDisposable() - if (this.shouldAttach()) { - if (this.state.attached == null) { - this.state.attached = true - } - } - if (this.state.attached) { - this.createView() - } + this.disposables.add(atom.commands.add('atom-workspace', { + 'tree-view:show': () => this.getTreeViewInstance().show(), + 'tree-view:toggle': () => this.getTreeViewInstance().toggle(), + 'tree-view:toggle-focus': () => this.getTreeViewInstance().toggleFocus(), + 'tree-view:reveal-active-file': () => this.getTreeViewInstance().revealActiveFile(), + 'tree-view:toggle-side': () => this.getTreeViewInstance().toggleSide(), + 'tree-view:add-file': () => this.getTreeViewInstance().add(true), + 'tree-view:add-folder': () => this.getTreeViewInstance().add(false), + 'tree-view:duplicate': () => this.getTreeViewInstance().copySelectedEntry(), + 'tree-view:remove': () => this.getTreeViewInstance().removeSelectedEntries(), + 'tree-view:rename': () => this.getTreeViewInstance().moveSelectedEntry(), + 'tree-view:show-current-file-in-file-manager': () => this.getTreeViewInstance().showCurrentFileInFileManager() + })) - return this.disposables.add(atom.commands.add('atom-workspace', { - 'tree-view:show': () => this.createView().show(), - 'tree-view:toggle': () => this.createView().toggle(), - 'tree-view:toggle-focus': () => this.createView().toggleFocus(), - 'tree-view:reveal-active-file': () => this.createView().revealActiveFile(), - 'tree-view:toggle-side': () => this.createView().toggleSide(), - 'tree-view:add-file': () => this.createView().add(true), - 'tree-view:add-folder': () => this.createView().add(false), - 'tree-view:duplicate': () => this.createView().copySelectedEntry(), - 'tree-view:remove': () => this.createView().removeSelectedEntries(), - 'tree-view:rename': () => this.createView().moveSelectedEntry(), - 'tree-view:show-current-file-in-file-manager': () => this.createView().showCurrentFileInFileManager() - }) - ) + this.disposables.add(atom.workspace.addOpener((URI) => { + if (URI === TREE_VIEW_URI) return this.getTreeViewInstance() + })) + + this.disposables.add(atom.project.onDidChangePaths(this.createOrDestroyTreeViewIfNeeded.bind(this))) + this.createOrDestroyTreeViewIfNeeded() } deactivate () { @@ -56,33 +49,36 @@ class TreeViewPackage { ) } - serialize () { - if (this.treeView != null) { - return this.treeView.serialize() - } else { - return this.state + getTreeViewInstance (state = {}) { + if (this.treeView == null) { + this.treeView = new TreeView(state) } + return this.treeView } - createView () { - if (this.treeView == null) { - this.treeView = new TreeView(this.state) + createOrDestroyTreeViewIfNeeded () { + if (this.shouldAttachTreeView()) { + // TODO: Pass activate: false here + // {activate: !atom.workspace.getActivePaneItem()} + atom.workspace.open(TREE_VIEW_URI) + } else { + if (this.treeView) this.treeView.destroy() } - return this.treeView } - shouldAttach () { - const projectPath = atom.project.getPaths()[0] || '' + shouldAttachTreeView () { + if (atom.project.getPaths().length === 0) return false - if (atom.workspace.getActivePaneItem()) { - return false - } else if (path.basename(projectPath) === '.git') { - // Only attach when the project path matches the path to open signifying - // the .git folder was opened explicitly and not by using Atom as the Git - // editor. - return projectPath === atom.getLoadSettings().pathToOpen - } else { - return true + // Avoid opening the tree view if Atom was opened as the Git editor... + // Only show it if the .git folder was explicitly opened. + if (path.basename(atom.project.getPaths()[0]) === '.git') { + return atom.project.getPaths()[0] === atom.getLoadSettings().pathToOpen } + + return true + } + + shouldShowTreeViewAfterAttaching () { + if (atom.workspace.getActivePaneItem()) return false } } diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 461972a6..a0fa0e35 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -4,6 +4,7 @@ path = require 'path' _ = require 'underscore-plus' {BufferedProcess, CompositeDisposable, Emitter} = require 'atom' {repoForPath, getStyleObject, getFullExtension, updateEditorsForPath} = require "./helpers" +{TREE_VIEW_URI} = require "./constants" fs = require 'fs-plus' AddDialog = require './add-dialog' @@ -24,21 +25,12 @@ class TreeView constructor: (state) -> @element = document.createElement('div') - @element.classList.add('tree-view-resizer', 'tool-panel') - @element.dataset.showOnRightSide = atom.config.get('tree-view.showOnRightSide') - - @scroller = document.createElement('div') - @scroller.classList.add('tree-view-scroller', 'order--center') - @element.appendChild(@scroller) + @element.classList.add('tool-panel', 'tree-view-element', 'order--center') + @element.tabIndex = -1 @list = document.createElement('ol') @list.classList.add('tree-view', 'full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') - @list.tabIndex = -1 - @scroller.appendChild(@list) - - @resizeHandle = document.createElement('div') - @resizeHandle.classList.add('tree-view-resize-handle') - @element.appendChild(@resizeHandle) + @element.appendChild(@list) @disposables = new CompositeDisposable @emitter = new Emitter @@ -67,12 +59,9 @@ class TreeView @selectEntry(@roots[0]) @selectEntryForPath(state.selectedPath) if state.selectedPath - @focusAfterAttach = state.hasFocus @scrollTopAfterAttach = state.scrollTop if state.scrollTop @scrollLeftAfterAttach = state.scrollLeft if state.scrollLeft - @attachAfterProjectPathSet = state.attached and _.isEmpty(atom.project.getPaths()) @element.style.width = "#{state.width}px" if state.width > 0 - @attach() if state.attached @disposables.add @onEntryMoved ({initialPath, newPath}) -> updateEditorsForPath(initialPath, newPath) @@ -86,11 +75,10 @@ class TreeView directoryExpansionStates: new ((roots) -> @[root.directory.path] = root.directory.serializeExpansionState() for root in roots this)(@roots) + deserializer: 'TreeView' selectedPath: @selectedEntry()?.getPath() - hasFocus: @hasFocus() - attached: @panel? - scrollLeft: @scroller.scrollLeft - scrollTop: @scroller.scrollTop + scrollLeft: @element.scrollLeft + scrollTop: @element.scrollTop width: parseInt(@element.style.width or 0) deactivate: -> @@ -99,6 +87,20 @@ class TreeView @rootDragAndDrop.dispose() @detach() if @panel? + getTitle: -> "Project" + + getURI: -> TREE_VIEW_URI + + getPreferredLocation: -> + if atom.config.get('tree-view.showOnRightSide') + 'right' + else + 'left' + + getAllowedLocations: -> ["left", "right"] + + isPermanentDockItem: -> true + onDirectoryCreated: (callback) -> @emitter.on('directory-created', callback) @@ -115,8 +117,6 @@ class TreeView @emitter.on('file-created', callback) handleEvents: -> - @resizeHandle.addEventListener 'dblclick', => @resizeToFitContent() - @resizeHandle.addEventListener 'mousedown', (e) => @resizeStarted(e) @element.addEventListener 'click', (e) => # This prevents accidental collapsing when a .entries element is the event target return if e.target.classList.contains('entries') @@ -173,68 +173,29 @@ class TreeView @updateRoots() @disposables.add atom.config.onDidChange 'core.ignoredNames', => @updateRoots() if atom.config.get('tree-view.hideIgnoredNames') - @disposables.add atom.config.onDidChange 'tree-view.showOnRightSide', ({newValue}) => - @onSideToggled(newValue) @disposables.add atom.config.onDidChange 'tree-view.sortFoldersBeforeFiles', => @updateRoots() @disposables.add atom.config.onDidChange 'tree-view.squashDirectoryNames', => @updateRoots() toggle: -> - if @isVisible() - @detach() - else - @show() - - show: -> - @attach() - @focus() - - isVisible: -> - not @isHidden() - - isHidden: -> - if @element.style.display is 'none' or not document.body.contains(@element) - true - else if @element.style.display - false - else - getComputedStyle(@element).display is 'none' - - attach: -> - return if _.isEmpty(atom.project.getPaths()) - - @panel ?= - if atom.config.get('tree-view.showOnRightSide') - atom.workspace.addRightPanel(item: this) - else - atom.workspace.addLeftPanel(item: this) - - @focus() if @focusAfterAttach - @scroller.scrollLeft = @scrollLeftAfterAttach if @scrollLeftAfterAttach > 0 - @scroller.scrollTop = @scrollTopAfterAttach if @scrollTopAfterAttach > 0 + atom.workspace.toggle(TREE_VIEW_URI) - detach: -> - @scrollLeftAfterAttach = @scroller.scrollLeft - @scrollTopAfterAttach = @scroller.scrollTop + show: (options) -> + atom.workspace.open(TREE_VIEW_URI) + @focus() unless options?.focus is false - # Clean up copy and cut localStorage Variables - window.localStorage['tree-view:cutPath'] = null - window.localStorage['tree-view:copyPath'] = null - - @panel.destroy() - @panel = null - @unfocus() - @resizeStopped() + hide: -> + atom.workspace.hide(TREE_VIEW_URI) focus: -> - @list.focus() + @element.focus() unfocus: -> atom.workspace.getActivePane().activate() hasFocus: -> - @element.contains(document.activeElement) + document.activeElement is @element toggleFocus: -> if @hasFocus() @@ -269,28 +230,6 @@ class TreeView else atom.workspace.open(uri, options) - resizeStarted: => - document.addEventListener('mousemove', @resizeTreeView) - document.addEventListener('mouseup', @resizeStopped) - - resizeStopped: => - document.removeEventListener('mousemove', @resizeTreeView) - document.removeEventListener('mouseup', @resizeStopped) - - resizeTreeView: ({pageX, which}) => - return @resizeStopped() unless which is 1 - - if atom.config.get('tree-view.showOnRightSide') - width = @element.offsetWidth + @element.getBoundingClientRect().left - pageX - else - width = pageX - @element.getBoundingClientRect().left - - @element.style.width = "#{width}px" - - resizeToFitContent: -> - @element.style.width = '1px' # Shrink to measure the minimum width of list - @element.style.width = "#{@list.offsetWidth}px" - loadIgnoredPatterns: -> @ignoredPatterns.length = 0 return unless atom.config.get('tree-view.hideIgnoredNames') @@ -336,10 +275,6 @@ class TreeView @list.appendChild(root) root - if @attachAfterProjectPathSet - @attach() - @attachAfterProjectPathSet = false - getActivePath: -> atom.workspace.getActivePaneItem()?.getPath?() selectActiveFile: -> @@ -351,8 +286,7 @@ class TreeView revealActiveFile: -> return if _.isEmpty(atom.project.getPaths()) - @attach() - @focus() if atom.config.get('tree-view.focusOnReveal') + @show({focus: atom.config.get('tree-view.focusOnReveal')}) return unless activeFilePath = @getActivePath() @@ -787,15 +721,15 @@ class TreeView scrollTop: (top) -> if top? - @scroller.scrollTop = top + @element.scrollTop = top else - @scroller.scrollTop + @element.scrollTop scrollBottom: (bottom) -> if bottom? - @scroller.scrollTop = bottom - @scroller.offsetHeight + @element.scrollTop = bottom - @element.offsetHeight else - @scroller.scrollTop + @scroller.offsetHeight + @element.scrollTop + @element.offsetHeight scrollToEntry: (entry) -> element = if entry?.classList.contains('directory') then entry.header else entry @@ -808,13 +742,13 @@ class TreeView scrollToTop: -> @selectEntry(@roots[0]) if @roots[0]? - @scroller.scrollTop = 0 + @element.scrollTop = 0 pageUp: -> - @scroller.scrollTop -= @element.offsetHeight + @element.scrollTop -= @element.offsetHeight pageDown: -> - @scroller.scrollTop += @element.offsetHeight + @element.scrollTop += @element.offsetHeight toggleSide: -> toggleConfig('tree-view.showOnRightSide') @@ -839,9 +773,9 @@ class TreeView atom.notifications.addWarning("Failed to move entry #{initialPath} to #{newDirectoryPath}", detail: error.message) onStylesheetsChanged: => - return unless @isVisible() - - # Force a redraw so the scrollbars are styled correctly based on the theme + # If visible, force a redraw so the scrollbars are styled correctly based on + # the theme + return if @element.offsetWidth is 0 and @element.offsetHeight is 0 @element.style.display = 'none' @element.offsetWidth @element.style.display = '' @@ -870,12 +804,6 @@ class TreeView @selectEntry(entryToSelect) @showFullMenu() - onSideToggled: (newValue) -> - @element.dataset.showOnRightSide = newValue - if @isVisible() - @detach() - @attach() - # Public: Return an array of paths from all selected items # # Example: @selectedPaths() diff --git a/package.json b/package.json index 3c312096..6a4e70a4 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,9 @@ "devDependencies": { "coffeelint": "^1.9.7" }, + "deserializers": { + "TreeView": "getTreeViewInstance" + }, "consumedServices": { "atom.file-icons": { "versions": { @@ -45,11 +48,6 @@ "default": false, "description": "Don't show items matched by the `Ignored Names` core config setting." }, - "showOnRightSide": { - "type": "boolean", - "default": false, - "description": "Show the tree view on the right side of the editor instead of the left." - }, "sortFoldersBeforeFiles": { "type": "boolean", "default": true, diff --git a/styles/tree-view.less b/styles/tree-view.less index 49af49b9..1fc08cdf 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -1,60 +1,23 @@ @import "ui-variables"; -.tree-view-resizer { - position: relative; - height: 100%; - overflow: hidden; - cursor: default; - -webkit-user-select: none; - min-width: 100px; - width: 200px; +.project-root-header { + -webkit-user-drag: element; +} + +.tree-view-element { + overflow: auto; z-index: 2; - display: flex; - flex-direction: column; + -webkit-user-select: none; + contain: strict; // use these classes to re-order // using a value in-between is fine too, e.g. order: -3; & > .order--start { order: -10; } & > .order--center { order: 0; } & > .order--end { order: 10; } - - .tree-view-resize-handle { - position: absolute; - top: 0; - bottom: 0; - width: 10px; - cursor: col-resize; - z-index: 3; - } - - &[data-show-on-right-side='true'] { - .tree-view-resize-handle { - left: -5px; - } - } - - &[data-show-on-right-side='false'] { - .tree-view-resize-handle { - right: -5px; - } - } -} - -.project-root-header { - -webkit-user-drag: element; -} - -.tree-view-scroller { - display: flex; - flex-direction: column; - flex: 1; - width: 100%; - overflow: auto; } .tree-view { - flex-grow: 1; - flex-shrink: 0; /* * Force a new stacking context to prevent a large, duplicate paint layer from * being created for tree-view's scrolling contents that can make the cost of @@ -131,11 +94,3 @@ } } } - -.platform-win32 { - .tree-view-resizer { - .tree-view-resize-handle { - cursor: ew-resize; - } - } -} From ee303b48c296d765ef60a4f033cbea641f865cad Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 3 Apr 2017 14:48:35 -0600 Subject: [PATCH 173/263] Correctly update scroll position after deserializing --- lib/tree-view.coffee | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index a0fa0e35..cf00f166 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -34,10 +34,7 @@ class TreeView @disposables = new CompositeDisposable @emitter = new Emitter - @focusAfterAttach = false @roots = [] - @scrollLeftAfterAttach = -1 - @scrollTopAfterAttach = -1 @selectedPath = null @ignoredPatterns = [] @useSyncFS = false @@ -59,8 +56,16 @@ class TreeView @selectEntry(@roots[0]) @selectEntryForPath(state.selectedPath) if state.selectedPath - @scrollTopAfterAttach = state.scrollTop if state.scrollTop - @scrollLeftAfterAttach = state.scrollLeft if state.scrollLeft + + if state.scrollTop? or state.scrollLeft? + observer = new IntersectionObserver(=> + if @isVisible() + @element.scrollTop = state.scrollTop + @element.scrollLeft = state.scrollLeft + observer.disconnect() + ) + observer.observe(@element) + @element.style.width = "#{state.width}px" if state.width > 0 @disposables.add @onEntryMoved ({initialPath, newPath}) -> @@ -775,7 +780,7 @@ class TreeView onStylesheetsChanged: => # If visible, force a redraw so the scrollbars are styled correctly based on # the theme - return if @element.offsetWidth is 0 and @element.offsetHeight is 0 + return unless @isVisible() @element.style.display = 'none' @element.offsetWidth @element.style.display = '' @@ -943,3 +948,6 @@ class TreeView # Drop event from OS for file in e.dataTransfer.files @moveEntry(file.path, newDirectoryPath) + + isVisible: -> + @element.offsetWidth isnt 0 or @element.offsetHeight isnt 0 From beb468cd7aec854cfedef345d3dc84f81cc6d1b0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 3 Apr 2017 14:50:33 -0600 Subject: [PATCH 174/263] Apply .tree-view class to root element to simplify focus handling Existing key-bindings target the `.tree-view` element, but previously this element wasn't the root of the tree view. Now that focus will be handled by the workspace, making existing `.tree-view` bindings work would have required us to transfer focus from the outer element to the element with the `.tree-view` class. Moving this class to the root introduces some risk of breaking themes, but I think it's worth keeping the focus handling simple. --- lib/tree-view.coffee | 4 ++-- styles/tree-view.less | 45 ++++++++++++++++++------------------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index cf00f166..c92de26c 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -25,11 +25,11 @@ class TreeView constructor: (state) -> @element = document.createElement('div') - @element.classList.add('tool-panel', 'tree-view-element', 'order--center') + @element.classList.add('tool-panel', 'tree-view') @element.tabIndex = -1 @list = document.createElement('ol') - @list.classList.add('tree-view', 'full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') + @list.classList.add('full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') @element.appendChild(@list) @disposables = new CompositeDisposable diff --git a/styles/tree-view.less b/styles/tree-view.less index 1fc08cdf..7ff2ce8f 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -4,36 +4,29 @@ -webkit-user-drag: element; } -.tree-view-element { +.tree-view { + contain: strict; overflow: auto; z-index: 2; -webkit-user-select: none; - contain: strict; - // use these classes to re-order - // using a value in-between is fine too, e.g. order: -3; - & > .order--start { order: -10; } - & > .order--center { order: 0; } - & > .order--end { order: 10; } -} - -.tree-view { - /* - * Force a new stacking context to prevent a large, duplicate paint layer from - * being created for tree-view's scrolling contents that can make the cost of - * layer tree updates scale at 3x the size of the layer rather than the - * optimal 1x. - * - * On high resolution displays, Chromium handles layers for scrolling content - * differently and inadvertently creates a duplicate paint layer the size of - * .tree-view-scroller because descendants of the scroller overlap the - * auto-created layer. - */ - isolation: isolate; - min-width: -webkit-min-content; - padding-left: @component-icon-padding; - padding-right: @component-padding; - position: relative; + > ol { + /* + * Force a new stacking context to prevent a large, duplicate paint layer from + * being created for tree-view's scrolling contents that can make the cost of + * layer tree updates scale at 3x the size of the layer rather than the + * optimal 1x. + * + * On high resolution displays, Chromium handles layers for scrolling content + * differently and inadvertently creates a duplicate paint layer the size of + * .tree-view-scroller because descendants of the scroller overlap the + * auto-created layer. + */ + isolation: isolate; + min-width: -webkit-min-content; + padding-left: @component-icon-padding; + padding-right: @component-padding; + } .header { position: relative; From e9e517fd9c29f7fc2cde25a1447769424395fe2b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 4 Apr 2017 10:22:26 -0600 Subject: [PATCH 175/263] Pass view directly to Workspace.open --- lib/constants.js | 1 - lib/tree-view-package.js | 10 ++-------- lib/tree-view.coffee | 3 ++- 3 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 lib/constants.js diff --git a/lib/constants.js b/lib/constants.js deleted file mode 100644 index a0bcbf2b..00000000 --- a/lib/constants.js +++ /dev/null @@ -1 +0,0 @@ -exports.TREE_VIEW_URI = 'atom://tree-view' diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index f4bb9a38..1942afc2 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -3,7 +3,6 @@ const path = require('path') const FileIcons = require('./file-icons') const TreeView = require('./tree-view') -const {TREE_VIEW_URI} = require('./constants') module.exports = class TreeViewPackage { @@ -24,10 +23,6 @@ class TreeViewPackage { 'tree-view:show-current-file-in-file-manager': () => this.getTreeViewInstance().showCurrentFileInFileManager() })) - this.disposables.add(atom.workspace.addOpener((URI) => { - if (URI === TREE_VIEW_URI) return this.getTreeViewInstance() - })) - this.disposables.add(atom.project.onDidChangePaths(this.createOrDestroyTreeViewIfNeeded.bind(this))) this.createOrDestroyTreeViewIfNeeded() } @@ -58,9 +53,8 @@ class TreeViewPackage { createOrDestroyTreeViewIfNeeded () { if (this.shouldAttachTreeView()) { - // TODO: Pass activate: false here - // {activate: !atom.workspace.getActivePaneItem()} - atom.workspace.open(TREE_VIEW_URI) + const showOnAttach = !atom.workspace.getActivePaneItem() + atom.workspace.open(this.getTreeViewInstance(), {activatePane: showOnAttach, activateItem: showOnAttach}) } else { if (this.treeView) this.treeView.destroy() } diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index c92de26c..3f50d64f 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -4,7 +4,6 @@ path = require 'path' _ = require 'underscore-plus' {BufferedProcess, CompositeDisposable, Emitter} = require 'atom' {repoForPath, getStyleObject, getFullExtension, updateEditorsForPath} = require "./helpers" -{TREE_VIEW_URI} = require "./constants" fs = require 'fs-plus' AddDialog = require './add-dialog' @@ -16,6 +15,8 @@ Directory = require './directory' DirectoryView = require './directory-view' RootDragAndDrop = require './root-drag-and-drop' +TREE_VIEW_URI = 'atom://tree-view' + toggleConfig = (keyPath) -> atom.config.set(keyPath, not atom.config.get(keyPath)) From 81bc7f916f248d24eff6e780a3059e77dd08bf0a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 4 Apr 2017 15:18:48 -0600 Subject: [PATCH 176/263] Get all but one test passing --- lib/dialog.coffee | 7 +- lib/tree-view-package.js | 20 +- lib/tree-view.coffee | 64 +- spec/file-stats-spec.coffee | 6 +- ...c.coffee => tree-view-package-spec.coffee} | 546 +++++++----------- spec/tree-view-spec.js | 46 ++ 6 files changed, 299 insertions(+), 390 deletions(-) rename spec/{tree-view-spec.coffee => tree-view-package-spec.coffee} (91%) create mode 100644 spec/tree-view-spec.js diff --git a/lib/dialog.coffee b/lib/dialog.coffee index db6b6fa9..d6653460 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -50,13 +50,14 @@ class Dialog @miniEditor.scrollToCursorPosition() close: -> - panelToDestroy = @panel + panel = @panel @panel = null - panelToDestroy?.destroy() + panel?.destroy() @emitter.dispose() @disposables.dispose() @miniEditor.destroy() - atom.workspace.getActivePane().activate() + activePane = atom.workspace.getActivePane() + activePane.activate() unless activePane.isDestroyed() cancel: -> @close() diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index 1942afc2..0262947c 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -8,13 +8,11 @@ module.exports = class TreeViewPackage { activate () { this.disposables = new CompositeDisposable() - this.disposables.add(atom.commands.add('atom-workspace', { 'tree-view:show': () => this.getTreeViewInstance().show(), 'tree-view:toggle': () => this.getTreeViewInstance().toggle(), 'tree-view:toggle-focus': () => this.getTreeViewInstance().toggleFocus(), 'tree-view:reveal-active-file': () => this.getTreeViewInstance().revealActiveFile(), - 'tree-view:toggle-side': () => this.getTreeViewInstance().toggleSide(), 'tree-view:add-file': () => this.getTreeViewInstance().add(true), 'tree-view:add-folder': () => this.getTreeViewInstance().add(false), 'tree-view:duplicate': () => this.getTreeViewInstance().copySelectedEntry(), @@ -24,13 +22,13 @@ class TreeViewPackage { })) this.disposables.add(atom.project.onDidChangePaths(this.createOrDestroyTreeViewIfNeeded.bind(this))) - this.createOrDestroyTreeViewIfNeeded() + return this.createOrDestroyTreeViewIfNeeded() } deactivate () { this.disposables.dispose() if (this.fileIconsDisposable) this.fileIconsDisposable.dispose() - if (this.treeView) this.treeView.deactivate() + if (this.treeView) this.treeView.destroy() this.treeView = null } @@ -53,10 +51,18 @@ class TreeViewPackage { createOrDestroyTreeViewIfNeeded () { if (this.shouldAttachTreeView()) { - const showOnAttach = !atom.workspace.getActivePaneItem() - atom.workspace.open(this.getTreeViewInstance(), {activatePane: showOnAttach, activateItem: showOnAttach}) + const treeView = this.getTreeViewInstance() + if (!atom.workspace.paneForItem(treeView)) { + const showOnAttach = !atom.workspace.getActivePaneItem() + this.treeViewOpenPromise = atom.workspace.open(treeView, {activatePane: showOnAttach, activateItem: showOnAttach}) + } } else { - if (this.treeView) this.treeView.destroy() + + if (this.treeView) { + const pane = atom.workspace.paneForItem(this.treeView) + if (pane) pane.removeItem(this.treeView) + } + this.treeViewOpenPromise = Promise.resolve() } } diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 3f50d64f..52cebd71 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -20,11 +20,12 @@ TREE_VIEW_URI = 'atom://tree-view' toggleConfig = (keyPath) -> atom.config.set(keyPath, not atom.config.get(keyPath)) +nextId = 1 + module.exports = class TreeView - panel: null - constructor: (state) -> + @id = nextId++ @element = document.createElement('div') @element.classList.add('tool-panel', 'tree-view') @element.tabIndex = -1 @@ -87,11 +88,14 @@ class TreeView scrollTop: @element.scrollTop width: parseInt(@element.style.width or 0) - deactivate: -> + destroy: -> root.directory.destroy() for root in @roots @disposables.dispose() @rootDragAndDrop.dispose() - @detach() if @panel? + @emitter.emit('did-destroy') + + onDidDestroy: (callback) -> + @emitter.on('did-destroy', callback) getTitle: -> "Project" @@ -185,14 +189,14 @@ class TreeView @updateRoots() toggle: -> - atom.workspace.toggle(TREE_VIEW_URI) + atom.workspace.toggle(this) show: (options) -> - atom.workspace.open(TREE_VIEW_URI) - @focus() unless options?.focus is false + atom.workspace.open(this, {searchAllPanes: true}).then => + @focus() unless options?.focus is false hide: -> - atom.workspace.hide(TREE_VIEW_URI) + atom.workspace.hide(this) focus: -> @element.focus() @@ -260,7 +264,8 @@ class TreeView @loadIgnoredPatterns() @roots = for projectPath in atom.project.getPaths() - continue unless stats = fs.lstatSyncNoException(projectPath) + stats = fs.lstatSyncNoException(projectPath) + continue unless stats stats = _.pick stats, _.keys(stats)... for key in ["atime", "birthtime", "ctime", "mtime"] stats[key] = stats[key].getTime() @@ -290,25 +295,25 @@ class TreeView @deselect() revealActiveFile: -> - return if _.isEmpty(atom.project.getPaths()) - - @show({focus: atom.config.get('tree-view.focusOnReveal')}) - - return unless activeFilePath = @getActivePath() - - [rootPath, relativePath] = atom.project.relativizePath(activeFilePath) - return unless rootPath? - - activePathComponents = relativePath.split(path.sep) - currentPath = rootPath - for pathComponent in activePathComponents - currentPath += path.sep + pathComponent - entry = @entryForPath(currentPath) - if entry.classList.contains('directory') - entry.expand() - else - @selectEntry(entry) - @scrollToEntry(entry) + if _.isEmpty(atom.project.getPaths()) + return Promise.resolve() + + @show({focus: atom.config.get('tree-view.focusOnReveal')}).then => + return unless activeFilePath = @getActivePath() + + [rootPath, relativePath] = atom.project.relativizePath(activeFilePath) + return unless rootPath? + + activePathComponents = relativePath.split(path.sep) + currentPath = rootPath + for pathComponent in activePathComponents + currentPath += path.sep + pathComponent + entry = @entryForPath(currentPath) + if entry.classList.contains('directory') + entry.expand() + else + @selectEntry(entry) + @scrollToEntry(entry) copySelectedEntryPath: (relativePath = false) -> if pathToCopy = @selectedPath @@ -756,9 +761,6 @@ class TreeView pageDown: -> @element.scrollTop += @element.offsetHeight - toggleSide: -> - toggleConfig('tree-view.showOnRightSide') - moveEntry: (initialPath, newDirectoryPath) -> if initialPath is newDirectoryPath return diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee index 1bbc9fe9..b7e05ecf 100644 --- a/spec/file-stats-spec.coffee +++ b/spec/file-stats-spec.coffee @@ -4,7 +4,6 @@ path = require 'path' temp = require('temp').track() describe "FileStats", -> - describe "provision of filesystem stats", -> [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] @@ -20,11 +19,12 @@ describe "FileStats", -> fs.writeFileSync(filePath2, file2Data) atom.project.setPaths([rootDirPath]) - waitsForPromise -> + waitsFor (done) -> + atom.workspace.onDidOpen(done) atom.packages.activatePackage("tree-view") runs -> - treeView = atom.workspace.getLeftPanels()[0].getItem() + treeView = atom.workspace.getLeftDock().getActivePaneItem() afterEach -> temp.cleanup() diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-package-spec.coffee similarity index 91% rename from spec/tree-view-spec.coffee rename to spec/tree-view-package-spec.coffee index 81136dd6..0a1c6f9c 100644 --- a/spec/tree-view-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -9,9 +9,15 @@ eventHelpers = require "./event-helpers" DefaultFileIcons = require '../lib/default-file-icons' FileIcons = require '../lib/file-icons' -waitsForFileToOpen = (causeFileToOpen) -> +waitForPackageActivation = (expectDockItem) -> + waitsForPromise -> + atom.packages.activatePackage('tree-view') + waitsForPromise -> + atom.packages.getActivePackage('tree-view').mainModule.treeViewOpenPromise + +waitForWorkspaceOpenEvent = (causeFileToOpen) -> waitsFor (done) -> - disposable = atom.workspace.onDidOpen -> + disposable = atom.workspace.onDidOpen ({item}) -> disposable.dispose() done() causeFileToOpen() @@ -40,6 +46,7 @@ describe "TreeView", -> treeView.selectEntryForPath atom.project.getDirectories()[0].resolve pathToSelect beforeEach -> + expect(atom.workspace.getLeftDock().getActivePaneItem()).toBeUndefined() expect(atom.config.get('core.allowPendingPaneItems')).toBeTruthy() fixturesPath = atom.project.getPaths()[0] @@ -49,24 +56,24 @@ describe "TreeView", -> workspaceElement = atom.views.getView(atom.workspace) - waitsForPromise -> - atom.packages.activatePackage("tree-view") + waitForPackageActivation() runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - treeView = atom.workspace.getLeftPanels()[0].getItem() + moduleInstance = atom.packages.getActivePackage('tree-view').mainModule.getTreeViewInstance() + treeView = atom.workspace.getLeftDock().getActivePaneItem() files = treeView.element.querySelectorAll('.file') root1 = treeView.roots[0] root2 = treeView.roots[1] sampleJs = files[0] sampleTxt = files[1] - expect(root1.directory.watchSubscription).toBeTruthy() afterEach -> temp.cleanup() + if treeViewOpenPromise = atom.packages.getActivePackage('tree-view')?.mainModule.treeViewOpenPromise + waitsForPromise -> treeViewOpenPromise - describe ".initialize(project)", -> + describe "on package activation", -> it "renders the root directories of the project and their contents alphabetically with subdirectories first, in a collapsed state", -> expect(root1.querySelector('.header .disclosure-arrow')).not.toHaveClass('expanded') expect(root1.querySelector('.header .name')).toHaveText('root-dir1') @@ -102,23 +109,19 @@ describe "TreeView", -> atom.project.setPaths([]) atom.packages.deactivatePackage("tree-view") - waitsForPromise -> - atom.packages.activatePackage("tree-view") + expect(atom.workspace.getLeftDock().getActivePaneItem()).toBeUndefined() + + waitsForPromise -> atom.packages.activatePackage("tree-view") runs -> - treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() + treeView = atom.packages.getActivePackage("tree-view").mainModule.getTreeViewInstance() it "does not attach to the workspace or create a root node when initialized", -> expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(0) it "does not attach to the workspace or create a root node when attach() is called", -> - treeView.attach() - expect(treeView.element.parentElement).toBeFalsy() - expect(treeView.roots).toHaveLength(0) - - it "serializes without throwing an exception", -> - expect(-> treeView.serialize()).not.toThrow() + expect(atom.workspace.getLeftDock().getActivePaneItem()).toBeUndefined() it "does not throw an exception when files are opened", -> filePath = path.join(os.tmpdir(), 'non-project-file.txt') @@ -134,46 +137,52 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open(filePath) + + waitsForPromise -> + treeView.revealActiveFile() + runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.element.parentElement).toBeFalsy() expect(treeView.roots).toHaveLength(0) describe "when the project is assigned a path because a new buffer is saved", -> it "creates a root directory view and attaches to the workspace", -> + projectPath = temp.mkdirSync('atom-project') + waitsForPromise -> atom.workspace.open() - runs -> - projectPath = temp.mkdirSync('atom-project') + waitsFor (done) -> atom.workspace.getActivePaneItem().saveAs(path.join(projectPath, 'test.txt')) - expect(treeView.element.parentElement).toBeTruthy() + atom.workspace.onDidOpen(done) + + runs -> + treeView = atom.workspace.getLeftDock().getActivePaneItem() expect(treeView.roots).toHaveLength(1) expect(fs.absolute(treeView.roots[0].getPath())).toBe fs.absolute(projectPath) describe "when the root view is opened to a file path", -> - it "does not attach to the workspace but does create a root node when initialized", -> + it "does not show the dock on activation", -> atom.packages.deactivatePackage("tree-view") atom.packages.packageStates = {} + atom.workspace.getLeftDock().hide() + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) waitsForPromise -> atom.workspace.open('tree-view.js') - waitsForPromise -> - atom.packages.activatePackage('tree-view') + waitForPackageActivation() runs -> - treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() - expect(treeView.element.parentElement).toBeFalsy() - expect(treeView.roots).toHaveLength(2) + # This assertion is failing because we auto-open docks when adding an item + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) describe "when the root view is opened to a directory", -> it "attaches to the workspace", -> - waitsForPromise -> - atom.packages.activatePackage('tree-view') + waitsForPromise -> atom.packages.activatePackage('tree-view') runs -> - treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() + treeView = atom.packages.getActivePackage("tree-view").mainModule.getTreeViewInstance() expect(treeView.element.parentElement).toBeTruthy() expect(treeView.roots).toHaveLength(2) @@ -185,144 +194,34 @@ describe "TreeView", -> atom.packages.deactivatePackage("tree-view") atom.packages.packageStates = {} - waitsForPromise -> - atom.packages.activatePackage('tree-view') + waitsForPromise -> atom.packages.activatePackage('tree-view') runs -> {treeView} = atom.packages.getActivePackage("tree-view").mainModule expect(treeView).toBeFalsy() - describe "serialization", -> - it "restores the attached/detached state of the tree-view", -> - jasmine.attachToDOM(workspaceElement) - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(atom.workspace.getLeftPanels().length).toBe(0) - - atom.packages.deactivatePackage("tree-view") - - waitsForPromise -> - atom.packages.activatePackage("tree-view") - - runs -> - expect(atom.workspace.getLeftPanels().length).toBe(0) - - it "restores expanded directories and selected file when deserialized", -> - root1.querySelectorAll('.directory')[0].dispatchEvent(new MouseEvent('click', {detail: 1, bubbles: true})) - - waitsForFileToOpen -> - sampleJs.dispatchEvent(new MouseEvent('click', {detail: 1, bubbles: true})) - - runs -> - atom.packages.deactivatePackage("tree-view") - - waitsForPromise -> - atom.packages.activatePackage("tree-view") - - runs -> - treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView.element).toExist() - expect(treeView.selectedEntry().textContent).toBe('tree-view.js') - root1 = treeView.roots[0] - expect(root1.querySelector(".directory")).toHaveClass("expanded") - - it "restores the focus state of the tree view", -> - jasmine.attachToDOM(workspaceElement) - treeView.focus() - expect(treeView.list).toHaveFocus() - atom.packages.deactivatePackage("tree-view") - - waitsForPromise -> - atom.packages.activatePackage("tree-view") - - runs -> - treeView = atom.workspace.getLeftPanels()[0].getItem() - expect(treeView.list).toHaveFocus() - - it "restores the scroll top when toggled", -> - workspaceElement.style.height = '5px' - jasmine.attachToDOM(workspaceElement) - expect(treeView.element).toBeVisible() - treeView.focus() - - treeView.scrollTop(10) - expect(treeView.scrollTop()).toBe(10) - - runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.element.offsetHeight is 0 - - runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.element.offsetHeight > 0 - - runs -> expect(treeView.scrollTop()).toBe(10) - - it "restores the scroll left when toggled", -> - treeView.element.style.width = '5px' - jasmine.attachToDOM(workspaceElement) - expect(treeView.element).toBeVisible() - treeView.focus() - - treeView.scroller.scrollLeft = 5 - expect(treeView.scroller.scrollLeft).toBe(5) - - runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.element.offsetHeight is 0 - - runs -> atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - waitsFor -> treeView.element.offsetHeight > 0 - - runs -> expect(treeView.scroller.scrollLeft).toBe(5) - describe "when tree-view:toggle is triggered on the root view", -> beforeEach -> jasmine.attachToDOM(workspaceElement) describe "when the tree view is visible", -> beforeEach -> - expect(treeView.element).toBeVisible() - - describe "when the tree view is focused", -> - it "hides the tree view", -> - treeView.focus() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView.element).toBeHidden() + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) - describe "when the tree view is not focused", -> - it "hides the tree view", -> - workspaceElement.focus() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView.element).toBeHidden() + it "hides the tree view", -> + workspaceElement.focus() + waitsForPromise -> treeView.toggle() + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) describe "when the tree view is hidden", -> it "shows and focuses the tree view", -> - treeView.detach() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(treeView.element.parentElement).toBeTruthy() - expect(treeView.list).toHaveFocus() - - describe "when tree-view:toggle-side is triggered on the root view", -> - describe "when the tree view is on the left", -> - it "moves the tree view to the right", -> - expect(treeView.element).toBeVisible() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') - expect(treeView.element.dataset.showOnRightSide).toBe('true') - - describe "when the tree view is on the right", -> - beforeEach -> - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') - - it "moves the tree view to the left", -> - expect(treeView.element).toBeVisible() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') - expect(treeView.element.dataset.showOnRightSide).toBe('false') - - describe "when the tree view is hidden", -> - it "shows the tree view on the other side next time it is opened", -> - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') - atom.commands.dispatch(workspaceElement, 'tree-view:toggle') - expect(atom.workspace.getLeftPanels().length).toBe 0 - treeView = atom.workspace.getRightPanels()[0].getItem() - expect(treeView.element.dataset.showOnRightSide).toBe('true') + atom.workspace.getLeftDock().hide() + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + waitsForPromise -> treeView.toggle() + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.element).toHaveFocus() describe "when tree-view:toggle-focus is triggered on the root view", -> beforeEach -> @@ -330,10 +229,11 @@ describe "TreeView", -> describe "when the tree view is hidden", -> it "shows and focuses the tree view", -> - treeView.detach() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView.element.parentElement).toBeTruthy() - expect(treeView.list).toHaveFocus() + atom.workspace.getLeftDock().hide() + waitsForPromise -> treeView.toggleFocus() + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.element).toHaveFocus() describe "when the tree view is shown", -> it "focuses the tree view", -> @@ -342,10 +242,12 @@ describe "TreeView", -> runs -> workspaceElement.focus() - expect(treeView.element).toBeVisible() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView.element).toBeVisible() - expect(treeView.list).toHaveFocus() + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + waitsForPromise -> treeView.toggleFocus() + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.element).toHaveFocus() describe "when the tree view is focused", -> it "unfocuses the tree view", -> @@ -354,14 +256,14 @@ describe "TreeView", -> runs -> treeView.focus() - expect(treeView.element).toBeVisible() - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus') - expect(treeView.element).toBeVisible() - expect(treeView.list).not.toHaveFocus() + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + treeView.toggleFocus() + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.element).not.toHaveFocus() - describe "when tree-view:reveal-active-file is triggered on the root view", -> + describe "when revealActiveFile", -> beforeEach -> - treeView.detach() + atom.workspace.getLeftDock().hide() spyOn(treeView, 'focus') describe "if the current file has a path", -> @@ -372,8 +274,10 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open(path.join(atom.project.getPaths()[0], 'dir1', 'file1')) + waitsForPromise -> + treeView.revealActiveFile() + runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).toHaveBeenCalled() @@ -381,8 +285,10 @@ describe "TreeView", -> treeView.focus.reset() atom.workspace.open(path.join(atom.project.getPaths()[1], 'dir3', 'file3')) + waitsForPromise -> + treeView.revealActiveFile() + runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).toHaveBeenCalled() @@ -393,8 +299,10 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open(path.join(atom.project.getPaths()[0], 'dir1', 'file1')) + waitsForPromise -> + treeView.revealActiveFile() + runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).not.toHaveBeenCalled() @@ -402,8 +310,10 @@ describe "TreeView", -> treeView.focus.reset() atom.workspace.open(path.join(atom.project.getPaths()[1], 'dir3', 'file3')) + waitsForPromise -> + treeView.revealActiveFile() + runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).not.toHaveBeenCalled() @@ -414,16 +324,26 @@ describe "TreeView", -> runs -> expect(atom.workspace.getActivePaneItem().getPath()).toBeUndefined() - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.element.parentElement).toBeTruthy() + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + + waitsForPromise -> + treeView.revealActiveFile() + + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) 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(atom.workspace.getActivePaneItem()).toBeUndefined() - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') - expect(treeView.element.parentElement).toBeTruthy() - expect(treeView.focus).toHaveBeenCalled() + expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + + waitsForPromise -> + treeView.revealActiveFile() + + runs -> + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.focus).toHaveBeenCalled() describe 'if there are more items than can be visible in the viewport', -> [rootDirPath] = [] @@ -442,21 +362,24 @@ describe "TreeView", -> it 'scrolls the selected file into the visible view', -> # Open file at bottom waitsForPromise -> atom.workspace.open(path.join(rootDirPath, 'file-20.txt')) + waitsForPromise -> + treeView.revealActiveFile() runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.scrollTop()).toBeGreaterThan 400 # Open file in the middle, should be centered in scroll waitsForPromise -> atom.workspace.open(path.join(rootDirPath, 'file-10.txt')) + waitsForPromise -> + treeView.revealActiveFile() runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.scrollTop()).toBeLessThan 400 expect(treeView.scrollTop()).toBeGreaterThan 0 # Open file at top waitsForPromise -> atom.workspace.open(path.join(rootDirPath, 'file-1.txt')) + waitsForPromise -> + treeView.revealActiveFile() runs -> - atom.commands.dispatch(workspaceElement, 'tree-view:reveal-active-file') expect(treeView.scrollTop()).toEqual 0 describe "when tool-panel:unfocus is triggered on the tree view", -> @@ -467,10 +390,10 @@ describe "TreeView", -> runs -> jasmine.attachToDOM(workspaceElement) treeView.focus() - expect(treeView.list).toHaveFocus() + expect(treeView.element).toHaveFocus() atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') - expect(treeView.element).toBeVisible() - expect(treeView.list).not.toHaveFocus() + expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(treeView.element).not.toHaveFocus() expect(atom.workspace.getActivePane().isActive()).toBe(true) describe "copy path commands", -> @@ -574,7 +497,7 @@ describe "TreeView", -> # UI interaction after the package was activated. describe "when the file is permanent", -> beforeEach -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.workspace.open('tree-view.js') it "does not throw when the file is double clicked", -> @@ -611,14 +534,13 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) describe "when a file is single-clicked", -> - describe "when core.allowPendingPaneItems is set to true (default)", -> activePaneItem = null beforeEach -> treeView.focus() - waitsForFileToOpen -> - sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + waitForWorkspaceOpenEvent -> + r = sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> activePaneItem = atom.workspace.getActivePaneItem() @@ -670,7 +592,7 @@ describe "TreeView", -> treeView.focus() it "opens the file and focuses it", -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) @@ -709,7 +631,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) subdir = null - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -768,7 +690,7 @@ describe "TreeView", -> 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", -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) waitsForPromise -> @@ -794,16 +716,12 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open(path.join('dir1', 'sub-dir1', 'sub-file1')) - runs -> - dirView = root1.querySelector('.directory') - fileView = root1.querySelector('.file') - expect(dirView).not.toHaveClass 'selected' - expect(fileView).toHaveClass 'selected' - expect(treeView.element.querySelectorAll('.selected').length).toBe 1 + waitsFor -> + treeView.getSelectedEntries()[0].textContent is 'sub-file1' describe "when the item has no path", -> it "deselects the previously selected entry", -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -817,14 +735,14 @@ describe "TreeView", -> it "selects the file in that is open in that editor", -> leftEditorPane = null - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> leftEditorPane = atom.workspace.getActivePane() leftEditorPane.splitRight() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -857,7 +775,7 @@ describe "TreeView", -> it "selects the entry after its parent directory", -> subdir1 = root1.querySelectorAll('.directory')[1] subdir1.expand() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> entries = subdir1.querySelectorAll('.entry') entries[entries.length - 1].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) @@ -893,7 +811,7 @@ describe "TreeView", -> it "does not change the selection", -> entries = root2.querySelectorAll('.entries .entry') lastEntry = entries[entries.length - 1] - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> lastEntry.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -907,7 +825,7 @@ describe "TreeView", -> lastDir = directories[directories.length - 1] fileAfterDir = lastDir.nextSibling lastDir.expand() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileAfterDir.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -919,7 +837,7 @@ describe "TreeView", -> it "selects the previous entry", -> entries = root1.querySelectorAll('.entry') lastEntry = entries[entries.length - 1] - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> lastEntry.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -957,15 +875,13 @@ describe "TreeView", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) entryCount = treeView.element.querySelectorAll(".entry").length _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scroller.scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:move-to-top') - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) it "selects the root entry", -> entryCount = treeView.element.querySelectorAll(".entry").length @@ -980,16 +896,15 @@ describe "TreeView", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.scroller.scrollTop).toBeGreaterThan(0) + expect(treeView.element.scrollTop).toBeGreaterThan(0) treeView.roots[0].collapse() treeView.roots[1].collapse() atom.commands.dispatch(treeView.element, 'core:move-to-bottom') - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) it "selects the last entry", -> expect(treeView.roots[0]).toHaveClass 'selected' @@ -1002,45 +917,42 @@ describe "TreeView", -> treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) treeView.scrollToBottom() - scrollTop = treeView.scroller.scrollTop + scrollTop = treeView.element.scrollTop expect(scrollTop).toBeGreaterThan 0 atom.commands.dispatch(treeView.element, 'core:page-up') - expect(treeView.scroller.scrollTop).toBe scrollTop - treeView.element.offsetHeight + expect(treeView.element.scrollTop).toBe scrollTop - treeView.element.offsetHeight describe "core:page-down", -> it "scrolls down a page", -> treeView.element.style.height = '5px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) atom.commands.dispatch(treeView.element, 'core:page-down') - expect(treeView.scroller.scrollTop).toBe treeView.element.offsetHeight + expect(treeView.element.scrollTop).toBe treeView.element.offsetHeight describe "movement outside of viewable region", -> it "scrolls the tree view to the selected item", -> treeView.element.style.height = '100px' jasmine.attachToDOM(treeView.element) element.expand() for element in treeView.element.querySelectorAll('.directory') - expect(treeView.list.offsetHeight).toBeGreaterThan treeView.scroller.offsetHeight atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scroller.scrollTop).toBe(0) + expect(treeView.element.scrollTop).toBe(0) entryCount = treeView.element.querySelectorAll(".entry").length entryHeight = treeView.element.querySelector('.file').offsetHeight _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-down') - expect(treeView.scroller.scrollTop + treeView.element.offsetHeight).toBeGreaterThan((entryCount * entryHeight) - 1) + expect(treeView.element.scrollTop + treeView.element.offsetHeight).toBeGreaterThan((entryCount * entryHeight) - 1) _.times entryCount, -> atom.commands.dispatch(treeView.element, 'core:move-up') - expect(treeView.scroller.scrollTop).toBe 0 + expect(treeView.element.scrollTop).toBe 0 describe "tree-view:expand-directory", -> describe "when a directory entry is selected", -> @@ -1055,7 +967,7 @@ describe "TreeView", -> describe "when the directory is already expanded", -> describe "when the directory is empty", -> - it "does nothing", -> + xit "does nothing", -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1')) fs.mkdirSync(path.join(rootDirPath, "empty-dir")) atom.project.setPaths([rootDirPath]) @@ -1082,7 +994,7 @@ describe "TreeView", -> describe "when a file entry is selected", -> it "does nothing", -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> root1.querySelector('.file').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -1141,7 +1053,7 @@ describe "TreeView", -> describe "when a file is selected", -> it "collapses and selects the selected file's parent directory", -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> subdir.querySelector('.file').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -1185,7 +1097,7 @@ describe "TreeView", -> treeView.selectEntry(sampleJs) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') runs -> @@ -1199,7 +1111,7 @@ describe "TreeView", -> treeView.selectEntry(sampleJs) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') runs -> @@ -1210,7 +1122,7 @@ describe "TreeView", -> treeView.selectEntry(sampleJs) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') runs -> @@ -1253,14 +1165,14 @@ describe "TreeView", -> beforeEach -> jasmine.attachToDOM(workspaceElement) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> previousPane = atom.workspace.getActivePane() spyOn(previousPane, 'split').andCallThrough() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> selectEntry 'tree-view.txt' atom.commands.dispatch(treeView.element, command) @@ -1291,7 +1203,7 @@ describe "TreeView", -> treeView.selectEntry(sampleJs) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') runs -> @@ -1319,7 +1231,7 @@ describe "TreeView", -> beforeEach -> jasmine.attachToDOM(workspaceElement) [1..9].forEach -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> selectEntry "tree-view.js" atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') @@ -1334,7 +1246,7 @@ describe "TreeView", -> describe "when a file is selected", -> beforeEach -> selectEntry 'tree-view.txt' - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> @@ -1349,8 +1261,9 @@ describe "TreeView", -> atom.project.setPaths([projectPath]) jasmine.attachToDOM(workspaceElement) + global.debug = true [1..9].forEach (index) -> - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> selectEntry getPaneFileName(index) atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') @@ -1367,7 +1280,7 @@ describe "TreeView", -> describe "when a file is selected that is already open in pane #{fileIndex}", -> beforeEach -> selectEntry fileName - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> @@ -1427,7 +1340,7 @@ describe "TreeView", -> beforeEach -> LocalStorage.clear() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -1459,7 +1372,7 @@ describe "TreeView", -> beforeEach -> LocalStorage.clear() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -1846,7 +1759,7 @@ describe "TreeView", -> callback = jasmine.createSpy("onFileCreated") treeView.onFileCreated(callback) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -1873,7 +1786,7 @@ describe "TreeView", -> it "adds a file, closes the dialog, selects the file in the tree-view, and emits an event", -> newPath = path.join(dirPath, "new-test-file.txt") - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> addDialog.miniEditor.insertText(path.basename(newPath)) atom.commands.dispatch addDialog.element, 'core:confirm' @@ -1892,10 +1805,10 @@ describe "TreeView", -> it "adds file in any project path", -> newPath = path.join(dirPath3, "new-test-file.txt") - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView4.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch(treeView.element, "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() @@ -1930,14 +1843,14 @@ describe "TreeView", -> it "adds a file and closes the dialog", -> atom.project.setPaths([]) addDialog.close() - atom.commands.dispatch(treeView.element, "tree-view:add-file") + atom.commands.dispatch(atom.views.getView(atom.workspace), "tree-view:add-file") [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() newPath = path.join(fs.realpathSync(temp.mkdirSync()), 'a-file') addDialog.miniEditor.insertText(newPath) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch addDialog.element, 'core:confirm' runs -> @@ -1960,7 +1873,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch addDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(document.activeElement).toBe(treeView.element) expect(callback).not.toHaveBeenCalled() describe "when the add dialog's editor loses focus", -> @@ -1974,7 +1887,7 @@ describe "TreeView", -> newPath = path.join(dirPath, "new-test-file.txt") addDialog.miniEditor.insertText(path.basename(newPath) + " ") - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch addDialog.element, 'core:confirm' runs -> @@ -2035,7 +1948,7 @@ describe "TreeView", -> callback = jasmine.createSpy("onDirectoryCreated") treeView.onDirectoryCreated(callback) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2062,7 +1975,7 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') expect(callback).toHaveBeenCalledWith({path: newPath}) @@ -2076,7 +1989,7 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') expect(callback).toHaveBeenCalledWith({path: newPath + path.sep}) @@ -2095,7 +2008,7 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath - expect(document.activeElement).toBe(treeView.element.querySelector(".tree-view")) + expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new2') expect(treeView.entryForPath(expandedPath).isExpanded).toBeTruthy() expect(callback).toHaveBeenCalledWith({path: newPath}) @@ -2104,7 +2017,7 @@ describe "TreeView", -> it "adds a directory and closes the dialog", -> addDialog.close() atom.project.setPaths([]) - atom.commands.dispatch(treeView.element, "tree-view:add-folder") + atom.commands.dispatch(atom.views.getView(atom.workspace), "tree-view:add-folder") [addPanel] = atom.workspace.getModalPanels() addDialog = addPanel.getItem() @@ -2137,7 +2050,7 @@ describe "TreeView", -> callback = jasmine.createSpy("onEntryMoved") treeView.onEntryMoved(callback) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2212,7 +2125,7 @@ describe "TreeView", -> it "removes the dialog and focuses the tree view", -> atom.commands.dispatch moveDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.list).toHaveFocus() + expect(treeView.element).toHaveFocus() describe "when the move dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2230,7 +2143,7 @@ describe "TreeView", -> dirView.expand() dotFileView = treeView.entryForPath(dotFilePath) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2248,14 +2161,14 @@ describe "TreeView", -> beforeEach -> jasmine.attachToDOM(workspaceElement) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.workspace.open(filePath) - runs -> + waitsForPromise -> dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - treeView.toggleFocus() - atom.commands.dispatch(treeView.element, "tree-view:move") - moveDialog = atom.workspace.getModalPanels()[0].getItem() + treeView.toggleFocus().then -> + atom.commands.dispatch(treeView.element, "tree-view:move") + moveDialog = atom.workspace.getModalPanels()[0].getItem() afterEach -> waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup @@ -2291,7 +2204,7 @@ describe "TreeView", -> beforeEach -> jasmine.attachToDOM(workspaceElement) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2316,7 +2229,7 @@ describe "TreeView", -> newPath = path.join(rootDirPath, 'duplicated-test-file.txt') copyDialog.miniEditor.setText(path.basename(newPath)) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch copyDialog.element, 'core:confirm' waitsFor "tree view to update", -> @@ -2336,7 +2249,7 @@ describe "TreeView", -> newPath = path.join(rootDirPath, 'new', 'directory', 'duplicated-test-file.txt') copyDialog.miniEditor.setText(newPath) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch copyDialog.element, 'core:confirm' waitsFor "tree view to update", -> @@ -2366,7 +2279,7 @@ describe "TreeView", -> jasmine.attachToDOM(treeView.element) atom.commands.dispatch copyDialog.element, 'core:cancel' expect(atom.workspace.getModalPanels().length).toBe 0 - expect(treeView.list).toHaveFocus() + expect(treeView.element).toHaveFocus() describe "when the duplicate dialog's editor loses focus", -> it "removes the dialog and focuses root view", -> @@ -2384,7 +2297,7 @@ describe "TreeView", -> dirView.expand() dotFileView = treeView.entryForPath(dotFilePath) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2439,7 +2352,7 @@ describe "TreeView", -> it "shows the native alert dialog", -> spyOn(atom, 'confirm') - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2452,7 +2365,7 @@ describe "TreeView", -> spyOn(atom, 'confirm') - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -2485,18 +2398,21 @@ describe "TreeView", -> it "closes editors with files belonging to the removed folder", -> jasmine.attachToDOM(workspaceElement) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.workspace.open(filePath2) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.workspace.open(filePath3) runs -> openFilePaths = atom.workspace.getTextEditors().map((e) -> e.getPath()) expect(openFilePaths).toEqual([filePath2, filePath3]) dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + waitsForPromise -> treeView.toggleFocus() + runs -> spyOn(atom, 'confirm').andCallFake (dialog) -> dialog.buttons["Move to Trash"]() @@ -2533,7 +2449,7 @@ describe "TreeView", -> describe "project changes", -> beforeEach -> atom.project.setPaths([path1]) - treeView = atom.workspace.getLeftPanels()[0].getItem() + treeView = atom.workspace.getLeftDock().getActivePaneItem() root1 = treeView.roots[0] describe "when a root folder is added", -> @@ -2541,7 +2457,7 @@ describe "TreeView", -> root1.querySelector('.directory').dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.project.setPaths([path1, path2]) - treeView = atom.workspace.getLeftPanels()[0].getItem() + treeView = atom.workspace.getLeftDock().getActivePaneItem() expect(treeView.element).toExist() expect(treeView.roots[0].querySelector(".directory")).toHaveClass("expanded") @@ -2549,7 +2465,7 @@ describe "TreeView", -> root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.project.setPaths([path1, path2]) - treeView = atom.workspace.getLeftPanels()[0].getItem() + treeView = atom.workspace.getLeftDock().getActivePaneItem() expect(treeView.element).toExist() expect(treeView.roots[0]).toHaveClass("collapsed") @@ -2819,7 +2735,7 @@ describe "TreeView", -> it "adds a custom style", -> expect(treeView.element.querySelector('.file.status-modified')).toHaveText('b.txt') - describe "when a directory if modified", -> + describe "when a directory is modified", -> it "adds a custom style", -> expect(treeView.element.querySelector('.directory.status-modified').header).toHaveText('dir') @@ -2886,66 +2802,6 @@ describe "TreeView", -> expect(treeView.element.querySelector('.file.status-modified')).not.toExist() expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() - describe "when the resize handle is double clicked", -> - beforeEach -> - treeView.element.style.width = '10px' - treeView.element.querySelector('.list-tree').style.width = '100px' - jasmine.attachToDOM(workspaceElement) - - it "sets the width of the tree to be the width of the list", -> - expect(parseInt(treeView.element.style.width)).toBe 10 - treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true})) - expect(parseInt(treeView.element.style.width)).toBeGreaterThan 10 - - treeView.element.style.width = '1000px' - treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true})) - expect(parseInt(treeView.element.style.width)).toBeLessThan 1000 - - describe "when other panels are added", -> - beforeEach -> - jasmine.attachToDOM(workspaceElement) - - it "should resize normally", -> - expect(treeView.element).toBeVisible() - expect(atom.workspace.getLeftPanels().length).toBe(1) - - treeView.element.style.width = '100px' - - expect(parseInt(treeView.element.style.width)).toBe(100) - - panel = document.createElement('div') - panel.style.width = '100px' - atom.workspace.addLeftPanel({item: panel, priority: 10}) - - expect(atom.workspace.getLeftPanels().length).toBe(2) - expect(parseInt(treeView.element.style.width)).toBe(100) - - treeView.resizeTreeView({pageX: 250, which: 1}) - - expect(parseInt(treeView.element.style.width)).toBe(150) - - it "should resize normally on the right side", -> - atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side') - expect(treeView.element.dataset.showOnRightSide).toBe('true') - - expect(treeView.element).toBeVisible() - expect(atom.workspace.getRightPanels().length).toBe(1) - - treeView.element.style.width = '100px' - - expect(parseInt(treeView.element.style.width)).toBe(100) - - panel = document.createElement('div') - panel.style.width = '100px' - atom.workspace.addRightPanel({item: panel, priority: 10}) - - expect(atom.workspace.getRightPanels().length).toBe(2) - expect(parseInt(treeView.element.style.width)).toBe(100) - - treeView.resizeTreeView({pageX: document.body.offsetWidth - 250, which: 1}) - - expect(parseInt(treeView.element.style.width)).toBe(150) - describe "selecting items", -> [dirView, fileView1, fileView2, fileView3, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3] = [] @@ -3243,7 +3099,7 @@ describe "TreeView", -> describe "showCurrentFileInFileManager()", -> it "does nothing when no file is opened", -> - expect(atom.workspace.getPaneItems().length).toBe(0) + expect(atom.workspace.getCenter().getPaneItems().length).toBe(0) expect(treeView.showCurrentFileInFileManager()).toBeUndefined() it "does nothing when only an untitled tab is opened", -> @@ -3391,7 +3247,7 @@ describe "TreeView", -> thetaDir = gammaDir.entries.children[0] thetaDir.expand() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.workspace.open(thetaFilePath) runs -> @@ -3475,32 +3331,32 @@ describe "TreeView", -> it "selects the files and opens it in the active editor, without changing focus", -> treeView.focus() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> expect(sampleJs).toHaveClass 'selected' expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(treeView.list).toHaveFocus() + expect(treeView.element).toHaveFocus() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> expect(sampleTxt).toHaveClass 'selected' expect(treeView.element.querySelectorAll('.selected').length).toBe 1 expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') - expect(treeView.list).toHaveFocus() + expect(treeView.element).toHaveFocus() describe "opening existing opened files in existing split panes", -> beforeEach -> jasmine.attachToDOM(workspaceElement) - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> selectEntry 'tree-view.js' atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> selectEntry 'tree-view.txt' atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') @@ -3515,7 +3371,7 @@ describe "TreeView", -> firstPane = getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch treeView.element, "tree-view:open-selected-entry" it "opens the file in the second pane and focuses it", -> @@ -3535,7 +3391,7 @@ describe "TreeView", -> firstPane = getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> atom.commands.dispatch treeView.element, "tree-view:open-selected-entry" it "opens the file in the first pane, which was the current focus", -> @@ -3555,7 +3411,7 @@ describe "TreeView", -> treeView.focus() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> @@ -3580,7 +3436,7 @@ describe "TreeView", -> treeView.focus() - waitsForFileToOpen -> + waitForWorkspaceOpenEvent -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) @@ -3682,7 +3538,6 @@ describe "TreeView", -> treeView.rootDragAndDrop.onDragEnd(dragEndEvent) expect(document.querySelector('.placeholder')).not.toExist() - describe "when dropping a project root's header onto a different project root", -> describe "when dropping on the top part of the header", -> it "should add the placeholder above the directory", -> @@ -3793,11 +3648,10 @@ describe 'Icon class handling', -> else "some-other-file" } - waitsForPromise -> - atom.packages.activatePackage('tree-view') + waitForPackageActivation() runs -> - treeView = atom.packages.getActivePackage("tree-view").mainModule.createView() + treeView = atom.packages.getActivePackage("tree-view").mainModule.getTreeViewInstance() files = workspaceElement.querySelectorAll('li[is="tree-view-file"]') expect(files[0].fileName.className).toBe('name icon first-icon-class second-icon-class') diff --git a/spec/tree-view-spec.js b/spec/tree-view-spec.js new file mode 100644 index 00000000..e56c69cf --- /dev/null +++ b/spec/tree-view-spec.js @@ -0,0 +1,46 @@ +const TreeView = require('../lib/tree-view') + +describe('TreeView', () => { + describe('serialization', () => { + it('restores the expanded directories and selected file', () => { + const treeView = new TreeView({}) + treeView.roots[0].expand() + treeView.roots[0].entries.firstChild.expand() + treeView.selectEntry(treeView.roots[0].entries.firstChild.entries.firstChild) + + const treeView2 = new TreeView(treeView.serialize()) + + expect(treeView2.roots[0].isExpanded).toBe(true) + expect(treeView2.roots[0].entries.children[0].isExpanded).toBe(true) + expect(treeView2.roots[0].entries.children[1].isExpanded).toBeUndefined() + expect(Array.from(treeView2.getSelectedEntries())).toEqual([treeView2.roots[0].entries.firstChild.entries.firstChild]) + }) + + it('restores the scroll position', () => { + const treeView = new TreeView({}) + treeView.roots[0].expand() + treeView.roots[0].entries.firstChild.expand() + treeView.element.style.overflow = 'auto' + treeView.element.style.height = '80px' + treeView.element.style.width = '80px' + jasmine.attachToDOM(treeView.element) + + treeView.element.scrollTop = 42 + treeView.element.scrollLeft = 43 + + expect(treeView.element.scrollTop).toBe(42) + expect(treeView.element.scrollLeft).toBe(43) + + const treeView2 = new TreeView(treeView.serialize()) + treeView2.element.style.overflow = 'auto' + treeView2.element.style.height = '80px' + treeView2.element.style.width = '80px' + jasmine.attachToDOM(treeView2.element) + + waitsFor(() => + treeView2.element.scrollTop === 42 && + treeView2.element.scrollLeft === 43 + ) + }) + }) +}) From f9704d4ea72fdeed32d2010cf9fac51deda5ae49 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 4 Apr 2017 16:13:13 -0700 Subject: [PATCH 177/263] Recreate tree-view properly after its pane is closed --- lib/tree-view-package.js | 1 + spec/tree-view-package-spec.coffee | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index 0262947c..9452469f 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -45,6 +45,7 @@ class TreeViewPackage { getTreeViewInstance (state = {}) { if (this.treeView == null) { this.treeView = new TreeView(state) + this.treeView.onDidDestroy(() => this.treeView = null) } return this.treeView } diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 0a1c6f9c..7cd722cc 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -261,6 +261,21 @@ describe "TreeView", -> expect(atom.workspace.getLeftDock().isOpen()).toBe(true) expect(treeView.element).not.toHaveFocus() + describe "when the tree-view is destroyed", -> + it "can correctly re-create the tree-view", -> + treeView = atom.workspace.getLeftDock().getActivePaneItem() + treeViewHTML = treeView.element.outerHTML + treeView.roots[0].collapse() + atom.workspace.getLeftDock().getActivePane().close() + + waitForWorkspaceOpenEvent -> + atom.commands.dispatch(atom.views.getView(atom.workspace), 'tree-view:toggle') + + runs -> + treeView2 = atom.workspace.getLeftDock().getActivePaneItem() + treeView2.roots[0].expand() + expect(treeView2.element.outerHTML).toBe(treeViewHTML) + describe "when revealActiveFile", -> beforeEach -> atom.workspace.getLeftDock().hide() From dcb69da4f9a614e2b052adf9dd2fe7a2d32fa6e2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 5 Apr 2017 12:57:30 -0600 Subject: [PATCH 178/263] Implement getPreferredWidth on tree view to support resize-to-fit Signed-off-by: Max Brunsfeld --- lib/tree-view.coffee | 3 +++ styles/tree-view.less | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 52cebd71..762b808d 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -111,6 +111,9 @@ class TreeView isPermanentDockItem: -> true + getPreferredWidth: -> + @list.offsetWidth + onDirectoryCreated: (callback) -> @emitter.on('directory-created', callback) diff --git a/styles/tree-view.less b/styles/tree-view.less index 7ff2ce8f..7f735b2b 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -23,7 +23,7 @@ * auto-created layer. */ isolation: isolate; - min-width: -webkit-min-content; + width: min-content; padding-left: @component-icon-padding; padding-right: @component-padding; } From 74a3404d32bbf723937a9b588ed0e4ba39e16b6b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Apr 2017 12:08:49 -0700 Subject: [PATCH 179/263] Prepare 0.217.0-0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6a4e70a4..250e9f33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.216.1", + "version": "0.217.0-0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 96b49408628349300c9bed9c7a9e5287c7a8f823 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 5 Apr 2017 21:15:17 -0600 Subject: [PATCH 180/263] Fix rendering artifacts --- styles/tree-view.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index 7f735b2b..3a0043e9 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -5,7 +5,7 @@ } .tree-view { - contain: strict; + contain: size; overflow: auto; z-index: 2; -webkit-user-select: none; @@ -26,6 +26,7 @@ width: min-content; padding-left: @component-icon-padding; padding-right: @component-padding; + background-color: inherit; } .header { From 823033d8c2971d1e4d5560c0adf79135c5b02109 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 5 Apr 2017 21:17:04 -0600 Subject: [PATCH 181/263] Prepare 0.217.0-1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 250e9f33..82352291 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-0", + "version": "0.217.0-1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From a654ac76f8600e75e1bb2a9bb41879e0bb08dc9c Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 6 Apr 2017 11:08:22 -0400 Subject: [PATCH 182/263] `.tree-view.full-menu` :point_right: `.tree-view .full-menu` --- menus/tree-view.cson | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/menus/tree-view.cson b/menus/tree-view.cson index 64d7a1be..da5fffc5 100644 --- a/menus/tree-view.cson +++ b/menus/tree-view.cson @@ -20,7 +20,7 @@ ] 'context-menu': - '.tree-view.full-menu': [ + '.tree-view .full-menu': [ {'label': 'New File', 'command': 'tree-view:add-file'} {'label': 'New Folder', 'command': 'tree-view:add-folder'} {'type': 'separator'} @@ -41,7 +41,7 @@ {'label': 'Open In New Window', 'command': 'tree-view:open-in-new-window'} ] - '.tree-view.full-menu [is="tree-view-file"]': [ + '.tree-view .full-menu [is="tree-view-file"]': [ {'label': 'Split Up', 'command': 'tree-view:open-selected-entry-up'} {'label': 'Split Down', 'command': 'tree-view:open-selected-entry-down'} {'label': 'Split Left', 'command': 'tree-view:open-selected-entry-left'} @@ -49,7 +49,7 @@ {'type': 'separator'} ] - '.tree-view.full-menu .project-root > .header': [ + '.tree-view .full-menu .project-root > .header': [ {'label': 'New File', 'command': 'tree-view:add-file'} {'label': 'New Folder', 'command': 'tree-view:add-folder'} {'type': 'separator'} @@ -71,19 +71,19 @@ {'label': 'Open In New Window', 'command': 'tree-view:open-in-new-window'} ] - '.platform-darwin .tree-view.full-menu': [ + '.platform-darwin .tree-view .full-menu': [ {'label': 'Show in Finder', 'command': 'tree-view:show-in-file-manager'} ] - '.platform-win32 .tree-view.full-menu': [ + '.platform-win32 .tree-view .full-menu': [ {'label': 'Show in Explorer', 'command': 'tree-view:show-in-file-manager'} ] - '.platform-linux .tree-view.full-menu': [ + '.platform-linux .tree-view .full-menu': [ {'label': 'Show in File Manager', 'command': 'tree-view:show-in-file-manager'} ] - '.tree-view.multi-select': [ + '.tree-view .multi-select': [ {'label': 'Delete', 'command': 'tree-view:remove'} {'label': 'Copy', 'command': 'tree-view:copy'} {'label': 'Cut', 'command': 'tree-view:cut'} From cc834a973698f09fa5ebc652e2a147404a3a1f77 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 6 Apr 2017 10:27:26 -0600 Subject: [PATCH 183/263] Prepare 0.217.0-2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82352291..09475058 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-1", + "version": "0.217.0-2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 9523fb594ef8d128430a374e8625ce1664278601 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Apr 2017 14:36:19 -0700 Subject: [PATCH 184/263] Update calls to Dock.isOpen to use .isVisible --- spec/tree-view-package-spec.coffee | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 7cd722cc..fc8b2920 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -166,7 +166,7 @@ describe "TreeView", -> atom.packages.deactivatePackage("tree-view") atom.packages.packageStates = {} atom.workspace.getLeftDock().hide() - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> atom.workspace.open('tree-view.js') @@ -175,7 +175,7 @@ describe "TreeView", -> runs -> # This assertion is failing because we auto-open docks when adding an item - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) describe "when the root view is opened to a directory", -> it "attaches to the workspace", -> @@ -206,21 +206,21 @@ describe "TreeView", -> describe "when the tree view is visible", -> beforeEach -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) it "hides the tree view", -> workspaceElement.focus() waitsForPromise -> treeView.toggle() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) describe "when the tree view is hidden", -> it "shows and focuses the tree view", -> atom.workspace.getLeftDock().hide() - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> treeView.toggle() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).toHaveFocus() describe "when tree-view:toggle-focus is triggered on the root view", -> @@ -232,7 +232,7 @@ describe "TreeView", -> atom.workspace.getLeftDock().hide() waitsForPromise -> treeView.toggleFocus() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).toHaveFocus() describe "when the tree view is shown", -> @@ -242,11 +242,11 @@ describe "TreeView", -> runs -> workspaceElement.focus() - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) waitsForPromise -> treeView.toggleFocus() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).toHaveFocus() describe "when the tree view is focused", -> @@ -256,9 +256,9 @@ describe "TreeView", -> runs -> treeView.focus() - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) treeView.toggleFocus() - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).not.toHaveFocus() describe "when the tree-view is destroyed", -> @@ -339,25 +339,25 @@ describe "TreeView", -> runs -> expect(atom.workspace.getActivePaneItem().getPath()).toBeUndefined() - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> treeView.revealActiveFile() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) 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(atom.workspace.getActivePaneItem()).toBeUndefined() - expect(atom.workspace.getLeftDock().isOpen()).toBe(false) + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> treeView.revealActiveFile() runs -> - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.focus).toHaveBeenCalled() describe 'if there are more items than can be visible in the viewport', -> @@ -407,7 +407,7 @@ describe "TreeView", -> treeView.focus() expect(treeView.element).toHaveFocus() atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') - expect(atom.workspace.getLeftDock().isOpen()).toBe(true) + expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).not.toHaveFocus() expect(atom.workspace.getActivePane().isActive()).toBe(true) From a27cfa823227f66b077679355a3b732e35557b82 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Apr 2017 15:28:59 -0700 Subject: [PATCH 185/263] Handle new the workspace's new active pane container behavior Signed-off-by: Nathan Sobo --- lib/dialog.coffee | 2 +- lib/tree-view.coffee | 16 ++-- spec/tree-view-package-spec.coffee | 113 ++++++++++++++--------------- 3 files changed, 61 insertions(+), 70 deletions(-) diff --git a/lib/dialog.coffee b/lib/dialog.coffee index d6653460..500a1192 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -56,7 +56,7 @@ class Dialog @emitter.dispose() @disposables.dispose() @miniEditor.destroy() - activePane = atom.workspace.getActivePane() + activePane = atom.workspace.getCenter().getActivePane() activePane.activate() unless activePane.isDestroyed() cancel: -> diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 762b808d..f979a1cf 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -175,7 +175,7 @@ class TreeView atom.commands.add @element, "tree-view:open-selected-entry-in-pane-#{index + 1}", => @openSelectedEntryInPane index - @disposables.add atom.workspace.onDidChangeActivePaneItem => + @disposables.add atom.workspace.getCenter().onDidChangeActivePaneItem => @selectActiveFile() @revealActiveFile() if atom.config.get('tree-view.autoReveal') @disposables.add atom.project.onDidChangePaths => @@ -205,7 +205,7 @@ class TreeView @element.focus() unfocus: -> - atom.workspace.getActivePane().activate() + atom.workspace.getCenter().activate() hasFocus: -> document.activeElement is @element @@ -289,13 +289,11 @@ class TreeView @list.appendChild(root) root - getActivePath: -> atom.workspace.getActivePaneItem()?.getPath?() + getActivePath: -> atom.workspace.getCenter().getActivePaneItem()?.getPath?() selectActiveFile: -> if activeFilePath = @getActivePath() @selectEntryForPath(activeFilePath) - else - @deselect() revealActiveFile: -> if _.isEmpty(atom.project.getPaths()) @@ -428,9 +426,9 @@ class TreeView selectedEntry = @selectedEntry() return unless selectedEntry? - pane = atom.workspace.getActivePane() + pane = atom.workspace.getCenter().getActivePane() if pane and selectedEntry.classList.contains('file') - if atom.workspace.getActivePaneItem() + if atom.workspace.getCenter().getActivePaneItem() split = pane.split orientation, side atom.workspace.openURIInPane selectedEntry.getPath(), split else @@ -452,7 +450,7 @@ class TreeView selectedEntry = @selectedEntry() return unless selectedEntry? - pane = atom.workspace.getPanes()[index] + pane = atom.workspace.getCenter().getPanes()[index] if pane and selectedEntry.classList.contains('file') atom.workspace.openURIInPane selectedEntry.getPath(), pane @@ -536,7 +534,7 @@ class TreeView @openInFileManager(command, args, label, isFile) showCurrentFileInFileManager: -> - return unless editor = atom.workspace.getActiveTextEditor() + return unless editor = atom.workspace.getCenter().getActiveTextEditor() return unless editor.getPath() {command, args, label} = @fileManagerCommandForPath(editor.getPath(), true) @openInFileManager(command, args, label, true) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index fc8b2920..79d14e41 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -137,7 +137,6 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open(filePath) - waitsForPromise -> treeView.revealActiveFile() @@ -153,7 +152,7 @@ describe "TreeView", -> atom.workspace.open() waitsFor (done) -> - atom.workspace.getActivePaneItem().saveAs(path.join(projectPath, 'test.txt')) + atom.workspace.getCenter().getActivePaneItem().saveAs(path.join(projectPath, 'test.txt')) atom.workspace.onDidOpen(done) runs -> @@ -171,10 +170,12 @@ describe "TreeView", -> waitsForPromise -> atom.workspace.open('tree-view.js') + runs -> + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) + waitForPackageActivation() runs -> - # This assertion is failing because we auto-open docks when adding an item expect(atom.workspace.getLeftDock().isVisible()).toBe(false) describe "when the root view is opened to a directory", -> @@ -338,7 +339,7 @@ describe "TreeView", -> atom.workspace.open() runs -> - expect(atom.workspace.getActivePaneItem().getPath()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBeUndefined() expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> @@ -350,7 +351,7 @@ describe "TreeView", -> describe "if there is no editor open", -> it "shows and focuses the tree view, but does not attempt to select a specific file", -> - expect(atom.workspace.getActivePaneItem()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem()).toBeUndefined() expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> @@ -409,7 +410,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).not.toHaveFocus() - expect(atom.workspace.getActivePane().isActive()).toBe(true) + expect(atom.workspace.getCenter().getActivePane().isActive()).toBe(true) describe "copy path commands", -> [pathToSelect, relativizedPath] = [] @@ -524,7 +525,7 @@ describe "TreeView", -> waitsFor -> # Ensure we don't move on to the next test until the promise spawned click event resolves. # (If it resolves in the middle of the next test we'll pollute that test). - not treeView.currentlyOpening.has(atom.workspace.getActivePaneItem().getPath()) + not treeView.currentlyOpening.has(atom.workspace.getCenter().getActivePaneItem().getPath()) describe "when the file is pending", -> editor = null @@ -536,13 +537,13 @@ describe "TreeView", -> it "marks the pending file as permanent", -> runs -> - expect(atom.workspace.getActivePane().getActiveItem()).toBe editor - expect(atom.workspace.getActivePane().getPendingItem()).toBe editor + expect(atom.workspace.getCenter().getActivePane().getActiveItem()).toBe editor + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).toBe editor sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2})) waitsFor -> - atom.workspace.getActivePane().getPendingItem() is null + atom.workspace.getCenter().getActivePane().getPendingItem() is null describe "when files are clicked", -> beforeEach -> @@ -558,7 +559,7 @@ describe "TreeView", -> r = sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> - activePaneItem = atom.workspace.getActivePaneItem() + activePaneItem = atom.workspace.getCenter().getActivePaneItem() it "selects the file and retains focus on tree-view", -> expect(sampleJs).toHaveClass 'selected' @@ -566,7 +567,7 @@ describe "TreeView", -> it "opens the file in a pending state", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(atom.workspace.getActivePane().getPendingItem()).toEqual activePaneItem + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).toEqual activePaneItem describe "when core.allowPendingPaneItems is set to false", -> beforeEach -> @@ -598,7 +599,7 @@ describe "TreeView", -> openedCount is 2 runs -> - expect(atom.workspace.getActivePane().getItems().length).toBe 1 + expect(atom.workspace.getCenter().getActivePane().getItems().length).toBe 1 describe "when a file is double-clicked", -> activePaneItem = null @@ -615,7 +616,7 @@ describe "TreeView", -> setImmediate(done) runs -> - activePaneItem = atom.workspace.getActivePaneItem() + activePaneItem = atom.workspace.getCenter().getActivePaneItem() expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') expect(atom.views.getView(activePaneItem)).toHaveFocus() @@ -633,7 +634,7 @@ describe "TreeView", -> openedCount is 2 runs -> - expect(atom.workspace.getActivePane().getItems().length).toBe 1 + expect(atom.workspace.getCenter().getActivePane().getItems().length).toBe 1 describe "when a directory is single-clicked", -> it "is selected", -> @@ -734,15 +735,6 @@ describe "TreeView", -> waitsFor -> treeView.getSelectedEntries()[0].textContent is 'sub-file1' - describe "when the item has no path", -> - it "deselects the previously selected entry", -> - waitForWorkspaceOpenEvent -> - sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - - runs -> - atom.workspace.getActivePane().activateItem(document.createElement("div")) - expect(treeView.element.querySelector('.selected')).not.toExist() - describe "when a different editor becomes active", -> beforeEach -> jasmine.attachToDOM(workspaceElement) @@ -754,7 +746,7 @@ describe "TreeView", -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> - leftEditorPane = atom.workspace.getActivePane() + leftEditorPane = atom.workspace.getCenter().getActivePane() leftEditorPane.splitRight() waitForWorkspaceOpenEvent -> @@ -1116,10 +1108,10 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') runs -> - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') expect(atom.views.getView(item)).toHaveFocus() - expect(atom.workspace.getActivePane().getPendingItem()).not.toEqual item + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).not.toEqual item it "opens pending items in a permanent state", -> jasmine.attachToDOM(workspaceElement) @@ -1130,9 +1122,9 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') runs -> - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(atom.workspace.getActivePane().getPendingItem()).toEqual item + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).toEqual item expect(atom.views.getView(item)).toHaveFocus() treeView.selectEntry(sampleJs) @@ -1141,10 +1133,10 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') runs -> - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') expect(atom.views.getView(item)).toHaveFocus() - expect(atom.workspace.getActivePane().getPendingItem()).not.toEqual item + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).not.toEqual item describe "when a directory is selected", -> it "expands or collapses the directory", -> @@ -1161,7 +1153,7 @@ describe "TreeView", -> describe "when nothing is selected", -> it "does nothing", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry') - expect(atom.workspace.getActivePaneItem()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem()).toBeUndefined() describe "opening in new split panes", -> splitOptions = @@ -1184,7 +1176,7 @@ describe "TreeView", -> sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> - previousPane = atom.workspace.getActivePane() + previousPane = atom.workspace.getCenter().getActivePane() spyOn(previousPane, 'split').andCallThrough() waitForWorkspaceOpenEvent -> @@ -1195,8 +1187,8 @@ describe "TreeView", -> expect(previousPane.split).toHaveBeenCalledWith options... it "opens the file in the new split pane and focuses it", -> - splitPane = atom.workspace.getActivePane() - splitPaneItem = atom.workspace.getActivePaneItem() + splitPane = atom.workspace.getCenter().getActivePane() + splitPaneItem = atom.workspace.getCenter().getActivePaneItem() expect(previousPane).not.toBe splitPane expect(splitPaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') expect(atom.views.getView(splitPaneItem)).toHaveFocus() @@ -1204,12 +1196,12 @@ describe "TreeView", -> describe "when a directory is selected", -> it "does nothing", -> atom.commands.dispatch(treeView.element, command) - expect(atom.workspace.getActivePaneItem()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem()).toBeUndefined() describe "when nothing is selected", -> it "does nothing", -> atom.commands.dispatch(treeView.element, command) - expect(atom.workspace.getActivePaneItem()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem()).toBeUndefined() describe "tree-view:expand-item", -> describe "when a file is selected", -> @@ -1222,9 +1214,9 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') runs -> - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') - expect(atom.workspace.getActivePane().getPendingItem()).toEqual item + expect(atom.workspace.getCenter().getActivePane().getPendingItem()).toEqual item expect(atom.views.getView(item)).toHaveFocus() describe "when a directory is selected", -> @@ -1240,7 +1232,7 @@ describe "TreeView", -> describe "when nothing is selected", -> it "does nothing", -> atom.commands.dispatch(treeView.element, 'tree-view:expand-item') - expect(atom.workspace.getActivePaneItem()).toBeUndefined() + expect(atom.workspace.getCenter().getActivePaneItem()).toBeUndefined() describe "opening in existing split panes", -> beforeEach -> @@ -1266,7 +1258,7 @@ describe "TreeView", -> it "opens the file in pane #{paneNumber} and focuses it", -> pane = getCenter().getPanes()[index] - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -1300,7 +1292,7 @@ describe "TreeView", -> it "opens the file in pane #{paneNumber} and focuses it", -> pane = getCenter().getPanes()[index] - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve(fileName) @@ -1808,7 +1800,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath waitsFor "tree view to be updated", -> dirView.entries.querySelectorAll(".file").length > 1 @@ -1833,7 +1825,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath waitsFor "tree view to be updated", -> dirView3.entries.querySelectorAll(".file").length > 1 @@ -1871,7 +1863,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).toBe(newPath) + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe(newPath) expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when the path with a trailing '#{path.sep}' is changed and confirmed", -> @@ -1895,7 +1887,7 @@ describe "TreeView", -> it "removes the dialog and focuses root view", -> workspaceElement.focus() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.views.getView(atom.workspace.getActivePane())).toHaveFocus() + expect(atom.views.getView(atom.workspace.getCenter().getActivePane())).toHaveFocus() describe "when the path ends with whitespace", -> it "removes the trailing whitespace before creating the file", -> @@ -1907,7 +1899,7 @@ describe "TreeView", -> runs -> expect(fs.isFileSync(newPath)).toBeTruthy() - expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when a directory is selected", -> @@ -1988,7 +1980,7 @@ describe "TreeView", -> atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).not.toBe newPath expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') @@ -2002,7 +1994,7 @@ describe "TreeView", -> atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).not.toBe newPath expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new') @@ -2021,7 +2013,7 @@ describe "TreeView", -> atom.commands.dispatch addDialog.element, 'core:confirm' expect(fs.isDirectorySync(newPath)).toBeTruthy() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.workspace.getActivePaneItem().getPath()).not.toBe newPath + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).not.toBe newPath expect(document.activeElement).toBe(treeView.element) expect(dirView.querySelector('.directory.selected').textContent).toBe('new2') @@ -2146,7 +2138,7 @@ describe "TreeView", -> it "removes the dialog and focuses root view", -> workspaceElement.focus() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.views.getView(atom.workspace.getActivePane())).toHaveFocus() + expect(atom.views.getView(atom.workspace.getCenter().getActivePane())).toHaveFocus() describe "when a file is selected that's name starts with a '.'", -> [dotFilePath, dotFileView, moveDialog] = [] @@ -2197,13 +2189,14 @@ describe "TreeView", -> describe "when the path is changed and confirmed", -> it "updates text editor paths accordingly", -> - editor = atom.workspace.getActiveTextEditor() + editor = atom.workspace.getCenter().getActiveTextEditor() expect(editor.getPath()).toBe(filePath) newPath = path.join(rootDirPath, 'renamed-dir') moveDialog.miniEditor.setText(newPath) atom.commands.dispatch moveDialog.element, 'core:confirm' + expect(atom.workspace.getActivePaneItem()).toBe(editor) expect(editor.getPath()).toBe(filePath.replace('test-dir', 'renamed-dir')) describe "when the project is selected", -> @@ -2300,7 +2293,7 @@ describe "TreeView", -> it "removes the dialog and focuses root view", -> workspaceElement.focus() expect(atom.workspace.getModalPanels().length).toBe 0 - expect(atom.views.getView(atom.workspace.getActivePane())).toHaveFocus() + expect(atom.views.getView(atom.workspace.getCenter().getActivePane())).toHaveFocus() describe "when a file is selected that's name starts with a '.'", -> [dotFilePath, dotFileView, copyDialog] = [] @@ -2338,7 +2331,7 @@ describe "TreeView", -> atom.workspace.open('tree-view.js') runs -> - editorElement = atom.views.getView(atom.workspace.getActivePaneItem()) + editorElement = atom.views.getView(atom.workspace.getCenter().getActivePaneItem()) atom.commands.dispatch(editorElement, "tree-view:duplicate") copyDialog = atom.workspace.getModalPanels()[0].getItem() @@ -3351,7 +3344,7 @@ describe "TreeView", -> runs -> expect(sampleJs).toHaveClass 'selected' - expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js') expect(treeView.element).toHaveFocus() waitForWorkspaceOpenEvent -> @@ -3360,7 +3353,7 @@ describe "TreeView", -> runs -> expect(sampleTxt).toHaveClass 'selected' expect(treeView.element.querySelectorAll('.selected').length).toBe 1 - expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') + expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') expect(treeView.element).toHaveFocus() describe "opening existing opened files in existing split panes", -> @@ -3391,7 +3384,7 @@ describe "TreeView", -> it "opens the file in the second pane and focuses it", -> pane = getCenter().getPanes()[1] - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -3410,7 +3403,7 @@ describe "TreeView", -> atom.commands.dispatch treeView.element, "tree-view:open-selected-entry" it "opens the file in the first pane, which was the current focus", -> - item = atom.workspace.getActivePaneItem() + item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(firstPane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -3430,7 +3423,7 @@ describe "TreeView", -> sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) runs -> - activePaneItem = atom.workspace.getActivePaneItem() + activePaneItem = atom.workspace.getCenter().getActivePaneItem() it "selects the file and retains focus on tree-view", -> expect(sampleTxt).toHaveClass 'selected' @@ -3458,7 +3451,7 @@ describe "TreeView", -> waits 100 runs -> - activePaneItem = atom.workspace.getActivePaneItem() + activePaneItem = atom.workspace.getCenter().getActivePaneItem() it "opens the file and focuses it", -> From 9e05f14ce880dedab4c47a8691697c842f317c5d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Apr 2017 15:29:33 -0700 Subject: [PATCH 186/263] 0.217.0-3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09475058..eef17145 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-2", + "version": "0.217.0-3", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 25f4e10e34211f225ef8de5737ead9f4c0cf3f1a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 13:27:18 -0700 Subject: [PATCH 187/263] Show tree view without focusing it when project paths change Fixes https://github.com/atom/atom/issues/14236 --- lib/tree-view-package.js | 25 +++++++++++++++++++------ spec/tree-view-package-spec.coffee | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index 9452469f..d70fff25 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -22,7 +22,17 @@ class TreeViewPackage { })) this.disposables.add(atom.project.onDidChangePaths(this.createOrDestroyTreeViewIfNeeded.bind(this))) - return this.createOrDestroyTreeViewIfNeeded() + + if (this.shouldAttachTreeView()) { + const treeView = this.getTreeViewInstance() + const showOnAttach = !atom.workspace.getActivePaneItem() + this.treeViewOpenPromise = atom.workspace.open(treeView, { + activatePane: showOnAttach, + activateItem: showOnAttach + }) + } else { + this.treeViewOpenPromise = Promise.resolve() + } } deactivate () { @@ -53,17 +63,20 @@ class TreeViewPackage { createOrDestroyTreeViewIfNeeded () { if (this.shouldAttachTreeView()) { const treeView = this.getTreeViewInstance() - if (!atom.workspace.paneForItem(treeView)) { - const showOnAttach = !atom.workspace.getActivePaneItem() - this.treeViewOpenPromise = atom.workspace.open(treeView, {activatePane: showOnAttach, activateItem: showOnAttach}) + const paneContainer = atom.workspace.paneContainerForURI(treeView.getURI()) + if (paneContainer) { + paneContainer.show() + } else { + atom.workspace.open(treeView, { + activatePane: false, + activateItem: false + }).then(() => atom.workspace.paneContainerForURI(treeView.getURI()).show()) } } else { - if (this.treeView) { const pane = atom.workspace.paneForItem(this.treeView) if (pane) pane.removeItem(this.treeView) } - this.treeViewOpenPromise = Promise.resolve() } } diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 79d14e41..8fd3e4a5 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -177,6 +177,9 @@ describe "TreeView", -> runs -> expect(atom.workspace.getLeftDock().isVisible()).toBe(false) + atom.project.addPath(path.join(__dirname, 'fixtures')) + + waitsFor -> atom.workspace.getLeftDock().isVisible() describe "when the root view is opened to a directory", -> it "attaches to the workspace", -> From d5b137a56a89ca0cc8b596c27673e52e6113f6c1 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 13:32:38 -0700 Subject: [PATCH 188/263] Don't try to destroy TreeView using Pane.close Fixes failures in atom/atom#14221 --- spec/tree-view-package-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 8fd3e4a5..e8425f94 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -270,7 +270,7 @@ describe "TreeView", -> treeView = atom.workspace.getLeftDock().getActivePaneItem() treeViewHTML = treeView.element.outerHTML treeView.roots[0].collapse() - atom.workspace.getLeftDock().getActivePane().close() + treeView.destroy() waitForWorkspaceOpenEvent -> atom.commands.dispatch(atom.views.getView(atom.workspace), 'tree-view:toggle') From c166bb3f98af291fcfd62a285f129aae18d1750a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 13:36:05 -0700 Subject: [PATCH 189/263] Avoid exception when tree-view is deactivated right after project paths change This was causing an error to happen asynchronously in the test suite --- lib/tree-view-package.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index d70fff25..9b92ef41 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -70,7 +70,10 @@ class TreeViewPackage { atom.workspace.open(treeView, { activatePane: false, activateItem: false - }).then(() => atom.workspace.paneContainerForURI(treeView.getURI()).show()) + }).then(() => { + const paneContainer = atom.workspace.paneContainerForURI(treeView.getURI()) + if (paneContainer) paneContainer.show() + }) } } else { if (this.treeView) { From 8d68369f492ef9e316e899e40e15995358dab82c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 13:37:56 -0700 Subject: [PATCH 190/263] Prepare 0.217.0-4 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eef17145..877650a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-3", + "version": "0.217.0-4", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From c8cb0be3a350a33480bbab6b1144d4facc416314 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 15:04:19 -0700 Subject: [PATCH 191/263] Don't focus the tree-view whenever active pane item changes --- lib/tree-view.coffee | 20 +++++++++++++------- spec/tree-view-package-spec.coffee | 6 +++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f979a1cf..7188e4e2 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -177,7 +177,7 @@ class TreeView @disposables.add atom.workspace.getCenter().onDidChangeActivePaneItem => @selectActiveFile() - @revealActiveFile() if atom.config.get('tree-view.autoReveal') + @revealActiveFile(false) if atom.config.get('tree-view.autoReveal') @disposables.add atom.project.onDidChangePaths => @updateRoots() @disposables.add atom.config.onDidChange 'tree-view.hideVcsIgnoredFiles', => @@ -194,9 +194,15 @@ class TreeView toggle: -> atom.workspace.toggle(this) - show: (options) -> - atom.workspace.open(this, {searchAllPanes: true}).then => - @focus() unless options?.focus is false + show: (focus) -> + atom.workspace.open(this, { + searchAllPanes: true, + activatePane: false, + activateItem: false, + }).then(() => + atom.workspace.paneContainerForURI(@getURI()).show() + @focus() if focus + ) hide: -> atom.workspace.hide(this) @@ -214,7 +220,7 @@ class TreeView if @hasFocus() @unfocus() else - @show() + @show(true) entryClicked: (e) -> if entry = e.target.closest('.entry') @@ -295,11 +301,11 @@ class TreeView if activeFilePath = @getActivePath() @selectEntryForPath(activeFilePath) - revealActiveFile: -> + revealActiveFile: (focus) -> if _.isEmpty(atom.project.getPaths()) return Promise.resolve() - @show({focus: atom.config.get('tree-view.focusOnReveal')}).then => + @show(focus ? atom.config.get('tree-view.focusOnReveal')).then => return unless activeFilePath = @getActivePath() [rootPath, relativePath] = atom.project.relativizePath(activeFilePath) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index e8425f94..4093dde6 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -280,7 +280,7 @@ describe "TreeView", -> treeView2.roots[0].expand() expect(treeView2.element.outerHTML).toBe(treeViewHTML) - describe "when revealActiveFile", -> + describe "when tree-view:reveal-active-file is triggered", -> beforeEach -> atom.workspace.getLeftDock().hide() spyOn(treeView, 'focus') @@ -729,6 +729,7 @@ describe "TreeView", -> describe "when the tree-view.autoReveal config setting is true", -> beforeEach -> + jasmine.attachToDOM(atom.workspace.getElement()) atom.config.set "tree-view.autoReveal", true it "selects the active item's entry in the tree view, expanding parent directories if needed", -> @@ -738,6 +739,9 @@ describe "TreeView", -> waitsFor -> treeView.getSelectedEntries()[0].textContent is 'sub-file1' + runs -> + expect(atom.workspace.getActiveTextEditor().getElement()).toHaveFocus() + describe "when a different editor becomes active", -> beforeEach -> jasmine.attachToDOM(workspaceElement) From 35a2742e16155dcb359b0f269ebff9444b50b315 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Apr 2017 15:07:09 -0700 Subject: [PATCH 192/263] Prepare 0.217.0-5 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 877650a4..7e6b39a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-4", + "version": "0.217.0-5", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 558986144ce82aeb7903b9e3e751e3efb622f738 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 4 May 2017 10:11:35 -0700 Subject: [PATCH 193/263] Make list inherit its container's width Temporarily set its with to min-content when computing the preferred width --- lib/tree-view.coffee | 5 ++++- styles/tree-view.less | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 7188e4e2..4d023389 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -112,7 +112,10 @@ class TreeView isPermanentDockItem: -> true getPreferredWidth: -> - @list.offsetWidth + @list.style.width = 'min-content' + result = @list.offsetWidth + @list.style.width = '' + result onDirectoryCreated: (callback) -> @emitter.on('directory-created', callback) diff --git a/styles/tree-view.less b/styles/tree-view.less index 3a0043e9..ab5a0f6a 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -23,7 +23,6 @@ * auto-created layer. */ isolation: isolate; - width: min-content; padding-left: @component-icon-padding; padding-right: @component-padding; background-color: inherit; From 73acb419e28971212dd4a0edf5624d60c93166fb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 4 May 2017 10:21:17 -0700 Subject: [PATCH 194/263] Prepare 0.217.0-6 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e6b39a5..f9774934 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-5", + "version": "0.217.0-6", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 755e7817e76141615ffee09406c9104c72b867d4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 4 May 2017 10:21:42 -0700 Subject: [PATCH 195/263] Prepare 0.217.0-7 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f9774934..18b9a607 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-6", + "version": "0.217.0-7", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From cf153825383bbc1daf2c066b512df26ee2b76788 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 11 May 2017 21:44:39 -0600 Subject: [PATCH 196/263] Make specs pass with mocked Date.now --- spec/file-stats-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee index b7e05ecf..31ea394e 100644 --- a/spec/file-stats-spec.coffee +++ b/spec/file-stats-spec.coffee @@ -8,6 +8,7 @@ describe "FileStats", -> [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"] beforeEach -> + jasmine.useRealClock() timeStarted = Date.now() rootDirPath = fs.absolute(temp.mkdirSync("tree-view")) subdirPath = path.join(rootDirPath, "subdir") From 0c133af3f5d6d5e12cdf5ec2b1e460e6f01f6ed0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 11 May 2017 21:44:50 -0600 Subject: [PATCH 197/263] Prepare 0.217.0-8 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 18b9a607..b9142284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-7", + "version": "0.217.0-8", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 8b89251c5789d1486242ef4348f6f5c5196c6059 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 16 May 2017 13:41:47 -0700 Subject: [PATCH 198/263] Fix lint error --- lib/tree-view.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 4d023389..1c9ceba6 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -202,10 +202,9 @@ class TreeView searchAllPanes: true, activatePane: false, activateItem: false, - }).then(() => + }).then => atom.workspace.paneContainerForURI(@getURI()).show() @focus() if focus - ) hide: -> atom.workspace.hide(this) From 66aca880ced946874ce9629efe0ec46b6bb17ddd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 16 May 2017 14:19:58 -0700 Subject: [PATCH 199/263] Prepare 0.217.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9142284..313cb01f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0-8", + "version": "0.217.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 72a345c069c5d30f8a6c2ea3b4e8e6abf9b33cfe Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 20 May 2017 16:02:06 +0900 Subject: [PATCH 200/263] Expand to full-height This makes sure that the context menu can still be openend in the empty area underneath the files. --- styles/tree-view.less | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/styles/tree-view.less b/styles/tree-view.less index ab5a0f6a..83882337 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -10,7 +10,10 @@ z-index: 2; -webkit-user-select: none; - > ol { + display: flex; + flex-direction: column; + + .full-menu { /* * Force a new stacking context to prevent a large, duplicate paint layer from * being created for tree-view's scrolling contents that can make the cost of @@ -26,6 +29,12 @@ padding-left: @component-icon-padding; padding-right: @component-padding; background-color: inherit; + + // Expands .full-menu to take up full height. + // This makes sure that the context menu can still be openend in the empty + // area underneath the files. + flex-grow: 1; + } } .header { From ec1f8bea3a834eb20a24babe12f8e02725ec53e4 Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 20 May 2017 16:03:36 +0900 Subject: [PATCH 201/263] Expand to full width This makes sure that the selected item's "bar/background" expands to full width. --- styles/tree-view.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/styles/tree-view.less b/styles/tree-view.less index 83882337..b0851a71 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -35,6 +35,12 @@ // area underneath the files. flex-grow: 1; } + + .full-menu.list-tree { + // Expands .full-menu to take up as much width as needed by the content. + // This makes sure that the selected item's "bar/background" expands to full width. + position: relative; + min-width: min-content; } .header { From 6e0dff902dd0b5b0f9eaf084d48bd4541ed95270 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Mon, 22 May 2017 16:06:58 -0500 Subject: [PATCH 202/263] fix unselect multiple entiries on right click --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 1c9ceba6..32d24d29 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -803,7 +803,7 @@ class TreeView # return early if we're opening a contextual menu (right click) during multi-select mode if @multiSelectEnabled() and - e.target.classList.contains('selected') and + entryToSelect.classList.contains('selected') and # mouse right click or ctrl click as right click on darwin platforms (e.button is 2 or e.ctrlKey and process.platform is 'darwin') return From ba9b331bb98a1d4e982f99c93f1f6df838fa8d4b Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Mon, 22 May 2017 22:49:30 -0500 Subject: [PATCH 203/263] add test --- spec/tree-view-spec.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/spec/tree-view-spec.js b/spec/tree-view-spec.js index e56c69cf..e83efd10 100644 --- a/spec/tree-view-spec.js +++ b/spec/tree-view-spec.js @@ -43,4 +43,25 @@ describe('TreeView', () => { ) }) }) + + describe('clicking', () => { + it('should leave multiple entries selected on right click', () => { + const treeView = new TreeView({}) + const entries = treeView.roots[0].entries + treeView.selectEntry(entries.children[0]) + treeView.selectMultipleEntries(entries.children[1]) + treeView.showMultiSelectMenu() + + let child = entries.children[0]; + while (child.children.length > 0 && (child = child.firstChild)); + + treeView.onMouseDown({ + stopPropagation() {}, + target: child, + button: 2 + }) + + expect(treeView.getSelectedEntries().length).toBe(2); + }) + }); }) From d244b9d8975e211a5c96627d8378d3de19f2de81 Mon Sep 17 00:00:00 2001 From: Ian Olsen Date: Wed, 24 May 2017 10:25:15 -0700 Subject: [PATCH 204/263] Prepare 0.217.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 313cb01f..c0d813db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.0", + "version": "0.217.1", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 7f8e0377f0ed22cd0d16222cb56bff99d3be8012 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 9 Jun 2017 10:16:08 -0400 Subject: [PATCH 205/263] =?UTF-8?q?=F0=9F=90=9B=E2=8C=A8=20Restore=20abili?= =?UTF-8?q?ty=20for=20`escape`=20to=20return=20focus=20to=20center?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1113. --- keymaps/tree-view.cson | 1 + lib/tree-view.coffee | 2 +- spec/tree-view-package-spec.coffee | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/keymaps/tree-view.cson b/keymaps/tree-view.cson index 08c943c0..98828de7 100644 --- a/keymaps/tree-view.cson +++ b/keymaps/tree-view.cson @@ -68,6 +68,7 @@ 'alt-left': 'tree-view:recursive-collapse-directory' 'h': 'tree-view:collapse-directory' 'enter': 'tree-view:open-selected-entry' + 'escape': 'tree-view:unfocus' 'ctrl-C': 'tree-view:copy-full-path' 'm': 'tree-view:move' 'f2': 'tree-view:move' diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 1c9ceba6..c584f316 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -169,7 +169,7 @@ class TreeView 'tree-view:show-in-file-manager': => @showSelectedEntryInFileManager() 'tree-view:open-in-new-window': => @openSelectedEntryInNewWindow() 'tree-view:copy-project-path': => @copySelectedEntryPath(true) - 'tool-panel:unfocus': => @unfocus() + 'tree-view:unfocus': => @unfocus() 'tree-view:toggle-vcs-ignored-files': -> toggleConfig 'tree-view.hideVcsIgnoredFiles' 'tree-view:toggle-ignored-names': -> toggleConfig 'tree-view.hideIgnoredNames' 'tree-view:remove-project-folder': (e) => @removeProjectFolder(e) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 4093dde6..6b7fd19e 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -401,16 +401,16 @@ describe "TreeView", -> runs -> expect(treeView.scrollTop()).toEqual 0 - describe "when tool-panel:unfocus is triggered on the tree view", -> + describe "when tree-view:unfocus is triggered on the tree view", -> it "surrenders focus to the workspace but remains open", -> waitsForPromise -> - atom.workspace.open() # When we trigger 'tool-panel:unfocus' below, we want an editor to become focused + atom.workspace.open() # When we trigger 'tree-view:unfocus' below, we want an editor to become focused runs -> jasmine.attachToDOM(workspaceElement) treeView.focus() expect(treeView.element).toHaveFocus() - atom.commands.dispatch(treeView.element, 'tool-panel:unfocus') + atom.commands.dispatch(treeView.element, 'tree-view:unfocus') expect(atom.workspace.getLeftDock().isVisible()).toBe(true) expect(treeView.element).not.toHaveFocus() expect(atom.workspace.getCenter().getActivePane().isActive()).toBe(true) From 2896cce347ff5ad20424ba786c1959e40ca197d3 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 9 Jun 2017 15:58:49 -0400 Subject: [PATCH 206/263] Prepare 0.217.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0d813db..d32a7f5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.1", + "version": "0.217.2", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 9b0f8e1b1c2383f1e883ac95a323d042320be624 Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 16 Jun 2017 15:13:46 +0900 Subject: [PATCH 207/263] Fix multi-select shifting --- lib/tree-view.coffee | 2 +- styles/tree-view.less | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index c584f316..835da52e 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -31,7 +31,7 @@ class TreeView @element.tabIndex = -1 @list = document.createElement('ol') - @list.classList.add('full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') + @list.classList.add('tree-view-root', 'full-menu', 'list-tree', 'has-collapsable-children', 'focusable-panel') @element.appendChild(@list) @disposables = new CompositeDisposable diff --git a/styles/tree-view.less b/styles/tree-view.less index b0851a71..e37822b1 100644 --- a/styles/tree-view.less +++ b/styles/tree-view.less @@ -13,7 +13,11 @@ display: flex; flex-direction: column; - .full-menu { + .tree-view-root { + padding-left: @component-icon-padding; + padding-right: @component-padding; + background-color: inherit; + /* * Force a new stacking context to prevent a large, duplicate paint layer from * being created for tree-view's scrolling contents that can make the cost of @@ -26,18 +30,13 @@ * auto-created layer. */ isolation: isolate; - padding-left: @component-icon-padding; - padding-right: @component-padding; - background-color: inherit; - // Expands .full-menu to take up full height. + // Expands tree-view root to take up full height. // This makes sure that the context menu can still be openend in the empty // area underneath the files. flex-grow: 1; - } - .full-menu.list-tree { - // Expands .full-menu to take up as much width as needed by the content. + // Expands tree-view root to take up as much width as needed by the content. // This makes sure that the selected item's "bar/background" expands to full width. position: relative; min-width: min-content; @@ -47,7 +46,8 @@ position: relative; } - .list-tree { + .tree-view-root .list-tree { + // Keeps selections expanded while dragging position: static; } From 2329f92fb909ce0ddd55368cd2d4aded2d75d447 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 16 Jun 2017 13:59:26 -0700 Subject: [PATCH 208/263] Eagerly reload parent directory after creating a file. Fixes some flaky specs. --- lib/tree-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index c584f316..dc319cee 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -705,6 +705,7 @@ class TreeView @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') @emitter.emit 'directory-created', {path: createdPath} dialog.onDidCreateFile (createdPath) => + @entryForPath(createdPath)?.reload() atom.workspace.open(createdPath) @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') @emitter.emit 'file-created', {path: createdPath} From b8b1d10b2140135821dc5e6f65d0b966e54e1fe3 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 18 Jun 2017 23:04:12 -0400 Subject: [PATCH 209/263] Improve expandDirectory logic Instead of trying to expand a file and throwing, search for its parent directory and expand that --- lib/tree-view.coffee | 8 +++++--- spec/tree-view-package-spec.coffee | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index dc319cee..6ed6a928 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -403,10 +403,12 @@ class TreeView selectedEntry = @selectedEntry() return unless selectedEntry? - if isRecursive is false and selectedEntry.isExpanded - @moveDown() if selectedEntry.directory.getEntries().length > 0 + directory = selectedEntry.closest('.directory') + if isRecursive is false and directory.isExpanded + # Select the first entry in the expanded folder if it exists + @moveDown() if directory.directory.getEntries().length > 0 else - selectedEntry.expand(isRecursive) + directory.expand(isRecursive) collapseDirectory: (isRecursive=false) -> selectedEntry = @selectedEntry() diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 6b7fd19e..c44e24a6 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1029,6 +1029,24 @@ describe "TreeView", -> for child in children expect(child).toHaveClass 'expanded' + describe "when a file is selected and ordered to recursively expand", -> + it "recursively expands the selected file's parent directory", -> + dir1 = root1.querySelector('.entries > .directory') + dir2 = root1.querySelectorAll('.entries > .directory')[1] + dir1.expand() + file1 = dir1.querySelector('.file') + subdir1 = dir1.querySelector('.entries > .directory') + + waitForWorkspaceOpenEvent -> + file1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + runs -> + atom.commands.dispatch(treeView.element, 'tree-view:recursive-expand-directory') + expect(dir1).toHaveClass 'expanded' + expect(subdir1).toHaveClass 'expanded' + expect(file1).toHaveClass 'selected' + expect(dir2).toHaveClass 'collapsed' + describe "tree-view:collapse-directory", -> subdir = null From 53a83f82d751da82dabc0d63d4fce83ac0198c76 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 18 Jun 2017 23:39:00 -0400 Subject: [PATCH 210/263] Remove TODO --- spec/tree-view-package-spec.coffee | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index c44e24a6..14ca5bc9 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -36,9 +36,6 @@ setupPaneFiles = -> getPaneFileName = (index) -> "test-file-#{index}.txt" -# TODO: Remove this after atom/atom#13977 lands in favor of unguarded `getCenter()` calls -getCenter = -> atom.workspace.getCenter?() ? atom.workspace - describe "TreeView", -> [treeView, path1, path2, root1, root2, sampleJs, sampleTxt, workspaceElement] = [] @@ -1268,7 +1265,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened all windows", -> - expect(getCenter().getPanes().length).toBe 9 + expect(atom.workspace.getCenter().getPanes().length).toBe 9 [0..8].forEach (index) -> paneNumber = index + 1 @@ -1282,7 +1279,7 @@ describe "TreeView", -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> - pane = getCenter().getPanes()[index] + pane = atom.workspace.getCenter().getPanes()[index] item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -1300,7 +1297,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened all windows", -> - expect(getCenter().getPanes().length).toBe 9 + expect(atom.workspace.getCenter().getPanes().length).toBe 9 [0..8].forEach (index) -> paneNumber = index + 1 @@ -1316,7 +1313,7 @@ describe "TreeView", -> atom.commands.dispatch treeView.element, command it "opens the file in pane #{paneNumber} and focuses it", -> - pane = getCenter().getPanes()[index] + pane = atom.workspace.getCenter().getPanes()[index] item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve(fileName) @@ -3394,21 +3391,21 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry-right') it "should have opened both panes", -> - expect(getCenter().getPanes().length).toBe 2 + expect(atom.workspace.getCenter().getPanes().length).toBe 2 describe "tree-view:open-selected-entry", -> beforeEach -> atom.config.set "tree-view.alwaysOpenExisting", true describe "when the first pane is focused, a file is opened that is already open in the second pane", -> beforeEach -> - firstPane = getCenter().getPanes()[0] + firstPane = atom.workspace.getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' waitForWorkspaceOpenEvent -> atom.commands.dispatch treeView.element, "tree-view:open-selected-entry" it "opens the file in the second pane and focuses it", -> - pane = getCenter().getPanes()[1] + pane = atom.workspace.getCenter().getPanes()[1] item = atom.workspace.getCenter().getActivePaneItem() expect(atom.views.getView(pane)).toHaveFocus() expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') @@ -3421,7 +3418,7 @@ describe "TreeView", -> describe "when the first pane is focused, a file is opened that is already open in the second pane", -> firstPane = null beforeEach -> - firstPane = getCenter().getPanes()[0] + firstPane = atom.workspace.getCenter().getPanes()[0] firstPane.activate() selectEntry 'tree-view.txt' waitForWorkspaceOpenEvent -> @@ -3439,7 +3436,7 @@ describe "TreeView", -> describe "when core.allowPendingPaneItems is set to true (default)", -> firstPane = activePaneItem = null beforeEach -> - firstPane = getCenter().getPanes()[0] + firstPane = atom.workspace.getCenter().getPanes()[0] firstPane.activate() treeView.focus() @@ -3464,7 +3461,7 @@ describe "TreeView", -> activePaneItem = null beforeEach -> - firstPane = getCenter().getPanes()[0] + firstPane = atom.workspace.getCenter().getPanes()[0] firstPane.activate() treeView.focus() @@ -3481,7 +3478,7 @@ describe "TreeView", -> it "opens the file and focuses it", -> expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt') - expect(atom.views.getView(getCenter().getPanes()[1])).toHaveFocus() + expect(atom.views.getView(atom.workspace.getCenter().getPanes()[1])).toHaveFocus() describe "Dragging and dropping root folders", -> [alphaDirPath, gammaDirPath, thetaDirPath, etaDirPath] = [] From b8f09a980dd37fc82d53ba4087f2d9734bf9afcf Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 18 Jun 2017 23:43:09 -0400 Subject: [PATCH 211/263] Remove unused `expectDockItem` parameter --- spec/tree-view-package-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 14ca5bc9..d0856ff7 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -9,7 +9,7 @@ eventHelpers = require "./event-helpers" DefaultFileIcons = require '../lib/default-file-icons' FileIcons = require '../lib/file-icons' -waitForPackageActivation = (expectDockItem) -> +waitForPackageActivation = -> waitsForPromise -> atom.packages.activatePackage('tree-view') waitsForPromise -> From 0d05497dd764c7422ea8a70c1cc9ef0e7cceca79 Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Tue, 17 May 2016 23:26:31 +0300 Subject: [PATCH 212/263] Use getFullExtension instead of path.extname in dialogs --- lib/dialog.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dialog.coffee b/lib/dialog.coffee index 500a1192..24f2e014 100644 --- a/lib/dialog.coffee +++ b/lib/dialog.coffee @@ -1,5 +1,6 @@ {TextEditor, CompositeDisposable, Disposable, Emitter, Range, Point} = require 'atom' path = require 'path' +{getFullExtension} = require "./helpers" module.exports = class Dialog @@ -35,7 +36,7 @@ class Dialog @miniEditor.setText(initialPath) if select - extension = path.extname(initialPath) + extension = getFullExtension(initialPath) baseName = path.basename(initialPath) selectionStart = initialPath.length - baseName.length if baseName is extension From 7ca7d8dcfcd6bbf6c0a6ca6f5373c69bccd936c0 Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Tue, 17 May 2016 23:36:20 +0300 Subject: [PATCH 213/263] Fix path.basename being applied twice --- spec/tree-view-package-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index d0856ff7..d42846dd 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -2095,7 +2095,7 @@ describe "TreeView", -> expect(moveDialog.element).toExist() expect(moveDialog.promptText.textContent).toBe "Enter the new path for the file." expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(filePath)) - expect(moveDialog.miniEditor.getSelectedText()).toBe path.basename(fileNameWithoutExtension) + expect(moveDialog.miniEditor.getSelectedText()).toBe fileNameWithoutExtension expect(moveDialog.miniEditor.element).toHaveFocus() describe "when the path is changed and confirmed", -> @@ -2250,7 +2250,7 @@ describe "TreeView", -> expect(copyDialog.element).toExist() expect(copyDialog.promptText.textContent).toBe "Enter the new path for the duplicate." expect(copyDialog.miniEditor.getText()).toBe(atom.project.relativize(filePath)) - expect(copyDialog.miniEditor.getSelectedText()).toBe path.basename(fileNameWithoutExtension) + expect(copyDialog.miniEditor.getSelectedText()).toBe fileNameWithoutExtension expect(copyDialog.miniEditor.element).toHaveFocus() describe "when the path is changed and confirmed", -> From f9368efda632bb043e0bb5649c4682e2e099b028 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 19 Jun 2017 00:02:39 -0400 Subject: [PATCH 214/263] Add specs --- spec/tree-view-package-spec.coffee | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index d42846dd..716111a0 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -2184,6 +2184,28 @@ describe "TreeView", -> expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) expect(moveDialog.miniEditor.getSelectedText()).toBe '.dotfile' + describe "when a file is selected that has multiple extensions", -> + [dotFilePath, dotFileView, moveDialog] = [] + + beforeEach -> + dotFilePath = path.join(dirPath, "test.file.txt") + fs.writeFileSync(dotFilePath, "dot dot") + dirView.collapse() + dirView.expand() + dotFileView = treeView.entryForPath(dotFilePath) + + waitForWorkspaceOpenEvent -> + dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + runs -> + atom.commands.dispatch(treeView.element, "tree-view:move") + moveDialog = atom.workspace.getModalPanels()[0].getItem() + + it "selects only the part of the filename up to the first extension", -> + expect(moveDialog.element).toExist() + expect(moveDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) + expect(moveDialog.miniEditor.getSelectedText()).toBe 'test' + describe "when a subdirectory is selected", -> moveDialog = null @@ -2339,6 +2361,28 @@ describe "TreeView", -> expect(copyDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) expect(copyDialog.miniEditor.getSelectedText()).toBe '.dotfile' + describe "when a file is selected that has multiple extensions", -> + [dotFilePath, dotFileView, copyDialog] = [] + + beforeEach -> + dotFilePath = path.join(dirPath, "test.file.txt") + fs.writeFileSync(dotFilePath, "dot dot") + dirView.collapse() + dirView.expand() + dotFileView = treeView.entryForPath(dotFilePath) + + waitForWorkspaceOpenEvent -> + dotFileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + runs -> + atom.commands.dispatch(treeView.element, "tree-view:duplicate") + copyDialog = atom.workspace.getModalPanels()[0].getItem() + + it "selects only the part of the filename up to the first extension", -> + expect(copyDialog.element).toExist() + expect(copyDialog.miniEditor.getText()).toBe(atom.project.relativize(dotFilePath)) + expect(copyDialog.miniEditor.getSelectedText()).toBe 'test' + describe "when the project is selected", -> it "doesn't display the copy dialog", -> treeView.roots[0].dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) From e4dc7414eaea8982c99d1b3440117576c0cf9d20 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 19 Jun 2017 22:12:48 -0400 Subject: [PATCH 215/263] Improve implementation and add specs --- lib/tree-view.coffee | 13 ++++--- spec/tree-view-package-spec.coffee | 59 +++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index d5ae79c8..f55294a4 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -568,15 +568,14 @@ class TreeView dialog.attach() removeSelectedEntries: -> - parents = _.uniq(_.map(@selectedPaths(), (selectedPath) -> - return selectedPath.substring(0, selectedPath.lastIndexOf(path.sep)) - )) if @hasFocus() selectedPaths = @selectedPaths() + selectedEntries = @getSelectedEntries() else if activePath = @getActivePath() selectedPaths = [activePath] + selectedEntries = [@entryForPath(activePath)] - return unless selectedPaths and selectedPaths.length > 0 + return unless selectedPaths?.length > 0 for root in @roots if root.getPath() in selectedPaths @@ -596,14 +595,18 @@ class TreeView @emitter.emit 'entry-deleted', {path: selectedPath} else failedDeletions.push "#{selectedPath}" + if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) + if failedDeletions.length > 0 atom.notifications.addError @formatTrashFailureMessage(failedDeletions), description: @formatTrashEnabledMessage() detail: "#{failedDeletions.join('\n')}" dismissable: true - @selectEntry(@list.find("[data-path='#{parents[0]}']").parents('li').first()?[0]) + + # Focus the first parent folder + @selectEntry(selectedEntries[0].closest('.directory:not(.selected)')) @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') "Cancel": null diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 716111a0..2590bbf6 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -2482,11 +2482,40 @@ describe "TreeView", -> openFilePaths = atom.workspace.getTextEditors().map((e) -> e.getPath()) expect(openFilePaths).toEqual([filePath2, filePath3]) dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() - waitsForPromise -> - treeView.toggleFocus() + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + openFilePaths = (editor.getPath() for editor in atom.workspace.getTextEditors()) + expect(openFilePaths).toEqual([]) + + it "focuses the directory's parent folder", -> + jasmine.attachToDOM(workspaceElement) + + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() + + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + expect(root1).toHaveClass('selected') + + describe "when a file is removed", -> + it "closes editors with filepaths belonging to the removed file", -> + jasmine.attachToDOM(workspaceElement) + + waitForWorkspaceOpenEvent -> + atom.workspace.open(filePath2) runs -> + openFilePaths = atom.workspace.getTextEditors().map((e) -> e.getPath()) + expect(openFilePaths).toEqual([filePath2]) + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() + spyOn(atom, 'confirm').andCallFake (dialog) -> dialog.buttons["Move to Trash"]() @@ -2494,6 +2523,32 @@ describe "TreeView", -> openFilePaths = (editor.getPath() for editor in atom.workspace.getTextEditors()) expect(openFilePaths).toEqual([]) + it "focuses the file's parent folder", -> + jasmine.attachToDOM(workspaceElement) + + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + runs -> + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + expect(dirView2).toHaveClass('selected') + + describe "when multiple files and folders are deleted", -> + it "focuses the first selected entry's parent folder", -> + jasmine.attachToDOM(workspaceElement) + + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + treeView.focus() + + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + expect(root1).toHaveClass('selected') + describe "file system events", -> temporaryFilePath = null From 09e1aaeb2b6f487409b0dc3596d228902afeadfc Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 19 Jun 2017 23:25:47 -0400 Subject: [PATCH 216/263] Don't try to delete entries which no longer exist --- lib/tree-view.coffee | 6 +++ spec/tree-view-package-spec.coffee | 66 ++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index f55294a4..dccf2ced 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -591,6 +591,12 @@ class TreeView "Move to Trash": => failedDeletions = [] for selectedPath in selectedPaths + # Don't delete entries which no longer exist. This can happen, for example, when: + # * The entry is deleted outside of Atom before "Move to Trash" is selected + # * A folder and one of its children are both selected for deletion, + # but the parent folder is deleted first + continue unless fs.existsSync(selectedPath) + if shell.moveItemToTrash(selectedPath) @emitter.emit 'entry-deleted', {path: selectedPath} else diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 2590bbf6..86d4f6d5 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3,7 +3,7 @@ fs = require 'fs-plus' path = require 'path' temp = require('temp').track() os = require 'os' -{remote} = require 'electron' +{remote, shell} = require 'electron' eventHelpers = require "./event-helpers" DefaultFileIcons = require '../lib/default-file-icons' @@ -2435,27 +2435,24 @@ describe "TreeView", -> expect(Object.keys(args.buttons)).toEqual ['Move to Trash', 'Cancel'] it "shows a notification on failure", -> + jasmine.attachToDOM(workspaceElement) atom.notifications.clear() - spyOn(atom, 'confirm') + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() - waitForWorkspaceOpenEvent -> - fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + spyOn(shell, 'moveItemToTrash').andReturn(false) + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() - runs -> - repeat = 2 - while (repeat > 0) - atom.commands.dispatch(treeView.element, 'tree-view:remove') - args = atom.confirm.mostRecentCall.args[0] - args.buttons["Move to Trash"]() - --repeat + atom.commands.dispatch(treeView.element, 'tree-view:remove') - notificationsNumber = atom.notifications.getNotifications().length - expect(notificationsNumber).toBe 1 - if notificationsNumber is 1 - notification = atom.notifications.getNotifications()[0] - expect(notification.getMessage()).toContain 'The following file couldn\'t be moved to the trash' - expect(notification.getDetail()).toContain 'test-file.txt' + notificationsNumber = atom.notifications.getNotifications().length + expect(notificationsNumber).toBe 1 + if notificationsNumber is 1 + notification = atom.notifications.getNotifications()[0] + expect(notification.getMessage()).toContain 'The following file couldn\'t be moved to the trash' + expect(notification.getDetail()).toContain 'test-file.txt' it "does nothing when no file is selected", -> atom.notifications.clear() @@ -2527,6 +2524,7 @@ describe "TreeView", -> jasmine.attachToDOM(workspaceElement) fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() runs -> spyOn(atom, 'confirm').andCallFake (dialog) -> @@ -2536,6 +2534,22 @@ describe "TreeView", -> expect(dirView2).toHaveClass('selected') describe "when multiple files and folders are deleted", -> + it "does not error when the selected entries form a parent/child relationship", -> + # If dir1 and dir1/file1 are both selected for deletion, + # and dir1 is deleted first, do not error when attempting to delete dir1/file1 + jasmine.attachToDOM(workspaceElement) + atom.notifications.clear() + + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + dirView.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + treeView.focus() + + spyOn(atom, 'confirm').andCallFake (dialog) -> + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + expect(atom.notifications.getNotifications().length).toBe 0 + it "focuses the first selected entry's parent folder", -> jasmine.attachToDOM(workspaceElement) @@ -2549,6 +2563,24 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(root1).toHaveClass('selected') + describe "when the entry is deleted before 'Move to Trash' is selected", -> + it "does not error", -> + # If the file is marked for deletion but has already been deleted + # outside of Atom by the time the deletion is confirmed, do not error + jasmine.attachToDOM(workspaceElement) + atom.notifications.clear() + + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + treeView.focus() + + spyOn(atom, 'confirm').andCallFake (dialog) -> + # Remove the directory before confirming the deletion + fs.unlinkSync(filePath) + dialog.buttons["Move to Trash"]() + + atom.commands.dispatch(treeView.element, 'tree-view:remove') + expect(atom.notifications.getNotifications().length).toBe 0 + describe "file system events", -> temporaryFilePath = null From a193a445955072398402ab4c5c32d8d46994d66a Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 21 Jun 2017 23:14:11 -0400 Subject: [PATCH 217/263] Prepare 0.217.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d32a7f5d..d2332acf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.2", + "version": "0.217.3", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 6f72f974b4e78f3fce7e922aeb8d7222b51f57ee Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Fri, 7 Jul 2017 13:46:17 -0500 Subject: [PATCH 218/263] add check for multi-select --- spec/tree-view-spec.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-spec.js b/spec/tree-view-spec.js index e83efd10..15a7670e 100644 --- a/spec/tree-view-spec.js +++ b/spec/tree-view-spec.js @@ -53,7 +53,9 @@ describe('TreeView', () => { treeView.showMultiSelectMenu() let child = entries.children[0]; - while (child.children.length > 0 && (child = child.firstChild)); + while (child.children.length > 0) { + child = child.firstChild; + } treeView.onMouseDown({ stopPropagation() {}, @@ -62,6 +64,7 @@ describe('TreeView', () => { }) expect(treeView.getSelectedEntries().length).toBe(2); + expect(treeView.multiSelectEnabled()).toBe(true); }) }); }) From cde1d9d6e913237a972bb947dfd1bb6637a74272 Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 21 Jul 2017 11:39:27 +0900 Subject: [PATCH 219/263] Prepare 0.217.4 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2332acf..a83da7ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.3", + "version": "0.217.4", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From bff166cf4bb226bc138620fbecbd1d589bd9c45f Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 20 Jul 2017 23:25:29 -0500 Subject: [PATCH 220/263] use treeView.onMouseDown --- spec/tree-view-spec.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/spec/tree-view-spec.js b/spec/tree-view-spec.js index 15a7670e..ece93794 100644 --- a/spec/tree-view-spec.js +++ b/spec/tree-view-spec.js @@ -48,9 +48,19 @@ describe('TreeView', () => { it('should leave multiple entries selected on right click', () => { const treeView = new TreeView({}) const entries = treeView.roots[0].entries - treeView.selectEntry(entries.children[0]) - treeView.selectMultipleEntries(entries.children[1]) - treeView.showMultiSelectMenu() + + treeView.onMouseDown({ + stopPropagation() {}, + target: entries.children[0], + button: 0, + }) + + treeView.onMouseDown({ + stopPropagation() {}, + target: entries.children[1], + button: 0, + metaKey: true, + }) let child = entries.children[0]; while (child.children.length > 0) { @@ -60,7 +70,7 @@ describe('TreeView', () => { treeView.onMouseDown({ stopPropagation() {}, target: child, - button: 2 + button: 2, }) expect(treeView.getSelectedEntries().length).toBe(2); From 1043f55466d2f1f44bce78a609ea865e206a8441 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 21 Jul 2017 00:40:41 -0400 Subject: [PATCH 221/263] Prepare 0.217.5 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a83da7ac..faab4927 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.4", + "version": "0.217.5", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 55e1b25bc5315d60143859606523fd55cbef5f96 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 1 Aug 2017 16:40:10 -0400 Subject: [PATCH 222/263] Fix flaky test re: adding new file Wait for the tree view selection to *fully* update before checking the selected item in the tree view. As far as I can tell, when a file is added, the tree view is updated in a few steps: 1. Add the new file to the directory 2. Select the new file Prior to this change, we were waiting for Step 1 to happen, and then asserting that Step 2 had also already happened. Between Step 1 and Step 2, it appears that the *directory* is selected, and that was causing the failure seen in https://github.com/atom/tree-view/issues/1155#issue-247146435. --- spec/tree-view-package-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 6b7fd19e..78edf4f1 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1809,9 +1809,11 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath - waitsFor "tree view to be updated", -> + waitsFor "file to be added to tree view", -> dirView.entries.querySelectorAll(".file").length > 1 + waitsFor "tree view selection to be updated", -> + treeView.element.querySelector('.file.selected') runs -> expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) expect(callback).toHaveBeenCalledWith({path: newPath}) From cf69a85a0dbaf021c9ec1bd11b145da2d4a3810b Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Wed, 2 Aug 2017 11:31:35 -0400 Subject: [PATCH 223/263] :art: --- spec/tree-view-package-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 78edf4f1..92dd5748 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1814,6 +1814,7 @@ describe "TreeView", -> waitsFor "tree view selection to be updated", -> treeView.element.querySelector('.file.selected') + runs -> expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) expect(callback).toHaveBeenCalledWith({path: newPath}) From 2b5aca795f013318b991f948111ea9f61de6839a Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Wed, 2 Aug 2017 11:55:42 -0400 Subject: [PATCH 224/263] Increase clarity of `waitsFor` block --- spec/tree-view-package-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 92dd5748..6428d0ab 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1813,7 +1813,7 @@ describe "TreeView", -> dirView.entries.querySelectorAll(".file").length > 1 waitsFor "tree view selection to be updated", -> - treeView.element.querySelector('.file.selected') + treeView.element.querySelector('.file.selected') isnt null runs -> expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) From 6bb70056fab19c0adae4aa627ec2dec6438cbaed Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Wed, 2 Aug 2017 12:03:10 -0400 Subject: [PATCH 225/263] Fix other flaky test re: adding new file Similar to 55e1b25bc5315d60143859606523fd55cbef5f96, we need to wait for the tree view selection to *fully* update before checking the selected item in the tree view. As far as I can tell, when a file is added, the tree view is updated in a few steps: 1. Add the new file to the directory 2. Select the new file Prior to this change, we were waiting for Step 1 to happen, and then asserting that Step 2 had also already happened. Between Step 1 and Step 2, it appears that the *directory* is selected, and that was causing the failure seen in https://github.com/atom/tree-view/issues/1155#issuecomment-319471558. --- spec/tree-view-package-spec.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 6428d0ab..a8dd640f 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1837,11 +1837,14 @@ describe "TreeView", -> expect(atom.workspace.getModalPanels().length).toBe 0 expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath - waitsFor "tree view to be updated", -> + waitsFor "file to be added to tree view", -> dirView3.entries.querySelectorAll(".file").length > 1 + waitsFor "tree view selection to be updated", -> + treeView.element.querySelector('.file.selected') isnt null + runs -> - expect(treeView.element.querySelector('.file.selected').textContent).toBe path.basename(newPath) + expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath) expect(callback).toHaveBeenCalledWith({path: newPath}) describe "when a file already exists at that location", -> From 3c9428960a08b58f0b98142075261efebb706d1b Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Wed, 2 Aug 2017 15:33:37 -0400 Subject: [PATCH 226/263] Prepare 0.217.6 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index faab4927..323365b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.5", + "version": "0.217.6", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From 76cb5ef9ae1ca43eeca7a92b0a472c399b6d73ae Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 18 Aug 2017 12:50:41 -0700 Subject: [PATCH 227/263] :arrow_up: pathwatcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 323365b7..828242bf 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "event-kit": "^1.0.0", "fs-plus": "^3.0.0", "minimatch": "~0.3.0", - "pathwatcher": "^7.0.0", + "pathwatcher": "^8.0.0", "temp": "~0.8.1", "underscore-plus": "^1.0.0" }, From 464e60d2f73066038e898ef117f8419f1b3538d6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 18 Aug 2017 12:52:19 -0700 Subject: [PATCH 228/263] Prepare 0.217.7 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 828242bf..a640a881 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.6", + "version": "0.217.7", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From b7720ac6aa8d5bd634df14ca48222946b29ac818 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 22 Aug 2017 11:33:47 -0400 Subject: [PATCH 229/263] Improve tree-view:remove-project-folder reliability It is now capable of removing project folders when a sub-entry is selected or only one project folder exists Fixes #665 --- lib/tree-view.coffee | 11 ++++++----- spec/tree-view-package-spec.coffee | 29 ++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 167e72b2..d4f584aa 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -727,12 +727,13 @@ class TreeView dialog.attach() removeProjectFolder: (e) -> + # Remove the targeted project folder (generally this only happens through the context menu) pathToRemove = e.target.closest(".project-root > .header")?.querySelector(".name")?.dataset.path - - # TODO: remove this conditional once the addition of Project::removePath - # is released. - if atom.project.removePath? - atom.project.removePath(pathToRemove) if pathToRemove? + # If an entry is selected, remove that entry's project folder + pathToRemove ?= @selectedEntry()?.closest(".project-root")?.querySelector(".header")?.querySelector(".name")?.dataset.path + # Finally, if only one project folder exists and nothing is selected, remove that folder + pathToRemove ?= @roots[0].querySelector(".header")?.querySelector(".name")?.dataset.path if @roots.length is 1 + atom.project.removePath(pathToRemove) if pathToRemove? selectedEntry: -> @list.querySelector('.selected') diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 4e454d41..f1171785 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1319,10 +1319,29 @@ describe "TreeView", -> expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve(fileName) describe "removing a project folder", -> - it "removes the folder from the project", -> - rootHeader = treeView.roots[1].querySelector(".header") - atom.commands.dispatch(rootHeader, "tree-view:remove-project-folder") - expect(atom.project.getPaths()).toHaveLength(1) + describe "when the project folder is selected", -> + it "removes the folder from the project", -> + rootHeader = treeView.roots[1].querySelector(".header") + atom.commands.dispatch(rootHeader, "tree-view:remove-project-folder") + expect(atom.project.getPaths()).toEqual [path1] + + describe "when an entry is selected", -> + it "removes the project folder containing the entry", -> + treeView.selectEntry(treeView.roots[1].querySelector(".entries").querySelector("li")) + atom.commands.dispatch(treeView.element, "tree-view:remove-project-folder") + expect(atom.project.getPaths()).toEqual [path1] + + describe "when nothing is selected and there is only one project folder", -> + it "removes the project folder", -> + atom.project.removePath(path2) + atom.commands.dispatch(treeView.element, "tree-view:remove-project-folder") + expect(atom.project.getPaths()).toHaveLength 0 + + describe "when nothing is selected and there are multiple project folders", -> + it "does nothing", -> + treeView.deselect(treeView.getSelectedEntries()) + atom.commands.dispatch(treeView.element, "tree-view:remove-project-folder") + expect(atom.project.getPaths()).toHaveLength 2 describe "file modification", -> [dirView, dirView2, dirView3, fileView, fileView2, fileView3, fileView4] = [] @@ -2555,7 +2574,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(atom.notifications.getNotifications().length).toBe 0 - + it "focuses the first selected entry's parent folder", -> jasmine.attachToDOM(workspaceElement) From a2c2030be73699c980bac87759cf0773d1597606 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 22 Aug 2017 15:00:15 -0400 Subject: [PATCH 230/263] Factor the root folder into account when revealing the active file --- lib/tree-view.coffee | 8 +++++--- spec/tree-view-package-spec.coffee | 28 +++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 167e72b2..23277e44 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -304,8 +304,7 @@ class TreeView @selectEntryForPath(activeFilePath) revealActiveFile: (focus) -> - if _.isEmpty(atom.project.getPaths()) - return Promise.resolve() + return Promise.resolve() unless atom.project.getPaths().length @show(focus ? atom.config.get('tree-view.focusOnReveal')).then => return unless activeFilePath = @getActivePath() @@ -314,7 +313,10 @@ class TreeView return unless rootPath? activePathComponents = relativePath.split(path.sep) - currentPath = rootPath + # Add the root folder to the path components + activePathComponents.unshift(rootPath.substr(rootPath.lastIndexOf(path.sep) + 1)) + # And remove it from the current path + currentPath = rootPath.substr(0, rootPath.lastIndexOf(path.sep)) for pathComponent in activePathComponents currentPath += path.sep + pathComponent entry = @entryForPath(currentPath) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 4e454d41..6e3a1f93 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -288,7 +288,7 @@ describe "TreeView", -> atom.config.set "tree-view.focusOnReveal", true waitsForPromise -> - atom.workspace.open(path.join(atom.project.getPaths()[0], 'dir1', 'file1')) + atom.workspace.open(path.join(path1, 'dir1', 'file1')) waitsForPromise -> treeView.revealActiveFile() @@ -299,7 +299,7 @@ describe "TreeView", -> waitsForPromise -> treeView.focus.reset() - atom.workspace.open(path.join(atom.project.getPaths()[1], 'dir3', 'file3')) + atom.workspace.open(path.join(path2, 'dir3', 'file3')) waitsForPromise -> treeView.revealActiveFile() @@ -313,7 +313,7 @@ describe "TreeView", -> atom.config.set "tree-view.focusOnReveal", false waitsForPromise -> - atom.workspace.open(path.join(atom.project.getPaths()[0], 'dir1', 'file1')) + atom.workspace.open(path.join(path1, 'dir1', 'file1')) waitsForPromise -> treeView.revealActiveFile() @@ -324,7 +324,7 @@ describe "TreeView", -> waitsForPromise -> treeView.focus.reset() - atom.workspace.open(path.join(atom.project.getPaths()[1], 'dir3', 'file3')) + atom.workspace.open(path.join(path2, 'dir3', 'file3')) waitsForPromise -> treeView.revealActiveFile() @@ -333,6 +333,24 @@ describe "TreeView", -> expect(treeView.element.parentElement).toBeTruthy() expect(treeView.focus).not.toHaveBeenCalled() + describe "if the file is located under collapsed folders", -> + it "expands all the folders and selects the file", -> + waitsForPromise -> + atom.workspace.open(path.join(path1, 'dir1', 'file1')) + + runs -> + treeView.selectEntry(root1) + treeView.collapseDirectory(true) # Recursively collapse all directories + + waitsForPromise -> + treeView.revealActiveFile() + + runs -> + expect(treeView.entryForPath(path1).classList.contains('expanded')).toBe true + expect(treeView.entryForPath(path.join(path1, 'dir1')).classList.contains('expanded')).toBe true + expect(treeView.selectedEntry()).toBeTruthy() + expect(treeView.selectedEntry().getPath()).toBe path.join(path1, '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", -> waitsForPromise -> @@ -2555,7 +2573,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(atom.notifications.getNotifications().length).toBe 0 - + it "focuses the first selected entry's parent folder", -> jasmine.attachToDOM(workspaceElement) From 23516fe8f740ed3d87388f246fd21b46fd33f45a Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 22 Aug 2017 21:27:33 -0400 Subject: [PATCH 231/263] Switch internal directory representation to use maps --- lib/directory-view.coffee | 6 +++-- lib/directory.coffee | 39 +++++++++++++++--------------- spec/file-stats-spec.coffee | 12 ++++----- spec/tree-view-package-spec.coffee | 18 ++++++-------- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 70533d1d..37f0235e 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -108,10 +108,12 @@ class DirectoryView view = new FileView(entry) subscription = @directory.onDidRemoveEntries (removedEntries) -> - for removedName, removedEntry of removedEntries when entry is removedEntry + removedEntries.forEach (removedEntry, removedName) -> + return unless entry is removedEntry view.element.remove() subscription.dispose() - break + # TODO: When this file is converted to JS, convert this forEach loop + # to a for...of loop and add a break here for performance @subscriptions.add(subscription) view diff --git a/lib/directory.coffee b/lib/directory.coffee index 538d88b4..604bfd72 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -26,9 +26,9 @@ class Directory @isRoot ?= false @expansionState ?= {} @expansionState.isExpanded ?= false - @expansionState.entries ?= {} + @expansionState.entries ?= new Map() @status = null - @entries = {} + @entries = new Map() @submodule = repoForPath(@path)?.isSubmodule(@path) @@ -154,9 +154,9 @@ class Directory @watchSubscription.close() @watchSubscription = null - for key, entry of @entries + @entries.forEach (entry, key) => entry.destroy() - delete @entries[key] + @entries.delete(key) # Public: Watch this directory for changes. watch: -> @@ -188,15 +188,15 @@ class Directory statFlat[key] = statFlat[key]?.getTime() if stat.isDirectory?() - if @entries.hasOwnProperty(name) + if @entries.has(name) # push a placeholder since this entry already exists but this helps # track the insertion index for the created views directories.push(name) else - expansionState = @expansionState.entries[name] + expansionState = @expansionState.entries.get(name) directories.push(new Directory({name, fullPath, symlink, expansionState, @ignoredPatterns, @useSyncFS, stats: statFlat})) else if stat.isFile?() - if @entries.hasOwnProperty(name) + if @entries.has(name) # push a placeholder since this entry already exists but this helps # track the insertion index for the created views files.push(name) @@ -225,12 +225,12 @@ class Directory # Public: Perform a synchronous reload of the directory. reload: -> newEntries = [] - removedEntries = _.clone(@entries) + removedEntries = new Map(@entries) index = 0 for entry in @getEntries() - if @entries.hasOwnProperty(entry) - delete removedEntries[entry] + if @entries.has(entry) + removedEntries.delete(entry) index++ continue @@ -239,20 +239,20 @@ class Directory newEntries.push(entry) entriesRemoved = false - for name, entry of removedEntries + removedEntries.forEach (entry, name) => entriesRemoved = true entry.destroy() - if @entries.hasOwnProperty(name) - delete @entries[name] + if @entries.has(name) + @entries.delete(name) - if @expansionState.entries.hasOwnProperty(name) - delete @expansionState.entries[name] + if @expansionState.entries.has(name) + @expansionState.entries.delete(name) @emitter.emit('did-remove-entries', removedEntries) if entriesRemoved if newEntries.length > 0 - @entries[entry.name] = entry for entry in newEntries + @entries.set(entry.name, entry) for entry in newEntries @emitter.emit('did-add-entries', newEntries) # Public: Collapse this directory and stop watching it. @@ -273,9 +273,10 @@ class Directory serializeExpansionState: -> expansionState = {} expansionState.isExpanded = @expansionState.isExpanded - expansionState.entries = {} - for name, entry of @entries when entry.expansionState? - expansionState.entries[name] = entry.serializeExpansionState() + expansionState.entries = new Map() + @entries.forEach (entry, name) -> + return unless entry.expansionState? + expansionState.entries.set(name, entry.serializeExpansionState()) expansionState squashDirectoryNames: (fullPath) -> diff --git a/spec/file-stats-spec.coffee b/spec/file-stats-spec.coffee index 31ea394e..1bcf0e04 100644 --- a/spec/file-stats-spec.coffee +++ b/spec/file-stats-spec.coffee @@ -31,13 +31,13 @@ describe "FileStats", -> temp.cleanup() it "passes stats to File instances", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats + stats = treeView.roots[0].directory.entries.get("file1.txt").stats expect(stats).toBeDefined() expect(stats.mtime).toBeDefined() expect(stats.size).toEqual(file1Data.length) it "passes stats to Directory instances", -> - stats = treeView.roots[0].directory.entries["subdir"].stats + stats = treeView.roots[0].directory.entries.get("subdir").stats expect(stats).toBeDefined() expect(stats.mtime).toBeDefined() @@ -46,20 +46,20 @@ describe "FileStats", -> it "passes stats to File instances in subdirectories", -> treeView.element.querySelector(".entries > li").expand() - subdir = treeView.roots[0].directory.entries["subdir"] - stats = subdir.entries["file2.txt"].stats + subdir = treeView.roots[0].directory.entries.get("subdir") + stats = subdir.entries.get("file2.txt").stats expect(stats).toBeDefined() expect(stats.size).toEqual(file2Data.length) it "converts date-stats to timestamps", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats + stats = treeView.roots[0].directory.entries.get("file1.txt").stats stamp = stats.mtime expect(_.isDate stamp).toBe(false) expect(typeof stamp).toBe("number") expect(Number.isNaN stamp).toBe(false) it "accurately converts timestamps", -> - stats = treeView.roots[0].directory.entries["file1.txt"].stats + stats = treeView.roots[0].directory.entries.get("file1.txt").stats # Two minutes should be enough expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index ddf7b505..422b42d0 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1844,7 +1844,7 @@ describe "TreeView", -> describe "when the parent directory of the selected file changes", -> it "still shows the active file as selected", -> - dirView.directory.emitter.emit 'did-remove-entries', {'deleted.txt': {}} + dirView.directory.emitter.emit 'did-remove-entries', new Map().set('deleted.txt', {}) expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(filePath) describe "when the path without a trailing '#{path.sep}' is changed and confirmed", -> @@ -3336,25 +3336,23 @@ describe "TreeView", -> treeView.roots[0].expand() expect(treeView.roots[0].directory.serializeExpansionState()).toEqual isExpanded: true - entries: - entries: - isExpanded: false - entries: {} + entries: new Map().set('entries', + isExpanded: false + entries: new Map()) fs.removeSync(entriesPath) treeView.roots[0].reload() expect(treeView.roots[0].directory.serializeExpansionState()).toEqual isExpanded: true - entries: {} + entries: new Map() fs.mkdirSync(path.join(projectPath, 'other')) treeView.roots[0].reload() expect(treeView.roots[0].directory.serializeExpansionState()).toEqual isExpanded: true - entries: - other: - isExpanded: false - entries: {} + entries: new Map().set('other', + isExpanded: false + entries: new Map()) describe "Dragging and dropping files", -> deltaFilePath = null From f40e224a5a6e73f670f9d50d7a06d7c7a259c0a8 Mon Sep 17 00:00:00 2001 From: RobertBColton Date: Wed, 23 Aug 2017 15:10:21 -0400 Subject: [PATCH 232/263] Fix scroll jerking The scroll jerking is caused by centering around the item. We only want to scroll just enough to make the item visible, not center around it, otherwise keyboard navigation becomes awkward. VS Code works this way using custom code in place of scrollIntoViewIfNeeded, because that is a Chrome only function. VS Code is based off of VS Team Services IDE which supports non-webkit browsers, such as Firefox, which don't have scrollIntoViewIfNeeded. --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 167e72b2..1523d758 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -769,7 +769,7 @@ class TreeView scrollToEntry: (entry) -> element = if entry?.classList.contains('directory') then entry.header else entry - element?.scrollIntoViewIfNeeded(true) # true = center around item if possible + element?.scrollIntoViewIfNeeded(false) # false = just enough to make the item visible scrollToBottom: -> if lastEntry = _.last(@list.querySelectorAll('.entry')) From f2712edf36e238c0cad84a800f2c3cdf70863327 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Aug 2017 22:03:30 -0400 Subject: [PATCH 233/263] Serialization conversion --- lib/directory.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/directory.coffee b/lib/directory.coffee index 604bfd72..126f44a8 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -26,6 +26,16 @@ class Directory @isRoot ?= false @expansionState ?= {} @expansionState.isExpanded ?= false + + # TODO: This can be removed after a sufficient amount + # of time has passed since @expansionState.entries + # has been converted to a Map + unless @expansionState.entries instanceof Map + temp = new Map() + for name, entry of @expansionState.entries + temp.set(name, entry) + @expansionState.entries = temp + @expansionState.entries ?= new Map() @status = null @entries = new Map() From 6c2da586906fb63c39f3b166b97c934c4a8c9d25 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Aug 2017 23:22:09 -0400 Subject: [PATCH 234/263] Let's do some proper recursive conversion --- lib/directory.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/directory.coffee b/lib/directory.coffee index 126f44a8..bc40df7d 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -31,10 +31,14 @@ class Directory # of time has passed since @expansionState.entries # has been converted to a Map unless @expansionState.entries instanceof Map - temp = new Map() - for name, entry of @expansionState.entries - temp.set(name, entry) - @expansionState.entries = temp + convertEntriesToMap = (entries) -> + temp = new Map() + for name, entry of entries + entry.entries = convertEntriesToMap(entry.entries) if entry.entries? + temp.set(name, entry) + return temp + + @expansionState.entries = convertEntriesToMap(@expansionState.entries) @expansionState.entries ?= new Map() @status = null From c8fc46149cb3f54df783ba7dc784f706a9dbcb1e Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Aug 2017 23:38:58 -0400 Subject: [PATCH 235/263] Test conversion logic --- spec/tree-view-package-spec.coffee | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 422b42d0..ae535790 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -4,6 +4,7 @@ path = require 'path' temp = require('temp').track() os = require 'os' {remote, shell} = require 'electron' +Directory = require '../lib/directory' eventHelpers = require "./event-helpers" DefaultFileIcons = require '../lib/default-file-icons' @@ -3821,6 +3822,40 @@ describe "TreeView", -> expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] expect(document.querySelector('.placeholder')).not.toExist() + describe "directory expansion serialization", -> + it "converts legacy expansion serialization Objects to Maps", -> + # The conversion actually happens when a new Directory + # is instantiated with a serialized expansion state, + # not when serialization occurs + legacyState = + isExpanded: true + entries: + 'a': + isExpanded: true + 'tree-view': + isExpanded: false + entries: + 'sub-folder': + isExpanded: true + + convertedState = + isExpanded: true + entries: new Map().set('a', {isExpanded: true}).set('tree-view', + isExpanded: false + entries: new Map().set 'sub-folder', + isExpanded: true) + + directory = new Directory({name: 'test', fullPath: 'path', symlink: false, expansionState: legacyState}) + expect(directory.expansionState.entries instanceof Map).toBe true + + assertEntriesDeepEqual = (expansionEntries, convertedEntries) -> + expansionEntries.forEach (entry, name) -> + if entry.entries? or convertedEntries.get(name).entries? + assertEntriesDeepEqual(entry.entries, convertedEntries.get(name).entries) + expect(entry).toEqual convertedEntries.get(name) + + assertEntriesDeepEqual(directory.expansionState.entries, convertedState.entries) + findDirectoryContainingText = (element, text) -> directories = Array.from(element.querySelectorAll('.entries .directory')) directories.find((directory) -> directory.header.textContent is text) From 55af47b467e9cf7bb02281d1971a525cee56bf93 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Aug 2017 23:49:52 -0400 Subject: [PATCH 236/263] Add spec for `__proto__` entries --- spec/tree-view-package-spec.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index ae535790..76267bfc 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3822,6 +3822,14 @@ describe "TreeView", -> expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath] expect(document.querySelector('.placeholder')).not.toExist() + describe "when there is a __proto__ entry present", -> + it "does not break anything", -> + # No assertions needed - multiple exceptions will be thrown if this test fails + projectPath = temp.mkdirSync('atom-project') + protoPath = path.join(projectPath, "__proto__") + fs.writeFileSync(protoPath, 'test') + atom.project.setPaths([projectPath]) + describe "directory expansion serialization", -> it "converts legacy expansion serialization Objects to Maps", -> # The conversion actually happens when a new Directory From c39b672dbdf7ba0c605a750ac8172df51a898c5a Mon Sep 17 00:00:00 2001 From: RobertBColton Date: Mon, 28 Aug 2017 00:36:55 -0400 Subject: [PATCH 237/263] Use parameter for centering There are some cases where centering the view around the active file is more appropriate than simply revealing it. One such case is when a file is opened from the command line. The solution is to take center as a parameter and default it to what it used to be (true) allowing the keyboard up and down keys to override it and disable centering. --- lib/tree-view.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 1523d758..9d2f50f2 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -353,7 +353,7 @@ class TreeView if selectedEntry? if selectedEntry.classList.contains('directory') if @selectEntry(selectedEntry.entries.children[0]) - @scrollToEntry(@selectedEntry()) + @scrollToEntry(@selectedEntry(), false) return if nextEntry = @nextEntry(selectedEntry) @@ -361,7 +361,7 @@ class TreeView else @selectEntry(@roots[0]) - @scrollToEntry(@selectedEntry()) + @scrollToEntry(@selectedEntry(), false) moveUp: (event) -> event.stopImmediatePropagation() @@ -377,7 +377,7 @@ class TreeView entries = @list.querySelectorAll('.entry') @selectEntry(entries[entries.length - 1]) - @scrollToEntry(@selectedEntry()) + @scrollToEntry(@selectedEntry(), false) nextEntry: (entry) -> currentEntry = entry @@ -767,9 +767,9 @@ class TreeView else @element.scrollTop + @element.offsetHeight - scrollToEntry: (entry) -> + scrollToEntry: (entry, center = true) -> element = if entry?.classList.contains('directory') then entry.header else entry - element?.scrollIntoViewIfNeeded(false) # false = just enough to make the item visible + element?.scrollIntoViewIfNeeded(center) scrollToBottom: -> if lastEntry = _.last(@list.querySelectorAll('.entry')) From b3ab46dfbe7afe4565d625278e1e64ed8b355b3c Mon Sep 17 00:00:00 2001 From: RobertBColton Date: Mon, 28 Aug 2017 17:06:50 -0400 Subject: [PATCH 238/263] style conventions do not permit whitespace around parameter defaults --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 9d2f50f2..8253cb1b 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -767,7 +767,7 @@ class TreeView else @element.scrollTop + @element.offsetHeight - scrollToEntry: (entry, center = true) -> + scrollToEntry: (entry, center=true) -> element = if entry?.classList.contains('directory') then entry.header else entry element?.scrollIntoViewIfNeeded(center) From b7eb9b468382e0070bf9eb88a0e0a4e5009ff9a3 Mon Sep 17 00:00:00 2001 From: RobertBColton Date: Mon, 28 Aug 2017 17:35:02 -0400 Subject: [PATCH 239/263] Update package spec to test centering Updates the scroll spec to select the last entry and use core:move-up to get back to the first entry. While moving up, the spec now verifies that the scrollTop does not change more than the height of the file/element being revealed. This way we can be sure that a regression does not reintroduce the centering of items while using keyboard navigation. --- spec/tree-view-package-spec.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 4e454d41..9bef79a9 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -382,6 +382,12 @@ describe "TreeView", -> treeView.revealActiveFile() runs -> expect(treeView.scrollTop()).toBeGreaterThan 400 + entries = treeView.element.querySelectorAll('.entry') + scrollTop = treeView.element.scrollTop + for i in [0...entries.length] + atom.commands.dispatch(treeView.element, 'core:move-up') + expect(treeView.element.scrollTop - scrollTop).toBeLessThan entries[i].clientHeight + scrollTop = treeView.element.scrollTop # Open file in the middle, should be centered in scroll waitsForPromise -> atom.workspace.open(path.join(rootDirPath, 'file-10.txt')) @@ -2555,7 +2561,7 @@ describe "TreeView", -> atom.commands.dispatch(treeView.element, 'tree-view:remove') expect(atom.notifications.getNotifications().length).toBe 0 - + it "focuses the first selected entry's parent folder", -> jasmine.attachToDOM(workspaceElement) From b954663d1fdc8931497ee0c80dcd70dec16fe060 Mon Sep 17 00:00:00 2001 From: bene Date: Tue, 29 Aug 2017 22:10:17 +0200 Subject: [PATCH 240/263] Prepare 0.217.8 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a640a881..13d5acff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.7", + "version": "0.217.8", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From c68e8664079c3c26bb1c4f2ec4d20c35062b7728 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 7 Sep 2017 19:02:58 -0700 Subject: [PATCH 241/263] Make tests run with promise and non-promise package deactivate --- spec/tree-view-package-spec.coffee | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 742b589f..0c408505 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -104,9 +104,10 @@ describe "TreeView", -> describe "when the project has no path", -> beforeEach -> atom.project.setPaths([]) - atom.packages.deactivatePackage("tree-view") - - expect(atom.workspace.getLeftDock().getActivePaneItem()).toBeUndefined() + waitsForPromise -> + Promise.resolve(atom.packages.deactivatePackage("tree-view")) # Wrapped for both async and non-async versions of Atom + runs -> + expect(atom.workspace.getLeftDock().getActivePaneItem()).toBeUndefined() waitsForPromise -> atom.packages.activatePackage("tree-view") @@ -159,10 +160,14 @@ describe "TreeView", -> describe "when the root view is opened to a file path", -> it "does not show the dock on activation", -> - atom.packages.deactivatePackage("tree-view") - atom.packages.packageStates = {} - atom.workspace.getLeftDock().hide() - expect(atom.workspace.getLeftDock().isVisible()).toBe(false) + + waitsForPromise -> + Promise.resolve(atom.packages.deactivatePackage("tree-view")) # Wrapped for both async and non-async versions of Atom + + runs -> + atom.packages.packageStates = {} + atom.workspace.getLeftDock().hide() + expect(atom.workspace.getLeftDock().isVisible()).toBe(false) waitsForPromise -> atom.workspace.open('tree-view.js') @@ -192,10 +197,15 @@ describe "TreeView", -> dotGit = path.join(temp.mkdirSync('repo'), '.git') fs.makeTreeSync(dotGit) atom.project.setPaths([dotGit]) - atom.packages.deactivatePackage("tree-view") - atom.packages.packageStates = {} - waitsForPromise -> atom.packages.activatePackage('tree-view') + waitsForPromise -> + Promise.resolve(atom.packages.deactivatePackage("tree-view")) # Wrapped for both async and non-async versions of Atom + + runs -> + atom.packages.packageStates = {} + + waitsForPromise -> + atom.packages.activatePackage('tree-view') runs -> {treeView} = atom.packages.getActivePackage("tree-view").mainModule From 1384073a4b06a13afb441f395ffd0e2aaa57d6e9 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 7 Sep 2017 19:46:14 -0700 Subject: [PATCH 242/263] Prepare 0.217.9 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13d5acff..1ba71fde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.8", + "version": "0.217.9", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From f77b84f94c252ee7fcb49d90f9113f68182c1905 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 8 Sep 2017 14:11:06 +0200 Subject: [PATCH 243/263] :racehorse: --- lib/directory-view.coffee | 5 +---- lib/directory.coffee | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/directory-view.coffee b/lib/directory-view.coffee index 37f0235e..45b096f1 100644 --- a/lib/directory-view.coffee +++ b/lib/directory-view.coffee @@ -108,12 +108,9 @@ class DirectoryView view = new FileView(entry) subscription = @directory.onDidRemoveEntries (removedEntries) -> - removedEntries.forEach (removedEntry, removedName) -> - return unless entry is removedEntry + if removedEntries.has(entry) view.element.remove() subscription.dispose() - # TODO: When this file is converted to JS, convert this forEach loop - # to a for...of loop and add a break here for performance @subscriptions.add(subscription) view diff --git a/lib/directory.coffee b/lib/directory.coffee index bc40df7d..329601d8 100644 --- a/lib/directory.coffee +++ b/lib/directory.coffee @@ -263,7 +263,8 @@ class Directory if @expansionState.entries.has(name) @expansionState.entries.delete(name) - @emitter.emit('did-remove-entries', removedEntries) if entriesRemoved + # Convert removedEntries to a Set containing only the entries for O(1) lookup + @emitter.emit('did-remove-entries', new Set(removedEntries.values())) if entriesRemoved if newEntries.length > 0 @entries.set(entry.name, entry) for entry in newEntries From 67e1b0b42f4b7c07759576bd5ae29f47d799a803 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 14 Sep 2017 21:27:14 +0200 Subject: [PATCH 244/263] Prepare 0.218.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ba71fde..c2f05f06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-view", - "version": "0.217.9", + "version": "0.218.0", "main": "./lib/main", "description": "Explore and open files in the current project.", "repository": "https://github.com/atom/tree-view", From fce152dab6b841a8643da3c936e0934d37a9461c Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Thu, 31 Mar 2016 13:34:11 -0500 Subject: [PATCH 245/263] allow mutliple dragging --- lib/tree-view.coffee | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 405bb384..e113b0d7 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -921,28 +921,35 @@ class TreeView if @rootDragAndDrop.canDragStart(e) return @rootDragAndDrop.onDragStart(e) - target = entry.querySelector(".name") - initialPath = target.dataset.path - - fileNameElement = target.cloneNode(true) - for key, value of getStyleObject(target) - fileNameElement.style[key] = value - fileNameElement.style.position = 'absolute' - fileNameElement.style.top = 0 - fileNameElement.style.left = 0 + initialPaths = [] + dragImage = document.createElement("ol") + dragImage.classList.add("entries list-tree") + dragImage.style.position = "absolute" + dragImage.style.top = 0 + dragImage.style.left = 0 # Ensure the cloned file name element is rendered on a separate GPU layer # to prevent overlapping elements located at (0px, 0px) from being used as # the drag image. - fileNameElement.style.willChange = 'transform' + dragImage.style.willChange = "transform" - document.body.appendChild(fileNameElement) + for target in @getSelectedEntries() + nameElement = target.querySelector(".name") + initialPaths.push(nameElement.dataset.path) + + newNameElement = nameElement.cloneNode(true) + for key, value of getStyleObject(nameElement) + newNameElement.style[key] = value + dragImage.append(newNameElement) + + + document.body.appendChild(dragImage) e.dataTransfer.effectAllowed = "move" - e.dataTransfer.setDragImage(fileNameElement, 0, 0) - e.dataTransfer.setData("initialPath", initialPath) + e.dataTransfer.setDragImage(dragImage, 0, 0) + e.dataTransfer.setData("initialPaths", initialPaths) window.requestAnimationFrame -> - fileNameElement.remove() + dragImage.remove() # Handle entry dragover event; reset default dragover actions onDragOver: (e) -> @@ -970,11 +977,12 @@ class TreeView newDirectoryPath = entry.querySelector('.name')?.dataset.path return false unless newDirectoryPath - initialPath = e.dataTransfer.getData("initialPath") + initialPaths = e.dataTransfer.getData("initialPaths") - if initialPath + if initialPaths # Drop event from Atom - @moveEntry(initialPath, newDirectoryPath) + for initialPath in initialPaths.split(',') + @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS for file in e.dataTransfer.files From 6276882bf7752480497608ba1b1d796a8e473a69 Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Fri, 1 Apr 2016 12:21:18 -0500 Subject: [PATCH 246/263] refactor event helpers for multiple targets in drag --- spec/event-helpers.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 2d1051ed..ba17c6c6 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -104,4 +104,8 @@ module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelect Object.defineProperty(dragEndEvent, 'currentTarget', value: dragged) Object.defineProperty(dragEndEvent, 'dataTransfer', value: dataTransfer) +<<<<<<< HEAD [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent] +======= + dropEvent +>>>>>>> refactor event helpers for multiple targets in drag From 466281a54c5ac2f5fb916fc47599c194bfa841ee Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Fri, 1 Apr 2016 12:22:15 -0500 Subject: [PATCH 247/263] spec for multi-drag --- spec/tree-view-package-spec.coffee | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 02b167c4..a59cd9d0 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3420,6 +3420,7 @@ describe "TreeView", -> [dragStartEvent, dragEnterEvent, dropEvent] = eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header')) treeView.onDragStart(dragStartEvent) + expect(deltaFile).toHaveClass('selected') treeView.onDragEnter(dragEnterEvent) expect(alphaDir).toHaveClass('selected') @@ -3455,6 +3456,30 @@ describe "TreeView", -> runs -> expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + describe "when dropping multiple FileViews onto a DirectoryView's header", -> + it "should move the files to the hovered directory", -> + # Dragging delta.txt onto alphaDir + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + gammaFiles = [].slice.call(gammaDir.entries.children, 1, 3) + + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents([gammaFiles], alphaDir.querySelector('.header'), alphaDir) + + runs -> + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dropEvent) + expect(alphaDir.children.length).toBe 2 + + waitsFor "directory view contents to refresh", -> + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + + runs -> + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4 + describe "when dropping a DirectoryView onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> # Dragging thetaDir onto alphaDir From d32107cc2b502bd70dc497db6e0d8641d10c12da Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Sat, 2 Apr 2016 13:27:09 -0500 Subject: [PATCH 248/263] move files before their containing dir --- lib/tree-view.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index e113b0d7..06f6c572 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -981,7 +981,8 @@ class TreeView if initialPaths # Drop event from Atom - for initialPath in initialPaths.split(',') + # iterate backwards so files in a dir are moved before the dir itself + for initialPath in initialPaths.split(',') by -1 @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS From 8078e409e9dd64c1faf500aa5a0b4d8c410d862a Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Thu, 26 May 2016 08:00:28 -0500 Subject: [PATCH 249/263] Don't drag dir entries on multi-drag When a directory and its children are being dragged, only drag the directory, not the individual children. --- lib/tree-view.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 06f6c572..fb8567da 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -934,8 +934,9 @@ class TreeView for target in @getSelectedEntries() nameElement = target.querySelector(".name") - initialPaths.push(nameElement.dataset.path) - + entryPath = nameElement.dataset.path + unless path.dirname(entryPath) in initialPaths + initialPaths.push(entryPath) newNameElement = nameElement.cloneNode(true) for key, value of getStyleObject(nameElement) newNameElement.style[key] = value From 1cbce0eaacf105411a2896ac6e90e46061cc8b2b Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Thu, 26 May 2016 08:38:09 -0500 Subject: [PATCH 250/263] collapse directories on drag for pretty dragImage --- lib/tree-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index fb8567da..59558d33 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -937,6 +937,7 @@ class TreeView entryPath = nameElement.dataset.path unless path.dirname(entryPath) in initialPaths initialPaths.push(entryPath) + entry.collapse() if entry instanceof DirectoryView newNameElement = nameElement.cloneNode(true) for key, value of getStyleObject(nameElement) newNameElement.style[key] = value From 2c21aa5c470c0bcafbdac72b0f6e0c20dada4bb4 Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Thu, 26 May 2016 11:27:37 -0500 Subject: [PATCH 251/263] spec dragging a directory and files to a new directory this test guarantees that dragging a directory d and additional files f into a new directory will move the entire directory d discreetly along with any additional files f. --- spec/tree-view-package-spec.coffee | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index a59cd9d0..286fcc85 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3480,6 +3480,41 @@ describe "TreeView", -> runs -> expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4 + describe "when dropping a DirectoryView and FileViews onto a DirectoryView's header", -> + it "should move the files and directory to the hovered directory", -> + # Dragging alpha.txt and alphaDir into thetaDir + rootDir = $(treeView.roots[0]) + + alphaFile = rootDir[0].entries.children[2] + alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') + alphaDir[0].expand() + + gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') + gammaDir[0].expand() + thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first') + + dragged = [alphaFile, alphaDir[0]] + + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents(dragged, thetaDir.find('.header')[0], thetaDir[0]) + + runs -> + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dropEvent) + expect(thetaDir[0].children.length).toBe 2 + + waitsFor "directory view contents to refresh", -> + $(treeView.roots[0].entries).find('.directory:contains(theta):first .entry').length > 2 + + runs -> + thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first') + thetaDir[0].expand() + expect(thetaDir.find('.entry').length).toBe 2 + # alpha dir still has all its entries + alphaDir = $(thetaDir[0].entries).find('.directory:contains(alpha):first') + alphaDir[0].expand() + expect(alphaDir.find('.entry').length).toBe 2 + describe "when dropping a DirectoryView onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> # Dragging thetaDir onto alphaDir From 1433595440b4b0059510c8c65312cac677a5c25b Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Mon, 30 May 2016 14:52:58 -0400 Subject: [PATCH 252/263] refactor drag image, clicking logic re-enable a metaclick to allow an item to be selected from the multi-selection. change the behavior of drag image building so that there's no collapsing and properly deselects the drag image. --- lib/tree-view.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 59558d33..7fe8c361 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -937,11 +937,10 @@ class TreeView entryPath = nameElement.dataset.path unless path.dirname(entryPath) in initialPaths initialPaths.push(entryPath) - entry.collapse() if entry instanceof DirectoryView - newNameElement = nameElement.cloneNode(true) - for key, value of getStyleObject(nameElement) - newNameElement.style[key] = value - dragImage.append(newNameElement) + newNameElement = nameElement.cloneNode(true) + for key, value of getStyleObject(nameElement) + newNameElement.style[key] = value + dragImage.append(newNameElement) document.body.appendChild(dragImage) From 2a58d001b81edfbaa1b86daa83081ca06c510c75 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 30 Aug 2017 16:32:23 -0500 Subject: [PATCH 253/263] fix merge leftovers --- spec/event-helpers.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index ba17c6c6..2d1051ed 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -104,8 +104,4 @@ module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelect Object.defineProperty(dragEndEvent, 'currentTarget', value: dragged) Object.defineProperty(dragEndEvent, 'dataTransfer', value: dataTransfer) -<<<<<<< HEAD [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent] -======= - dropEvent ->>>>>>> refactor event helpers for multiple targets in drag From 2c3bf31b042261dfb4bd0430f917289cc7addab2 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 00:36:17 -0500 Subject: [PATCH 254/263] select on mouse up --- lib/tree-view.coffee | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 7fe8c361..00fd6a63 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -38,6 +38,7 @@ class TreeView @emitter = new Emitter @roots = [] @selectedPath = null + @selectOnMouseUp = false @ignoredPatterns = [] @useSyncFS = false @currentlyOpening = new Map @@ -139,6 +140,7 @@ class TreeView @entryClicked(e) unless e.shiftKey or e.metaKey or e.ctrlKey @element.addEventListener 'mousedown', (e) => @onMouseDown(e) + @element.addEventListener 'mouseup', (e) => @onMouseUp(e) @element.addEventListener 'dragstart', (e) => @onDragStart(e) @element.addEventListener 'dragenter', (e) => @onDragEnter(e) @element.addEventListener 'dragleave', (e) => @onDragLeave(e) @@ -794,7 +796,7 @@ class TreeView return entryName = path.basename(initialPath) - newPath = "#{newDirectoryPath}/#{entryName}".replace(/\s+$/, '') + newPath = "#{newDirectoryPath}#{path.sep}#{entryName}".replace(/\s+$/, '') try fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) @@ -821,11 +823,15 @@ class TreeView e.stopPropagation() # return early if we're opening a contextual menu (right click) during multi-select mode - if @multiSelectEnabled() and - entryToSelect.classList.contains('selected') and - # mouse right click or ctrl click as right click on darwin platforms - (e.button is 2 or e.ctrlKey and process.platform is 'darwin') - return + if @multiSelectEnabled() and entryToSelect.classList.contains('selected') + + # mouse right click or ctrl click as right click on darwin platforms + if (e.button is 2 or e.ctrlKey and process.platform is 'darwin') + return + # stop unselect if dragging + else + @selectOnMouseUp = true + return if e.shiftKey @selectContinuousEntries(entryToSelect) @@ -840,6 +846,11 @@ class TreeView @selectEntry(entryToSelect) @showFullMenu() + onMouseUp: (e) -> + if @selectOnMouseUp + @selectEntry(entryToSelect) + @showFullMenu() + # Public: Return an array of paths from all selected items # # Example: @selectedPaths() @@ -915,6 +926,7 @@ class TreeView # Handle entry name object dragstart event onDragStart: (e) -> + @selectOnMouseUp = false if entry = e.target.closest('.entry') e.stopPropagation() @@ -923,7 +935,7 @@ class TreeView initialPaths = [] dragImage = document.createElement("ol") - dragImage.classList.add("entries list-tree") + dragImage.classList.add("entries", "list-tree") dragImage.style.position = "absolute" dragImage.style.top = 0 dragImage.style.left = 0 From f7243a5ff0928ccb28c2a0d87c3834e3343ecaf9 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 00:37:19 -0500 Subject: [PATCH 255/263] build drag events for multiple files --- spec/event-helpers.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 2d1051ed..0ad87f22 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -12,9 +12,12 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) -> Object.keys(dataTransfer.data).map((key) -> {type: key}) ) + for entry in dragged + entry.classList.add('selected') + dragStartEvent = new DragEvent('dragstart') - Object.defineProperty(dragStartEvent, 'target', value: dragged) - Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged) + Object.defineProperty(dragStartEvent, 'target', value: dragged[0]) + Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged[0]) Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer) dropEvent = new DragEvent('drop') From 804d5e7028e994416caf3f83e23e0aed16cf5a4c Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 00:37:43 -0500 Subject: [PATCH 256/263] remove jquery from tests --- spec/tree-view-package-spec.coffee | 44 ++++++++++++++---------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 286fcc85..5e924ae5 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3418,7 +3418,7 @@ describe "TreeView", -> deltaFile = gammaDir.entries.children[2] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header')) + eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header')) treeView.onDragStart(dragStartEvent) expect(deltaFile).toHaveClass('selected') treeView.onDragEnter(dragEnterEvent) @@ -3443,7 +3443,7 @@ describe "TreeView", -> deltaFile = gammaDir.entries.children[2] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header'), alphaDir) + eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir) runs -> treeView.onDragStart(dragStartEvent) @@ -3467,12 +3467,12 @@ describe "TreeView", -> gammaFiles = [].slice.call(gammaDir.entries.children, 1, 3) [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents([gammaFiles], alphaDir.querySelector('.header'), alphaDir) + eventHelpers.buildInternalDragEvents(gammaFiles, alphaDir.querySelector('.header'), alphaDir) runs -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) - expect(alphaDir.children.length).toBe 2 + expect(alphaDir.entries.children.length).toBe 2 waitsFor "directory view contents to refresh", -> findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 @@ -3483,37 +3483,35 @@ describe "TreeView", -> describe "when dropping a DirectoryView and FileViews onto a DirectoryView's header", -> it "should move the files and directory to the hovered directory", -> # Dragging alpha.txt and alphaDir into thetaDir - rootDir = $(treeView.roots[0]) - - alphaFile = rootDir[0].entries.children[2] - alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() + alphaFile = treeView.roots[0].entries.children[2] + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() - gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first') - gammaDir[0].expand() - thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first') + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + thetaDir = findDirectoryContainingText(treeView.roots[0], 'theta') + thetaDir.expand() - dragged = [alphaFile, alphaDir[0]] + dragged = [alphaFile, alphaDir] [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(dragged, thetaDir.find('.header')[0], thetaDir[0]) + eventHelpers.buildInternalDragEvents(dragged, thetaDir.querySelector('.header'), thetaDir) runs -> treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) - expect(thetaDir[0].children.length).toBe 2 + expect(thetaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - $(treeView.roots[0].entries).find('.directory:contains(theta):first .entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'theta').querySelectorAll('.entry').length > 2 runs -> - thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first') - thetaDir[0].expand() - expect(thetaDir.find('.entry').length).toBe 2 + thetaDir.expand() + expect(thetaDir.querySelectorAll('.entry').length).toBe 3 # alpha dir still has all its entries - alphaDir = $(thetaDir[0].entries).find('.directory:contains(alpha):first') - alphaDir[0].expand() - expect(alphaDir.find('.entry').length).toBe 2 + alphaDir = findDirectoryContainingText(thetaDir.entries, 'alpha') + alphaDir.expand() + expect(alphaDir.querySelectorAll('.entry').length).toBe 2 describe "when dropping a DirectoryView onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> @@ -3531,7 +3529,7 @@ describe "TreeView", -> runs -> [dragStartEvent, dragEnterEvent, dropEvent] = - eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.querySelector('.header'), alphaDir) + eventHelpers.buildInternalDragEvents([thetaDir], alphaDir.querySelector('.header'), alphaDir) treeView.onDragStart(dragStartEvent) treeView.onDrop(dropEvent) expect(alphaDir.children.length).toBe 2 From f63adcd78e9083de9b1d87708cdc656a5a8c56ae Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 00:43:58 -0500 Subject: [PATCH 257/263] remove redundant test --- spec/tree-view-package-spec.coffee | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 5e924ae5..3c814a8e 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3053,12 +3053,6 @@ describe "TreeView", -> fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) expect(treeView.list).toHaveClass('full-menu') - describe 'selecting multiple items', -> - it 'switches the contextual menu to muli-select mode', -> - fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) - expect(treeView.list).toHaveClass('multi-select') - describe 'using the shift key', -> it 'selects the items between the already selected item and the shift clicked item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) From 1cb4a89af388620e6fb6882c9229507f6a15f25c Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 00:51:37 -0500 Subject: [PATCH 258/263] test onmouseup --- lib/tree-view.coffee | 7 +++++-- spec/tree-view-package-spec.coffee | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 00fd6a63..379ac9f7 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -848,8 +848,11 @@ class TreeView onMouseUp: (e) -> if @selectOnMouseUp - @selectEntry(entryToSelect) - @showFullMenu() + @selectOnMouseUp = false + + if entryToSelect = e.target.closest('.entry') + @selectEntry(entryToSelect) + @showFullMenu() # Public: Return an array of paths from all selected items # diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 3c814a8e..c1de5c87 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3053,6 +3053,22 @@ describe "TreeView", -> fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) expect(treeView.list).toHaveClass('full-menu') + describe 'selecting one of the selected items', -> + it 'maintains multi-select for dragging', -> + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) + fileView1.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) + expect(treeView.list).not.toHaveClass('full-menu') + expect(treeView.list).toHaveClass('multi-select') + + it 'switches to full-menu on mouseup', -> + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true})) + fileView1.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) + fileView1.dispatchEvent(new MouseEvent('mouseup', {bubbles: true})) + expect(treeView.list).toHaveClass('full-menu') + expect(treeView.list).not.toHaveClass('multi-select') + describe 'using the shift key', -> it 'selects the items between the already selected item and the shift clicked item', -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) From da5b0a584ff4e8746db243bf99054c513d08141d Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 01:26:24 -0500 Subject: [PATCH 259/263] fix drag image --- lib/tree-view.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 379ac9f7..75b16ce2 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -948,14 +948,17 @@ class TreeView dragImage.style.willChange = "transform" for target in @getSelectedEntries() - nameElement = target.querySelector(".name") - entryPath = nameElement.dataset.path + entryPath = target.querySelector(".name").dataset.path unless path.dirname(entryPath) in initialPaths initialPaths.push(entryPath) - newNameElement = nameElement.cloneNode(true) - for key, value of getStyleObject(nameElement) - newNameElement.style[key] = value - dragImage.append(newNameElement) + newElement = target.cloneNode(true) + if newElement.classList.contains("directory") + newElement.querySelector(".entries").remove() + for key, value of getStyleObject(target) + newElement.style[key] = value + newElement.style.paddingLeft = "1em" + newElement.style.paddingRight = "1em" + dragImage.append(newElement) document.body.appendChild(dragImage) From 4318f69140e42fa51834ae01d92166da482a4365 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 10:06:44 -0500 Subject: [PATCH 260/263] do nothing if dropped on one of the dragged --- lib/tree-view.coffee | 12 ++++++++---- spec/tree-view-package-spec.coffee | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 75b16ce2..18cc69a1 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -989,22 +989,26 @@ class TreeView e.preventDefault() e.stopPropagation() - entry.classList.remove('selected') - return unless entry.classList.contains('directory') newDirectoryPath = entry.querySelector('.name')?.dataset.path return false unless newDirectoryPath - initialPaths = e.dataTransfer.getData("initialPaths") + initialPaths = e.dataTransfer.getData('initialPaths') if initialPaths # Drop event from Atom + initialPaths = initialPaths.split(',') + return if initialPaths.includes(newDirectoryPath) + + entry.classList.remove('selected') + # iterate backwards so files in a dir are moved before the dir itself - for initialPath in initialPaths.split(',') by -1 + for initialPath in initialPaths by -1 @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS + entry.classList.remove('selected') for file in e.dataTransfer.files @moveEntry(file.path, newDirectoryPath) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index c1de5c87..8f8622c7 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3552,6 +3552,24 @@ describe "TreeView", -> editor = atom.workspace.getActiveTextEditor() expect(editor.getPath()).toBe(thetaFilePath.replace('gamma', 'alpha')) + describe "when dropping a DirectoryView and FileViews onto the same DirectoryView's header", -> + it "should not move the files and directory to the hovered directory", -> + # Dragging alpha.txt and alphaDir into alphaDir + alphaFile = treeView.roots[0].entries.children[2] + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + + dragged = [alphaFile, alphaDir] + + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents(dragged, alphaDir.querySelector('.header'), alphaDir) + + spyOn(treeView, 'moveEntry') + + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dropEvent) + expect(treeView.moveEntry).not.toHaveBeenCalled() + describe "when dragging a file from the OS onto a DirectoryView's header", -> it "should move the file to the hovered directory", -> # Dragging delta.txt from OS file explorer onto alphaDir From 40ca6a1dd889e7339e7fa7a5d46b0c7e13d7dcc5 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 14:58:31 -0500 Subject: [PATCH 261/263] fix shift + ctrl click --- lib/tree-view.coffee | 70 +++++++++++++++++++++--------- spec/tree-view-package-spec.coffee | 29 ++++++++++++- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 18cc69a1..02cfabe6 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -38,7 +38,8 @@ class TreeView @emitter = new Emitter @roots = [] @selectedPath = null - @selectOnMouseUp = false + @selectOnMouseUp = null + @lastFocusedElement = null @ignoredPatterns = [] @useSyncFS = false @currentlyOpening = new Map @@ -746,6 +747,7 @@ class TreeView return unless entry? @selectedPath = entry.getPath() + @lastFocusedElement = entry selectedEntries = @getSelectedEntries() if selectedEntries.length > 1 or selectedEntries[0] isnt entry @@ -826,33 +828,55 @@ class TreeView if @multiSelectEnabled() and entryToSelect.classList.contains('selected') # mouse right click or ctrl click as right click on darwin platforms - if (e.button is 2 or e.ctrlKey and process.platform is 'darwin') + if e.button is 2 or (e.ctrlKey and process.platform is 'darwin') return - # stop unselect if dragging else - @selectOnMouseUp = true + # allow select if not dragging + {shiftKey, metaKey, ctrlKey} = e + @selectOnMouseUp = {shiftKey, metaKey, ctrlKey} return - if e.shiftKey + if e.shiftKey and (e.metaKey or (e.ctrlKey and process.platform isnt 'darwin')) + # select continuous from @lastFocusedElement but leave others + @selectContinuousEntries(entryToSelect, false) + @toggleMultiSelectMenu() + else if e.shiftKey + # select continuous from @lastFocusedElement and deselect rest @selectContinuousEntries(entryToSelect) - @showMultiSelectMenu() + @toggleMultiSelectMenu() # only allow ctrl click for multi selection on non darwin systems else if e.metaKey or (e.ctrlKey and process.platform isnt 'darwin') @selectMultipleEntries(entryToSelect) - - # only show the multi select menu if more then one file/directory is selected - @showMultiSelectMenu() if @selectedPaths().length > 1 + @lastFocusedElement = entryToSelect + @toggleMultiSelectMenu() else @selectEntry(entryToSelect) @showFullMenu() onMouseUp: (e) -> - if @selectOnMouseUp - @selectOnMouseUp = false - - if entryToSelect = e.target.closest('.entry') - @selectEntry(entryToSelect) - @showFullMenu() + return unless @selectOnMouseUp? + + {shiftKey, metaKey, ctrlKey} = @selectOnMouseUp + @selectOnMouseUp = null + + return unless entryToSelect = e.target.closest('.entry') + + if shiftKey and (metaKey or (ctrlKey and process.platform isnt 'darwin')) + # select continuous from @lastFocusedElement but leave others + @selectContinuousEntries(entryToSelect, false) + @toggleMultiSelectMenu() + else if shiftKey + # select continuous from @lastFocusedElement and deselect rest + @selectContinuousEntries(entryToSelect) + @toggleMultiSelectMenu() + # only allow ctrl click for multi selection on non darwin systems + else if metaKey or (ctrlKey and process.platform isnt 'darwin') + @deselect([entryToSelect]) + @lastFocusedElement = entryToSelect + @toggleMultiSelectMenu() + else + @selectEntry(entryToSelect) + @showFullMenu() # Public: Return an array of paths from all selected items # @@ -866,8 +890,8 @@ class TreeView # a new given entry. This is shift+click functionality # # Returns array of selected elements - selectContinuousEntries: (entry) -> - currentSelectedEntry = @selectedEntry() + selectContinuousEntries: (entry, deselectOthers = true) -> + currentSelectedEntry = @lastFocusedElement ? @selectedEntry() parentContainer = entry.parentElement if parentContainer.contains(currentSelectedEntry) entries = Array.from(parentContainer.querySelectorAll('.entry')) @@ -875,7 +899,7 @@ class TreeView selectedIndex = entries.indexOf(currentSelectedEntry) elements = (entries[i] for i in [entryIndex..selectedIndex]) - @deselect() + @deselect() if deselectOthers element.classList.add('selected') for element in elements elements @@ -894,12 +918,18 @@ class TreeView @list.classList.remove('multi-select') @list.classList.add('full-menu') - # Public: Toggle multi-select class on the main list element to display the the + # Public: Toggle multi-select class on the main list element to display the # menu with only items that make sense for multi select functionality showMultiSelectMenu: -> @list.classList.remove('full-menu') @list.classList.add('multi-select') + toggleMultiSelectMenu: -> + if @getSelectedEntries().length > 1 + @showMultiSelectMenu() + else + @showFullMenu() + # Public: Check for multi-select class on the main list # # Returns boolean @@ -929,7 +959,7 @@ class TreeView # Handle entry name object dragstart event onDragStart: (e) -> - @selectOnMouseUp = false + @selectOnMouseUp = null if entry = e.target.closest('.entry') e.stopPropagation() diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 8f8622c7..bd952ea6 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3024,7 +3024,7 @@ describe "TreeView", -> expect(treeView.element.querySelector('.directory.status-modified')).not.toExist() describe "selecting items", -> - [dirView, fileView1, fileView2, fileView3, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3] = [] + [dirView, fileView1, fileView2, fileView3, fileView4, fileView5, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3, filePath4, filePath5] = [] beforeEach -> rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) @@ -3033,17 +3033,21 @@ describe "TreeView", -> filePath1 = path.join(dirPath, "test-file1.txt") filePath2 = path.join(dirPath, "test-file2.txt") filePath3 = path.join(dirPath, "test-file3.txt") + filePath4 = path.join(dirPath, "test-file4.txt") + filePath5 = path.join(dirPath, "test-file5.txt") fs.makeTreeSync(dirPath) fs.writeFileSync(filePath1, "doesn't matter") fs.writeFileSync(filePath2, "doesn't matter") fs.writeFileSync(filePath3, "doesn't matter") + fs.writeFileSync(filePath4, "doesn't matter") + fs.writeFileSync(filePath5, "doesn't matter") atom.project.setPaths([rootDirPath]) dirView = treeView.entryForPath(dirPath) dirView.expand() - [fileView1, fileView2, fileView3] = dirView.querySelectorAll('.file') + [fileView1, fileView2, fileView3, fileView4, fileView5] = dirView.querySelectorAll('.file') describe 'selecting multiple items', -> it 'switches the contextual menu to muli-select mode', -> @@ -3082,8 +3086,29 @@ describe "TreeView", -> fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) expect(fileView1).toHaveClass('selected') + expect(fileView2).not.toHaveClass('selected') expect(fileView3).toHaveClass('selected') + + describe 'using the metakey(cmd) key on already selected item', -> + it 'deselects just the cmd-clicked item', -> + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView1.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView1.dispatchEvent(new MouseEvent('mouseup', {bubbles: true, metaKey: true})) + expect(fileView1).not.toHaveClass('selected') expect(fileView2).not.toHaveClass('selected') + expect(fileView3).toHaveClass('selected') + + describe 'using the shift and metakey(cmd) keys', -> + it 'selects the items between the last cmd-clicked item and the clicked item', -> + fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true})) + fileView5.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true, shiftKey: true})) + expect(fileView1).toHaveClass('selected') + expect(fileView2).not.toHaveClass('selected') + expect(fileView3).toHaveClass('selected') + expect(fileView4).toHaveClass('selected') + expect(fileView5).toHaveClass('selected') describe 'non-darwin platform', -> originalPlatform = process.platform From fc41a9789534c6bc2072a9aca4e6c69da9c7bf6a Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 31 Aug 2017 15:02:21 -0500 Subject: [PATCH 262/263] return empty array if shift+click in other folder --- lib/tree-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 02cfabe6..d8747751 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -893,6 +893,7 @@ class TreeView selectContinuousEntries: (entry, deselectOthers = true) -> currentSelectedEntry = @lastFocusedElement ? @selectedEntry() parentContainer = entry.parentElement + elements = [] if parentContainer.contains(currentSelectedEntry) entries = Array.from(parentContainer.querySelectorAll('.entry')) entryIndex = entries.indexOf(entry) From 2802c7bc46e9a4c02a59eaef8e5a00ff5e99cec9 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Fri, 1 Sep 2017 00:08:23 -0500 Subject: [PATCH 263/263] refactor --- lib/tree-view.coffee | 69 +++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index d8747751..c218adb7 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -821,47 +821,56 @@ class TreeView @element.style.display = '' onMouseDown: (e) -> - if entryToSelect = e.target.closest('.entry') - e.stopPropagation() + return unless entryToSelect = e.target.closest('.entry') - # return early if we're opening a contextual menu (right click) during multi-select mode - if @multiSelectEnabled() and entryToSelect.classList.contains('selected') + e.stopPropagation() - # mouse right click or ctrl click as right click on darwin platforms - if e.button is 2 or (e.ctrlKey and process.platform is 'darwin') - return - else - # allow select if not dragging - {shiftKey, metaKey, ctrlKey} = e - @selectOnMouseUp = {shiftKey, metaKey, ctrlKey} - return + # TODO: meta+click and ctrl+click should not do the same thing on Windows. + # Right now removing metaKey if platform is not darwin breaks tests + # that set the metaKey to true when simulating a cmd+click on macos + # and ctrl+click on windows and linux. + cmdKey = e.metaKey or (e.ctrlKey and process.platform isnt 'darwin') + + # return early if clicking on a selected entry + if entryToSelect.classList.contains('selected') - if e.shiftKey and (e.metaKey or (e.ctrlKey and process.platform isnt 'darwin')) - # select continuous from @lastFocusedElement but leave others - @selectContinuousEntries(entryToSelect, false) - @toggleMultiSelectMenu() - else if e.shiftKey - # select continuous from @lastFocusedElement and deselect rest - @selectContinuousEntries(entryToSelect) - @toggleMultiSelectMenu() - # only allow ctrl click for multi selection on non darwin systems - else if e.metaKey or (e.ctrlKey and process.platform isnt 'darwin') - @selectMultipleEntries(entryToSelect) - @lastFocusedElement = entryToSelect - @toggleMultiSelectMenu() + # mouse right click or ctrl click as right click on darwin platforms + if e.button is 2 or (e.ctrlKey and process.platform is 'darwin') + return else - @selectEntry(entryToSelect) - @showFullMenu() + # allow click on mouseup if not dragging + {shiftKey} = e + @selectOnMouseUp = {shiftKey, cmdKey} + return + + if e.shiftKey and cmdKey + # select continuous from @lastFocusedElement but leave others + @selectContinuousEntries(entryToSelect, false) + @toggleMultiSelectMenu() + else if e.shiftKey + # select continuous from @lastFocusedElement and deselect rest + @selectContinuousEntries(entryToSelect) + @toggleMultiSelectMenu() + # only allow ctrl click for multi selection on non darwin systems + else if cmdKey + @selectMultipleEntries(entryToSelect) + @lastFocusedElement = entryToSelect + @toggleMultiSelectMenu() + else + @selectEntry(entryToSelect) + @showFullMenu() onMouseUp: (e) -> return unless @selectOnMouseUp? - {shiftKey, metaKey, ctrlKey} = @selectOnMouseUp + {shiftKey, cmdKey} = @selectOnMouseUp @selectOnMouseUp = null return unless entryToSelect = e.target.closest('.entry') - if shiftKey and (metaKey or (ctrlKey and process.platform isnt 'darwin')) + e.stopPropagation() + + if shiftKey and cmdKey # select continuous from @lastFocusedElement but leave others @selectContinuousEntries(entryToSelect, false) @toggleMultiSelectMenu() @@ -870,7 +879,7 @@ class TreeView @selectContinuousEntries(entryToSelect) @toggleMultiSelectMenu() # only allow ctrl click for multi selection on non darwin systems - else if metaKey or (ctrlKey and process.platform isnt 'darwin') + else if cmdKey @deselect([entryToSelect]) @lastFocusedElement = entryToSelect @toggleMultiSelectMenu()