Merge pull request #15554 from atom/dg-package-async-deactivate

Allow Promises to be returned by a packages deactivate method
This commit is contained in:
Damien Guard
2017-09-09 10:57:25 -07:00
committed by GitHub
12 changed files with 199 additions and 98 deletions

View File

@@ -65,7 +65,7 @@ export default async function ({test, benchmarkPaths}) {
console.log(textualOutput)
}
global.atom.reset()
await global.atom.reset()
}
}

View File

@@ -22,10 +22,12 @@ describe "the `grammars` global", ->
atom.packages.activatePackage('language-git')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
try
temp.cleanupSync()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
try
temp.cleanupSync()
describe ".selectGrammar(filePath)", ->
it "always returns a grammar", ->

View File

@@ -15,8 +15,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-javascript')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
it "returns the minimum indent level for the given row range", ->
@@ -175,8 +177,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-coffee-script')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe ".toggleLineCommentsForBufferRows(start, end)", ->
it "comments/uncomments lines in the given range", ->
@@ -222,8 +226,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-css')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe ".toggleLineCommentsForBufferRows(start, end)", ->
it "comments/uncomments lines in the given range", ->
@@ -274,8 +280,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-css')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe "when commenting lines", ->
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
@@ -294,8 +302,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-xml')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe "when uncommenting lines", ->
it "removes the leading whitespace from the comment end pattern match", ->
@@ -313,8 +323,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-javascript')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
it "maintains cursor buffer position when a folding/unfolding", ->
editor.setCursorBufferPosition([5, 5])
@@ -403,8 +415,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-javascript')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe ".unfoldAll()", ->
it "unfolds every folded line", ->
@@ -481,8 +495,10 @@ describe "LanguageMode", ->
atom.packages.activatePackage('language-css')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
describe "suggestedIndentForBufferRow", ->
it "does not return negative values (regression)", ->

View File

@@ -56,8 +56,10 @@ describe "PackageManager", ->
spyOn(atom.packages, 'loadAvailablePackage')
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
it "sets hasLoadedInitialPackages", ->
expect(atom.packages.hasLoadedInitialPackages()).toBe false
@@ -172,8 +174,10 @@ describe "PackageManager", ->
model2 = {worksWithViewProvider2: true}
afterEach ->
atom.packages.deactivatePackage('package-with-view-providers')
atom.packages.unloadPackage('package-with-view-providers')
waitsForPromise ->
atom.packages.deactivatePackage('package-with-view-providers')
runs ->
atom.packages.unloadPackage('package-with-view-providers')
it "does not load the view providers immediately", ->
pack = atom.packages.loadPackage("package-with-view-providers")
@@ -641,7 +645,11 @@ describe "PackageManager", ->
runs ->
expect(mainModule.activate.callCount).toBe 1
waitsForPromise ->
atom.packages.deactivatePackage('package-with-activation-hooks')
runs ->
promise = atom.packages.activatePackage('package-with-activation-hooks')
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
atom.packages.triggerDeferredActivationHooks()
@@ -704,7 +712,9 @@ describe "PackageManager", ->
expect(pack.mainModule.someNumber).not.toBe 77
pack.mainModule.someNumber = 77
atom.packages.serializePackage("package-with-serialization")
waitsForPromise ->
atom.packages.deactivatePackage("package-with-serialization")
runs ->
spyOn(pack.mainModule, 'activate').andCallThrough()
waitsForPromise ->
atom.packages.activatePackage("package-with-serialization")
@@ -872,6 +882,7 @@ describe "PackageManager", ->
expect(events.length).toBe(1)
expect(events[0].type).toBe("user-command")
waitsForPromise ->
atom.packages.deactivatePackage("package-with-keymaps")
waitsForPromise ->
@@ -1041,12 +1052,15 @@ describe "PackageManager", ->
consumerModule.consumeFirstServiceV4.reset()
consumerModule.consumeSecondService.reset()
waitsForPromise ->
atom.packages.deactivatePackage("package-with-provided-services")
runs ->
expect(firstServiceV3Disposed).toBe true
expect(firstServiceV4Disposed).toBe true
expect(secondServiceDisposed).toBe true
waitsForPromise ->
atom.packages.deactivatePackage("package-with-consumed-services")
waitsForPromise ->
@@ -1112,8 +1126,11 @@ describe "PackageManager", ->
runs ->
spyOn(pack1.mainModule, 'deactivate')
spyOn(pack2.mainModule, 'serialize')
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
expect(pack1.mainModule.deactivate).toHaveBeenCalled()
expect(pack2.mainModule.serialize).not.toHaveBeenCalled()
@@ -1131,7 +1148,10 @@ describe "PackageManager", ->
expect(atom.packages.isPackageActive("package-with-deactivate")).toBeTruthy()
spyOn(pack.mainModule, 'deactivate').andCallThrough()
waitsForPromise ->
atom.packages.deactivatePackage("package-with-deactivate")
runs ->
expect(pack.mainModule.deactivate).toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-with-module")).toBeFalsy()
@@ -1145,26 +1165,38 @@ describe "PackageManager", ->
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
waitsForPromise ->
atom.packages.deactivatePackage("package-that-throws-on-activate")
runs ->
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
it "absorbs exceptions that are thrown by the package module's deactivate method", ->
spyOn(console, 'error')
thrownError = null
waitsForPromise ->
atom.packages.activatePackage("package-that-throws-on-deactivate")
waitsForPromise ->
try
atom.packages.deactivatePackage("package-that-throws-on-deactivate")
catch error
thrownError = error
runs ->
expect(-> atom.packages.deactivatePackage("package-that-throws-on-deactivate")).not.toThrow()
expect(thrownError).toBeNull()
expect(console.error).toHaveBeenCalled()
it "removes the package's grammars", ->
waitsForPromise ->
atom.packages.activatePackage('package-with-grammars')
runs ->
waitsForPromise ->
atom.packages.deactivatePackage('package-with-grammars')
runs ->
expect(atom.grammars.selectGrammar('a.alot').name).toBe 'Null Grammar'
expect(atom.grammars.selectGrammar('a.alittle').name).toBe 'Null Grammar'
@@ -1172,8 +1204,10 @@ describe "PackageManager", ->
waitsForPromise ->
atom.packages.activatePackage('package-with-keymaps')
runs ->
waitsForPromise ->
atom.packages.deactivatePackage('package-with-keymaps')
runs ->
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-1'))).toHaveLength 0
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-2'))).toHaveLength 0
@@ -1181,8 +1215,10 @@ describe "PackageManager", ->
waitsForPromise ->
atom.packages.activatePackage('package-with-styles')
runs ->
waitsForPromise ->
atom.packages.deactivatePackage('package-with-styles')
runs ->
one = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/1.css")
two = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/2.less")
three = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/3.css")
@@ -1196,17 +1232,26 @@ describe "PackageManager", ->
runs ->
expect(atom.config.get 'editor.increaseIndentPattern', scope: ['.source.omg']).toBe '^a'
waitsForPromise ->
atom.packages.deactivatePackage("package-with-settings")
runs ->
expect(atom.config.get 'editor.increaseIndentPattern', scope: ['.source.omg']).toBeUndefined()
it "invokes ::onDidDeactivatePackage listeners with the deactivated package", ->
deactivatedPackage = null
waitsForPromise ->
atom.packages.activatePackage("package-with-main")
runs ->
deactivatedPackage = null
atom.packages.onDidDeactivatePackage (pack) -> deactivatedPackage = pack
waitsForPromise ->
atom.packages.deactivatePackage("package-with-main")
runs ->
expect(deactivatedPackage.name).toBe "package-with-main"
describe "::activate()", ->
@@ -1220,10 +1265,11 @@ describe "PackageManager", ->
expect(loadedPackages.length).toBeGreaterThan 0
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
jasmine.restoreDeprecationsSnapshot()
waitsForPromise ->
atom.packages.deactivatePackages()
runs ->
atom.packages.unloadPackages()
jasmine.restoreDeprecationsSnapshot()
it "sets hasActivatedInitialPackages", ->
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(null)
@@ -1286,6 +1332,9 @@ describe "PackageManager", ->
it "disables an enabled package", ->
packageName = 'package-with-main'
pack = null
activatedPackages = null
waitsForPromise ->
atom.packages.activatePackage(packageName)
@@ -1295,7 +1344,11 @@ describe "PackageManager", ->
pack = atom.packages.disablePackage(packageName)
waitsFor ->
activatedPackages = atom.packages.getActivePackages()
activatedPackages.length is 0
runs ->
expect(activatedPackages).not.toContain(pack)
expect(atom.config.get('core.disabledPackages')).toContain packageName
@@ -1322,7 +1375,8 @@ describe "PackageManager", ->
atom.themes.activateThemes()
afterEach ->
atom.themes.deactivateThemes()
waitsForPromise ->
atom.themes.deactivateThemes()
it "enables and disables a theme", ->
packageName = 'theme-with-package-file'

View File

@@ -138,7 +138,8 @@ describe "Package", ->
jasmine.attachToDOM(editorElement)
afterEach ->
theme.deactivate() if theme?
waitsForPromise ->
Promise.resolve(theme.deactivate()) if theme?
describe "when the theme contains a single style file", ->
it "loads and applies css", ->
@@ -200,8 +201,10 @@ describe "Package", ->
it "deactivated event fires on .deactivate()", ->
theme.onDidDeactivate spy = jasmine.createSpy()
theme.deactivate()
expect(spy).toHaveBeenCalled()
waitsForPromise ->
Promise.resolve(theme.deactivate())
runs ->
expect(spy).toHaveBeenCalled()
describe ".loadMetadata()", ->
[packagePath, metadata] = []

View File

@@ -108,10 +108,14 @@ beforeEach ->
afterEach ->
ensureNoDeprecatedFunctionCalls()
ensureNoDeprecatedStylesheets()
atom.reset()
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
warnIfLeakingPathSubscriptions()
waits(0) # yield to ui thread to make screen update more frequently
waitsForPromise ->
atom.reset()
runs ->
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
warnIfLeakingPathSubscriptions()
waits(0) # yield to ui thread to make screen update more frequently
warnIfLeakingPathSubscriptions = ->
watchedPaths = pathwatcher.getWatchedPaths()

View File

@@ -685,7 +685,7 @@ describe('TextEditorRegistry', function () {
registry.setGrammarOverride(editor, 'source.c')
registry.setGrammarOverride(editor2, 'source.js')
atom.packages.deactivatePackage('language-javascript')
await atom.packages.deactivatePackage('language-javascript')
const editorCopy = TextEditor.deserialize(editor.serialize(), atom)
const editor2Copy = TextEditor.deserialize(editor2.serialize(), atom)

View File

@@ -8,9 +8,11 @@ describe "atom.themes", ->
spyOn(console, 'warn')
afterEach ->
atom.themes.deactivateThemes()
try
temp.cleanupSync()
waitsForPromise ->
atom.themes.deactivateThemes()
runs ->
try
temp.cleanupSync()
describe "theme getters and setters", ->
beforeEach ->

View File

@@ -328,20 +328,14 @@ class AtomEnvironment extends Model
@contextMenu.clear()
@packages.reset()
@workspace.reset(@packages)
@registerDefaultOpeners()
@project.reset(@packages)
@workspace.subscribeToEvents()
@grammars.clear()
@textEditors.clear()
@views.clear()
@packages.reset().then =>
@workspace.reset(@packages)
@registerDefaultOpeners()
@project.reset(@packages)
@workspace.subscribeToEvents()
@grammars.clear()
@textEditors.clear()
@views.clear()
destroy: ->
return if not @project
@@ -702,6 +696,11 @@ class AtomEnvironment extends Model
windowCloseRequested: true,
projectHasPaths: @project.getPaths().length > 0
})
.then (closing) =>
if closing
@packages.deactivatePackages().then -> closing
else
closing
@listenForUpdates()
@@ -758,7 +757,6 @@ class AtomEnvironment extends Model
return if not @project
@storeWindowBackground()
@packages.deactivatePackages()
@saveBlobStoreSync()
@unloaded = true

View File

@@ -77,9 +77,9 @@ module.exports = class PackageManager {
this.themeManager = themeManager
}
reset () {
async reset () {
this.serviceHub.clear()
this.deactivatePackages()
await this.deactivatePackages()
this.loadedPackages = {}
this.preloadedPackages = {}
this.packageStates = {}
@@ -744,21 +744,30 @@ module.exports = class PackageManager {
}
// Deactivate all packages
deactivatePackages () {
this.config.transact(() => {
this.getLoadedPackages().forEach(pack => this.deactivatePackage(pack.name, true))
})
async deactivatePackages () {
await this.config.transactAsync(() =>
Promise.all(this.getLoadedPackages().map(pack => this.deactivatePackage(pack.name, true)))
)
this.unobserveDisabledPackages()
this.unobservePackagesWithKeymapsDisabled()
}
// Deactivate the package with the given name
deactivatePackage (name, suppressSerialization) {
async deactivatePackage (name, suppressSerialization) {
const pack = this.getLoadedPackage(name)
if (pack == null) {
return
}
if (!suppressSerialization && this.isPackageActive(pack.name)) {
this.serializePackage(pack)
}
pack.deactivate()
const deactivationResult = pack.deactivate()
if (deactivationResult && typeof deactivationResult.then === 'function') {
await deactivationResult
}
delete this.activePackages[pack.name]
delete this.activatingPackages[pack.name]
this.emitter.emit('did-deactivate-package', pack)

View File

@@ -506,14 +506,29 @@ class Package
@configSchemaRegisteredOnActivate = false
@deactivateResources()
@deactivateKeymaps()
if @mainActivated
try
@mainModule?.deactivate?()
@mainModule?.deactivateConfig?()
@mainActivated = false
@mainInitialized = false
catch e
console.error "Error deactivating package '#{@name}'", e.stack
unless @mainActivated
@emitter.emit 'did-deactivate'
return
try
deactivationResult = @mainModule?.deactivate?()
catch e
console.error "Error deactivating package '#{@name}'", e.stack
# We support then-able async promises as well as sync ones from deactivate
if deactivationResult?.then is 'function'
deactivationResult.then => @afterDeactivation()
else
@afterDeactivation()
afterDeactivation: ->
try
@mainModule?.deactivateConfig?()
catch e
console.error "Error deactivating package '#{@name}'", e.stack
@mainActivated = false
@mainInitialized = false
@emitter.emit 'did-deactivate'
deactivateResources: ->

View File

@@ -262,33 +262,31 @@ class ThemeManager
new Promise (resolve) =>
# @config.observe runs the callback once, then on subsequent changes.
@config.observe 'core.themes', =>
@deactivateThemes()
@deactivateThemes().then =>
@warnForNonExistentThemes()
@refreshLessCache() # Update cache for packages in core.themes config
@warnForNonExistentThemes()
promises = []
for themeName in @getEnabledThemeNames()
if @packageManager.resolvePackagePath(themeName)
promises.push(@packageManager.activatePackage(themeName))
else
console.warn("Failed to activate theme '#{themeName}' because it isn't installed.")
@refreshLessCache() # Update cache for packages in core.themes config
promises = []
for themeName in @getEnabledThemeNames()
if @packageManager.resolvePackagePath(themeName)
promises.push(@packageManager.activatePackage(themeName))
else
console.warn("Failed to activate theme '#{themeName}' because it isn't installed.")
Promise.all(promises).then =>
@addActiveThemeClasses()
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@initialLoadComplete = true
@emitter.emit 'did-change-active-themes'
resolve()
Promise.all(promises).then =>
@addActiveThemeClasses()
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@initialLoadComplete = true
@emitter.emit 'did-change-active-themes'
resolve()
deactivateThemes: ->
@removeActiveThemeClasses()
@unwatchUserStylesheet()
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
null
results = @getActiveThemes().map((pack) => @packageManager.deactivatePackage(pack.name))
Promise.all(results.filter((r) -> typeof r?.then is 'function'))
isInitialLoadComplete: -> @initialLoadComplete