mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Use the CommandRegistry to register activation event listeners
Commands registered with the command registry will always be handled first, so as long as we disable any listeners in the registry that were already invoked for the current command, we don’t need to disable jQuery methods before replaying the command after activating the package. This commit adds the ability to call .disableInvokedListeners on the event passed to the command listeners. This returns a function which can be called to reenable them.
This commit is contained in:
@@ -93,7 +93,7 @@ describe "PackageManager", ->
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe "when the package metadata includes activation events", ->
|
||||
[mainModule, promise] = []
|
||||
[mainModule, promise, workspaceCommandListener] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
@@ -103,6 +103,9 @@ describe "PackageManager", ->
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
spyOn(Package.prototype, 'requireMainModule').andCallThrough()
|
||||
|
||||
workspaceCommandListener = jasmine.createSpy('workspaceCommandListener')
|
||||
atom.commands.add '.workspace', 'activation-event', workspaceCommandListener
|
||||
|
||||
promise = atom.packages.activatePackage('package-with-activation-events')
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
@@ -118,21 +121,23 @@ describe "PackageManager", ->
|
||||
|
||||
runs ->
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("activation event handler")
|
||||
globalCommandHandler = jasmine.createSpy("activation global command handler")
|
||||
editorView.command 'activation-event', eventHandler
|
||||
commandDisposable = atom.commands.add '.workspace', 'activation-event', globalCommandHandler
|
||||
legacyCommandListener = jasmine.createSpy("legacyCommandListener")
|
||||
editorView.command 'activation-event', legacyCommandListener
|
||||
editorCommandListener = jasmine.createSpy("editorCommandListener")
|
||||
atom.commands.add '.editor', 'activation-event', editorCommandListener
|
||||
editorView[0].dispatchEvent(new CustomEvent('activation-event', bubbles: true))
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(mainModule.activationEventCallCount).toBe 1
|
||||
expect(mainModule.activationCommandCallCount).toBe 1
|
||||
expect(eventHandler.callCount).toBe 1
|
||||
expect(globalCommandHandler.callCount).toBe 1
|
||||
expect(legacyCommandListener.callCount).toBe 1
|
||||
expect(editorCommandListener.callCount).toBe 1
|
||||
expect(workspaceCommandListener.callCount).toBe 1
|
||||
editorView[0].dispatchEvent(new CustomEvent('activation-event', bubbles: true))
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(mainModule.activationCommandCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(globalCommandHandler.callCount).toBe 2
|
||||
expect(legacyCommandListener.callCount).toBe 2
|
||||
expect(editorCommandListener.callCount).toBe 2
|
||||
expect(workspaceCommandListener.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
it "activates the package immediately when the events are empty", ->
|
||||
|
||||
@@ -137,25 +137,6 @@ class CommandRegistry
|
||||
|
||||
commands
|
||||
|
||||
disableCommands: (target, commandName) ->
|
||||
if @rootNode.contains(target)
|
||||
currentTarget = target
|
||||
else
|
||||
currentTarget = @rootNode
|
||||
|
||||
disabledListeners = []
|
||||
loop
|
||||
for listener in @listenersByCommandName[commandName] ? []
|
||||
if currentTarget.webkitMatchesSelector(listener.selector)
|
||||
listener.enabled = false
|
||||
disabledListeners.push(listener)
|
||||
|
||||
break if currentTarget is @rootNode
|
||||
currentTarget = currentTarget.parentNode
|
||||
|
||||
new Disposable =>
|
||||
listener.enabled = true for listener in disabledListeners
|
||||
|
||||
# Public: Simulate the dispatch of a command on a DOM node.
|
||||
#
|
||||
# This can be useful for testing when you want to simulate the invocation of a
|
||||
@@ -184,6 +165,7 @@ class CommandRegistry
|
||||
immediatePropagationStopped = false
|
||||
matched = false
|
||||
currentTarget = originalEvent.target
|
||||
invokedListeners = []
|
||||
|
||||
syntheticEvent = Object.create originalEvent,
|
||||
eventPhase: value: Event.BUBBLING_PHASE
|
||||
@@ -195,6 +177,9 @@ class CommandRegistry
|
||||
originalEvent.stopImmediatePropagation()
|
||||
propagationStopped = true
|
||||
immediatePropagationStopped = true
|
||||
disableInvokedListeners: value: ->
|
||||
listener.enabled = false for listener in invokedListeners
|
||||
-> listener.enabled = true for listener in invokedListeners
|
||||
|
||||
loop
|
||||
matchingListeners =
|
||||
@@ -206,6 +191,7 @@ class CommandRegistry
|
||||
|
||||
for listener in matchingListeners when listener.enabled
|
||||
break if immediatePropagationStopped
|
||||
invokedListeners.push(listener)
|
||||
listener.callback.call(currentTarget, syntheticEvent)
|
||||
|
||||
break unless currentTarget?
|
||||
|
||||
@@ -5,7 +5,7 @@ async = require 'async'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
Q = require 'q'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
@@ -270,7 +270,7 @@ class Package
|
||||
deactivate: ->
|
||||
@activationDeferred?.reject()
|
||||
@activationDeferred = null
|
||||
@unsubscribeFromActivationEvents()
|
||||
@activationCommandSubscriptions?.dispose()
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
if @mainActivated
|
||||
@@ -333,51 +333,32 @@ class Package
|
||||
|
||||
subscribeToActivationEvents: ->
|
||||
return unless @metadata.activationEvents?
|
||||
|
||||
@activationCommandSubscriptions = new CompositeDisposable
|
||||
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
atom.workspaceView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
for eventName in @metadata.activationEvents
|
||||
@activationCommandSubscriptions.add(
|
||||
atom.commands.add('.workspace', eventName, @handleActivationEvent)
|
||||
)
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
atom.workspaceView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
eventName = @metadata.activationEvents
|
||||
@activationCommandSubscriptions.add(
|
||||
atom.commands.add('.workspace', eventName, @handleActivationEvent)
|
||||
)
|
||||
else
|
||||
atom.workspaceView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
for eventName, selector of @metadata.activationEvents
|
||||
@activationCommandSubscriptions.add(
|
||||
atom.commands.add(selector, eventName, @handleActivationEvent)
|
||||
)
|
||||
|
||||
handleActivationEvent: (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
disabledCommands = atom.commands.disableCommands(event.target, event.type)
|
||||
event.stopImmediatePropagation()
|
||||
@activationCommandSubscriptions.dispose()
|
||||
reenableInvokedListeners = event.disableInvokedListeners()
|
||||
@activateNow()
|
||||
event.target.dispatchEvent(new CustomEvent(event.type, bubbles: true))
|
||||
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
|
||||
disabledCommands.dispose()
|
||||
@unsubscribeFromActivationEvents()
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
unsubscribeFromActivationEvents: ->
|
||||
return unless atom.workspaceView?
|
||||
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
atom.workspaceView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
atom.workspaceView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
atom.workspaceView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
disabledHandler = ->
|
||||
$ ?= require('./space-pen-extensions').$
|
||||
element = $(event.target)
|
||||
while element.length
|
||||
if eventHandlers = element.handlers()?[event.type]
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.disabledHandler = eventHandler.handler
|
||||
eventHandler.handler = disabledHandler
|
||||
bubblePathEventHandlers.push(eventHandler)
|
||||
element = element.parent()
|
||||
bubblePathEventHandlers
|
||||
|
||||
restoreEventHandlersOnBubblePath: (eventHandlers) ->
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.handler = eventHandler.disabledHandler
|
||||
delete eventHandler.disabledHandler
|
||||
reenableInvokedListeners()
|
||||
|
||||
# Does the given module path contain native code?
|
||||
isNativeModule: (modulePath) ->
|
||||
|
||||
Reference in New Issue
Block a user