Async buffer events

This commit is contained in:
Daniel Hengeveld
2015-10-20 16:01:35 +02:00
parent 60180677a2
commit 2ff283a6b0
3 changed files with 101 additions and 20 deletions

View File

@@ -332,7 +332,11 @@ describe "GitRepositoryAsync", ->
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
xdescribe "buffer events", ->
# This tests the async implementation's events directly, but ultimately I
# think we want users to just be able to subscribe to events on GitRepository
# and have them bubble up from async-land
describe "buffer events", ->
[editor] = []
beforeEach ->
@@ -345,32 +349,57 @@ describe "GitRepositoryAsync", ->
editor.insertNewline()
statusHandler = jasmine.createSpy('statusHandler')
atom.project.getRepositories()[0].onDidChangeStatus statusHandler
repo = atom.project.getRepositories()[0]
repo.async.onDidChangeStatus statusHandler
editor.save()
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
waitsFor ->
statusHandler.callCount == 1
runs ->
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
it "emits a status-changed event when a buffer is reloaded", ->
fs.writeFileSync(editor.getPath(), 'changed')
statusHandler = jasmine.createSpy('statusHandler')
atom.project.getRepositories()[0].onDidChangeStatus statusHandler
atom.project.getRepositories()[0].async.onDidChangeStatus statusHandler
editor.getBuffer().reload()
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
editor.getBuffer().reload()
expect(statusHandler.callCount).toBe 1
reloadHandler = jasmine.createSpy 'reloadHandler'
waitsFor ->
statusHandler.callCount == 1
runs ->
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
buffer = editor.getBuffer()
buffer.onDidReload(reloadHandler)
buffer.reload()
waitsFor ->
reloadHandler.callCount == 1
runs ->
expect(statusHandler.callCount).toBe 1
it "emits a status-changed event when a buffer's path changes", ->
fs.writeFileSync(editor.getPath(), 'changed')
statusHandler = jasmine.createSpy('statusHandler')
atom.project.getRepositories()[0].onDidChangeStatus statusHandler
atom.project.getRepositories()[0].async.onDidChangeStatus statusHandler
editor.getBuffer().emitter.emit 'did-change-path'
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
editor.getBuffer().emitter.emit 'did-change-path'
expect(statusHandler.callCount).toBe 1
waitsFor ->
statusHandler.callCount == 1
runs ->
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
pathHandler = jasmine.createSpy('pathHandler')
buffer = editor.getBuffer()
buffer.onDidChangePath pathHandler
buffer.emitter.emit 'did-change-path'
waitsFor ->
pathHandler.callCount == 1
runs ->
expect(statusHandler.callCount).toBe 1
it "stops listening to the buffer when the repository is destroyed (regression)", ->
atom.project.getRepositories()[0].destroy()

View File

@@ -13,18 +13,35 @@ const GitUtils = require('git-utils')
const _ = require('underscore-plus')
module.exports = class GitRepositoryAsync {
static open (path) {
static open (path, options = {}) {
// QUESTION: Should this wrap Git.Repository and reject with a nicer message?
return new GitRepositoryAsync(path)
return new GitRepositoryAsync(path, options)
}
constructor (path) {
constructor (path, options) {
this.repo = null
this.emitter = new Emitter()
this.subscriptions = new CompositeDisposable()
this.pathStatusCache = {}
this._gitUtilsRepo = GitUtils.open(path) // TODO remove after porting ::relativize
this.repoPromise = Git.Repository.open(path)
var {project, refreshOnWindowFocus} = options
this.project = project
if (refreshOnWindowFocus === undefined) {
refreshOnWindowFocus = true
}
if (refreshOnWindowFocus) {
// TODO
}
if (this.project) {
this.subscriptions.add(this.project.onDidAddBuffer((buffer) => {
this.subscribeToBuffer(buffer)
}))
this.project.getBuffers().forEach((buffer) => { this.subscribeToBuffer(buffer) })
}
}
destroy () {
@@ -73,6 +90,7 @@ module.exports = class GitRepositoryAsync {
// Returns a Promise that resolves to the status bit of a given path if it has
// one, otherwise 'current'.
getPathStatus (_path) {
console.log('getting path status for', _path)
var relativePath = this._gitUtilsRepo.relativize(_path)
return this.repoPromise.then((repo) => {
return this._filterStatusesByPath(_path)
@@ -80,6 +98,7 @@ module.exports = class GitRepositoryAsync {
var cachedStatus = this.pathStatusCache[relativePath] || 0
var status = statuses[0] ? statuses[0].statusBit() : Git.Status.STATUS.CURRENT
if (status !== cachedStatus) {
console.log('async emitting', {path: _path, pathStatus: status})
this.emitter.emit('did-change-status', {path: _path, pathStatus: status})
}
this.pathStatusCache[relativePath] = status
@@ -134,8 +153,37 @@ module.exports = class GitRepositoryAsync {
})
}
// Utility functions
// =================
// Section: Private
// ================
subscribeToBuffer (buffer) {
var getBufferPathStatus = () => {
var _path = buffer.getPath()
var bufferSubscriptions = new CompositeDisposable()
if (_path) {
// We don't need to do anything with this promise, we just want the
// emitted event side effect
this.getPathStatus(_path)
}
bufferSubscriptions.add(
buffer.onDidSave(getBufferPathStatus),
buffer.onDidReload(getBufferPathStatus),
buffer.onDidChangePath(getBufferPathStatus)
)
bufferSubscriptions.add(() => {
buffer.onDidDestroy(() => {
bufferSubscriptions.dispose()
this.subscriptions.remove(bufferSubscriptions)
})
})
this.subscriptions.add(bufferSubscriptions)
return
}
}
getCachedPathStatus (_path) {
return this.pathStatusCache[this._gitUtilsRepo.relativize(_path)]

View File

@@ -76,7 +76,7 @@ class GitRepository
unless @repo?
throw new Error("No Git repository found searching path: #{path}")
@async = GitRepositoryAsync.open(path)
@async = GitRepositoryAsync.open(path, options)
@statuses = {}
@upstream = {ahead: 0, behind: 0}
@@ -319,6 +319,9 @@ class GitRepository
# Returns a {Number} representing the status. This value can be passed to
# {::isStatusModified} or {::isStatusNew} to get more information.
getPathStatus: (path) ->
# Trigger events emitted on the async repo as well
@async.getPathStatus(path)
repo = @getRepo(path)
relativePath = @relativize(path)
currentPathStatus = @statuses[relativePath] ? 0
@@ -479,6 +482,7 @@ class GitRepository
# Refreshes the current git status in an outside process and asynchronously
# updates the relevant properties.
refreshStatus: ->
@async.refreshStatus()
@handlerPath ?= require.resolve('./repository-status-handler')
@statusTask?.terminate()