diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index b4474c3fa..055533762 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -1,4 +1,5 @@ RootView = require 'root-view' +{$$} = require 'space-pen' describe "the `atom` global", -> describe ".loadPackage(name)", -> @@ -17,6 +18,33 @@ describe "the `atom` global", -> atom.loadPackage("package-with-module") expect(rootView.activatePackage).toHaveBeenCalledWith(extension) + describe "keymap loading", -> + describe "when package.json does not contain a 'keymaps' manifest", -> + it "loads all keymaps in the directory", -> + element1 = $$ -> @div class: 'test-1' + element2 = $$ -> @div class: 'test-2' + + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() + expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined() + + atom.loadPackage("package-with-module") + + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1" + expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2" + + describe "when package.json contains a 'keymaps' manifest", -> + it "loads only the keymaps specified by the manifest, in the specified order", -> + element1 = $$ -> @div class: 'test-1' + element3 = $$ -> @div class: 'test-3' + + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() + + atom.loadPackage("package-with-keymaps-manifest") + + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1' + expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2' + expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined() + it "loads stylesheets associated with the package", -> stylesheetPath = require.resolve("fixtures/packages/package-with-module/stylesheets/styles.css") expect(stylesheetElementForId(stylesheetPath).length).toBe 0 diff --git a/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-1.json b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-1.json new file mode 100644 index 000000000..050770117 --- /dev/null +++ b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-1.json @@ -0,0 +1,5 @@ +{ + ".test-1": { + "ctrl-z": "keymap-1" + } +} diff --git a/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-2.cson b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-2.cson new file mode 100644 index 000000000..43912b006 --- /dev/null +++ b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-2.cson @@ -0,0 +1,3 @@ +".test-1": + "ctrl-z": "keymap-2" + "ctrl-n": "keymap-2" diff --git a/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-3.cson b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-3.cson new file mode 100644 index 000000000..7a839b940 --- /dev/null +++ b/spec/fixtures/packages/package-with-keymaps-manifest/keymaps/keymap-3.cson @@ -0,0 +1,2 @@ +".test-3": + "ctrl-y": "keymap-3" diff --git a/spec/fixtures/packages/package-with-keymaps-manifest/package.cson b/spec/fixtures/packages/package-with-keymaps-manifest/package.cson new file mode 100644 index 000000000..e7bdccfa7 --- /dev/null +++ b/spec/fixtures/packages/package-with-keymaps-manifest/package.cson @@ -0,0 +1 @@ +keymaps: ["keymap-2", "keymap-1"] diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-1.cson b/spec/fixtures/packages/package-with-module/keymaps/keymap-1.cson new file mode 100644 index 000000000..8954c80f8 --- /dev/null +++ b/spec/fixtures/packages/package-with-module/keymaps/keymap-1.cson @@ -0,0 +1,2 @@ +".test-1": + "ctrl-z": "test-1" diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-2.cson b/spec/fixtures/packages/package-with-module/keymaps/keymap-2.cson new file mode 100644 index 000000000..a0ffb4109 --- /dev/null +++ b/spec/fixtures/packages/package-with-module/keymaps/keymap-2.cson @@ -0,0 +1,2 @@ +".test-2": + "ctrl-z": "test-2" diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index c31695dfe..c7dc053ca 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -12,10 +12,9 @@ Editor = require 'editor' TokenizedBuffer = require 'tokenized-buffer' fs = require 'fs' require 'window' - requireStylesheet "jasmine.css" - require.paths.unshift(require.resolve('fixtures/packages')) +[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = [] # Load TextMate bundles, which specs rely on (but not other packages) atom.loadPackages(atom.getAvailableTextMateBundles()) @@ -24,7 +23,11 @@ beforeEach -> window.fixturesProject = new Project(require.resolve('fixtures')) window.resetTimeouts() - # reset config after each spec; don't load or save from/to `config.json` + # used to reset keymap after each spec + bindingSetsToRestore = _.clone(keymap.bindingSets) + bindingSetsByFirstKeystrokeToRestore = _.clone(keymap.bindingSetsByFirstKeystroke) + + # reset config before each spec; don't load or save from/to `config.json` window.config = new Config() spyOn(config, 'load') spyOn(config, 'save') @@ -42,6 +45,8 @@ beforeEach -> spyOn(TokenizedBuffer.prototype, "tokenizeInBackground").andCallFake -> @tokenizeNextChunk() afterEach -> + keymap.bindingSets = bindingSetsToRestore + keymap.bindingSetsByFirstKeystrokeToRestore = bindingSetsByFirstKeystrokeToRestore delete window.rootView if window.rootView $('#jasmine-content').empty() window.fixturesProject.destroy() diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index 9c3dd071d..16ab72d15 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -3,29 +3,42 @@ fs = require 'fs' module.exports = class AtomPackage extends Package + metadata: null + keymapsDirPath: null + constructor: (@name) -> super - @module = require(@path) - @module.name = @name + @keymapsDirPath = fs.join(@path, 'keymaps') + if @requireModule + @module = require(@path) + @module.name = @name load: -> try + @loadMetadata() @loadKeymaps() @loadStylesheets() - rootView.activatePackage(@module) + rootView.activatePackage(@module) if @module catch e console.error "Failed to load package named '#{@name}'", e.stack + loadMetadata: -> + if metadataPath = fs.resolveExtension(fs.join(@path, "package"), ['cson', 'json']) + @metadata = fs.readObject(metadataPath) + loadKeymaps: -> for keymapPath in @getKeymapPaths() keymap.load(keymapPath) getKeymapPaths: -> - keymapsDirPath = fs.join(@path, 'keymaps') - if fs.exists keymapsDirPath - fs.list keymapsDirPath + if keymaps = @metadata?.keymaps + keymaps.map (relativePath) => + fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', '']) else - [] + if fs.exists(@keymapsDirPath) + fs.list(@keymapsDirPath) + else + [] loadStylesheets: -> for stylesheetPath in @getStylesheetPaths() diff --git a/src/app/package.coffee b/src/app/package.coffee index 47e99039b..49d6445ac 100644 --- a/src/app/package.coffee +++ b/src/app/package.coffee @@ -11,10 +11,21 @@ class Package else new AtomPackage(name).load() + + name: null + path: null + requireModule: null + module: null + constructor: (@name) -> @path = require.resolve(@name, verifyExistence: false) throw new Error("No package found named '#{@name}'") unless @path - @path = fs.directory(@path) unless fs.isDirectory(@path) + + if fs.isDirectory(@path) + @requireModule = false + else + @requireModule = true + @path = fs.directory(@path) load: -> for grammar in @getGrammars() diff --git a/src/stdlib/fs.coffee b/src/stdlib/fs.coffee index e3b87eb07..4d2bd1760 100644 --- a/src/stdlib/fs.coffee +++ b/src/stdlib/fs.coffee @@ -124,11 +124,27 @@ module.exports = md5ForPath: (path) -> $native.md5ForPath(path) - resolve: (paths...) -> - to = paths.pop() - for from in paths - path = @join(from, to) - return path if @exists(path) + resolve: (args...) -> + extensions = args.pop() if _.isArray(_.last(args)) + pathToResolve = args.pop() + loadPaths = args + + for loadPath in loadPaths + candidatePath = @join(loadPath, pathToResolve) + if extensions + if resolvedPath = @resolveExtension(candidatePath, extensions) + return resolvedPath + else + return candidatePath if @exists(candidatePath) + undefined + + resolveExtension: (path, extensions) -> + for extension in extensions + if extension == "" + return path if @exists(path) + else + pathWithExtension = path + "." + extension + return pathWithExtension if @exists(pathWithExtension) undefined isCompressedExtension: (ext) ->