Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

support nested repos #469

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/directory-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class DirectoryView {
this.element.header = this.header
this.element.entries = this.entries
this.element.directoryName = this.directoryName
this.element.refreshRepoStatus = this.refreshRepoStatus.bind(this)
}

updateIcon () {
Expand Down Expand Up @@ -134,6 +135,17 @@ class DirectoryView {
}
}

refreshRepoStatus (isRecursive = true, includeCollapsed = false) {
this.directory.refreshRepoStatus()
if (isRecursive) {
for (let entry of this.entries.children) {
if (entry.classList.contains('directory') && (entry.isExpanded || includeCollapsed)) {
entry.refreshRepoStatus(true, includeCollapsed)
}
}
}
}

toggleExpansion (isRecursive) {
if (isRecursive == null) {
isRecursive = false
Expand Down
41 changes: 25 additions & 16 deletions lib/directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const {CompositeDisposable, Emitter} = require('atom')
const fs = require('fs-plus')
const PathWatcher = require('pathwatcher')
const File = require('./file')
const {repoForPath} = require('./helpers')
const {repoForPath, getRepoCacheSize} = require('./helpers')

module.exports =
class Directory {
Expand All @@ -30,6 +30,10 @@ class Directory {
this.lowerCasePath = this.path.toLowerCase()
this.lowerCaseRealPath = this.lowerCasePath
}
this.repo = repoForPath(this.path)
if (this.repo && atom.config.get('tree-view.refreshVcsStatusOnProjectOpen') >= getRepoCacheSize()) {
this.refreshRepoStatus()
}

if (this.isRoot == null) {
this.isRoot = false
Expand Down Expand Up @@ -69,8 +73,7 @@ class Directory {
this.status = null
this.entries = new Map()

const repo = repoForPath(this.path)
this.submodule = repo && repo.isSubmodule(this.path)
this.submodule = this.repo && this.repo.isSubmodule(this.path)

this.subscribeToRepo()
this.updateStatus()
Expand Down Expand Up @@ -129,24 +132,30 @@ class Directory {
}
}

refreshRepoStatus () {
if (this.repo == null) return

this.repo.refreshIndex()
this.repo.refreshStatus()
}

// Subscribe to project's repo for changes to the Git status of this directory.
subscribeToRepo () {
const repo = repoForPath(this.path)
if (repo == null) return
if (this.repo == null) return

this.subscriptions.add(repo.onDidChangeStatus(event => {
this.subscriptions.add(this.repo.onDidChangeStatus(event => {
if (this.contains(event.path)) {
this.updateStatus(repo)
this.updateStatus(this.repo)
}
}))
this.subscriptions.add(repo.onDidChangeStatuses(() => {
this.updateStatus(repo)
this.subscriptions.add(this.repo.onDidChangeStatuses(() => {
this.updateStatus(this.repo)
}))
}

// Update the status property of this directory using the repo.
updateStatus () {
const repo = repoForPath(this.path)
updateStatus (repo = null) {
if (repo == null) repo = this.repo
if (repo == null) return

let newStatus = null
Expand All @@ -156,7 +165,8 @@ class Directory {
newStatus = 'ignored-name'
} else {
let status
if (this.isRoot) {
if (!repo.relativize(this.path + '/')) {
// repo root directory
// repo.getDirectoryStatus will always fail for the
// root because the path is relativized + concatenated with '/'
// making the matching string be '/'. Then path.indexOf('/')
Expand Down Expand Up @@ -184,8 +194,7 @@ class Directory {
// Is the given path ignored?
isPathIgnored (filePath) {
if (atom.config.get('tree-view.hideVcsIgnoredFiles')) {
const repo = repoForPath(this.path)
if (repo && repo.isProjectAtRoot() && repo.isPathIgnored(filePath)) return true
if (this.repo && this.repo.isProjectAtRoot() && this.repo.isPathIgnored(filePath)) return true
}

if (atom.config.get('tree-view.hideIgnoredNames')) {
Expand Down Expand Up @@ -276,7 +285,7 @@ class Directory {
} catch (error) {
names = []
}
names.sort(new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}).compare)
names.sort(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare)

const files = []
const directories = []
Expand Down Expand Up @@ -319,7 +328,7 @@ class Directory {
// track the insertion index for the created views
files.push(name)
} else {
files.push(new File({name, fullPath, symlink, ignoredNames: this.ignoredNames, useSyncFS: this.useSyncFS, stats: statFlat}))
files.push(new File({ name, fullPath, symlink, ignoredNames: this.ignoredNames, useSyncFS: this.useSyncFS, stats: statFlat }))
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions lib/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class File {

this.path = fullPath
this.realPath = this.path
this.repo = repoForPath(this.path)

this.subscribeToRepo()
this.updateStatus()
Expand Down Expand Up @@ -49,22 +50,21 @@ class File {

// Subscribe to the project's repo for changes to the Git status of this file.
subscribeToRepo () {
const repo = repoForPath(this.path)
if (repo == null) return
if (this.repo == null) return

this.subscriptions.add(repo.onDidChangeStatus(event => {
this.subscriptions.add(this.repo.onDidChangeStatus(event => {
if (this.isPathEqual(event.path)) {
this.updateStatus(repo)
this.updateStatus(this.repo)
}
}))
this.subscriptions.add(repo.onDidChangeStatuses(() => {
this.updateStatus(repo)
this.subscriptions.add(this.repo.onDidChangeStatuses(() => {
this.updateStatus(this.repo)
}))
}

// Update the status property of this directory using the repo.
updateStatus () {
const repo = repoForPath(this.path)
updateStatus (repo) {
if (repo == null) repo = this.repo
if (repo == null) return

let newStatus = null
Expand Down
16 changes: 12 additions & 4 deletions lib/get-icon-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,19 @@ class IconServices {
iconClass = 'icon-file-symlink-directory'
} else {
iconClass = 'icon-file-directory'
if (view.directory.isRoot) {
const repo = repoForPath(view.directory.path)
if (repo && repo.isProjectAtRoot()) iconClass = 'icon-repo'
let repo = repoForPath(view.directory.path)
if (repo) {
let relPath = repo.relativize(view.directory.path + '/')
if (relPath != null && relPath.length === 0) {
iconClass = 'icon-repo'
}
} else {
if (view.directory.submodule) iconClass = 'icon-file-submodule'
if (view.directory.isRoot) {
const repo = repoForPath(view.directory.path)
if (repo && repo.isProjectAtRoot()) iconClass = 'icon-repo'
} else {
if (view.directory.submodule) iconClass = 'icon-file-submodule'
}
}
}
classes.push(iconClass)
Expand Down
70 changes: 67 additions & 3 deletions lib/helpers.coffee
Original file line number Diff line number Diff line change
@@ -1,11 +1,75 @@
path = require "path"
fs = require 'fs-plus'
{GitRepository} = require 'atom'

module.exports =
repositoryCache: {}
fakeProjectRoots: []

getRepoCache: ->
module.exports.repositoryCache

isFakeProjectRoot: (checkPath) ->
path.normalize(checkPath) in module.exports.fakeProjectRoots

getRepoCacheSize: ->
Object.keys(module.exports.repositoryCache).length

resetRepoCache: ->
module.exports.repositoryCache = {}

repoForPath: (goalPath) ->
result = null
project = null
projectIndex = null
_this = module.exports
for projectPath, i in atom.project.getPaths()
if goalPath is projectPath or goalPath.indexOf(projectPath + path.sep) is 0
return atom.project.getRepositories()[i]
null
if goalPath.indexOf(projectPath) is 0
project = projectPath
projectIndex = i
# can't find related projects, so repo can't be assigned
return null unless project?
walkUpwards = (startDir, toDir, projectIndex) ->
if fs.existsSync(startDir + '/.git')
for provider in atom.project.repositoryProviders
if _this.repositoryCache[startDir]
return _this.repositoryCache[startDir]
for dProvider in atom.project.directoryProviders
break if directory = dProvider.directoryForURISync(startDir)
directory ?= atom.project.defaultDirectoryProvider.directoryForURISync(startDir)
repo = GitRepository.open(startDir, {project: provider.project, \
refreshOnWindowFocus: atom.config.get('tree-view.refreshVcsStatusOnFocusChange') > _this.getRepoCacheSize()})
return null unless repo
repo.onDidDestroy( ->
delete _this.repositoryCache[startDir]
indexToRemove = null
for dir, i in atom.project.getDirectories()
if startDir is dir.getPath()
indexToRemove = i
break
atom.project.rootDirectories.splice(indexToRemove, 1)
atom.project.repositories.splice(indexToRemove, 1)
)
existsInAtom = false
for dir in atom.project.rootDirectories
if dir.getRealPathSync() is directory.getRealPathSync()
existsInAtom = true
break
if not existsInAtom
atom.project.repositories.splice(0, 0, repo)
atom.project.rootDirectories.splice(0, 0, directory)
_this.fakeProjectRoots.push(startDir)
_this.repositoryCache[startDir] = repo
return repo
if startDir is toDir
# top of project
if atom.project.getRepositories()[projectIndex]
return atom.project.getRepositories()[projectIndex]
return null
dirName = path.dirname(startDir)
return null if dirName is startDir # reached top
return walkUpwards(dirName, project, projectIndex)
return walkUpwards(path.normalize(goalPath), project, projectIndex)

getStyleObject: (el) ->
styleProperties = window.getComputedStyle(el)
Expand Down
6 changes: 4 additions & 2 deletions lib/tree-view-package.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const {Disposable, CompositeDisposable} = require('atom')

const helpers = require('./helpers')
const getIconServices = require('./get-icon-services')
const TreeView = require('./tree-view')

Expand All @@ -17,7 +17,8 @@ class TreeViewPackage {
'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()
'tree-view:show-current-file-in-file-manager': () => this.getTreeViewInstance().showCurrentFileInFileManager(),
'tree-view:refresh-vcs-status': () => this.getTreeViewInstance().refreshVcsStatus()
}))

const treeView = this.getTreeViewInstance()
Expand All @@ -32,6 +33,7 @@ class TreeViewPackage {
this.disposables.dispose()
await this.treeViewOpenPromise // Wait for Tree View to finish opening before destroying it
if (this.treeView) this.treeView.destroy()
helpers.resetRepoCache()
this.treeView = null
}

Expand Down
12 changes: 11 additions & 1 deletion lib/tree-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ path = require 'path'

_ = require 'underscore-plus'
{BufferedProcess, CompositeDisposable, Emitter} = require 'atom'
{repoForPath, getStyleObject, getFullExtension} = require "./helpers"
{repoForPath, getStyleObject, getFullExtension, isFakeProjectRoot} = require "./helpers"
fs = require 'fs-plus'

AddDialog = require './add-dialog'
Expand Down Expand Up @@ -237,6 +237,7 @@ class TreeView
'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)
'tree-view:refresh-folder-vcs-status': (e) => @refreshVcsStatus(e)

[0..8].forEach (index) =>
atom.commands.add @element, "tree-view:open-selected-entry-in-pane-#{index + 1}", =>
Expand All @@ -258,6 +259,15 @@ class TreeView
@disposables.add atom.config.onDidChange 'tree-view.squashDirectoryNames', =>
@updateRoots()

refreshVcsStatus: (e) ->
unless e
refreshFrom = @list.querySelectorAll('.project-root')
else
refreshFrom = [@selectedEntry()]
for refreshPoint in refreshFrom
if refreshPoint? and refreshPoint.refreshRepoStatus
refreshPoint.refreshRepoStatus(true, includeCollapsed = true)

toggle: ->
atom.workspace.toggle(this)

Expand Down
3 changes: 3 additions & 0 deletions menus/tree-view.cson
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
{'label': 'Add Project Folder', 'command': 'application:add-project-folder'}
{'type': 'separator'}

{'label': 'Refresh VCS Status', 'command': 'tree-view:refresh-folder-vcs-status'}
{'type': 'separator'}

{'label': 'Copy Full Path', 'command': 'tree-view:copy-full-path'}
{'label': 'Copy Project Path', 'command': 'tree-view:copy-project-path'}
{'label': 'Open in New Window', 'command': 'tree-view:open-in-new-window'}
Expand Down
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@
"type": "boolean",
"default": false,
"description": "When opening a file, always focus an already-existing view of the file even if it's in a another pane."
},
"refreshVcsStatusOnFocusChange": {
"title": "Refresh VCS Status On Focus Change for first N repos it met",
"type": "integer",
"default": 1,
"description": "Refresh VCS Status when focus of Atom editor changes of first N repos. In case of many nested repos Atom can be freezing, so consider this value to be low."
},
"refreshVcsStatusOnProjectOpen": {
"title": "Refresh VCS Status On Project Open for first N repos it met",
"type": "integer",
"default": 10,
"description": "Refresh VCS Status once Atom project is opened of first N repos. Can decrease start-up time if amount of repositories and the option number are high."
}
}
}
Loading