From ade1ef7a4cd275edd81e4ae3a58948891a7e16ef Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 18 Nov 2015 13:59:17 -0800 Subject: [PATCH 01/13] Load deserializers from files specified in package.json --- .../packages/package-with-deserializers/package.json | 7 +++++++ .../package-with-deserializers/the-deserializer.js | 6 ++++++ spec/package-manager-spec.coffee | 10 ++++++++++ src/package.coffee | 7 +++++++ 4 files changed, 30 insertions(+) create mode 100644 spec/fixtures/packages/package-with-deserializers/package.json create mode 100644 spec/fixtures/packages/package-with-deserializers/the-deserializer.js diff --git a/spec/fixtures/packages/package-with-deserializers/package.json b/spec/fixtures/packages/package-with-deserializers/package.json new file mode 100644 index 000000000..867457e0a --- /dev/null +++ b/spec/fixtures/packages/package-with-deserializers/package.json @@ -0,0 +1,7 @@ +{ + "name": "package-with-deserializers", + "version": "1.0.0", + "atom-deserializers": { + "TheDeserializerName": "./the-deserializer.js" + } +} diff --git a/spec/fixtures/packages/package-with-deserializers/the-deserializer.js b/spec/fixtures/packages/package-with-deserializers/the-deserializer.js new file mode 100644 index 000000000..d70e62bfd --- /dev/null +++ b/spec/fixtures/packages/package-with-deserializers/the-deserializer.js @@ -0,0 +1,6 @@ +module.exports = function (state) { + return { + wasDeserializedBy: 'TheDeserializer', + state: state + } +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 4b5f3c26d..59e076834 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -79,6 +79,16 @@ describe "PackageManager", -> expect(loadedPackage.name).toBe "package-with-main" + it "registers any deserializers specified in the package's package.json", -> + atom.packages.loadPackage("package-with-deserializers") + + state = {deserializer: 'TheDeserializerName', a: 'b'} + + expect(atom.deserializers.deserialize(state)).toEqual { + wasDeserializedBy: 'TheDeserializer' + state: state + } + describe "::unloadPackage(name)", -> describe "when the package is active", -> it "throws an error", -> diff --git a/src/package.coffee b/src/package.coffee index 4cd6a18fd..b40cabac3 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -84,6 +84,7 @@ class Package @loadKeymaps() @loadMenus() @loadStylesheets() + @loadDeserializers() @settingsPromise = @loadSettings() @requireMainModule() unless @mainModule? or @activationShouldBeDeferred() catch error @@ -253,6 +254,12 @@ class Package @stylesheets = @getStylesheetPaths().map (stylesheetPath) => [stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)] + loadDeserializers: -> + for name, implementationPath of @metadata['atom-deserializers'] + deserialize = require(path.join(@path, implementationPath)) + atom.deserializers.add({name, deserialize}) + return + getStylesheetsPath: -> path.join(@path, 'styles') From 46272cd1927aac453a69397ffa6675c506ba3ac8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 18 Nov 2015 14:31:16 -0800 Subject: [PATCH 02/13] Load deserializers lazily --- .../{the-deserializer.js => deserializer-1.js} | 2 +- .../package-with-deserializers/deserializer-2.js | 6 ++++++ .../package-with-deserializers/package.json | 3 ++- spec/package-manager-spec.coffee | 13 +++++++++---- src/package.coffee | 10 ++++++++-- 5 files changed, 26 insertions(+), 8 deletions(-) rename spec/fixtures/packages/package-with-deserializers/{the-deserializer.js => deserializer-1.js} (62%) create mode 100644 spec/fixtures/packages/package-with-deserializers/deserializer-2.js diff --git a/spec/fixtures/packages/package-with-deserializers/the-deserializer.js b/spec/fixtures/packages/package-with-deserializers/deserializer-1.js similarity index 62% rename from spec/fixtures/packages/package-with-deserializers/the-deserializer.js rename to spec/fixtures/packages/package-with-deserializers/deserializer-1.js index d70e62bfd..f4d7a1488 100644 --- a/spec/fixtures/packages/package-with-deserializers/the-deserializer.js +++ b/spec/fixtures/packages/package-with-deserializers/deserializer-1.js @@ -1,6 +1,6 @@ module.exports = function (state) { return { - wasDeserializedBy: 'TheDeserializer', + wasDeserializedBy: 'Deserializer1', state: state } } diff --git a/spec/fixtures/packages/package-with-deserializers/deserializer-2.js b/spec/fixtures/packages/package-with-deserializers/deserializer-2.js new file mode 100644 index 000000000..3099d2b15 --- /dev/null +++ b/spec/fixtures/packages/package-with-deserializers/deserializer-2.js @@ -0,0 +1,6 @@ +module.exports = function (state) { + return { + wasDeserializedBy: 'Deserializer2', + state: state + } +} diff --git a/spec/fixtures/packages/package-with-deserializers/package.json b/spec/fixtures/packages/package-with-deserializers/package.json index 867457e0a..377a5faf8 100644 --- a/spec/fixtures/packages/package-with-deserializers/package.json +++ b/spec/fixtures/packages/package-with-deserializers/package.json @@ -2,6 +2,7 @@ "name": "package-with-deserializers", "version": "1.0.0", "atom-deserializers": { - "TheDeserializerName": "./the-deserializer.js" + "Deserializer1": "./deserializer-1.js", + "Deserializer2": "./deserializer-2.js" } } diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 59e076834..843e9a932 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -82,11 +82,16 @@ describe "PackageManager", -> it "registers any deserializers specified in the package's package.json", -> atom.packages.loadPackage("package-with-deserializers") - state = {deserializer: 'TheDeserializerName', a: 'b'} + state1 = {deserializer: 'Deserializer1', a: 'b'} + expect(atom.deserializers.deserialize(state1)).toEqual { + wasDeserializedBy: 'Deserializer1' + state: state1 + } - expect(atom.deserializers.deserialize(state)).toEqual { - wasDeserializedBy: 'TheDeserializer' - state: state + state2 = {deserializer: 'Deserializer2', c: 'd'} + expect(atom.deserializers.deserialize(state2)).toEqual { + wasDeserializedBy: 'Deserializer2' + state: state2 } describe "::unloadPackage(name)", -> diff --git a/src/package.coffee b/src/package.coffee index b40cabac3..f933f93c3 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -256,8 +256,14 @@ class Package loadDeserializers: -> for name, implementationPath of @metadata['atom-deserializers'] - deserialize = require(path.join(@path, implementationPath)) - atom.deserializers.add({name, deserialize}) + do => + deserializePath = path.join(@path, implementationPath) + deserializeFunction = null + atom.deserializers.add + name: name, + deserialize: -> + deserializeFunction ?= require(deserializePath) + deserializeFunction.apply(this, arguments) return getStylesheetsPath: -> From a0a402c3f89be74fa90569f591a46708c41a6258 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 19 Nov 2015 14:12:03 -0800 Subject: [PATCH 03/13] Remove 'atom' prefix from deserializers package.json key --- spec/fixtures/packages/package-with-deserializers/package.json | 2 +- src/package.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/packages/package-with-deserializers/package.json b/spec/fixtures/packages/package-with-deserializers/package.json index 377a5faf8..c55e1444a 100644 --- a/spec/fixtures/packages/package-with-deserializers/package.json +++ b/spec/fixtures/packages/package-with-deserializers/package.json @@ -1,7 +1,7 @@ { "name": "package-with-deserializers", "version": "1.0.0", - "atom-deserializers": { + "deserializers": { "Deserializer1": "./deserializer-1.js", "Deserializer2": "./deserializer-2.js" } diff --git a/src/package.coffee b/src/package.coffee index f933f93c3..a49c70868 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -255,7 +255,7 @@ class Package [stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)] loadDeserializers: -> - for name, implementationPath of @metadata['atom-deserializers'] + for name, implementationPath of @metadata.deserializers do => deserializePath = path.join(@path, implementationPath) deserializeFunction = null From 91b651e86c52f48ed2911158369ea8b434ec987c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 19 Nov 2015 15:54:47 -0800 Subject: [PATCH 04/13] Make model constructor argument to addViewProvider optional --- spec/view-registry-spec.coffee | 15 ++++++++ src/view-registry.coffee | 66 ++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index a2b4965a5..16672b25d 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -47,6 +47,21 @@ describe "ViewRegistry", -> expect(view2 instanceof TestView).toBe true expect(view2.model).toBe subclassModel + describe "when a view provider is registered generically, and works with the object", -> + it "constructs a view element and assigns the model on it", -> + model = {a: 'b'} + + registry.addViewProvider (model) -> + if model.a is 'b' + element = document.createElement('div') + element.className = 'test-element' + element + + view = registry.getView({a: 'b'}) + expect(view.className).toBe 'test-element' + + expect(-> registry.getView({a: 'c'})).toThrow() + describe "when no view provider is registered for the object's constructor", -> it "throws an exception", -> expect(-> registry.getView(new Object)).toThrow() diff --git a/src/view-registry.coffee b/src/view-registry.coffee index 0f07600ae..e754a010b 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -3,6 +3,8 @@ Grim = require 'grim' {Disposable} = require 'event-kit' _ = require 'underscore-plus' +AnyConstructor = Symbol('any-constructor') + # Essential: `ViewRegistry` handles the association between model and view # types in Atom. We call this association a View Provider. As in, for a given # model, this class can provide a view via {::getView}, as long as the @@ -76,16 +78,27 @@ class ViewRegistry # textEditorElement # ``` # - # * `modelConstructor` Constructor {Function} for your model. + # * `modelConstructor` (optional) Constructor {Function} for your model. If + # a constructor is given, the `createView` function will only be used + # for model objects inheriting from that constructor. Otherwise, it will + # will be called for any object. # * `createView` Factory {Function} that is passed an instance of your model - # and must return a subclass of `HTMLElement` or `undefined`. + # and must return a subclass of `HTMLElement` or `undefined`. If it returns + # `undefined`, then the registry will continue to search for other view + # providers. # # Returns a {Disposable} on which `.dispose()` can be called to remove the # added provider. addViewProvider: (modelConstructor, createView) -> if arguments.length is 1 - Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.") - provider = modelConstructor + switch typeof modelConstructor + when 'function' + provider = {createView: modelConstructor, modelConstructor: AnyConstructor} + when 'object' + Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.") + provider = modelConstructor + else + throw new TypeError("Arguments to addViewProvider must be functions") else provider = {modelConstructor, createView} @@ -153,25 +166,34 @@ class ViewRegistry createView: (object) -> if object instanceof HTMLElement - object - else if object?.element instanceof HTMLElement - object.element - else if object?.jquery - object[0] - else if provider = @findProvider(object) - element = provider.createView?(object, @atomEnvironment) - unless element? - element = new provider.viewConstructor - element.initialize?(object) ? element.setModel?(object) - element - else if viewConstructor = object?.getViewClass?() - view = new viewConstructor(object) - view[0] - else - throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") + return object - findProvider: (object) -> - find @providers, ({modelConstructor}) -> object instanceof modelConstructor + if object?.element instanceof HTMLElement + return object.element + + if object?.jquery + return object[0] + + for provider in @providers + if provider.modelConstructor is AnyConstructor + if element = provider.createView(object, @atomEnvironment) + return element + continue + + if object instanceof provider.modelConstructor + if element = provider.createView?(object, @atomEnvironment) + return element + + if viewConstructor = provider.viewConstructor + element = new viewConstructor + element.initialize?(object) ? element.setModel?(object) + return element + + if viewConstructor = object?.getViewClass?() + view = new viewConstructor(object) + return view[0] + + throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") updateDocument: (fn) -> @documentWriters.push(fn) From cb2b068d779a7e182c50e0f9ed47998e46f0f992 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 19 Nov 2015 16:58:08 -0800 Subject: [PATCH 05/13] Load view providers from files specified in package.json --- .../package-with-view-providers/package.json | 8 ++++++++ .../package-with-view-providers/view-provider-1.js | 9 +++++++++ .../package-with-view-providers/view-provider-2.js | 9 +++++++++ spec/package-manager-spec.coffee | 13 +++++++++++++ spec/package-spec.coffee | 1 + src/atom-environment.coffee | 2 +- src/package-manager.coffee | 6 ++++-- src/package.coffee | 8 +++++++- 8 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 spec/fixtures/packages/package-with-view-providers/package.json create mode 100644 spec/fixtures/packages/package-with-view-providers/view-provider-1.js create mode 100644 spec/fixtures/packages/package-with-view-providers/view-provider-2.js diff --git a/spec/fixtures/packages/package-with-view-providers/package.json b/spec/fixtures/packages/package-with-view-providers/package.json new file mode 100644 index 000000000..7f7405c32 --- /dev/null +++ b/spec/fixtures/packages/package-with-view-providers/package.json @@ -0,0 +1,8 @@ +{ + "name": "package-with-view-providers", + "version": "1.0.0", + "viewProviders": [ + "./view-provider-1", + "./view-provider-2" + ] +} diff --git a/spec/fixtures/packages/package-with-view-providers/view-provider-1.js b/spec/fixtures/packages/package-with-view-providers/view-provider-1.js new file mode 100644 index 000000000..e4f0dcc0b --- /dev/null +++ b/spec/fixtures/packages/package-with-view-providers/view-provider-1.js @@ -0,0 +1,9 @@ +'use strict' + +module.exports = function (model) { + if (model.worksWithViewProvider1) { + let element = document.createElement('div') + element.dataset['createdBy'] = 'view-provider-1' + return element + } +} diff --git a/spec/fixtures/packages/package-with-view-providers/view-provider-2.js b/spec/fixtures/packages/package-with-view-providers/view-provider-2.js new file mode 100644 index 000000000..a3b58a3aa --- /dev/null +++ b/spec/fixtures/packages/package-with-view-providers/view-provider-2.js @@ -0,0 +1,9 @@ +'use strict' + +module.exports = function (model) { + if (model.worksWithViewProvider2) { + let element = document.createElement('div') + element.dataset['createdBy'] = 'view-provider-2' + return element + } +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 843e9a932..169e09b76 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -94,6 +94,19 @@ describe "PackageManager", -> state: state2 } + it "registers any view providers specified in the package's package.json", -> + atom.packages.loadPackage("package-with-view-providers") + + model1 = {worksWithViewProvider1: true} + element1 = atom.views.getView(model1) + expect(element1 instanceof HTMLDivElement).toBe true + expect(element1.dataset.createdBy).toBe 'view-provider-1' + + model2 = {worksWithViewProvider2: true} + element2 = atom.views.getView(model2) + expect(element2 instanceof HTMLDivElement).toBe true + expect(element2.dataset.createdBy).toBe 'view-provider-2' + describe "::unloadPackage(name)", -> describe "when the package is active", -> it "throws an error", -> diff --git a/spec/package-spec.coffee b/spec/package-spec.coffee index 63a80a7db..f49d2ed7c 100644 --- a/spec/package-spec.coffee +++ b/spec/package-spec.coffee @@ -10,6 +10,7 @@ describe "Package", -> keymapManager: atom.keymaps, commandRegistry: atom.command, grammarRegistry: atom.grammars, themeManager: atom.themes, menuManager: atom.menu, contextMenuManager: atom.contextMenu, + deserializerManager: atom.deserializers, viewRegistry: atom.views, devMode: false ) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index ac76daf04..c56bcb493 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -151,7 +151,7 @@ class AtomEnvironment extends Model @packages = new PackageManager({ devMode, configDirPath, resourcePath, safeMode, @config, styleManager: @styles, commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications, - grammarRegistry: @grammars + grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views }) @themes = new ThemeManager({ diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 789b2eae5..5b0264212 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -31,7 +31,8 @@ class PackageManager constructor: (params) -> { configDirPath, @devMode, safeMode, @resourcePath, @config, @styleManager, - @notificationManager, @keymapManager, @commandRegistry, @grammarRegistry + @notificationManager, @keymapManager, @commandRegistry, @grammarRegistry, + @deserializerManager, @viewRegistry } = params @emitter = new Emitter @@ -375,7 +376,8 @@ class PackageManager options = { path: packagePath, metadata, packageManager: this, @config, @styleManager, @commandRegistry, @keymapManager, @devMode, @notificationManager, - @grammarRegistry, @themeManager, @menuManager, @contextMenuManager + @grammarRegistry, @themeManager, @menuManager, @contextMenuManager, + @deserializerManager, @viewRegistry } if metadata.theme pack = new ThemePackage(options) diff --git a/src/package.coffee b/src/package.coffee index a49c70868..1d98c8161 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -33,7 +33,7 @@ class Package { @path, @metadata, @packageManager, @config, @styleManager, @commandRegistry, @keymapManager, @devMode, @notificationManager, @grammarRegistry, @themeManager, - @menuManager, @contextMenuManager + @menuManager, @contextMenuManager, @deserializerManager, @viewRegistry } = params @emitter = new Emitter @@ -85,6 +85,7 @@ class Package @loadMenus() @loadStylesheets() @loadDeserializers() + @loadViewProviders() @settingsPromise = @loadSettings() @requireMainModule() unless @mainModule? or @activationShouldBeDeferred() catch error @@ -266,6 +267,11 @@ class Package deserializeFunction.apply(this, arguments) return + loadViewProviders: -> + for implementationPath in @metadata.viewProviders + @viewRegistry.addViewProvider(require(path.join(@path, implementationPath))) + return + getStylesheetsPath: -> path.join(@path, 'styles') From 2d29fd6e79262bd5b8c902f86456ef5a21f224ae Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 20 Nov 2015 13:28:05 -0800 Subject: [PATCH 06/13] Load config schemas from packages' package.json files --- .../package-with-json-config-schema/package.json | 13 +++++++++++++ spec/package-manager-spec.coffee | 15 +++++++++++++++ src/package.coffee | 13 ++++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 spec/fixtures/packages/package-with-json-config-schema/package.json diff --git a/spec/fixtures/packages/package-with-json-config-schema/package.json b/spec/fixtures/packages/package-with-json-config-schema/package.json new file mode 100644 index 000000000..960d87fc1 --- /dev/null +++ b/spec/fixtures/packages/package-with-json-config-schema/package.json @@ -0,0 +1,13 @@ +{ + "name": "package-with-json-config-schema", + "configSchema": { + "a": { + "type": "number", + "default": 5 + }, + "b": { + "type": "string", + "default": "five" + } + } +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 169e09b76..656b4b691 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -194,6 +194,21 @@ describe "PackageManager", -> expect(atom.config.set('package-with-config-schema.numbers.one', '10')).toBe true expect(atom.config.get('package-with-config-schema.numbers.one')).toBe 10 + it "assigns the config schema when the package contains a schema in its package.json", -> + expect(atom.config.get('package-with-json-config-schema')).toBeUndefined() + + waitsForPromise -> + atom.packages.activatePackage('package-with-json-config-schema') + + runs -> + expect(atom.config.getSchema('package-with-json-config-schema')).toEqual { + type: 'object' + properties: { + a: {type: 'number', default: 5} + b: {type: 'string', default: 'five'} + } + } + describe "when the package metadata includes `activationCommands`", -> [mainModule, promise, workspaceCommandListener, registration] = [] diff --git a/src/package.coffee b/src/package.coffee index 1d98c8161..d1354340f 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -133,11 +133,14 @@ class Package activateConfig: -> return if @configActivated - @requireMainModule() unless @mainModule? - if @mainModule? - if @mainModule.config? and typeof @mainModule.config is 'object' - @config.setSchema @name, {type: 'object', properties: @mainModule.config} - @mainModule.activateConfig?() + if configSchema = @metadata.configSchema + @config.setSchema @name, {type: 'object', properties: configSchema} + else + @requireMainModule() unless @mainModule? + if @mainModule? + if @mainModule.config? and typeof @mainModule.config is 'object' + @config.setSchema @name, {type: 'object', properties: @mainModule.config} + @mainModule.activateConfig?() @configActivated = true activateStylesheets: -> From 5f1947ef1f15393fd25b929914de5fcb6f40c37f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 20 Nov 2015 13:42:17 -0800 Subject: [PATCH 07/13] Guard for undefined package.json fields --- src/package.coffee | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/package.coffee b/src/package.coffee index d1354340f..b2a87d1d8 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -259,21 +259,23 @@ class Package [stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)] loadDeserializers: -> - for name, implementationPath of @metadata.deserializers - do => - deserializePath = path.join(@path, implementationPath) - deserializeFunction = null - atom.deserializers.add - name: name, - deserialize: -> - deserializeFunction ?= require(deserializePath) - deserializeFunction.apply(this, arguments) - return + if @metadata.deserializers? + for name, implementationPath of @metadata.deserializers + do => + deserializePath = path.join(@path, implementationPath) + deserializeFunction = null + atom.deserializers.add + name: name, + deserialize: -> + deserializeFunction ?= require(deserializePath) + deserializeFunction.apply(this, arguments) + return loadViewProviders: -> - for implementationPath in @metadata.viewProviders - @viewRegistry.addViewProvider(require(path.join(@path, implementationPath))) - return + if @metadata.viewProviders? + for implementationPath in @metadata.viewProviders + @viewRegistry.addViewProvider(require(path.join(@path, implementationPath))) + return getStylesheetsPath: -> path.join(@path, 'styles') From d6e5ea85642155cf28e224034a558254dd4922fe Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Nov 2015 16:05:32 -0800 Subject: [PATCH 08/13] Defer requiring packages' main modules if they use new package.json fields --- .../package-with-deserializers/index.js | 3 ++ .../package-with-deserializers/package.json | 1 + .../package-with-view-providers/index.js | 3 ++ .../package-with-view-providers/package.json | 1 + spec/package-manager-spec.coffee | 36 ++++++++-------- src/package.coffee | 43 +++++++++++-------- 6 files changed, 53 insertions(+), 34 deletions(-) create mode 100644 spec/fixtures/packages/package-with-deserializers/index.js create mode 100644 spec/fixtures/packages/package-with-view-providers/index.js diff --git a/spec/fixtures/packages/package-with-deserializers/index.js b/spec/fixtures/packages/package-with-deserializers/index.js new file mode 100644 index 000000000..19bba5ecb --- /dev/null +++ b/spec/fixtures/packages/package-with-deserializers/index.js @@ -0,0 +1,3 @@ +module.exports = { + activate: function() {} +} diff --git a/spec/fixtures/packages/package-with-deserializers/package.json b/spec/fixtures/packages/package-with-deserializers/package.json index c55e1444a..daa5776bf 100644 --- a/spec/fixtures/packages/package-with-deserializers/package.json +++ b/spec/fixtures/packages/package-with-deserializers/package.json @@ -1,6 +1,7 @@ { "name": "package-with-deserializers", "version": "1.0.0", + "main": "./index", "deserializers": { "Deserializer1": "./deserializer-1.js", "Deserializer2": "./deserializer-2.js" diff --git a/spec/fixtures/packages/package-with-view-providers/index.js b/spec/fixtures/packages/package-with-view-providers/index.js new file mode 100644 index 000000000..19bba5ecb --- /dev/null +++ b/spec/fixtures/packages/package-with-view-providers/index.js @@ -0,0 +1,3 @@ +module.exports = { + activate: function() {} +} diff --git a/spec/fixtures/packages/package-with-view-providers/package.json b/spec/fixtures/packages/package-with-view-providers/package.json index 7f7405c32..989473f95 100644 --- a/spec/fixtures/packages/package-with-view-providers/package.json +++ b/spec/fixtures/packages/package-with-view-providers/package.json @@ -1,5 +1,6 @@ { "name": "package-with-view-providers", + "main": "./index", "version": "1.0.0", "viewProviders": [ "./view-provider-1", diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 656b4b691..94a6c43c5 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -80,7 +80,7 @@ describe "PackageManager", -> expect(loadedPackage.name).toBe "package-with-main" it "registers any deserializers specified in the package's package.json", -> - atom.packages.loadPackage("package-with-deserializers") + pack = atom.packages.loadPackage("package-with-deserializers") state1 = {deserializer: 'Deserializer1', a: 'b'} expect(atom.deserializers.deserialize(state1)).toEqual { @@ -94,8 +94,10 @@ describe "PackageManager", -> state: state2 } + expect(pack.mainModule).toBeNull() + it "registers any view providers specified in the package's package.json", -> - atom.packages.loadPackage("package-with-view-providers") + pack = atom.packages.loadPackage("package-with-view-providers") model1 = {worksWithViewProvider1: true} element1 = atom.views.getView(model1) @@ -107,6 +109,21 @@ describe "PackageManager", -> expect(element2 instanceof HTMLDivElement).toBe true expect(element2.dataset.createdBy).toBe 'view-provider-2' + expect(pack.mainModule).toBeNull() + + it "registers the config schema in the package's metadata, if present", -> + pack = atom.packages.loadPackage("package-with-json-config-schema") + + expect(atom.config.getSchema('package-with-json-config-schema')).toEqual { + type: 'object' + properties: { + a: {type: 'number', default: 5} + b: {type: 'string', default: 'five'} + } + } + + expect(pack.mainModule).toBeNull() + describe "::unloadPackage(name)", -> describe "when the package is active", -> it "throws an error", -> @@ -194,21 +211,6 @@ describe "PackageManager", -> expect(atom.config.set('package-with-config-schema.numbers.one', '10')).toBe true expect(atom.config.get('package-with-config-schema.numbers.one')).toBe 10 - it "assigns the config schema when the package contains a schema in its package.json", -> - expect(atom.config.get('package-with-json-config-schema')).toBeUndefined() - - waitsForPromise -> - atom.packages.activatePackage('package-with-json-config-schema') - - runs -> - expect(atom.config.getSchema('package-with-json-config-schema')).toEqual { - type: 'object' - properties: { - a: {type: 'number', default: 5} - b: {type: 'string', default: 'five'} - } - } - describe "when the package metadata includes `activationCommands`", -> [mainModule, promise, workspaceCommandListener, registration] = [] diff --git a/src/package.coffee b/src/package.coffee index b2a87d1d8..b97424d04 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -86,12 +86,22 @@ class Package @loadStylesheets() @loadDeserializers() @loadViewProviders() + @registerConfigSchemaFromMetadata() @settingsPromise = @loadSettings() - @requireMainModule() unless @mainModule? or @activationShouldBeDeferred() + if @shouldRequireMainModuleOnLoad() and not @mainModule? + @requireMainModule() catch error @handleError("Failed to load the #{@name} package", error) this + shouldRequireMainModuleOnLoad: -> + not ( + @metadata.deserializers? or + @metadata.viewProviders? or + @metadata.configSchema? or + @activationShouldBeDeferred() + ) + reset: -> @stylesheets = [] @keymaps = [] @@ -119,9 +129,11 @@ class Package activateNow: -> try - @activateConfig() + @requireMainModule() unless @mainModule? + @registerConfigSchemaFromMainModule() @activateStylesheets() if @mainModule? and not @mainActivated + @mainModule.activateConfig?() @mainModule.activate?(@packageManager.getPackageState(@name) ? {}) @mainActivated = true @activateServices() @@ -130,18 +142,19 @@ class Package @resolveActivationPromise?() - activateConfig: -> - return if @configActivated - + registerConfigSchemaFromMetadata: -> if configSchema = @metadata.configSchema @config.setSchema @name, {type: 'object', properties: configSchema} - else - @requireMainModule() unless @mainModule? - if @mainModule? - if @mainModule.config? and typeof @mainModule.config is 'object' - @config.setSchema @name, {type: 'object', properties: @mainModule.config} - @mainModule.activateConfig?() - @configActivated = true + @configSchemaRegistered = true + + registerConfigSchemaFromMainModule: -> + return if @configSchemaRegistered + + if @mainModule? + if @mainModule.config? and typeof @mainModule.config is 'object' + @config.setSchema @name, {type: 'object', properties: @mainModule.config} + + @configSchemaRegistered = true activateStylesheets: -> return if @stylesheetsActivated @@ -368,20 +381,16 @@ class Package @resolveActivationPromise = null @activationCommandSubscriptions?.dispose() @deactivateResources() - @deactivateConfig() @deactivateKeymaps() if @mainActivated try @mainModule?.deactivate?() + @mainModule?.deactivateConfig?() @mainActivated = false catch e console.error "Error deactivating package '#{@name}'", e.stack @emitter.emit 'did-deactivate' - deactivateConfig: -> - @mainModule?.deactivateConfig?() - @configActivated = false - deactivateResources: -> grammar.deactivate() for grammar in @grammars settings.deactivate() for settings in @settings From 24938c0361366be8715a1a1886e336dfd20dade5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Nov 2015 16:28:08 -0800 Subject: [PATCH 09/13] Defer loading view providers until activation or deserializer use Signed-off-by: Nathan Sobo --- .../deserializer.js | 3 + .../package-with-view-providers/package.json | 3 + spec/package-manager-spec.coffee | 59 +++++++++++++++---- src/package.coffee | 11 ++-- 4 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 spec/fixtures/packages/package-with-view-providers/deserializer.js diff --git a/spec/fixtures/packages/package-with-view-providers/deserializer.js b/spec/fixtures/packages/package-with-view-providers/deserializer.js new file mode 100644 index 000000000..334e7b2ab --- /dev/null +++ b/spec/fixtures/packages/package-with-view-providers/deserializer.js @@ -0,0 +1,3 @@ +module.exports = function (state) { + return {state: state} +} diff --git a/spec/fixtures/packages/package-with-view-providers/package.json b/spec/fixtures/packages/package-with-view-providers/package.json index 989473f95..f67477280 100644 --- a/spec/fixtures/packages/package-with-view-providers/package.json +++ b/spec/fixtures/packages/package-with-view-providers/package.json @@ -2,6 +2,9 @@ "name": "package-with-view-providers", "main": "./index", "version": "1.0.0", + "deserializers": { + "DeserializerFromPackageWithViewProviders": "./deserializer" + }, "viewProviders": [ "./view-provider-1", "./view-provider-2" diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 94a6c43c5..d82f37acb 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -96,20 +96,57 @@ describe "PackageManager", -> expect(pack.mainModule).toBeNull() - it "registers any view providers specified in the package's package.json", -> - pack = atom.packages.loadPackage("package-with-view-providers") - + describe "when there are view providers specified in the package's package.json", -> model1 = {worksWithViewProvider1: true} - element1 = atom.views.getView(model1) - expect(element1 instanceof HTMLDivElement).toBe true - expect(element1.dataset.createdBy).toBe 'view-provider-1' - model2 = {worksWithViewProvider2: true} - element2 = atom.views.getView(model2) - expect(element2 instanceof HTMLDivElement).toBe true - expect(element2.dataset.createdBy).toBe 'view-provider-2' - expect(pack.mainModule).toBeNull() + afterEach -> + atom.packages.deactivatePackage('package-with-view-providers') + atom.packages.unloadPackage('package-with-view-providers') + + it "does not load the view providers immediately", -> + pack = atom.packages.loadPackage("package-with-view-providers") + expect(pack.mainModule).toBeNull() + + expect(-> atom.views.getView(model1)).toThrow() + expect(-> atom.views.getView(model2)).toThrow() + + it "registers the view providers when the package is activated", -> + pack = atom.packages.loadPackage("package-with-view-providers") + + waitsForPromise -> + atom.packages.activatePackage("package-with-view-providers").then -> + element1 = atom.views.getView(model1) + expect(element1 instanceof HTMLDivElement).toBe true + expect(element1.dataset.createdBy).toBe 'view-provider-1' + + element2 = atom.views.getView(model2) + expect(element2 instanceof HTMLDivElement).toBe true + expect(element2.dataset.createdBy).toBe 'view-provider-2' + + it "registers the view providers when any of the package's deserializers are used", -> + pack = atom.packages.loadPackage("package-with-view-providers") + + spyOn(atom.views, 'addViewProvider').andCallThrough() + atom.deserializers.deserialize({ + deserializer: 'DeserializerFromPackageWithViewProviders', + a: 'b' + }) + expect(atom.views.addViewProvider.callCount).toBe 2 + + atom.deserializers.deserialize({ + deserializer: 'DeserializerFromPackageWithViewProviders', + a: 'b' + }) + expect(atom.views.addViewProvider.callCount).toBe 2 + + element1 = atom.views.getView(model1) + expect(element1 instanceof HTMLDivElement).toBe true + expect(element1.dataset.createdBy).toBe 'view-provider-1' + + element2 = atom.views.getView(model2) + expect(element2 instanceof HTMLDivElement).toBe true + expect(element2.dataset.createdBy).toBe 'view-provider-2' it "registers the config schema in the package's metadata, if present", -> pack = atom.packages.loadPackage("package-with-json-config-schema") diff --git a/src/package.coffee b/src/package.coffee index b97424d04..f437d327a 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -85,7 +85,6 @@ class Package @loadMenus() @loadStylesheets() @loadDeserializers() - @loadViewProviders() @registerConfigSchemaFromMetadata() @settingsPromise = @loadSettings() if @shouldRequireMainModuleOnLoad() and not @mainModule? @@ -131,6 +130,7 @@ class Package try @requireMainModule() unless @mainModule? @registerConfigSchemaFromMainModule() + @registerViewProviders() @activateStylesheets() if @mainModule? and not @mainActivated @mainModule.activateConfig?() @@ -279,16 +279,17 @@ class Package deserializeFunction = null atom.deserializers.add name: name, - deserialize: -> + deserialize: => + @registerViewProviders() deserializeFunction ?= require(deserializePath) deserializeFunction.apply(this, arguments) return - loadViewProviders: -> - if @metadata.viewProviders? + registerViewProviders: -> + if @metadata.viewProviders? and not @registeredViewProviders for implementationPath in @metadata.viewProviders @viewRegistry.addViewProvider(require(path.join(@path, implementationPath))) - return + @registeredViewProviders = true getStylesheetsPath: -> path.join(@path, 'styles') From 327cf6997bbff1a058d47baa3cdf8ceab90489e5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Nov 2015 17:35:38 -0800 Subject: [PATCH 10/13] Remember which packages use atom APIs at main module load time Signed-off-by: Nathan Sobo --- .../package-with-eval-time-api-calls/index.js | 5 ++++ .../package.json | 5 ++++ .../packages/package-with-main/package.cson | 1 + spec/package-manager-spec.coffee | 24 +++++++++++++++++++ spec/package-spec.coffee | 11 +++------ spec/spec-helper.coffee | 6 +++++ src/deserializer-manager.coffee | 3 +++ src/package.coffee | 12 +++++++++- src/view-registry.coffee | 3 +++ 9 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 spec/fixtures/packages/package-with-eval-time-api-calls/index.js create mode 100644 spec/fixtures/packages/package-with-eval-time-api-calls/package.json diff --git a/spec/fixtures/packages/package-with-eval-time-api-calls/index.js b/spec/fixtures/packages/package-with-eval-time-api-calls/index.js new file mode 100644 index 000000000..e16db0743 --- /dev/null +++ b/spec/fixtures/packages/package-with-eval-time-api-calls/index.js @@ -0,0 +1,5 @@ +atom.deserializers.add('MyDeserializer', function (state) { + return {state: state, a: 'b'} +}) + +exports.activate = function () {} diff --git a/spec/fixtures/packages/package-with-eval-time-api-calls/package.json b/spec/fixtures/packages/package-with-eval-time-api-calls/package.json new file mode 100644 index 000000000..7a76abb5f --- /dev/null +++ b/spec/fixtures/packages/package-with-eval-time-api-calls/package.json @@ -0,0 +1,5 @@ +{ + "name": "package-with-eval-time-api-calls", + "version": "1.2.3", + "main": "./index" +} diff --git a/spec/fixtures/packages/package-with-main/package.cson b/spec/fixtures/packages/package-with-main/package.cson index e799f6ca8..75910bbcc 100644 --- a/spec/fixtures/packages/package-with-main/package.cson +++ b/spec/fixtures/packages/package-with-main/package.cson @@ -1 +1,2 @@ 'main': 'main-module.coffee' +'version': '2.3.4' diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index d82f37acb..4e88a1c2f 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -1,6 +1,7 @@ path = require 'path' Package = require '../src/package' {Disposable} = require 'atom' +{mockLocalStorage} = require './spec-helper' describe "PackageManager", -> workspaceElement = null @@ -161,6 +162,29 @@ describe "PackageManager", -> expect(pack.mainModule).toBeNull() + describe "when a package does not have deserializers, view providers or a config schema in its package.json", -> + beforeEach -> + atom.packages.unloadPackage('package-with-main') + mockLocalStorage() + + it "defers loading the package's main module if the package previously used no Atom APIs when its main module was required", -> + pack1 = atom.packages.loadPackage('package-with-main') + expect(pack1.mainModule).toBeDefined() + + atom.packages.unloadPackage('package-with-main') + + pack2 = atom.packages.loadPackage('package-with-main') + expect(pack2.mainModule).toBeNull() + + it "does not defer loading the package's main module if the package previously used Atom APIs when its main module was required", -> + pack1 = atom.packages.loadPackage('package-with-eval-time-api-calls') + expect(pack1.mainModule).toBeDefined() + + atom.packages.unloadPackage('package-with-eval-time-api-calls') + + pack2 = atom.packages.loadPackage('package-with-eval-time-api-calls') + expect(pack2.mainModule).not.toBeNull() + describe "::unloadPackage(name)", -> describe "when the package is active", -> it "throws an error", -> diff --git a/spec/package-spec.coffee b/spec/package-spec.coffee index f49d2ed7c..92218e749 100644 --- a/spec/package-spec.coffee +++ b/spec/package-spec.coffee @@ -1,6 +1,7 @@ path = require 'path' Package = require '../src/package' ThemePackage = require '../src/theme-package' +{mockLocalStorage} = require './spec-helper' describe "Package", -> build = (constructor, path) -> @@ -20,10 +21,7 @@ describe "Package", -> describe "when the package contains incompatible native modules", -> beforeEach -> - items = {} - spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined - spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null - spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined + mockLocalStorage() it "does not activate it", -> packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module') @@ -55,10 +53,7 @@ describe "Package", -> describe "::rebuild()", -> beforeEach -> - items = {} - spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined - spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null - spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined + mockLocalStorage() it "returns a promise resolving to the results of `apm rebuild`", -> packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index') diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index f31c67298..67883511b 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -265,3 +265,9 @@ window.advanceClock = (delta=1) -> true callback() for callback in callbacks + +exports.mockLocalStorage = -> + items = {} + spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item.toString(); undefined + spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null + spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee index 7f6cb0f65..3c73a0b02 100644 --- a/src/deserializer-manager.coffee +++ b/src/deserializer-manager.coffee @@ -39,6 +39,9 @@ class DeserializerManager delete @deserializers[deserializer.name] for deserializer in deserializers return + getDeserializerCount: -> + Object.keys(@deserializers).length + # Public: Deserialize the state and params. # # * `state` The state {Object} to deserialize. diff --git a/src/package.coffee b/src/package.coffee index f437d327a..5ff008ec7 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -98,7 +98,8 @@ class Package @metadata.deserializers? or @metadata.viewProviders? or @metadata.configSchema? or - @activationShouldBeDeferred() + @activationShouldBeDeferred() or + localStorage.getItem(@getCanDeferMainModuleRequireStorageKey()) is 'true' ) reset: -> @@ -426,7 +427,13 @@ class Package mainModulePath = @getMainModulePath() if fs.isFileSync(mainModulePath) @mainModuleRequired = true + + previousViewProviderCount = @viewRegistry.getViewProviderCount() + previousDeserializerCount = @deserializerManager.getDeserializerCount() @mainModule = require(mainModulePath) + if (@viewRegistry.getViewProviderCount() is previousViewProviderCount and + @deserializerManager.getDeserializerCount() is previousDeserializerCount) + localStorage.setItem(@getCanDeferMainModuleRequireStorageKey(), 'true') getMainModulePath: -> return @mainModulePath if @resolvedMainModulePath @@ -620,6 +627,9 @@ class Package electronVersion = process.versions['electron'] ? process.versions['atom-shell'] "installed-packages:#{@name}:#{@metadata.version}:electron-#{electronVersion}:incompatible-native-modules" + getCanDeferMainModuleRequireStorageKey: -> + "installed-packages:#{@name}:#{@metadata.version}:can-defer-main-module-require" + # Get the incompatible native modules that this package depends on. # This recurses through all dependencies and requires all modules that # contain a `.node` file. diff --git a/src/view-registry.coffee b/src/view-registry.coffee index e754a010b..ef7151353 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -106,6 +106,9 @@ class ViewRegistry new Disposable => @providers = @providers.filter (p) -> p isnt provider + getViewProviderCount: -> + @providers.length + # Essential: Get the view associated with an object in the workspace. # # If you're just *using* the workspace, you shouldn't need to access the view From f35dddee2f40a0a5edbd3c7afd0b1c19404ae3b6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Nov 2015 18:01:26 -0800 Subject: [PATCH 11/13] Reregister config schema when package is reactivated Signed-off-by: Nathan Sobo --- src/package.coffee | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/package.coffee b/src/package.coffee index 5ff008ec7..08b434583 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -85,7 +85,7 @@ class Package @loadMenus() @loadStylesheets() @loadDeserializers() - @registerConfigSchemaFromMetadata() + @configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata() @settingsPromise = @loadSettings() if @shouldRequireMainModuleOnLoad() and not @mainModule? @requireMainModule() @@ -130,7 +130,7 @@ class Package activateNow: -> try @requireMainModule() unless @mainModule? - @registerConfigSchemaFromMainModule() + @configSchemaRegisteredOnActivate = @registerConfigSchemaFromMainModule() @registerViewProviders() @activateStylesheets() if @mainModule? and not @mainActivated @@ -146,16 +146,16 @@ class Package registerConfigSchemaFromMetadata: -> if configSchema = @metadata.configSchema @config.setSchema @name, {type: 'object', properties: configSchema} - @configSchemaRegistered = true + true + else + false registerConfigSchemaFromMainModule: -> - return if @configSchemaRegistered - - if @mainModule? + if @mainModule? and not @configSchemaRegisteredOnLoad if @mainModule.config? and typeof @mainModule.config is 'object' @config.setSchema @name, {type: 'object', properties: @mainModule.config} - - @configSchemaRegistered = true + return true + false activateStylesheets: -> return if @stylesheetsActivated @@ -382,6 +382,7 @@ class Package @activationPromise = null @resolveActivationPromise = null @activationCommandSubscriptions?.dispose() + @configSchemaRegisteredOnActivate = false @deactivateResources() @deactivateKeymaps() if @mainActivated From aecc2598f9d3af978334df37df5badddc8b512d0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 24 Nov 2015 15:14:39 -0800 Subject: [PATCH 12/13] Restore private activateConfig method to fix settings-view --- src/package.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/package.coffee b/src/package.coffee index 08b434583..b831b3c55 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -157,6 +157,9 @@ class Package return true false + # TODO: Remove. Settings view calls this method currently. + activateConfig: -> @registerConfigSchemaFromMainModule() + activateStylesheets: -> return if @stylesheetsActivated From a2f14685fcdfa2970db7658e97f046c9493c2a0a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 30 Nov 2015 10:15:16 -0800 Subject: [PATCH 13/13] :arrow_up: autocomplete-snippets --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b488ba3ce..31086502b 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", "autocomplete-plus": "2.23.0", - "autocomplete-snippets": "1.8.0", + "autocomplete-snippets": "1.9.0", "autoflow": "0.26.0", "autosave": "0.23.0", "background-tips": "0.26.0",