├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── keymaps └── tree-view-git-modified.cson ├── lib ├── tree-view-git-modified-pane-view.coffee ├── tree-view-git-modified-view.coffee └── tree-view-git-modified.coffee ├── menus └── tree-view-git-modified.cson ├── package.json ├── screenshots └── tree-view-git-modified.png ├── spec └── tree-view-git-modified-spec.coffee └── styles └── tree-view-git-modified.less /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.7.3 2 | * Fix #22 bug on atom 1.18.0 (File list failing to display after Atom 1.17 update.) 3 | 4 | ## 0.7.2 5 | * Fix errors after Atom API updates 6 | 7 | ## 0.7.1 8 | * Disabled serialize state to avoid errors on console 9 | 10 | ## 0.6.4 11 | * Updated repository access to work properly with Atom 1.8.0 12 | 13 | ## 0.5.11 14 | * Merged Avoid duplicate entries by (simon04) 15 | * Merged Using default theme background color on selected file by (Skoda) 16 | 17 | ## 0.5.10 18 | * Added support for multiple project folders 19 | * Changed tree icon to github icon 20 | * Fixed font color for new files when selected 21 | * Added letter to show file status on right side of filename 22 | * Added project name to tree header 23 | 24 | ## 0.1.0 - First Release 25 | * Initial implementation of tree-view-git-modified 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 R. Javier Vega 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-git-modified package 2 | 3 | Shows a list of git modified files on top of the tree-view for easy access. Opens all git modified files at once with a simple command. 4 | 5 | This plugin was developed by using code samples from the following atom plugins: 6 | - tree-view-open-files 7 | - open-git-modified-files 8 | 9 | ## Usage 10 | 11 | ``` 12 | 'ctrl-alt-o': 'tree-view-git-modified:toggle' // Shows or hides panel 13 | 'ctrl-cmd-o': 'tree-view-git-modified:openAll' // Open all git modified files 14 | ``` 15 | 16 | ![A screenshot of your package](https://raw.githubusercontent.com/rjaviervega/tree-view-git-modified/master/screenshots/tree-view-git-modified.png) 17 | 18 | ## Issues 19 | 20 | - This package doesn't work well when the package tree-view-git-projects is installed. 21 | - Known bug, if a project folder is removed from the tree and then added again in the same session the list of modified files will not be refresh until Atom is restarted (looks like a bug on Atom related to adding a project folder after it is removed). 22 | 23 | ## TODO 24 | 25 | - Include option to customize background color of selected items on tree-view. 26 | - Include icons to show git statuses and icons to quickly add/remove from git index. 27 | - Review why tree-view-git-projects breaks this package and try to make them compatible. 28 | - Add additional test cases. 29 | 30 | ## License MIT. 31 | -------------------------------------------------------------------------------- /keymaps/tree-view-git-modified.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/behind-atom-keymaps-in-depth 10 | 'atom-workspace': 11 | 'ctrl-alt-o': 'tree-view-git-modified:toggle' 12 | 'ctrl-cmd-o': 'tree-view-git-modified:openAll' 13 | -------------------------------------------------------------------------------- /lib/tree-view-git-modified-pane-view.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'event-kit' 2 | _ = require 'lodash' 3 | {$} = require 'space-pen' 4 | 5 | module.exports = 6 | class TreeViewOpenFilesPaneView 7 | 8 | constructor: (repo) -> 9 | @items = [] 10 | @panes = [] 11 | @activeItem = null 12 | @repo = repo 13 | @paneSub = new CompositeDisposable 14 | 15 | repoPath = repo.getWorkingDirectory() 16 | repoName = repoPath.split('/')[repoPath.split('/').length-1] 17 | 18 | @element = document.createElement('li') 19 | @element.setAttribute('is', 'tree-view-git-modified') 20 | @element.classList.add('tree-view-git-modified', 'list-nested-item', 'expanded') 21 | @container = document.createElement('ol') 22 | @container.classList.add('entries', 'list-tree') 23 | @header = document.createElement('div') 24 | @header.classList.add('header', 'list-item') 25 | 26 | headerSpan = document.createElement('span') 27 | headerSpan.classList.add('name', 'icon', 'icon-mark-github') 28 | headerSpan.setAttribute('data-name', 'Git Modified: ' + repoName) 29 | headerSpan.innerText = 'Git Modified: ' + repoName 30 | @header.appendChild headerSpan 31 | @element.appendChild @header 32 | @element.appendChild @container 33 | 34 | $(@header).on 'click', -> 35 | nested = $(this).closest('.list-nested-item') 36 | nested.toggleClass('expanded') 37 | nested.toggleClass('collapsed') 38 | 39 | # @loadRepo() 40 | self = this 41 | 42 | $(@element).on 'click', '.list-item[is=tree-view-file]', -> 43 | atom.workspace.open(self.entryForElement(this).item) 44 | 45 | setRepo: (repo) -> 46 | self = this 47 | @repo = repo 48 | @reloadStatuses self, repo 49 | if (repo.onDidChangeStatuses? or repo.onDidChangeStatus?) 50 | repo.onDidChangeStatuses => 51 | self.reloadStatuses self, repo 52 | , (err) -> 53 | console.log err 54 | repo.onDidChangeStatus (item) => 55 | self.reloadStatuses self, repo 56 | , (err) -> 57 | console.log err 58 | else 59 | self.removeAll() 60 | 61 | reloadStatuses: (self, repo) -> 62 | if self.isReloading 63 | console.warn 'Already performing reloadStatuses -- skipping this one!' 64 | else if repo? 65 | self.isReloading = true 66 | self.removeAll() 67 | repoPath = repo.getWorkingDirectory() 68 | innerRepo = repo.getRepo() 69 | for filePath in Object.keys(innerRepo.getStatus()) 70 | if innerRepo.isPathModified(filePath) 71 | self.addItem filePath, repoPath, 'status-modified' 72 | if repo.isPathNew(filePath) 73 | self.addItem filePath, repoPath, 'status-new' 74 | self.isReloading = false 75 | 76 | setPane: (pane) -> 77 | @paneSub.add pane.observeActiveItem (item) => 78 | @activeItem = item 79 | @setActiveEntry item 80 | 81 | @paneSub.add pane.onDidChangeActiveItem (item) => 82 | if (!item) 83 | @activeEntry?.classList.remove 'selected' 84 | 85 | @paneSub.add pane.onDidChangeActive (isActive) => 86 | @activeItem = pane.activeItem 87 | if (isActive) 88 | @setActiveEntry pane.activeItem 89 | 90 | addItem: (item, repoPath, status) -> 91 | # Checks if item already exists to avoid adding it twice 92 | exists = _.findIndex @items, (itemsItem) -> itemsItem.item is item 93 | 94 | if (exists < 0) 95 | listItem = document.createElement('li') 96 | listItem.classList.add('file', 'list-item', status) 97 | listItem.setAttribute('is', 'tree-view-file') 98 | listItemName = document.createElement('span') 99 | listItemName.innerText = item.split('/')[item.split('/').length-1] 100 | listItemName.classList.add('name', 'icon', 'icon-file-text') 101 | listItemName.setAttribute('data-path', item) 102 | listItemName.setAttribute('data-name', item) 103 | listItemName.setAttribute('data-repo-path', repoPath) 104 | listItem.appendChild listItemName 105 | 106 | listItemStatus = document.createElement('span') 107 | 108 | if (status == 'status-modified') 109 | listItemStatus.innerText = 'M' 110 | 111 | if (status == 'status-new') 112 | listItemStatus.innerText = 'N' 113 | 114 | listItemStatus.classList.add('pull-right') 115 | 116 | listItem.appendChild listItemStatus 117 | 118 | @container.appendChild listItem 119 | 120 | item = repoPath + '/' + item 121 | 122 | @items.push item: item, element: listItem 123 | 124 | if (@activeItem) 125 | @setActiveEntry @activeItem 126 | 127 | updateTitle: (item, siblings=true, useLongTitle=false) -> 128 | title = item.getTitle() 129 | 130 | if siblings 131 | for entry in @items 132 | if entry.item isnt item and entry.item.getTitle?() == title 133 | useLongTitle = true 134 | @updateTitle entry.item, false, true 135 | 136 | if useLongTitle and item.getLongTitle? 137 | title = item.getLongTitle() 138 | 139 | if entry = @entryForItem(item) 140 | $(entry.element).find('.name').text title 141 | 142 | entryForItem: (item) -> 143 | _.detect @items, (entry) -> 144 | if item.buffer && item.buffer.file 145 | item.buffer.file.path.indexOf(entry.item) > -1 146 | 147 | entryForElement: (item) -> 148 | _.detect @items, (entry) -> 149 | if (entry.element is item) 150 | return item 151 | 152 | setActiveEntry: (item) -> 153 | if item 154 | @activeEntry?.classList.remove 'selected' 155 | if entry = @entryForItem item 156 | entry.element.classList.add 'selected' 157 | @activeEntry = entry.element 158 | 159 | removeAll: -> 160 | for item in @items 161 | item.element.remove() 162 | @items = [] 163 | 164 | # Returns an object that can be retrieved when package is activated 165 | serialize: -> 166 | 167 | hide: -> 168 | @element.classList.add 'hidden' 169 | 170 | show: -> 171 | @element.classList.remove 'hidden' 172 | 173 | # Tear down any state and detach 174 | destroy: -> 175 | @element.remove() 176 | @paneSub.dispose() 177 | -------------------------------------------------------------------------------- /lib/tree-view-git-modified-view.coffee: -------------------------------------------------------------------------------- 1 | {requirePackages} = require 'atom-utils' 2 | {CompositeDisposable} = require 'event-kit' 3 | 4 | TreeViewGitModifiedPaneView = require './tree-view-git-modified-pane-view' 5 | 6 | module.exports = 7 | class TreeViewGitModifiedView 8 | 9 | constructor: (serializedState) -> 10 | # Create root element 11 | @element = document.createElement('li') 12 | @treeViewGitModifiedPaneViewArray = [] 13 | @paneSub = new CompositeDisposable 14 | @loadRepos() 15 | @paneSub.add atom.project.onDidChangePaths (path) => 16 | @loadRepos() 17 | 18 | loadRepos: -> 19 | self = this 20 | 21 | # Remove all existing panels 22 | for tree in @treeViewGitModifiedPaneViewArray 23 | tree.hide() 24 | 25 | repos = atom.project.getRepositories() 26 | for repo in repos 27 | if repo == null 28 | for tree in self.treeViewGitModifiedPaneViewArray 29 | if repo.path == tree.repo.path 30 | tree.show() 31 | else 32 | treeViewGitModifiedPaneView = new TreeViewGitModifiedPaneView repo 33 | treeViewGitModifiedPaneView.setRepo repo 34 | self.treeViewGitModifiedPaneViewArray.push treeViewGitModifiedPaneView 35 | self.element.appendChild treeViewGitModifiedPaneView.element 36 | 37 | self.paneSub.add atom.workspace.observePanes (pane) => 38 | treeViewGitModifiedPaneView.setPane pane 39 | 40 | # Tear down any state and detach 41 | destroy: -> 42 | @element.remove() 43 | @paneSub.dispose() 44 | 45 | getElement: -> 46 | @element 47 | 48 | # Toggle the visibility of this view 49 | toggle: -> 50 | if @element.parentElement? 51 | @hide() 52 | else 53 | @show() 54 | 55 | hide: -> 56 | @element.remove() 57 | 58 | show: -> 59 | atom.packages.activatePackage('tree-view').then((pkg) => 60 | if pkg && pkg.mainModule && pkg.mainModule.treeView 61 | treeView = pkg.mainModule.treeView 62 | if treeView 63 | #treeView.element.querySelector('.tree-view-scroller').style.background = treeView.element.querySelector('.tree-view').style.background 64 | #parentElement = treeView.element.querySelector('.tree-view-scroller .tree-view') 65 | parentElement = treeView.element.querySelector('.full-menu.list-tree') 66 | parentElement.insertBefore(@element, parentElement.firstChild) 67 | ) 68 | -------------------------------------------------------------------------------- /lib/tree-view-git-modified.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | {requirePackages} = require 'atom-utils' 3 | TreeViewGitModifiedView = require './tree-view-git-modified-view' 4 | 5 | module.exports = TreeViewGitModified = 6 | 7 | treeViewGitModifiedView: null 8 | subscriptions: null 9 | isVisible: true 10 | 11 | activate: (state) -> 12 | @treeViewGitModifiedView = new TreeViewGitModifiedView(state.treeViewGitModifiedViewState) 13 | @isVisible = state.isVisible 14 | 15 | # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable 16 | @subscriptions = new CompositeDisposable 17 | 18 | # Register command that toggles this view 19 | @subscriptions.add atom.commands.add 'atom-workspace', 'tree-view-git-modified:toggle': => @toggle() 20 | @subscriptions.add atom.commands.add 'atom-workspace', 'tree-view-git-modified:openAll': => @openAll() 21 | @subscriptions.add atom.commands.add 'atom-workspace', 'tree-view-git-modified:show': => @show() 22 | @subscriptions.add atom.commands.add 'atom-workspace', 'tree-view-git-modified:hide': => @hide() 23 | 24 | @subscriptions.add atom.project.onDidChangePaths (path) => 25 | @show() 26 | 27 | atom.packages.activatePackage('tree-view').then((pkg) => 28 | 29 | if pkg && pkg.mainModule && pkg.mainModule.treeView 30 | 31 | treeView = pkg.mainModule.treeView 32 | if (!@treeViewGitModifiedView) 33 | @treeViewGitModifiedView = new TreeViewGitModifiedView 34 | 35 | if (treeView && @isVisible) or (@isVisible is undefined) 36 | @treeViewGitModifiedView.show() 37 | 38 | atom.commands.add 'atom-workspace', 'tree-view:toggle', => 39 | if treeView?.isVisible() 40 | @treeViewGitModifiedView.hide() 41 | else 42 | if @isVisible 43 | @treeViewGitModifiedView.show() 44 | 45 | atom.commands.add 'atom-workspace', 'tree-view:show', => 46 | if @isVisible 47 | @treeViewGitModifiedView.show() 48 | , null) 49 | 50 | 51 | deactivate: -> 52 | @subscriptions.dispose() 53 | @treeViewGitModifiedView.destroy() 54 | 55 | serialize: -> 56 | isVisible: @isVisible 57 | # treeViewGitModifiedViewState: @treeViewGitModifiedView.serialize() 58 | 59 | toggle: -> 60 | if @isVisible 61 | @treeViewGitModifiedView.hide() 62 | else 63 | @treeViewGitModifiedView.show() 64 | @isVisible = !@isVisible 65 | 66 | show: -> 67 | @treeViewGitModifiedView.show() 68 | @isVisible = true 69 | 70 | hide: -> 71 | @treeViewGitModifiedView.hide() 72 | @isVisible = false 73 | 74 | openAll: -> 75 | self = this 76 | repos = atom.project.getRepositories() 77 | for repo in repos 78 | innerRepo = repo.repo 79 | for filePath in Object.keys(innerRepo.getStatus()) 80 | if innerRepo.isPathModified(filePath) or innerRepo.isPathNew(filePath) 81 | atom.workspace.open(innerRepo.getWorkingDirectory() + '/' + filePath) 82 | -------------------------------------------------------------------------------- /menus/tree-view-git-modified.cson: -------------------------------------------------------------------------------- 1 | # See https://atom.io/docs/latest/hacking-atom-package-word-count#menus for more details 2 | 'context-menu': 3 | 'atom-text-editor': [ 4 | { 5 | 'label': 'Toggle tree-view-git-modified' 6 | 'command': 'tree-view-git-modified:toggle' 7 | } 8 | ] 9 | 'menu': [ 10 | { 11 | 'label': 'Packages' 12 | 'submenu': [ 13 | 'label': 'Tree View Git Modified' 14 | 'submenu': [ 15 | { 16 | 'label': 'Toggle' 17 | 'command': 'tree-view-git-modified:toggle' 18 | }, 19 | { 20 | 'label': 'Open All' 21 | 'command': 'tree-view-git-modified:openAll' 22 | } 23 | ] 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-view-git-modified", 3 | "main": "./lib/tree-view-git-modified", 4 | "version": "0.7.3", 5 | "description": "View all git modified files on top of your tree-view panel.", 6 | "repository": "https://github.com/rjaviervega/tree-view-git-modified", 7 | "license": "MIT", 8 | "engines": { 9 | "atom": ">=0.174.0 <2.0.0" 10 | }, 11 | "dependencies": { 12 | "atom-utils": ">=0.6.3", 13 | "event-kit": "^0.8.0", 14 | "lodash": "^2.4.1", 15 | "space-pen": "^3.8.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /screenshots/tree-view-git-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjaviervega/tree-view-git-modified/186b55ab8e8a427fa3f4aaa35d6b75682384b7c9/screenshots/tree-view-git-modified.png -------------------------------------------------------------------------------- /spec/tree-view-git-modified-spec.coffee: -------------------------------------------------------------------------------- 1 | TreeViewGitModified = require '../lib/tree-view-git-modified' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "TreeViewGitModified", -> 9 | [workspaceElement, activationPromise] = [] 10 | 11 | beforeEach -> 12 | workspaceElement = atom.views.getView(atom.workspace) 13 | activationPromise = atom.packages.activatePackage('tree-view-git-modified') 14 | 15 | describe "when the tree-view-git-modified:show event is triggered", -> 16 | it "shows the modal panel", -> 17 | # Before the activation event the view is not on the DOM, and no panel 18 | # has been created 19 | expect(workspaceElement.querySelector('.tree-view-git-modified')).not.toExist() 20 | 21 | # This is an activation event, triggering it will cause the package to be 22 | # activated. 23 | atom.commands.dispatch workspaceElement, 'tree-view-git-modified:show' 24 | 25 | waitsForPromise -> 26 | activationPromise 27 | 28 | runs -> 29 | expect(workspaceElement.querySelector('.tree-view-git-modified')).toExist() 30 | 31 | # TODO: Include additional tests for all methods 32 | -------------------------------------------------------------------------------- /styles/tree-view-git-modified.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .tree-view-git-modified { 8 | .status-new { 9 | color: green !important; 10 | } 11 | } 12 | --------------------------------------------------------------------------------