├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── lib ├── tree-view-open-files-pane-view.coffee ├── tree-view-open-files-view.coffee └── tree-view-open-files.coffee ├── package.json └── styles └── tree-view-open-files.less /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.3 2 | * Show modified status 3 | 4 | ## 0.2.2 5 | * Fix another error with weird state. 6 | 7 | ## 0.2.1 8 | * Fix an error when pane items get into a weird state. 9 | 10 | ## 0.2.0 11 | * Add maximum height option. The list will scroll when it grows larger than this height. 12 | 13 | ## 0.1.9 14 | * Fix error when closing last tab 15 | 16 | ## 0.1.8 17 | * Fix view not being attached when the tree view is initially hidden. 18 | 19 | ## 0.1.7 20 | * Fix attempt to dispose undefined when a pane item has no onDidChangeTitle event 21 | 22 | ## 0.1.5 23 | * Fix entries losing their long titles when an item is added to the list 24 | 25 | ## 0.1.3 26 | * Fix highlighted background not covering width when list scrolls horizontally 27 | 28 | ## 0.1.2 29 | * Close button 30 | 31 | ## 0.1.0 - First Release 32 | * List panes and their files 33 | * Change active pane item by clicking entry 34 | * Collapse/expand panes 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Dominic Adelaar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-view-open-files 2 | 3 | Show open files in a list above the tree view. (like Sublime Text) 4 | 5 | Shows a separate collapsible list for each pane. 6 | 7 | ## Todo 8 | 9 | * Allow panes to be renamed 10 | -------------------------------------------------------------------------------- /lib/tree-view-open-files-pane-view.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'event-kit' 2 | _ = require 'lodash' 3 | {$} = require 'space-pen' 4 | 5 | module.exports = 6 | class TreeViewOpenFilesPaneView 7 | constructor: -> 8 | @items = [] 9 | @activeItem = null 10 | @paneSub = new CompositeDisposable 11 | 12 | @element = document.createElement('ul') 13 | @element.classList.add('list-tree', 'has-collapsable-children') 14 | nested = document.createElement('li') 15 | nested.classList.add('list-nested-item', 'expanded') 16 | @container = document.createElement('ul') 17 | @container.classList.add('list-tree') 18 | header = document.createElement('div') 19 | header.classList.add('list-item') 20 | 21 | headerSpan = document.createElement('span') 22 | headerSpan.classList.add('name', 'icon', 'icon-file-directory') 23 | headerSpan.setAttribute('data-name', 'Pane') 24 | headerSpan.innerText = 'Pane' 25 | header.appendChild headerSpan 26 | nested.appendChild header 27 | nested.appendChild @container 28 | @element.appendChild nested 29 | 30 | $(@element).on 'click', '.list-nested-item > .list-item', -> 31 | nested = $(this).closest('.list-nested-item') 32 | nested.toggleClass('expanded') 33 | nested.toggleClass('collapsed') 34 | self = this 35 | $(@element).on 'click', '.list-item[is=tree-view-file]', -> 36 | self.pane.activateItem self.entryForElement(this).item 37 | 38 | 39 | setPane: (pane) -> 40 | @pane = pane 41 | 42 | @paneSub.add pane.observeItems (item) => 43 | listItem = document.createElement('li') 44 | listItem.classList.add('file', 'list-item') 45 | listItem.setAttribute('is', 'tree-view-file') 46 | closer = document.createElement('button') 47 | closer.classList.add('close-open-file') 48 | $(closer).on 'click', => 49 | pane.destroyItem @entryForElement(listItem).item 50 | listItem.appendChild closer 51 | listItemName = document.createElement('span') 52 | listItemName.classList.add('name', 'icon', 'icon-file-text') 53 | listItemName.setAttribute('data-path', item.getPath?()) 54 | listItemName.setAttribute('data-name', item.getTitle?()) 55 | listItem.appendChild listItemName 56 | @container.appendChild listItem 57 | if item.onDidChangeTitle? 58 | titleSub = item.onDidChangeTitle => 59 | @updateTitle item 60 | 61 | @paneSub.add titleSub 62 | if item.onDidChangeModified? 63 | @paneSub.add item.onDidChangeModified (modified) => 64 | @updateModifiedState item, modified 65 | 66 | @items.push item: item, element: listItem 67 | @updateTitle item 68 | 69 | @paneSub.add pane.observeActiveItem (item) => 70 | @setActiveEntry item 71 | 72 | @paneSub.add pane.onDidRemoveItem ({item}) => 73 | @removeEntry item 74 | 75 | @paneSub.add pane.onDidDestroy => @paneSub.dispose() 76 | 77 | updateTitle: (item, siblings=true, useLongTitle=false) -> 78 | title = item.getTitle() 79 | 80 | if siblings 81 | for entry in @items 82 | if entry.item isnt item and entry.item.getTitle?() == title 83 | useLongTitle = true 84 | @updateTitle entry.item, false, true 85 | 86 | 87 | if useLongTitle and item.getLongTitle? 88 | title = item.getLongTitle() 89 | 90 | if entry = @entryForItem(item) 91 | $(entry.element).find('.name').text title 92 | 93 | updateModifiedState: (item, modified) -> 94 | entry = @entryForItem(item) 95 | 96 | entry?.element.classList.toggle 'modified', modified 97 | 98 | entryForItem: (item) -> 99 | _.detect @items, (entry) -> entry.item is item 100 | 101 | entryForElement: (item) -> 102 | _.detect @items, (entry) -> entry.element is item 103 | 104 | setActiveEntry: (item) -> 105 | if item 106 | @activeEntry?.classList.remove 'selected' 107 | if entry = @entryForItem item 108 | entry.element.classList.add 'selected' 109 | @activeEntry = entry.element 110 | 111 | removeEntry: (item) -> 112 | index = _.findIndex @items, (entry) -> entry.item is item 113 | 114 | if index >= 0 115 | @items[index].element.remove() 116 | @items.splice index, 1 117 | 118 | @updateTitle(entry.item) for entry in @items 119 | 120 | # Returns an object that can be retrieved when package is activated 121 | serialize: -> 122 | 123 | # Tear down any state and detach 124 | destroy: -> 125 | @element.remove() 126 | @paneSub.dispose() 127 | -------------------------------------------------------------------------------- /lib/tree-view-open-files-view.coffee: -------------------------------------------------------------------------------- 1 | {requirePackages} = require 'atom-utils' 2 | {CompositeDisposable} = require 'event-kit' 3 | _ = require 'lodash' 4 | 5 | TreeViewOpenFilesPaneView = require './tree-view-open-files-pane-view' 6 | 7 | module.exports = 8 | class TreeViewOpenFilesView 9 | constructor: (serializeState) -> 10 | # Create root element 11 | @element = document.createElement('div') 12 | @element.classList.add('tree-view-open-files') 13 | @groups = [] 14 | @paneSub = new CompositeDisposable 15 | @paneSub.add atom.workspace.observePanes (pane) => 16 | @addTabGroup pane 17 | destroySub = pane.onDidDestroy => 18 | destroySub.dispose() 19 | @removeTabGroup pane 20 | @paneSub.add destroySub 21 | 22 | @configSub = atom.config.observe 'tree-view-open-files.maxHeight', (maxHeight) => 23 | @element.style.maxHeight = if maxHeight > 0 then "#{maxHeight}px" else 'none' 24 | 25 | addTabGroup: (pane) -> 26 | group = new TreeViewOpenFilesPaneView 27 | group.setPane pane 28 | @groups.push group 29 | @element.appendChild group.element 30 | 31 | removeTabGroup: (pane) -> 32 | group = _.findIndex @groups, (group) -> group.pane is pane 33 | @groups[group].destroy() 34 | @groups.splice group, 1 35 | 36 | # Returns an object that can be retrieved when package is activated 37 | serialize: -> 38 | 39 | # Tear down any state and detach 40 | destroy: -> 41 | @element.remove() 42 | @paneSub.dispose() 43 | @configSub.dispose() 44 | 45 | # Toggle the visibility of this view 46 | toggle: -> 47 | if @element.parentElement? 48 | @hide() 49 | else 50 | @show() 51 | 52 | hide: -> 53 | @element.remove() 54 | 55 | show: -> 56 | requirePackages('tree-view').then ([treeView]) => 57 | treeView.treeView.find('.tree-view-scroller').css 'background', treeView.treeView.find('.tree-view').css 'background' 58 | treeView.treeView.prepend @element 59 | -------------------------------------------------------------------------------- /lib/tree-view-open-files.coffee: -------------------------------------------------------------------------------- 1 | {requirePackages} = require 'atom-utils' 2 | TreeViewOpenFilesView = require './tree-view-open-files-view' 3 | 4 | module.exports = 5 | treeViewOpenFilesView: null 6 | 7 | config: 8 | maxHeight: 9 | type: 'integer' 10 | default: 250 11 | min: 0 12 | description: 'Maximum height of the list before scrolling is required. Set to 0 to disable scrolling.' 13 | 14 | activate: (state) -> 15 | requirePackages('tree-view').then ([treeView]) => 16 | @treeViewOpenFilesView = new TreeViewOpenFilesView 17 | 18 | if treeView.treeView 19 | @treeViewOpenFilesView.show() 20 | 21 | atom.commands.add 'atom-workspace', 'tree-view:toggle', => 22 | if treeView.treeView?.is(':visible') 23 | @treeViewOpenFilesView.show() 24 | else 25 | @treeViewOpenFilesView.hide() 26 | 27 | atom.commands.add 'atom-workspace', 'tree-view:show', => 28 | @treeViewOpenFilesView.show() 29 | 30 | deactivate: -> 31 | @treeViewOpenFilesView.destroy() 32 | 33 | serialize: -> 34 | #TreeViewOpenFilesViewState: @TreeViewOpenFilesView.serialize() 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-view-open-files", 3 | "main": "./lib/tree-view-open-files", 4 | "version": "0.3.0", 5 | "description": "Show open files in a list above the tree view.", 6 | "repository": "https://github.com/postcasio/tree-view-open-files", 7 | "license": "MIT", 8 | "engines": { 9 | "atom": ">=1.1.0" 10 | }, 11 | "dependencies": { 12 | "atom-utils": ">=0.7.4", 13 | "event-kit": "^0.8.0", 14 | "lodash": "^2.4.1", 15 | "space-pen": "^3.8.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /styles/tree-view-open-files.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .tree-view-open-files { 4 | > .list-tree { 5 | width: 100%; 6 | overflow: auto; 7 | padding-left: 5px; 8 | padding-right: 10px; 9 | } 10 | 11 | 12 | overflow: auto; 13 | flex: 0 0 1; 14 | position: relative; 15 | order: 0; 16 | 17 | .close-open-file { 18 | background: none; 19 | border: none; 20 | margin-left: -24px; 21 | display: inline-block; 22 | color: @text-color; 23 | 24 | &:hover { 25 | color: @text-color-highlight; 26 | } 27 | } 28 | 29 | .close-open-file::before { 30 | font-family: 'Octicons Regular'; 31 | font-weight: normal; 32 | font-style: normal; 33 | display: inline-block; 34 | line-height: 1; 35 | -webkit-font-smoothing: antialiased; 36 | text-decoration: none; 37 | font-size: 12px; 38 | width: 12px; 39 | height: 12px; 40 | content: "\f081"; 41 | position: relative; 42 | top: -1px; 43 | } 44 | 45 | .modified .close-open-file::before { 46 | content: ''; 47 | width: 8px; 48 | height: 8px; 49 | border: 2px solid @background-color-info; 50 | border-radius: 12px; 51 | margin: 0 2px; 52 | 53 | } 54 | 55 | .modified .close-open-file:hover::before { 56 | content: "\f081"; 57 | border: none; 58 | border-radius: 0; 59 | width: 12px; 60 | height: 12px; 61 | margin: 0; 62 | } 63 | 64 | > .list-tree > .list-nested-item > .list-item { 65 | line-height: 3em; 66 | } 67 | } 68 | 69 | .tree-view-resizer .tree-view-scroller { 70 | flex: 1 1; 71 | height: 100%; 72 | overflow: auto; 73 | position: relative; 74 | 75 | .tree-view { 76 | height: auto; 77 | min-height: inherit; 78 | } 79 | } 80 | --------------------------------------------------------------------------------