mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Merge pull request #7638 from atom-community/jf-dispatch-grammar-used
Add activationHooks + Trigger `{grammar-package-name}:grammar-used` Hook When A text-editor-element's Grammar Is Set
This commit is contained in:
5
spec/fixtures/packages/package-with-activation-hooks/index.coffee
vendored
Normal file
5
spec/fixtures/packages/package-with-activation-hooks/index.coffee
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports =
|
||||
activateCallCount: 0
|
||||
|
||||
activate: ->
|
||||
@activateCallCount++
|
||||
5
spec/fixtures/packages/package-with-activation-hooks/package.cson
vendored
Normal file
5
spec/fixtures/packages/package-with-activation-hooks/package.cson
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "package-with-activation-hooks",
|
||||
"version": "0.1.0",
|
||||
"activationHooks": ['language-fictitious:grammar-used']
|
||||
}
|
||||
1
spec/fixtures/packages/package-with-empty-activation-hooks/index.coffee
vendored
Normal file
1
spec/fixtures/packages/package-with-empty-activation-hooks/index.coffee
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = activate: ->
|
||||
5
spec/fixtures/packages/package-with-empty-activation-hooks/package.json
vendored
Normal file
5
spec/fixtures/packages/package-with-empty-activation-hooks/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "package-with-empty-activation-hooks",
|
||||
"version": "0.1.0",
|
||||
"activationHooks": []
|
||||
}
|
||||
@@ -189,7 +189,7 @@ describe "PackageManager", ->
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe "when the package metadata includes `activationCommands`", ->
|
||||
[mainModule, promise, workspaceCommandListener] = []
|
||||
[mainModule, promise, workspaceCommandListener, registration] = []
|
||||
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
@@ -200,10 +200,14 @@ describe "PackageManager", ->
|
||||
spyOn(Package.prototype, 'requireMainModule').andCallThrough()
|
||||
|
||||
workspaceCommandListener = jasmine.createSpy('workspaceCommandListener')
|
||||
atom.commands.add '.workspace', 'activation-command', workspaceCommandListener
|
||||
registration = atom.commands.add '.workspace', 'activation-command', workspaceCommandListener
|
||||
|
||||
promise = atom.packages.activatePackage('package-with-activation-commands')
|
||||
|
||||
afterEach ->
|
||||
registration?.dispose()
|
||||
mainModule = null
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
workspaceElement.dispatchEvent(new CustomEvent('activation-command', bubbles: true))
|
||||
@@ -286,6 +290,41 @@ describe "PackageManager", ->
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-with-invalid-settings package settings")
|
||||
|
||||
describe "when the package metadata includes `activationHooks`", ->
|
||||
[mainModule, promise] = []
|
||||
|
||||
beforeEach ->
|
||||
mainModule = require './fixtures/packages/package-with-activation-hooks/index'
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
spyOn(Package.prototype, 'requireMainModule').andCallThrough()
|
||||
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
|
||||
it "defers requiring/activating the main module until an triggering of an activation hook occurs", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
|
||||
runs ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
it "activates the package immediately when activationHooks is empty", ->
|
||||
mainModule = require './fixtures/packages/package-with-empty-activation-hooks/index'
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
|
||||
runs ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-empty-activation-hooks')
|
||||
|
||||
runs ->
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
describe "when the package has no main module", ->
|
||||
it "does not throw an exception", ->
|
||||
spyOn(console, "error")
|
||||
@@ -750,8 +789,7 @@ describe "PackageManager", ->
|
||||
package1 = atom.packages.loadPackage('package-with-main')
|
||||
package2 = atom.packages.loadPackage('package-with-index')
|
||||
package3 = atom.packages.loadPackage('package-with-activation-commands')
|
||||
spyOn(atom.packages, 'getLoadedPackages').andReturn([package1, package2])
|
||||
|
||||
spyOn(atom.packages, 'getLoadedPackages').andReturn([package1, package2, package3])
|
||||
activateSpy = jasmine.createSpy('activateSpy')
|
||||
atom.packages.onDidActivateInitialPackages(activateSpy)
|
||||
|
||||
|
||||
@@ -895,3 +895,57 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].value).toBe 'b'
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens.length).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].value).toBe 'c'
|
||||
|
||||
describe 'when a file is opened', ->
|
||||
[registration, editor, called] = []
|
||||
beforeEach ->
|
||||
runs ->
|
||||
called = false
|
||||
registration = atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', -> called = true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
afterEach: ->
|
||||
registration?.dispose?()
|
||||
atom.packages.deactivatePackages()
|
||||
atom.packages.unloadPackages()
|
||||
|
||||
it 'triggers the grammar-used hook', ->
|
||||
waitsFor ->
|
||||
called is true
|
||||
|
||||
runs ->
|
||||
expect(called).toBe true
|
||||
|
||||
describe 'when changing the grammar of an open file', ->
|
||||
[coffeeRegistration, coffeeCalled] = []
|
||||
|
||||
beforeEach ->
|
||||
coffeeCalled = false
|
||||
coffeeRegistration = atom.packages.onDidTriggerActivationHook('language-coffee-script:grammar-used', -> coffeeCalled = true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
afterEach ->
|
||||
coffeeRegistration?.dispose()
|
||||
|
||||
it 'triggers the grammar-used hook', ->
|
||||
waitsFor ->
|
||||
called is true
|
||||
|
||||
runs ->
|
||||
expect(called).toBe true
|
||||
expect(coffeeCalled).toBe false
|
||||
editor.setGrammar(atom.grammars.selectGrammar('.coffee'))
|
||||
|
||||
waitsFor ->
|
||||
coffeeCalled is true
|
||||
|
||||
runs ->
|
||||
expect(coffeeCalled).toBe true
|
||||
|
||||
@@ -30,6 +30,7 @@ module.exports =
|
||||
class PackageManager
|
||||
constructor: ({configDirPath, @devMode, safeMode, @resourcePath}) ->
|
||||
@emitter = new Emitter
|
||||
@activationHookEmitter = new Emitter
|
||||
@packageDirPaths = []
|
||||
unless safeMode
|
||||
if @devMode
|
||||
@@ -409,6 +410,14 @@ class PackageManager
|
||||
else
|
||||
Q.reject(new Error("Failed to load package '#{name}'"))
|
||||
|
||||
triggerActivationHook: (hook) ->
|
||||
return new Error("Cannot trigger an empty activation hook") unless hook? and _.isString(hook) and hook.length > 0
|
||||
@activationHookEmitter.emit(hook)
|
||||
|
||||
onDidTriggerActivationHook: (hook, callback) ->
|
||||
return unless hook? and _.isString(hook) and hook.length > 0
|
||||
@activationHookEmitter.on(hook, callback)
|
||||
|
||||
# Deactivate all packages
|
||||
deactivatePackages: ->
|
||||
atom.config.transact =>
|
||||
|
||||
@@ -67,6 +67,7 @@ class Package
|
||||
mainModulePath: null
|
||||
resolvedMainModulePath: false
|
||||
mainModule: null
|
||||
mainActivated: false
|
||||
|
||||
###
|
||||
Section: Construction
|
||||
@@ -122,7 +123,7 @@ class Package
|
||||
@loadMenus()
|
||||
@loadStylesheets()
|
||||
@settingsPromise = @loadSettings()
|
||||
@requireMainModule() unless @hasActivationCommands()
|
||||
@requireMainModule() unless @mainModule? or @activationShouldBeDeferred()
|
||||
catch error
|
||||
@handleError("Failed to load the #{@name} package", error)
|
||||
this
|
||||
@@ -133,6 +134,7 @@ class Package
|
||||
@menus = []
|
||||
@grammars = []
|
||||
@settings = []
|
||||
@mainActivated = false
|
||||
|
||||
activate: ->
|
||||
@grammarsPromise ?= @loadGrammars()
|
||||
@@ -142,8 +144,8 @@ class Package
|
||||
@measure 'activateTime', =>
|
||||
try
|
||||
@activateResources()
|
||||
if @hasActivationCommands()
|
||||
@subscribeToActivationCommands()
|
||||
if @activationShouldBeDeferred()
|
||||
@subscribeToDeferredActivation()
|
||||
else
|
||||
@activateNow()
|
||||
catch error
|
||||
@@ -155,7 +157,7 @@ class Package
|
||||
try
|
||||
@activateConfig()
|
||||
@activateStylesheets()
|
||||
if @requireMainModule()
|
||||
if @mainModule? and not @mainActivated
|
||||
@mainModule.activate?(atom.packages.getPackageState(@name) ? {})
|
||||
@mainActivated = true
|
||||
@activateServices()
|
||||
@@ -167,7 +169,7 @@ class Package
|
||||
activateConfig: ->
|
||||
return if @configActivated
|
||||
|
||||
@requireMainModule()
|
||||
@requireMainModule() unless @mainModule?
|
||||
if @mainModule?
|
||||
if @mainModule.config? and typeof @mainModule.config is 'object'
|
||||
atom.config.setSchema @name, {type: 'object', properties: @mainModule.config}
|
||||
@@ -382,6 +384,7 @@ class Package
|
||||
if @mainActivated
|
||||
try
|
||||
@mainModule?.deactivate?()
|
||||
@mainActivated = false
|
||||
catch e
|
||||
console.error "Error deactivating package '#{@name}'", e.stack
|
||||
@emit 'deactivated' if includeDeprecatedAPIs
|
||||
@@ -443,11 +446,21 @@ class Package
|
||||
path.join(@path, 'index')
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
|
||||
activationShouldBeDeferred: ->
|
||||
@hasActivationCommands() or @hasActivationHooks()
|
||||
|
||||
hasActivationHooks: ->
|
||||
@getActivationHooks()?.length > 0
|
||||
|
||||
hasActivationCommands: ->
|
||||
for selector, commands of @getActivationCommands()
|
||||
return true if commands.length > 0
|
||||
false
|
||||
|
||||
subscribeToDeferredActivation: ->
|
||||
@subscribeToActivationCommands()
|
||||
@subscribeToActivationHooks()
|
||||
|
||||
subscribeToActivationCommands: ->
|
||||
@activationCommandSubscriptions = new CompositeDisposable
|
||||
for selector, commands of @getActivationCommands()
|
||||
@@ -516,6 +529,27 @@ class Package
|
||||
|
||||
@activationCommands
|
||||
|
||||
subscribeToActivationHooks: ->
|
||||
@activationHookSubscriptions = new CompositeDisposable
|
||||
for hook in @getActivationHooks()
|
||||
do (hook) =>
|
||||
@activationHookSubscriptions.add(atom.packages.onDidTriggerActivationHook(hook, => @activateNow())) if hook? and _.isString(hook) and hook.trim().length > 0
|
||||
|
||||
return
|
||||
|
||||
getActivationHooks: ->
|
||||
return @activationHooks if @metadata? and @activationHooks?
|
||||
|
||||
@activationHooks = []
|
||||
|
||||
if @metadata.activationHooks?
|
||||
if _.isArray(@metadata.activationHooks)
|
||||
@activationHooks.push(@metadata.activationHooks...)
|
||||
else if _.isString(@metadata.activationHooks)
|
||||
@activationHooks.push(@metadata.activationHooks)
|
||||
|
||||
@activationHooks = _.uniq(@activationHooks)
|
||||
|
||||
# Does the given module path contain native code?
|
||||
isNativeModule: (modulePath) ->
|
||||
try
|
||||
|
||||
@@ -154,8 +154,7 @@ class TextEditorElement extends HTMLElement
|
||||
@component.focused() if event.relatedTarget is this
|
||||
|
||||
addGrammarScopeAttribute: ->
|
||||
grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
||||
@dataset.grammar = grammarScope
|
||||
@dataset.grammar = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
||||
|
||||
addMiniAttribute: ->
|
||||
@setAttributeNode(document.createAttribute("mini"))
|
||||
|
||||
@@ -2861,7 +2861,6 @@ class TextEditor extends Model
|
||||
|
||||
handleGrammarChange: ->
|
||||
@unfoldAll()
|
||||
@emit 'grammar-changed' if includeDeprecatedAPIs
|
||||
@emitter.emit 'did-change-grammar', @getGrammar()
|
||||
|
||||
handleMarkerCreated: (marker) =>
|
||||
|
||||
@@ -72,7 +72,7 @@ class TokenizedBuffer extends Model
|
||||
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
|
||||
|
||||
setGrammar: (grammar, score) ->
|
||||
return if grammar is @grammar
|
||||
return unless grammar? and grammar isnt @grammar
|
||||
|
||||
@grammar = grammar
|
||||
@rootScopeDescriptor = new ScopeDescriptor(scopes: [@grammar.scopeName])
|
||||
@@ -103,8 +103,7 @@ class TokenizedBuffer extends Model
|
||||
@disposables.add(@configSubscriptions)
|
||||
|
||||
@retokenizeLines()
|
||||
|
||||
@emit 'grammar-changed', grammar if Grim.includeDeprecatedAPIs
|
||||
atom.packages.triggerActivationHook("#{grammar.packageName}:grammar-used")
|
||||
@emitter.emit 'did-change-grammar', grammar
|
||||
|
||||
getGrammarSelectionContent: ->
|
||||
|
||||
Reference in New Issue
Block a user