diff --git a/native/mac/atom.icns b/native/mac/atom.icns index 9d710caf7..5ee78e5d4 100644 Binary files a/native/mac/atom.icns and b/native/mac/atom.icns differ diff --git a/spec/app/atom-package-spec.coffee b/spec/app/atom-package-spec.coffee new file mode 100644 index 000000000..f483f74ab --- /dev/null +++ b/spec/app/atom-package-spec.coffee @@ -0,0 +1,67 @@ +RootView = require 'root-view' +AtomPackage = require 'atom-package' +fs = require 'fs' + +describe "AtomPackage", -> + describe ".load()", -> + afterEach -> + rootView.deactivate() + + describe "when the package metadata includes activation events", -> + [packageMainModule, pack] = [] + + beforeEach -> + new RootView(fixturesProject.getPath()) + pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-activation-events')) + packageMainModule = require 'fixtures/packages/package-with-activation-events/main' + spyOn(packageMainModule, 'activate').andCallThrough() + pack.load() + + it "defers activating the package until an activation event bubbles to the root view", -> + expect(packageMainModule.activate).not.toHaveBeenCalled() + rootView.trigger 'activation-event' + expect(packageMainModule.activate).toHaveBeenCalled() + + it "triggers the activation event on all handlers registered during activation", -> + rootView.open('sample.js') + editor = rootView.getActiveEditor() + eventHandler = jasmine.createSpy("activation-event") + editor.command 'activation-event', eventHandler + editor.trigger 'activation-event' + expect(packageMainModule.activate.callCount).toBe 1 + expect(packageMainModule.activationEventCallCount).toBe 1 + expect(eventHandler.callCount).toBe 1 + editor.trigger 'activation-event' + expect(packageMainModule.activationEventCallCount).toBe 2 + expect(eventHandler.callCount).toBe 2 + expect(packageMainModule.activate.callCount).toBe 1 + + describe "when the package does not specify a main module", -> + describe "when the package has an index.coffee", -> + it "uses index.coffee as the main module", -> + new RootView(fixturesProject.getPath()) + pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-module')) + packageMainModule = require 'fixtures/packages/package-with-module' + spyOn(packageMainModule, 'activate').andCallThrough() + + expect(packageMainModule.activate).not.toHaveBeenCalled() + pack.load() + expect(packageMainModule.activate).toHaveBeenCalled() + + describe "when the package doesn't have an index.coffee", -> + it "does not throw an exception or log an error", -> + spyOn(console, "error") + spyOn(console, "warn") + new RootView(fixturesProject.getPath()) + pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-keymaps-manifest')) + + expect(-> pack.load()).not.toThrow() + expect(console.error).not.toHaveBeenCalled() + expect(console.warn).not.toHaveBeenCalled() + + describe "when a package is activated", -> + it "loads config defaults based on the `configDefaults` key", -> + expect(config.get('package-with-module.numbers.one')).toBeUndefined() + atom.loadPackage("package-with-module") + expect(config.get('package-with-module.numbers.one')).toBe 1 + expect(config.get('package-with-module.numbers.two')).toBe 2 diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index 73474a2a1..3638f0d61 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -2,11 +2,16 @@ RootView = require 'root-view' {$$} = require 'space-pen' describe "the `atom` global", -> + beforeEach -> + new RootView + + afterEach -> + rootView.deactivate() + describe ".loadPackage(name)", -> [extension, stylesheetPath] = [] beforeEach -> - rootView = new RootView extension = require "package-with-module" stylesheetPath = require.resolve("fixtures/packages/package-with-module/stylesheets/styles.css") @@ -14,9 +19,9 @@ describe "the `atom` global", -> removeStylesheet(stylesheetPath) it "requires and activates the package's main module if it exists", -> - spyOn(rootView, 'activatePackage').andCallThrough() + spyOn(atom, 'activateAtomPackage').andCallThrough() atom.loadPackage("package-with-module") - expect(rootView.activatePackage).toHaveBeenCalled() + expect(atom.activateAtomPackage).toHaveBeenCalled() it "logs warning instead of throwing an exception if a package fails to load", -> config.set("core.disabledPackages", []) @@ -70,6 +75,7 @@ describe "the `atom` global", -> syntax.on 'grammars-loaded', eventHandler disabledPackages = config.get("core.disabledPackages") disabledPackages.push('textmate-package.tmbundle') + disabledPackages.push('package-with-snippets') config.set "core.disabledPackages", disabledPackages atom.loadPackages() @@ -78,3 +84,61 @@ describe "the `atom` global", -> runs -> expect(Worker.prototype.terminate).toHaveBeenCalled() expect(Worker.prototype.terminate.calls.length).toBe 1 + + describe "package lifecycle", -> + [pack, packageModule] = [] + + beforeEach -> + pack = + name: "package" + packageMain: + activate: jasmine.createSpy("activate") + deactivate: -> + serialize: -> "it worked" + + packageModule = pack.packageMain + + describe ".activateAtomPackage(package)", -> + it "calls activate on the package", -> + atom.activateAtomPackage(pack) + expect(packageModule.activate).toHaveBeenCalledWith(undefined) + + it "calls activate on the package module with its previous state", -> + atom.activateAtomPackage(pack) + packageModule.activate.reset() + + serializedState = rootView.serialize() + rootView.deactivate() + RootView.deserialize(serializedState) + + atom.activateAtomPackage(pack) + expect(packageModule.activate).toHaveBeenCalledWith("it worked") + + describe ".deactivateAtomPackages()", -> + it "deactivates and removes the package module from the package module map", -> + atom.activateAtomPackage(pack) + spyOn(packageModule, "deactivate").andCallThrough() + atom.deactivateAtomPackages() + expect(packageModule.deactivate).toHaveBeenCalled() + expect(rootView.packages.length).toBe 0 + + describe ".serializeAtomPackages()", -> + it "absorbs exceptions that are thrown by the package module's serialize methods", -> + spyOn(console, 'error') + + atom.activateAtomPackage + name: "bad-egg" + packageMain: + activate: -> + serialize: -> throw new Error("I'm broken") + + atom.activateAtomPackage + name: "good-egg" + packageMain: + activate: -> + serialize: -> "I still get called" + + packageStates = atom.serializeAtomPackages() + expect(packageStates['good-egg']).toBe "I still get called" + expect(packageStates['bad-egg']).toBeUndefined() + expect(console.error).toHaveBeenCalled() diff --git a/spec/app/language-mode-spec.coffee b/spec/app/language-mode-spec.coffee index 775baefa1..a68f778dc 100644 --- a/spec/app/language-mode-spec.coffee +++ b/spec/app/language-mode-spec.coffee @@ -19,192 +19,6 @@ describe "LanguageMode", -> expect(jsEditSession.languageMode.grammar.name).toBe "JavaScript" jsEditSession.destroy() - describe "bracket insertion", -> - beforeEach -> - editSession.buffer.setText("") - - describe "when more than one character is inserted", -> - it "does not insert a matching bracket", -> - editSession.insertText("woah(") - expect(editSession.buffer.getText()).toBe "woah(" - - describe "when there is a word character after the cursor", -> - it "does not insert a matching bracket", -> - editSession.buffer.setText("ab") - editSession.setCursorBufferPosition([0, 1]) - editSession.insertText("(") - - expect(editSession.buffer.getText()).toBe "a(b" - - describe "when there are multiple cursors", -> - it "inserts ) at each cursor", -> - editSession.buffer.setText("()\nab\n[]\n12") - editSession.setCursorBufferPosition([3, 1]) - editSession.addCursorAtBufferPosition([2, 1]) - editSession.addCursorAtBufferPosition([1, 1]) - editSession.addCursorAtBufferPosition([0, 1]) - editSession.insertText ')' - - expect(editSession.buffer.getText()).toBe "())\na)b\n[)]\n1)2" - - describe "when there is a non-word character after the cursor", -> - it "inserts a closing bracket after an opening bracket is inserted", -> - editSession.buffer.setText("}") - editSession.setCursorBufferPosition([0, 0]) - editSession.insertText '{' - expect(buffer.lineForRow(0)).toBe "{}}" - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - describe "when the cursor is at the end of the line", -> - it "inserts a closing bracket after an opening bracket is inserted", -> - editSession.buffer.setText("") - editSession.insertText '{' - expect(buffer.lineForRow(0)).toBe "{}" - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - editSession.buffer.setText("") - editSession.insertText '(' - expect(buffer.lineForRow(0)).toBe "()" - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - editSession.buffer.setText("") - editSession.insertText '[' - expect(buffer.lineForRow(0)).toBe "[]" - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - editSession.buffer.setText("") - editSession.insertText '"' - expect(buffer.lineForRow(0)).toBe '""' - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - editSession.buffer.setText("") - editSession.insertText "'" - expect(buffer.lineForRow(0)).toBe "''" - expect(editSession.getCursorBufferPosition()).toEqual([0,1]) - - describe "when the cursor is on a closing bracket and a closing bracket is inserted", -> - describe "when the closing bracket was there previously", -> - it "inserts a closing bracket", -> - editSession.insertText '()x' - editSession.setCursorBufferPosition([0, 1]) - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "())x" - expect(editSession.getCursorBufferPosition().column).toBe 2 - - describe "when the closing bracket was automatically inserted from inserting an opening bracket", -> - it "only moves cursor over the closing bracket one time", -> - editSession.insertText '(' - expect(buffer.lineForRow(0)).toBe "()" - editSession.setCursorBufferPosition([0, 1]) - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "()" - expect(editSession.getCursorBufferPosition()).toEqual [0, 2] - - editSession.setCursorBufferPosition([0, 1]) - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "())" - expect(editSession.getCursorBufferPosition()).toEqual [0, 2] - - it "moves cursor over the closing bracket after other text is inserted", -> - editSession.insertText '(' - editSession.insertText 'ok cool' - expect(buffer.lineForRow(0)).toBe "(ok cool)" - editSession.setCursorBufferPosition([0, 8]) - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "(ok cool)" - expect(editSession.getCursorBufferPosition()).toEqual [0, 9] - - it "works with nested brackets", -> - editSession.insertText '(' - editSession.insertText '1' - editSession.insertText '(' - editSession.insertText '2' - expect(buffer.lineForRow(0)).toBe "(1(2))" - editSession.setCursorBufferPosition([0, 4]) - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "(1(2))" - expect(editSession.getCursorBufferPosition()).toEqual [0, 5] - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "(1(2))" - expect(editSession.getCursorBufferPosition()).toEqual [0, 6] - - it "works with mixed brackets", -> - editSession.insertText '(' - editSession.insertText '}' - expect(buffer.lineForRow(0)).toBe "(})" - editSession.insertText ')' - expect(buffer.lineForRow(0)).toBe "(})" - expect(editSession.getCursorBufferPosition()).toEqual [0, 3] - - it "closes brackets with the same begin/end character correctly", -> - editSession.insertText '"' - editSession.insertText 'ok' - expect(buffer.lineForRow(0)).toBe '"ok"' - expect(editSession.getCursorBufferPosition()).toEqual [0, 3] - editSession.insertText '"' - expect(buffer.lineForRow(0)).toBe '"ok"' - expect(editSession.getCursorBufferPosition()).toEqual [0, 4] - - describe "when there is text selected on a single line", -> - it "wraps the selection with brackets", -> - editSession.insertText 'text' - editSession.moveCursorToBottom() - editSession.selectToTop() - editSession.selectAll() - editSession.insertText '(' - expect('(text)').toBe buffer.getText() - expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [0, 5]] - expect(editSession.getSelection().isReversed()).toBeTruthy() - - describe "when there is text selected on multiple lines", -> - it "wraps the selection with brackets", -> - editSession.insertText 'text\nabcd' - editSession.moveCursorToBottom() - editSession.selectToTop() - editSession.selectAll() - editSession.insertText '(' - expect('(text\nabcd)').toBe buffer.getText() - expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [1, 4]] - expect(editSession.getSelection().isReversed()).toBeTruthy() - - describe "when inserting a quote", -> - describe "when a word character is before the cursor", -> - it "does not automatically insert closing quote", -> - editSession.buffer.setText("abc") - editSession.setCursorBufferPosition([0, 3]) - editSession.insertText '"' - expect(buffer.lineForRow(0)).toBe "abc\"" - - editSession.buffer.setText("abc") - editSession.setCursorBufferPosition([0, 3]) - editSession.insertText '\'' - expect(buffer.lineForRow(0)).toBe "abc\'" - - describe "when a non word character is before the cursor", -> - it "automatically insert closing quote", -> - editSession.buffer.setText("ab@") - editSession.setCursorBufferPosition([0, 3]) - editSession.insertText '"' - expect(buffer.lineForRow(0)).toBe "ab@\"\"" - expect(editSession.getCursorBufferPosition()).toEqual [0, 4] - - describe "when the cursor is on an empty line", -> - it "automatically insert closing quote", -> - editSession.buffer.setText("") - editSession.setCursorBufferPosition([0, 0]) - editSession.insertText '"' - expect(buffer.lineForRow(0)).toBe "\"\"" - expect(editSession.getCursorBufferPosition()).toEqual [0, 1] - - describe "bracket deletion", -> - it "deletes the end bracket when it directly proceeds a begin bracket that is being backspaced", -> - buffer.setText("") - editSession.setCursorBufferPosition([0, 0]) - editSession.insertText '{' - expect(buffer.lineForRow(0)).toBe "{}" - editSession.backspace() - expect(buffer.lineForRow(0)).toBe "" - describe "javascript", -> beforeEach -> editSession = fixturesProject.buildEditSessionForPath('sample.js', autoIndent: false) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 1e2dc9786..3e4945118 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -50,6 +50,14 @@ describe "RootView", -> expect(rootView.getEditors()[0].getText()).toEqual "" expect(rootView.getTitle()).toBe 'untitled' + describe ".deactivate()", -> + it "deactivates all packages", -> + pack = atom.loadPackage("package-with-module") + atom.activateAtomPackage(pack) + spyOn(pack.packageMain, "deactivate").andCallThrough() + rootView.deactivate() + expect(pack.packageMain.deactivate).toHaveBeenCalled() + describe "@deserialize()", -> viewState = null @@ -151,25 +159,6 @@ describe "RootView", -> expect(rootView.find('.pane').length).toBe 1 expect(rootView.find('.pane').children().length).toBe 0 - describe ".serialize()", -> - it "absorbs exceptions that are thrown by the package module's serialize methods", -> - spyOn(console, 'error') - - rootView.activatePackage("bad-egg", - activate: -> - serialize: -> throw new Error("I'm broken") - ) - - rootView.activatePackage("good-egg" - activate: -> - serialize: -> "I still get called" - ) - - data = rootView.serialize() - expect(data.packageStates['good-egg']).toBe "I still get called" - expect(data.packageStates['bad-egg']).toBeUndefined() - expect(console.error).toHaveBeenCalled() - describe "focus", -> describe "when there is an active editor", -> it "hands off focus to the active editor", -> @@ -418,54 +407,6 @@ describe "RootView", -> rootView.focusNextPane() expect(view1.focus).toHaveBeenCalled() - describe "packages", -> - packageModule = null - - beforeEach -> - packageModule = - configDefaults: foo: { bar: 2, baz: 3 } - activate: jasmine.createSpy("activate") - deactivate: -> - serialize: -> "it worked" - - describe ".activatePackage(name, packageModule)", -> - it "calls activate on the package module", -> - rootView.activatePackage('package', packageModule) - expect(packageModule.activate).toHaveBeenCalledWith(rootView, undefined) - - it "calls activate on the package module with its previous state", -> - rootView.activatePackage('package', packageModule) - packageModule.activate.reset() - - newRootView = RootView.deserialize(rootView.serialize()) - newRootView.activatePackage('package', packageModule) - expect(packageModule.activate).toHaveBeenCalledWith(newRootView, "it worked") - newRootView.remove() - - it "loads config defaults based on the `configDefaults` key", -> - expect(config.get('foo.bar')).toBeUndefined() - rootView.activatePackage('package', packageModule) - config.set("package.foo.bar", 1) - expect(config.get('package.foo.bar')).toBe 1 - expect(config.get('package.foo.baz')).toBe 3 - - describe ".deactivatePackage(packageName)", -> - it "deactivates and removes the package module from the package module map", -> - rootView.activatePackage('package', packageModule) - expect(rootView.packageModules['package']).toBeTruthy() - spyOn(packageModule, "deactivate").andCallThrough() - rootView.deactivatePackage('package') - expect(packageModule.deactivate).toHaveBeenCalled() - expect(rootView.packageModules['package']).toBeFalsy() - - it "is called when the rootView is deactivated to deactivate all packages", -> - rootView.activatePackage('package', packageModule) - spyOn(rootView, "deactivatePackage").andCallThrough() - spyOn(packageModule, "deactivate").andCallThrough() - rootView.deactivate() - expect(rootView.deactivatePackage).toHaveBeenCalled() - expect(packageModule.deactivate).toHaveBeenCalled() - describe "keymap wiring", -> commandHandler = null beforeEach -> diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index 99613d9b9..c1a2fbc62 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -1,4 +1,5 @@ TextMateGrammar = require 'text-mate-grammar' +TextMatePackage = require 'text-mate-package' plist = require 'plist' fs = require 'fs' _ = require 'underscore' @@ -257,3 +258,14 @@ describe "TextMateGrammar", -> {tokens, ruleStack} = grammar.tokenizeLine("if(1){if(1){m()}}") expect(tokens[5]).toEqual value: "if", scopes: ["source.c", "meta.block.c", "keyword.control.c"] expect(tokens[10]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"] + + describe "when the grammar is CSON", -> + it "loads the grammar and correctly parses a keyword", -> + spyOn(syntax, 'addGrammar') + pack = new TextMatePackage(fixturesProject.resolve("packages/package-with-a-cson-grammar.tmbundle")) + pack.load() + grammar = pack.grammars[0] + expect(grammar).toBeTruthy() + expect(grammar.scopeName).toBe "source.alot" + {tokens} = grammar.tokenizeLine("this is alot of code") + expect(tokens[1]).toEqual value: "alot", scopes: ["source.alot", "keyword.alot"] diff --git a/spec/fixtures/packages/package-with-a-cson-grammar.tmbundle/Syntaxes/alot.cson b/spec/fixtures/packages/package-with-a-cson-grammar.tmbundle/Syntaxes/alot.cson new file mode 100644 index 000000000..d30d33f56 --- /dev/null +++ b/spec/fixtures/packages/package-with-a-cson-grammar.tmbundle/Syntaxes/alot.cson @@ -0,0 +1,11 @@ +'fileTypes': ['alot'] +'name': 'Alot' +'scopeName': 'source.alot' +'patterns': [ + { + 'captures': + '0': + 'name': 'keyword.alot' + 'match': 'alot' + } +] diff --git a/spec/fixtures/packages/package-with-activation-events/main.coffee b/spec/fixtures/packages/package-with-activation-events/main.coffee new file mode 100644 index 000000000..fc2588ef9 --- /dev/null +++ b/spec/fixtures/packages/package-with-activation-events/main.coffee @@ -0,0 +1,6 @@ +module.exports = + activationEventCallCount: 0 + + activate: -> + rootView.getActiveEditor()?.command 'activation-event', => + @activationEventCallCount++ diff --git a/spec/fixtures/packages/package-with-activation-events/package.cson b/spec/fixtures/packages/package-with-activation-events/package.cson new file mode 100644 index 000000000..80903d6f4 --- /dev/null +++ b/spec/fixtures/packages/package-with-activation-events/package.cson @@ -0,0 +1,2 @@ +'activationEvents': ['activation-event'] +'main': 'main' diff --git a/spec/fixtures/packages/package-with-module/index.coffee b/spec/fixtures/packages/package-with-module/index.coffee index 6b54f633a..49287b94d 100644 --- a/spec/fixtures/packages/package-with-module/index.coffee +++ b/spec/fixtures/packages/package-with-module/index.coffee @@ -1,6 +1,8 @@ -AtomPackage = require 'atom-package' - module.exports = -class MyPackage extends AtomPackage + configDefaults: + numbers: { one: 1, two: 2 } + activate: -> @activateCalled = true + + deactivate: -> \ No newline at end of file diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index d00160158..29a339235 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -17,12 +17,20 @@ fixturePackagesPath = require.resolve('fixtures/packages') require.paths.unshift(fixturePackagesPath) [bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = [] -# Load TextMate bundles, which specs rely on (but not other packages) -atom.loadTextMatePackages() +# Specs rely on TextMate bundles (but not atom packages) +window.loadTextMatePackages = -> + TextMatePackage = require 'text-mate-package' + config.packageDirPaths.unshift(fixturePackagesPath) + window.textMatePackages = [] + for path in atom.getPackagePaths() when TextMatePackage.testName(path) + window.textMatePackages.push atom.loadPackage(fs.base(path)) + +window.loadTextMatePackages() beforeEach -> window.fixturesProject = new Project(require.resolve('fixtures')) window.resetTimeouts() + atom.atomPackageStates = {} # used to reset keymap after each spec bindingSetsToRestore = _.clone(keymap.bindingSets) @@ -30,7 +38,6 @@ beforeEach -> # reset config before each spec; don't load or save from/to `config.json` window.config = new Config() - config.packageDirPaths.unshift(fixturePackagesPath) spyOn(config, 'load') spyOn(config, 'save') config.set "editor.fontSize", 16 diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index 15a4e465a..d9971e9c5 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -1,42 +1,88 @@ Package = require 'package' fs = require 'fs' +_ = require 'underscore' +$ = require 'jquery' module.exports = class AtomPackage extends Package metadata: null - keymapsDirPath: null - autoloadStylesheets: true + packageMain: null - constructor: (@name) -> - super - @keymapsDirPath = fs.join(@path, 'keymaps') - - load: -> + load: ({activateImmediately}={}) -> try @loadMetadata() @loadKeymaps() - @loadStylesheets() if @autoloadStylesheets - rootView?.activatePackage(@name, this) unless @isDirectory + @loadStylesheets() + if @metadata.activationEvents and not activateImmediately + @subscribeToActivationEvents() + else + @activatePackageMain() catch e console.warn "Failed to load package named '#{@name}'", e.stack this + disableEventHandlersOnBubblePath: (event) -> + bubblePathEventHandlers = [] + disabledHandler = -> + element = $(event.target) + while element.length + if eventHandlers = element.data('events')?[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 + + unsubscribeFromActivationEvents: (activateHandler) -> + if _.isArray(@metadata.activationEvents) + rootView.off(event, activateHandler) for event in @metadata.activationEvents + else + rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents + + subscribeToActivationEvents: () -> + activateHandler = (event) => + bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event) + @activatePackageMain() + $(event.target).trigger(event) + @restoreEventHandlersOnBubblePath(bubblePathEventHandlers) + @unsubscribeFromActivationEvents(activateHandler) + + if _.isArray(@metadata.activationEvents) + rootView.command(event, activateHandler) for event in @metadata.activationEvents + else + rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents + + activatePackageMain: -> + mainPath = @path + mainPath = fs.join(mainPath, @metadata.main) if @metadata.main + mainPath = require.resolve(mainPath) + if fs.isFile(mainPath) + @packageMain = require(mainPath) + config.setDefaults(@name, @packageMain.configDefaults) + atom.activateAtomPackage(this) + loadMetadata: -> - if metadataPath = fs.resolveExtension(fs.join(@path, "package"), ['cson', 'json']) + if metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json']) @metadata = fs.readObject(metadataPath) + @metadata ?= {} loadKeymaps: -> - if keymaps = @metadata?.keymaps - keymaps = keymaps.map (relativePath) => - fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', '']) - keymap.load(keymapPath) for keymapPath in keymaps + keymapsDirPath = fs.join(@path, 'keymaps') + + if @metadata.keymaps + for path in @metadata.keymaps + keymapPath = fs.resolve(keymapsDirPath, path, ['cson', 'json', '']) + keymap.load(keymapPath) else - keymap.loadDirectory(@keymapsDirPath) + keymap.loadDirectory(keymapsDirPath) loadStylesheets: -> - for stylesheetPath in @getStylesheetPaths() - requireStylesheet(stylesheetPath) - - getStylesheetPaths: -> stylesheetDirPath = fs.join(@path, 'stylesheets') - fs.list(stylesheetDirPath) + for stylesheetPath in fs.list(stylesheetDirPath) + requireStylesheet(stylesheetPath) \ No newline at end of file diff --git a/src/app/atom.coffee b/src/app/atom.coffee index b080e03bb..ec9246539 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -12,42 +12,58 @@ _.extend atom, exitWhenDone: window.location.params.exitWhenDone loadedThemes: [] pendingBrowserProcessCallbacks: {} + loadedPackages: [] + activatedAtomPackages: [] + atomPackageStates: {} + + activateAtomPackage: (pack) -> + @activatedAtomPackages.push(pack) + pack.packageMain.activate(@atomPackageStates[pack.name]) + + deactivateAtomPackages: -> + pack.packageMain.deactivate?() for pack in @activatedAtomPackages + @activatedAtomPackages = [] + + serializeAtomPackages: -> + packageStates = {} + for pack in @activatedAtomPackages + try + packageStates[pack.name] = pack.packageMain.serialize?() + catch e + console?.error("Exception serializing '#{pack.name}' package's module\n", e.stack) + packageStates + + loadPackage: (name, options) -> + packagePath = _.find @getPackagePaths(), (packagePath) -> fs.base(packagePath) == name + pack = Package.build(packagePath) + pack?.load(options) loadPackages: -> - {packages, asyncTextMatePackages} = _.groupBy @getPackages(), (pack) -> - if pack instanceof TextMatePackage and pack.name isnt 'text.tmbundle' - 'asyncTextMatePackages' + textMatePackages = [] + for path in @getPackagePaths() + pack = Package.build(path) + @loadedPackages.push(pack) + if pack instanceof TextMatePackage and fs.base(pack.path) isnt 'text.tmbundle' + textMatePackages.push(pack) else - 'packages' + pack.load() - pack.load() for pack in packages - if asyncTextMatePackages.length - new LoadTextMatePackagesTask(asyncTextMatePackages).start() + new LoadTextMatePackagesTask(textMatePackages).start() if textMatePackages.length > 0 - getPackages: -> - @packages ?= @getPackageNames().map((name) -> Package.build(name)) - .filter((pack) -> pack?) - new Array(@packages...) + getLoadedPackages: -> + _.clone(@loadedPackages) - loadTextMatePackages: -> - pack.load() for pack in @getTextMatePackages() - - getTextMatePackages: -> - @getPackages().filter (pack) -> pack instanceof TextMatePackage - - loadPackage: (name) -> - Package.build(name)?.load() - - getPackageNames: -> + getPackagePaths: -> disabledPackages = config.get("core.disabledPackages") ? [] - allPackageNames = [] + packagePaths = [] for packageDirPath in config.packageDirPaths - packageNames = fs.list(packageDirPath) - .filter((packagePath) -> fs.isDirectory(packagePath)) - .map((packagePath) -> fs.base(packagePath)) - allPackageNames.push(packageNames...) - _.unique(allPackageNames) - .filter (name) -> not _.contains(disabledPackages, name) + for packagePath in fs.list(packageDirPath) + continue if not fs.isDirectory(packagePath) + continue if fs.base(packagePath) in disabledPackages + continue if packagePath in packagePaths + packagePaths.push(packagePath) + + packagePaths loadThemes: -> themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax'] diff --git a/src/app/deferred-atom-package.coffee b/src/app/deferred-atom-package.coffee deleted file mode 100644 index 1f328fc02..000000000 --- a/src/app/deferred-atom-package.coffee +++ /dev/null @@ -1,36 +0,0 @@ -AtomPackage = require 'atom-package' -_ = require 'underscore' - -module.exports = -class DeferredAtomPackage extends AtomPackage - - constructor: -> - super - - @autoloadStylesheets = false - - activate: (@rootView, @state) -> - @instance = null - onLoadEvent = (e) => @onLoadEvent(e, @getInstance()) - if _.isArray(@loadEvents) - for event in @loadEvents - @rootView.command(event, onLoadEvent) - else - for event, selector of @loadEvents - @rootView.command(event, selector, onLoadEvent) - this - - deactivate: -> @instance?.deactivate?() - - serialize: -> - if @instance - @instance.serialize?() - else - @state - - getInstance: -> - unless @instance - @loadStylesheets() - InstanceClass = require @instanceClass - @instance = InstanceClass.activate(@rootView, @state) - @instance diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index 85148f3a7..3e1c1512c 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -8,78 +8,10 @@ class LanguageMode buffer = null grammar = null editSession = null - pairedCharacters: - '(': ')' - '[': ']' - '{': '}' - '"': '"' - "'": "'" constructor: (@editSession) -> @buffer = @editSession.buffer @reloadGrammar() - @bracketMarkers = [] - - _.adviseBefore @editSession, 'insertText', (text) => - return true if @editSession.hasMultipleCursors() - - cursorBufferPosition = @editSession.getCursorBufferPosition() - previousCharacter = @editSession.getTextInBufferRange([cursorBufferPosition.add([0, -1]), cursorBufferPosition]) - nextCharacter = @editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])]) - - if @isOpeningBracket(text) and not @editSession.getSelection().isEmpty() - @wrapSelectionInBrackets(text) - return false - - hasWordAfterCursor = /\w/.test(nextCharacter) - hasWordBeforeCursor = /\w/.test(previousCharacter) - - autoCompleteOpeningBracket = @isOpeningBracket(text) and not hasWordAfterCursor and not (@isQuote(text) and hasWordBeforeCursor) - skipOverExistingClosingBracket = false - if @isClosingBracket(text) and nextCharacter == text - if bracketMarker = _.find(@bracketMarkers, (marker) => @editSession.getMarkerBufferRange(marker)?.end.isEqual(cursorBufferPosition)) - skipOverExistingClosingBracket = true - - if skipOverExistingClosingBracket - @editSession.destroyMarker(bracketMarker) - _.remove(@bracketMarkers, bracketMarker) - @editSession.moveCursorRight() - false - else if autoCompleteOpeningBracket - @editSession.insertText(text + @pairedCharacters[text]) - @editSession.moveCursorLeft() - range = [cursorBufferPosition, cursorBufferPosition.add([0, text.length])] - @bracketMarkers.push @editSession.markBufferRange(range) - false - - _.adviseBefore @editSession, 'backspace', => - return if @editSession.hasMultipleCursors() - return unless @editSession.getSelection().isEmpty() - - cursorBufferPosition = @editSession.getCursorBufferPosition() - previousCharacter = @editSession.getTextInBufferRange([cursorBufferPosition.add([0, -1]), cursorBufferPosition]) - nextCharacter = @editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])]) - if @pairedCharacters[previousCharacter] is nextCharacter - @editSession.transact => - @editSession.moveCursorLeft() - @editSession.delete() - @editSession.delete() - false - - wrapSelectionInBrackets: (bracket) -> - pair = @pairedCharacters[bracket] - @editSession.mutateSelectedText (selection) => - return if selection.isEmpty() - - range = selection.getBufferRange() - options = reverse: selection.isReversed() - selection.insertText("#{bracket}#{selection.getText()}#{pair}") - selectionStart = range.start.add([0, 1]) - if range.start.row is range.end.row - selectionEnd = range.end.add([0, 1]) - else - selectionEnd = range.end - selection.setBufferRange([selectionStart, selectionEnd], options) reloadGrammar: -> path = @buffer.getPath() @@ -92,23 +24,6 @@ class LanguageMode throw new Error("No grammar found for path: #{path}") unless @grammar previousGrammar isnt @grammar - isQuote: (string) -> - /'|"/.test(string) - - isOpeningBracket: (string) -> - @pairedCharacters[string]? - - isClosingBracket: (string) -> - @getInvertedPairedCharacters()[string]? - - getInvertedPairedCharacters: -> - return @invertedPairedCharacters if @invertedPairedCharacters - - @invertedPairedCharacters = {} - for open, close of @pairedCharacters - @invertedPairedCharacters[close] = open - @invertedPairedCharacters - toggleLineCommentsForBufferRows: (start, end) -> scopes = @editSession.scopesForBufferPosition([start, 0]) return unless commentStartString = syntax.getProperty(scopes, "editor.commentStart") diff --git a/src/app/load-text-mate-packages-handler.coffee b/src/app/load-text-mate-packages-handler.coffee index c2e675bd1..c6dd9a18a 100644 --- a/src/app/load-text-mate-packages-handler.coffee +++ b/src/app/load-text-mate-packages-handler.coffee @@ -1,5 +1,5 @@ TextMatePackage = require 'text-mate-package' module.exports = - loadPackage: (name) -> - callTaskMethod('packageLoaded', new TextMatePackage(name).readGrammars()) + loadPackage: (path) -> + callTaskMethod('packageLoaded', new TextMatePackage(path).readGrammars()) diff --git a/src/app/load-text-mate-packages-task.coffee b/src/app/load-text-mate-packages-task.coffee index 0bb0ee805..9340f2c38 100644 --- a/src/app/load-text-mate-packages-task.coffee +++ b/src/app/load-text-mate-packages-task.coffee @@ -16,10 +16,10 @@ class LoadTextMatePackagesTask extends Task return @package = @packages.shift() - @loadPackage(@package.name) + @loadPackage(@package.path) - loadPackage: (name) -> - @callWorkerMethod('loadPackage', name) + loadPackage: (path) -> + @callWorkerMethod('loadPackage', path) packageLoaded: (grammars) -> @package.loadGrammars(grammars) diff --git a/src/app/package.coffee b/src/app/package.coffee index 01bdde1fa..5a7897347 100644 --- a/src/app/package.coffee +++ b/src/app/package.coffee @@ -2,34 +2,17 @@ fs = require 'fs' module.exports = class Package - @resolve: (name) -> - path = require.resolve(name, verifyExistence: false) - return path if path - throw new Error("No package found named '#{name}'") - - @build: (name) -> + @build: (path) -> TextMatePackage = require 'text-mate-package' AtomPackage = require 'atom-package' - if TextMatePackage.testName(name) - new TextMatePackage(name) + + if TextMatePackage.testName(path) + new TextMatePackage(path) else - if fs.isDirectory(@resolve(name)) - new AtomPackage(name) - else - try - PackageClass = require name - new PackageClass(name) if typeof PackageClass is 'function' - catch e - console.warn "Failed to load package named '#{name}'", e.stack + new AtomPackage(path) name: null path: null - isDirectory: false - module: null - constructor: (@name) -> - @path = Package.resolve(@name) - @isDirectory = fs.isDirectory(@path) - @path = fs.directory(@path) unless @isDirectory - - activate: (rootView) -> + constructor: (@path) -> + @name = fs.base(@path) diff --git a/src/app/project.coffee b/src/app/project.coffee index 7a6c3a219..058262ede 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -118,6 +118,10 @@ class Project getEditSessions: -> new Array(@editSessions...) + eachEditSession: (callback) -> + callback(editSession) for editSession in @getEditSessions() + @on 'edit-session-created', (editSession) -> callback(editSession) + removeEditSession: (editSession) -> _.remove(@editSessions, editSession) @@ -125,9 +129,12 @@ class Project buffers = [] for editSession in @editSessions when not _.include(buffers, editSession.buffer) buffers.push editSession.buffer - buffers + eachBuffer: (callback) -> + callback(buffer) for buffer in @getBuffers() + @on 'buffer-created', (buffer) -> callback(buffer) + bufferForPath: (filePath) -> if filePath? filePath = @resolve(filePath) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 2e298ba91..c7533f46e 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -29,19 +29,19 @@ class RootView extends View else projectOrPathToOpen = projectPath # This will migrate people over to the new project serialization scheme. It should be removed eventually. - rootView = new RootView(projectOrPathToOpen , packageStates: packageStates, suppressOpen: true) + atom.atomPackageStates = packageStates ? {} + + rootView = new RootView(projectOrPathToOpen , suppressOpen: true) rootView.setRootPane(rootView.deserializeView(panesViewState)) if panesViewState rootView - packageModules: null - packageStates: null + packages: null title: null pathToOpenIsFile: false - initialize: (projectOrPathToOpen, { @packageStates, suppressOpen } = {}) -> + initialize: (projectOrPathToOpen, { suppressOpen } = {}) -> window.rootView = this - @packageStates ?= {} - @packageModules = {} + @packages = [] @viewClasses = { "Pane": Pane, "PaneRow": PaneRow, @@ -68,7 +68,7 @@ class RootView extends View serialize: -> projectState: @project?.serialize() panesViewState: @panes.children().view()?.serialize() - packageStates: @serializePackages() + packageStates: atom.serializeAtomPackages() handleFocus: (e) -> if @getActiveEditor() @@ -118,33 +118,15 @@ class RootView extends View afterAttach: (onDom) -> @focus() if onDom - serializePackages: -> - packageStates = {} - for name, packageModule of @packageModules - try - packageStates[name] = packageModule.serialize?() - catch e - console?.error("Exception serializing '#{name}' package's module\n", e.stack) - packageStates - registerViewClass: (viewClass) -> @viewClasses[viewClass.name] = viewClass deserializeView: (viewState) -> @viewClasses[viewState.viewClass]?.deserialize(viewState, this) - activatePackage: (name, packageModule) -> - config.setDefaults(name, packageModule.configDefaults) if packageModule.configDefaults? - @packageModules[name] = packageModule - packageModule.activate(this, @packageStates[name]) - - deactivatePackage: (name) -> - @packageModules[name].deactivate?() - delete @packageModules[name] - deactivate: -> atom.setRootViewStateForPath(@project.getPath(), @serialize()) - @deactivatePackage(name) for name of @packageModules + atom.deactivateAtomPackages() @remove() open: (path, options = {}) -> @@ -274,9 +256,11 @@ class RootView extends View callback(editor) for editor in @getEditors() @on 'editor:attached', (e, editor) -> callback(editor) + eachEditSession: (callback) -> + @project.eachEditSession(callback) + eachBuffer: (callback) -> - callback(buffer) for buffer in @project.getBuffers() - @project.on 'buffer-created', (buffer) -> callback(buffer) + @project.eachBuffer(callback) indexOfPane: (pane) -> index = -1 diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index 64ca52e8f..5fb7d4c61 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -9,9 +9,12 @@ module.exports = class TextMateGrammar @readFromPath: (path) -> grammarContent = null - plist.parseString fs.read(path), (e, data) -> - throw new Error(e) if e - grammarContent = data[0] + if fs.extension(path) is '.cson' + grammarContent = fs.readObject(path) + else + plist.parseString fs.read(path), (e, data) -> + throw new Error(e) if e + grammarContent = data[0] throw new Error("Failed to load grammar at path `#{path}`") unless grammarContent grammarContent diff --git a/src/app/text-mate-package.coffee b/src/app/text-mate-package.coffee index 97eea6189..2a54d12fe 100644 --- a/src/app/text-mate-package.coffee +++ b/src/app/text-mate-package.coffee @@ -28,7 +28,7 @@ class TextMatePackage extends Package try @loadGrammars() catch e - console.warn "Failed to load package named '#{@name}'", e.stack + console.warn "Failed to load package at '#{@path}'", e.stack this getGrammars: -> @grammars diff --git a/src/app/window.coffee b/src/app/window.coffee index c71d381c8..901c6dea8 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -37,8 +37,8 @@ windowAdditions = # Note: RootView assigns itself on window on initialization so that # window.rootView is available when loading user configuration attachRootView: (pathToOpen) -> - if rootViewState = atom.getRootViewStateForPath(pathToOpen) - RootView.deserialize(rootViewState) + if pathState = atom.getRootViewStateForPath(pathToOpen) + RootView.deserialize(pathState) else new RootView(pathToOpen) diff --git a/src/packages/autocomplete/index.coffee b/src/packages/autocomplete/index.coffee deleted file mode 100644 index 902c08e24..000000000 --- a/src/packages/autocomplete/index.coffee +++ /dev/null @@ -1,6 +0,0 @@ -AtomPackage = require 'atom-package' -AutocompleteView = require './src/autocomplete-view' - -module.exports = -class Autocomplete extends AtomPackage - activate: (rootView) -> AutocompleteView.activate(rootView) diff --git a/src/packages/autocomplete/src/autocomplete-view.coffee b/src/packages/autocomplete/lib/autocomplete-view.coffee similarity index 97% rename from src/packages/autocomplete/src/autocomplete-view.coffee rename to src/packages/autocomplete/lib/autocomplete-view.coffee index f2c200d44..a3b3cc93f 100644 --- a/src/packages/autocomplete/src/autocomplete-view.coffee +++ b/src/packages/autocomplete/lib/autocomplete-view.coffee @@ -4,10 +4,6 @@ SelectList = require 'select-list' module.exports = class AutocompleteView extends SelectList - @activate: (rootView) -> - rootView.eachEditor (editor) -> - new AutocompleteView(editor) if editor.attached and not editor.mini - @viewClass: -> "autocomplete #{super} popover-list" editor: null @@ -21,7 +17,6 @@ class AutocompleteView extends SelectList initialize: (@editor) -> super - @handleEvents() @setCurrentBuffer(@editor.getBuffer()) diff --git a/src/packages/autocomplete/lib/autocomplete.coffee b/src/packages/autocomplete/lib/autocomplete.coffee new file mode 100644 index 000000000..d98ed5a75 --- /dev/null +++ b/src/packages/autocomplete/lib/autocomplete.coffee @@ -0,0 +1,10 @@ +AutocompleteView = require './autocomplete-view' + +module.exports = + autoCompleteViews: [] + + activate: -> + rootView.eachEditor (editor) => + if editor.attached and not editor.mini + @autoCompleteViews.push new AutocompleteView(editor) + diff --git a/src/packages/autocomplete/package.cson b/src/packages/autocomplete/package.cson new file mode 100644 index 000000000..45380b0c4 --- /dev/null +++ b/src/packages/autocomplete/package.cson @@ -0,0 +1,3 @@ +'main': 'lib/autocomplete' +'activationEvents': + 'autocomplete:attach': '.editor' \ No newline at end of file diff --git a/src/packages/autocomplete/spec/autocomplete-spec.coffee b/src/packages/autocomplete/spec/autocomplete-spec.coffee index d9b2da85a..84b9d666e 100644 --- a/src/packages/autocomplete/spec/autocomplete-spec.coffee +++ b/src/packages/autocomplete/spec/autocomplete-spec.coffee @@ -1,37 +1,33 @@ $ = require 'jquery' -Autocomplete = require 'autocomplete/src/autocomplete-view' +AutocompleteView = require 'autocomplete/lib/autocomplete-view' +Autocomplete = require 'autocomplete/lib/autocomplete' Buffer = require 'buffer' Editor = require 'editor' RootView = require 'root-view' describe "Autocomplete", -> - autocomplete = null - editor = null - miniEditor = null - beforeEach -> - editor = new Editor(editSession: fixturesProject.buildEditSessionForPath('sample.js')) - atom.loadPackage('autocomplete') - autocomplete = new Autocomplete(editor) - miniEditor = autocomplete.miniEditor + rootView = new RootView(require.resolve('fixtures/sample.js')) + rootView.simulateDomAttachment() afterEach -> - editor?.remove() + rootView.deactivate() - describe "@activate(rootView)", -> + describe "@activate()", -> it "activates autocomplete on all existing and future editors (but not on autocomplete's own mini editor)", -> - rootView = new RootView(require.resolve('fixtures/sample.js')) - rootView.simulateDomAttachment() - Autocomplete.activate(rootView) + spyOn(AutocompleteView.prototype, 'initialize').andCallThrough() + autocompletePackage = atom.loadPackage("autocomplete") + expect(AutocompleteView.prototype.initialize).not.toHaveBeenCalled() + leftEditor = rootView.getActiveEditor() rightEditor = rootView.getActiveEditor().splitRight() - spyOn(Autocomplete.prototype, 'initialize') - leftEditor.trigger 'autocomplete:attach' expect(leftEditor.find('.autocomplete')).toExist() expect(rightEditor.find('.autocomplete')).not.toExist() + expect(AutocompleteView.prototype.initialize).toHaveBeenCalled() + autoCompleteView = leftEditor.find('.autocomplete').view() autoCompleteView.trigger 'core:cancel' expect(leftEditor.find('.autocomplete')).not.toExist() @@ -39,9 +35,20 @@ describe "Autocomplete", -> rightEditor.trigger 'autocomplete:attach' expect(rightEditor.find('.autocomplete')).toExist() - expect(Autocomplete.prototype.initialize).not.toHaveBeenCalled() +describe "AutocompleteView", -> + autocomplete = null + editor = null + miniEditor = null - rootView.deactivate() + beforeEach -> + new RootView + editor = new Editor(editSession: fixturesProject.buildEditSessionForPath('sample.js')) + atom.loadPackage('autocomplete') + autocomplete = new AutocompleteView(editor) + miniEditor = autocomplete.miniEditor + + afterEach -> + editor?.remove() describe 'autocomplete:attach event', -> it "shows autocomplete view and focuses its mini-editor", -> diff --git a/src/packages/autoflow/index.coffee b/src/packages/autoflow/autoflow.coffee similarity index 91% rename from src/packages/autoflow/index.coffee rename to src/packages/autoflow/autoflow.coffee index 73b52d8ed..058b9c3b5 100644 --- a/src/packages/autoflow/index.coffee +++ b/src/packages/autoflow/autoflow.coffee @@ -1,8 +1,5 @@ -AtomPackage = require 'atom-package' - module.exports = -class Autoflow extends AtomPackage - activate: (rootView) -> + activate: -> rootView.command 'autoflow:reflow-paragraph', '.editor', (e) => @reflowParagraph(e.currentTargetView()) diff --git a/src/packages/autoflow/package.cson b/src/packages/autoflow/package.cson new file mode 100644 index 000000000..b137bc94e --- /dev/null +++ b/src/packages/autoflow/package.cson @@ -0,0 +1 @@ +'main': 'autoflow' \ No newline at end of file diff --git a/src/packages/bracket-matcher/index.coffee b/src/packages/bracket-matcher/lib/bracket-matcher.coffee similarity index 55% rename from src/packages/bracket-matcher/index.coffee rename to src/packages/bracket-matcher/lib/bracket-matcher.coffee index 74e8e9e8f..8cf47f879 100644 --- a/src/packages/bracket-matcher/index.coffee +++ b/src/packages/bracket-matcher/lib/bracket-matcher.coffee @@ -1,10 +1,15 @@ -AtomPackage = require 'atom-package' _ = require 'underscore' {$$} = require 'space-pen' Range = require 'range' module.exports = -class BracketMatcher extends AtomPackage + pairedCharacters: + '(': ')' + '[': ']' + '{': '}' + '"': '"' + "'": "'" + startPairMatches: '(': ')' '[': ']' @@ -17,8 +22,9 @@ class BracketMatcher extends AtomPackage pairHighlighted: false - activate: (rootView) -> + activate: -> rootView.eachEditor (editor) => @subscribeToEditor(editor) if editor.attached + rootView.eachEditSession (editSession) => @subscribeToEditSession(editSession) subscribeToEditor: (editor) -> editor.on 'cursor:moved.bracket-matcher', => @updateMatch(editor) @@ -118,3 +124,84 @@ class BracketMatcher extends AtomPackage underlayer.append(@createView(editor, matchPosition)) underlayer.append(@createView(editor, position)) @pairHighlighted = true + + subscribeToEditSession: (editSession) -> + @bracketMarkers = [] + + _.adviseBefore editSession, 'insertText', (text) => + return true if editSession.hasMultipleCursors() + + cursorBufferPosition = editSession.getCursorBufferPosition() + previousCharacter = editSession.getTextInBufferRange([cursorBufferPosition.add([0, -1]), cursorBufferPosition]) + nextCharacter = editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])]) + + if @isOpeningBracket(text) and not editSession.getSelection().isEmpty() + @wrapSelectionInBrackets(editSession, text) + return false + + hasWordAfterCursor = /\w/.test(nextCharacter) + hasWordBeforeCursor = /\w/.test(previousCharacter) + + autoCompleteOpeningBracket = @isOpeningBracket(text) and not hasWordAfterCursor and not (@isQuote(text) and hasWordBeforeCursor) + skipOverExistingClosingBracket = false + if @isClosingBracket(text) and nextCharacter == text + if bracketMarker = _.find(@bracketMarkers, (marker) => editSession.getMarkerBufferRange(marker)?.end.isEqual(cursorBufferPosition)) + skipOverExistingClosingBracket = true + + if skipOverExistingClosingBracket + editSession.destroyMarker(bracketMarker) + _.remove(@bracketMarkers, bracketMarker) + editSession.moveCursorRight() + false + else if autoCompleteOpeningBracket + editSession.insertText(text + @pairedCharacters[text]) + editSession.moveCursorLeft() + range = [cursorBufferPosition, cursorBufferPosition.add([0, text.length])] + @bracketMarkers.push editSession.markBufferRange(range) + false + + _.adviseBefore editSession, 'backspace', => + return if editSession.hasMultipleCursors() + return unless editSession.getSelection().isEmpty() + + cursorBufferPosition = editSession.getCursorBufferPosition() + previousCharacter = editSession.getTextInBufferRange([cursorBufferPosition.add([0, -1]), cursorBufferPosition]) + nextCharacter = editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])]) + if @pairedCharacters[previousCharacter] is nextCharacter + editSession.transact => + editSession.moveCursorLeft() + editSession.delete() + editSession.delete() + false + + wrapSelectionInBrackets: (editSession, bracket) -> + pair = @pairedCharacters[bracket] + editSession.mutateSelectedText (selection) => + return if selection.isEmpty() + + range = selection.getBufferRange() + options = reverse: selection.isReversed() + selection.insertText("#{bracket}#{selection.getText()}#{pair}") + selectionStart = range.start.add([0, 1]) + if range.start.row is range.end.row + selectionEnd = range.end.add([0, 1]) + else + selectionEnd = range.end + selection.setBufferRange([selectionStart, selectionEnd], options) + + isQuote: (string) -> + /'|"/.test(string) + + getInvertedPairedCharacters: -> + return @invertedPairedCharacters if @invertedPairedCharacters + + @invertedPairedCharacters = {} + for open, close of @pairedCharacters + @invertedPairedCharacters[close] = open + @invertedPairedCharacters + + isOpeningBracket: (string) -> + @pairedCharacters[string]? + + isClosingBracket: (string) -> + @getInvertedPairedCharacters()[string]? diff --git a/src/packages/bracket-matcher/package.cson b/src/packages/bracket-matcher/package.cson new file mode 100644 index 000000000..59e060d88 --- /dev/null +++ b/src/packages/bracket-matcher/package.cson @@ -0,0 +1 @@ +'main': 'lib/bracket-matcher' diff --git a/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee b/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee index 69e882502..6b07071c4 100644 --- a/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee +++ b/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee @@ -1,63 +1,66 @@ RootView = require 'root-view' describe "bracket matching", -> - [rootView, editor] = [] + [editor, editSession, buffer] = [] beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) atom.loadPackage('bracket-matcher') rootView.attachToDom() editor = rootView.getActiveEditor() + editSession = editor.activeEditSession + buffer = editSession.buffer afterEach -> rootView.deactivate() - describe "when the cursor is before a starting pair", -> - it "highlights the starting pair and ending pair", -> - editor.moveCursorToEndOfLine() - editor.moveCursorLeft() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) - expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) - - describe "when the cursor is after a starting pair", -> - it "highlights the starting pair and ending pair", -> - editor.moveCursorToEndOfLine() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) - expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) - - describe "when the cursor is before an ending pair", -> - it "highlights the starting pair and ending pair", -> - editor.moveCursorToBottom() - editor.moveCursorLeft() - editor.moveCursorLeft() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) - expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) - - describe "when the cursor is after an ending pair", -> - it "highlights the starting pair and ending pair", -> - editor.moveCursorToBottom() - editor.moveCursorLeft() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) - expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) - - describe "when the cursor is moved off a pair", -> - it "removes the starting pair and ending pair highlights", -> - editor.moveCursorToEndOfLine() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - editor.moveCursorToBeginningOfLine() - expect(editor.underlayer.find('.bracket-matcher').length).toBe 0 - - describe "pair balancing", -> - describe "when a second starting pair preceeds the first ending pair", -> - it "advances to the second ending pair", -> - editor.setCursorBufferPosition([8,42]) + describe "matching bracket highlighting", -> + describe "when the cursor is before a starting pair", -> + it "highlights the starting pair and ending pair", -> + editor.moveCursorToEndOfLine() + editor.moveCursorLeft() expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 - expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([8,42]) - expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([8,54]) + expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) + expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) + + describe "when the cursor is after a starting pair", -> + it "highlights the starting pair and ending pair", -> + editor.moveCursorToEndOfLine() + expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 + expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) + expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) + + describe "when the cursor is before an ending pair", -> + it "highlights the starting pair and ending pair", -> + editor.moveCursorToBottom() + editor.moveCursorLeft() + editor.moveCursorLeft() + expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 + expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) + expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) + + describe "when the cursor is after an ending pair", -> + it "highlights the starting pair and ending pair", -> + editor.moveCursorToBottom() + editor.moveCursorLeft() + expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 + expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0]) + expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28]) + + describe "when the cursor is moved off a pair", -> + it "removes the starting pair and ending pair highlights", -> + editor.moveCursorToEndOfLine() + expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 + editor.moveCursorToBeginningOfLine() + expect(editor.underlayer.find('.bracket-matcher').length).toBe 0 + + describe "pair balancing", -> + describe "when a second starting pair preceeds the first ending pair", -> + it "advances to the second ending pair", -> + editor.setCursorBufferPosition([8,42]) + expect(editor.underlayer.find('.bracket-matcher').length).toBe 2 + expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([8,42]) + expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([8,54]) describe "when editor:go-to-matching-bracket is triggered", -> describe "when the cursor is before the starting pair", -> @@ -84,3 +87,189 @@ describe "bracket matching", -> editor.setCursorBufferPosition([12, 1]) editor.trigger "editor:go-to-matching-bracket" expect(editor.getCursorBufferPosition()).toEqual [0, 28] + + describe "matching bracket insertion", -> + beforeEach -> + editSession.buffer.setText("") + + describe "when more than one character is inserted", -> + it "does not insert a matching bracket", -> + editSession.insertText("woah(") + expect(editSession.buffer.getText()).toBe "woah(" + + describe "when there is a word character after the cursor", -> + it "does not insert a matching bracket", -> + editSession.buffer.setText("ab") + editSession.setCursorBufferPosition([0, 1]) + editSession.insertText("(") + + expect(editSession.buffer.getText()).toBe "a(b" + + describe "when there are multiple cursors", -> + it "inserts ) at each cursor", -> + editSession.buffer.setText("()\nab\n[]\n12") + editSession.setCursorBufferPosition([3, 1]) + editSession.addCursorAtBufferPosition([2, 1]) + editSession.addCursorAtBufferPosition([1, 1]) + editSession.addCursorAtBufferPosition([0, 1]) + editSession.insertText ')' + + expect(editSession.buffer.getText()).toBe "())\na)b\n[)]\n1)2" + + describe "when there is a non-word character after the cursor", -> + it "inserts a closing bracket after an opening bracket is inserted", -> + editSession.buffer.setText("}") + editSession.setCursorBufferPosition([0, 0]) + editSession.insertText '{' + expect(buffer.lineForRow(0)).toBe "{}}" + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + describe "when the cursor is at the end of the line", -> + it "inserts a closing bracket after an opening bracket is inserted", -> + editSession.buffer.setText("") + editSession.insertText '{' + expect(buffer.lineForRow(0)).toBe "{}" + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + editSession.buffer.setText("") + editSession.insertText '(' + expect(buffer.lineForRow(0)).toBe "()" + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + editSession.buffer.setText("") + editSession.insertText '[' + expect(buffer.lineForRow(0)).toBe "[]" + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + editSession.buffer.setText("") + editSession.insertText '"' + expect(buffer.lineForRow(0)).toBe '""' + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + editSession.buffer.setText("") + editSession.insertText "'" + expect(buffer.lineForRow(0)).toBe "''" + expect(editSession.getCursorBufferPosition()).toEqual([0,1]) + + describe "when the cursor is on a closing bracket and a closing bracket is inserted", -> + describe "when the closing bracket was there previously", -> + it "inserts a closing bracket", -> + editSession.insertText '()x' + editSession.setCursorBufferPosition([0, 1]) + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "())x" + expect(editSession.getCursorBufferPosition().column).toBe 2 + + describe "when the closing bracket was automatically inserted from inserting an opening bracket", -> + it "only moves cursor over the closing bracket one time", -> + editSession.insertText '(' + expect(buffer.lineForRow(0)).toBe "()" + editSession.setCursorBufferPosition([0, 1]) + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "()" + expect(editSession.getCursorBufferPosition()).toEqual [0, 2] + + editSession.setCursorBufferPosition([0, 1]) + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "())" + expect(editSession.getCursorBufferPosition()).toEqual [0, 2] + + it "moves cursor over the closing bracket after other text is inserted", -> + editSession.insertText '(' + editSession.insertText 'ok cool' + expect(buffer.lineForRow(0)).toBe "(ok cool)" + editSession.setCursorBufferPosition([0, 8]) + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "(ok cool)" + expect(editSession.getCursorBufferPosition()).toEqual [0, 9] + + it "works with nested brackets", -> + editSession.insertText '(' + editSession.insertText '1' + editSession.insertText '(' + editSession.insertText '2' + expect(buffer.lineForRow(0)).toBe "(1(2))" + editSession.setCursorBufferPosition([0, 4]) + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "(1(2))" + expect(editSession.getCursorBufferPosition()).toEqual [0, 5] + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "(1(2))" + expect(editSession.getCursorBufferPosition()).toEqual [0, 6] + + it "works with mixed brackets", -> + editSession.insertText '(' + editSession.insertText '}' + expect(buffer.lineForRow(0)).toBe "(})" + editSession.insertText ')' + expect(buffer.lineForRow(0)).toBe "(})" + expect(editSession.getCursorBufferPosition()).toEqual [0, 3] + + it "closes brackets with the same begin/end character correctly", -> + editSession.insertText '"' + editSession.insertText 'ok' + expect(buffer.lineForRow(0)).toBe '"ok"' + expect(editSession.getCursorBufferPosition()).toEqual [0, 3] + editSession.insertText '"' + expect(buffer.lineForRow(0)).toBe '"ok"' + expect(editSession.getCursorBufferPosition()).toEqual [0, 4] + + describe "when there is text selected on a single line", -> + it "wraps the selection with brackets", -> + editSession.insertText 'text' + editSession.moveCursorToBottom() + editSession.selectToTop() + editSession.selectAll() + editSession.insertText '(' + expect('(text)').toBe buffer.getText() + expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [0, 5]] + expect(editSession.getSelection().isReversed()).toBeTruthy() + + describe "when there is text selected on multiple lines", -> + it "wraps the selection with brackets", -> + editSession.insertText 'text\nabcd' + editSession.moveCursorToBottom() + editSession.selectToTop() + editSession.selectAll() + editSession.insertText '(' + expect('(text\nabcd)').toBe buffer.getText() + expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [1, 4]] + expect(editSession.getSelection().isReversed()).toBeTruthy() + + describe "when inserting a quote", -> + describe "when a word character is before the cursor", -> + it "does not automatically insert closing quote", -> + editSession.buffer.setText("abc") + editSession.setCursorBufferPosition([0, 3]) + editSession.insertText '"' + expect(buffer.lineForRow(0)).toBe "abc\"" + + editSession.buffer.setText("abc") + editSession.setCursorBufferPosition([0, 3]) + editSession.insertText '\'' + expect(buffer.lineForRow(0)).toBe "abc\'" + + describe "when a non word character is before the cursor", -> + it "automatically insert closing quote", -> + editSession.buffer.setText("ab@") + editSession.setCursorBufferPosition([0, 3]) + editSession.insertText '"' + expect(buffer.lineForRow(0)).toBe "ab@\"\"" + expect(editSession.getCursorBufferPosition()).toEqual [0, 4] + + describe "when the cursor is on an empty line", -> + it "automatically insert closing quote", -> + editSession.buffer.setText("") + editSession.setCursorBufferPosition([0, 0]) + editSession.insertText '"' + expect(buffer.lineForRow(0)).toBe "\"\"" + expect(editSession.getCursorBufferPosition()).toEqual [0, 1] + + describe "matching bracket deletion", -> + it "deletes the end bracket when it directly proceeds a begin bracket that is being backspaced", -> + buffer.setText("") + editSession.setCursorBufferPosition([0, 0]) + editSession.insertText '{' + expect(buffer.lineForRow(0)).toBe "{}" + editSession.backspace() + expect(buffer.lineForRow(0)).toBe "" diff --git a/src/packages/command-logger/src/command-logger-view.coffee b/src/packages/command-logger/lib/command-logger-view.coffee similarity index 95% rename from src/packages/command-logger/src/command-logger-view.coffee rename to src/packages/command-logger/lib/command-logger-view.coffee index 9dfa03360..584c01d5a 100644 --- a/src/packages/command-logger/src/command-logger-view.coffee +++ b/src/packages/command-logger/lib/command-logger-view.coffee @@ -4,18 +4,12 @@ _ = require 'underscore' module.exports = class CommandLoggerView extends ScrollView - @activate: (rootView, state) -> - @instance = new CommandLoggerView(rootView) - @content: (rootView) -> @div class: 'command-logger', tabindex: -1, => @h1 class: 'category-header', outlet: 'categoryHeader' @h1 class: 'category-summary', outlet: 'categorySummary' @div class: 'tree-map', outlet: 'treeMap' - @serialize: -> - @instance.serialize() - eventLog: null ignoredEvents: [ 'core:backspace' @@ -30,7 +24,7 @@ class CommandLoggerView extends ScrollView 'tree-view:directory-modified' ] - initialize: (@rootView) -> + initialize: -> super @command 'core:cancel', => @detach() @@ -176,7 +170,7 @@ class CommandLoggerView extends ScrollView d3.select('.command-logger').on('click', -> zoom(root)) attach: -> - @rootView.append(this) + rootView.append(this) @addTreeMap() @focus() @@ -184,8 +178,5 @@ class CommandLoggerView extends ScrollView return if @detaching @detaching = true super - @rootView.focus() + rootView.focus() @detaching = false - - serialize: -> - eventLog: @eventLog diff --git a/src/packages/command-logger/index.coffee b/src/packages/command-logger/lib/command-logger.coffee similarity index 50% rename from src/packages/command-logger/index.coffee rename to src/packages/command-logger/lib/command-logger.coffee index 027035980..074f2e015 100644 --- a/src/packages/command-logger/index.coffee +++ b/src/packages/command-logger/lib/command-logger.coffee @@ -1,18 +1,14 @@ -DeferredAtomPackage = require 'deferred-atom-package' $ = require 'jquery' module.exports = -class CommandLogger extends DeferredAtomPackage - - loadEvents: ['command-logger:toggle'] - - instanceClass: 'command-logger/src/command-logger-view' - - activate: (rootView, state={})-> - super + eventLog: {} + commandLoggerView: null + originalTrigger: null + activate: (state={})-> @eventLog = state.eventLog ? {} rootView.command 'command-logger:clear-data', => @eventLog = {} + rootView.command 'command-logger:toggle', => @createView().toggle(@eventLog) registerTriggeredEvent = (eventName) => eventNameLog = @eventLog[eventName] @@ -23,10 +19,22 @@ class CommandLogger extends DeferredAtomPackage @eventLog[eventName] = eventNameLog eventNameLog.count++ eventNameLog.lastRun = new Date().getTime() - originalTrigger = $.fn.trigger + trigger = $.fn.trigger + @originalTrigger = trigger $.fn.trigger = (eventName) -> eventName = eventName.type if eventName.type registerTriggeredEvent(eventName) if $(this).events()[eventName] - originalTrigger.apply(this, arguments) + trigger.apply(this, arguments) - onLoadEvent: (event, instance) -> instance.toggle(@eventLog) + deactivate: -> + $.fn.trigger = @originalTrigger if @originalTrigger? + @commandLoggerView = null + @eventLog = {} + + serialize: -> + {@eventLog} + + createView: -> + unless @commandLoggerView + CommandLoggerView = require 'command-logger/lib/command-logger-view' + @commandLoggerView = new CommandLoggerView diff --git a/src/packages/command-logger/package.cson b/src/packages/command-logger/package.cson new file mode 100644 index 000000000..ae57a4b8c --- /dev/null +++ b/src/packages/command-logger/package.cson @@ -0,0 +1 @@ +'main': 'lib/command-logger' diff --git a/src/packages/command-logger/spec/command-logger-spec.coffee b/src/packages/command-logger/spec/command-logger-spec.coffee index e2a06da2b..ab9d4e478 100644 --- a/src/packages/command-logger/spec/command-logger-spec.coffee +++ b/src/packages/command-logger/spec/command-logger-spec.coffee @@ -1,12 +1,13 @@ RootView = require 'root-view' -CommandLogger = require 'command-logger/src/command-logger-view' +CommandLogger = require 'command-logger/lib/command-logger-view' describe "CommandLogger", -> - [rootView, commandLogger, editor] = [] + [commandLogger, editor] = [] beforeEach -> - rootView = new RootView(require.resolve('fixtures/sample.js')) - commandLogger = atom.loadPackage('command-logger') + new RootView(require.resolve('fixtures/sample.js')) + commandLogger = atom.loadPackage('command-logger').packageMain + commandLogger.eventLog = {} editor = rootView.getActiveEditor() afterEach -> @@ -43,7 +44,7 @@ describe "CommandLogger", -> describe "when an event is ignored", -> it "does not create a node for that event", -> - commandLoggerView = commandLogger.getInstance() + commandLoggerView = commandLogger.createView() commandLoggerView.ignoredEvents.push 'editor:delete-line' editor.trigger 'editor:delete-line' commandLoggerView.eventLog = commandLogger.eventLog diff --git a/src/packages/command-palette/index.coffee b/src/packages/command-palette/index.coffee deleted file mode 100644 index 15d41efb5..000000000 --- a/src/packages/command-palette/index.coffee +++ /dev/null @@ -1,10 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class CommandPalette extends DeferredAtomPackage - - loadEvents: ['command-palette:toggle'] - - instanceClass: 'command-palette/src/command-palette-view' - - onLoadEvent: (event, instance) -> instance.attach() diff --git a/src/packages/command-palette/src/command-palette-view.coffee b/src/packages/command-palette/lib/command-palette-view.coffee similarity index 85% rename from src/packages/command-palette/src/command-palette-view.coffee rename to src/packages/command-palette/lib/command-palette-view.coffee index 90c083d0d..2acb80753 100644 --- a/src/packages/command-palette/src/command-palette-view.coffee +++ b/src/packages/command-palette/lib/command-palette-view.coffee @@ -5,8 +5,8 @@ _ = require 'underscore' module.exports = class CommandPaletteView extends SelectList - @activate: (rootView) -> - @instance = new CommandPaletteView(rootView) + @activate: -> + new CommandPaletteView @viewClass: -> "#{super} command-palette overlay from-top" @@ -16,12 +16,17 @@ class CommandPaletteView extends SelectList previouslyFocusedElement: null keyBindings: null - initialize: (@rootView) -> - @command 'command-palette:toggle', => - @cancel() - false + initialize: -> super + rootView.command 'command-palette:toggle', => @toggle() + + toggle: -> + if @hasParent() + @cancel() + else + @attach() + attach: -> super @@ -34,7 +39,7 @@ class CommandPaletteView extends SelectList events = _.sortBy events, (e) -> e.eventDescription @setArray(events) - @appendTo(@rootView) + @appendTo(rootView) @miniEditor.focus() itemForElement: ({eventName, eventDescription}) -> diff --git a/src/packages/command-palette/package.cson b/src/packages/command-palette/package.cson new file mode 100644 index 000000000..e76ea2d38 --- /dev/null +++ b/src/packages/command-palette/package.cson @@ -0,0 +1,2 @@ +'main': 'lib/command-palette-view' +'activationEvents': ['command-palette:toggle'] diff --git a/src/packages/command-palette/spec/command-palette-spec.coffee b/src/packages/command-palette/spec/command-palette-spec.coffee index 1f56f1ab5..a57533dd9 100644 --- a/src/packages/command-palette/spec/command-palette-spec.coffee +++ b/src/packages/command-palette/spec/command-palette-spec.coffee @@ -1,17 +1,17 @@ RootView = require 'root-view' -CommandPalette = require 'command-palette/src/command-palette-view' +CommandPalette = require 'command-palette/lib/command-palette-view' $ = require 'jquery' _ = require 'underscore' describe "CommandPalette", -> - [rootView, palette] = [] + [palette] = [] beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) - atom.loadPackage("command-palette").getInstance() - palette = CommandPalette.instance + atom.loadPackage("command-palette") rootView.attachToDom().focus() rootView.trigger 'command-palette:toggle' + palette = rootView.find('.command-palette').view() afterEach -> rootView.remove() diff --git a/src/packages/command-panel/index.coffee b/src/packages/command-panel/index.coffee deleted file mode 100644 index 4f1a26c8a..000000000 --- a/src/packages/command-panel/index.coffee +++ /dev/null @@ -1,33 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class CommandPanel extends DeferredAtomPackage - - loadEvents: [ - 'command-panel:toggle' - 'command-panel:toggle-preview' - 'command-panel:find-in-file' - 'command-panel:find-in-project' - 'command-panel:repeat-relative-address' - 'command-panel:repeat-relative-address-in-reverse' - 'command-panel:set-selection-as-regex-address' - ] - - instanceClass: 'command-panel/src/command-panel-view' - - onLoadEvent: (event, instance) -> - switch event.type - when 'command-panel:toggle' - instance.toggle() - when 'command-panel:toggle-preview' - instance.togglePreview() - when 'command-panel:find-in-file' - instance.attach("/") - when 'command-panel:find-in-project' - instance.attach("Xx/") - when 'command-panel:repeat-relative-address' - instance.repeatRelativeAddress() - when 'command-panel:repeat-relative-address-in-reverse' - instance.repeatRelativeAddressInReverse() - when 'command-panel:set-selection-as-regex-address' - instance.setSelectionAsLastRelativeAddress() diff --git a/src/packages/command-panel/src/command-interpreter.coffee b/src/packages/command-panel/lib/command-interpreter.coffee similarity index 95% rename from src/packages/command-panel/src/command-interpreter.coffee rename to src/packages/command-panel/lib/command-interpreter.coffee index 0c8a08d8a..c2328ceed 100644 --- a/src/packages/command-panel/src/command-interpreter.coffee +++ b/src/packages/command-panel/lib/command-interpreter.coffee @@ -6,7 +6,7 @@ class CommandInterpreter constructor: (@project) -> eval: (string, activeEditSession) -> - @parser ?= PEG.buildParser(fs.read(require.resolve 'command-panel/commands.pegjs')) + @parser ?= PEG.buildParser(fs.read(require.resolve 'command-panel/lib/commands.pegjs')) compositeCommand = @parser.parse(string) @lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress() compositeCommand.execute(@project, activeEditSession) diff --git a/src/packages/command-panel/src/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee similarity index 69% rename from src/packages/command-panel/src/command-panel-view.coffee rename to src/packages/command-panel/lib/command-panel-view.coffee index d27711a70..0cb7c1e1a 100644 --- a/src/packages/command-panel/src/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -1,28 +1,15 @@ {View, $$, $$$} = require 'space-pen' -CommandInterpreter = require 'command-panel/src/command-interpreter' -RegexAddress = require 'command-panel/src/commands/regex-address' -CompositeCommand = require 'command-panel/src/commands/composite-command' -PreviewList = require 'command-panel/src/preview-list' +CommandInterpreter = require './command-interpreter' +RegexAddress = require './commands/regex-address' +CompositeCommand = require './commands/composite-command' +PreviewList = require './preview-list' Editor = require 'editor' {SyntaxError} = require('pegjs').parser - _ = require 'underscore' module.exports = class CommandPanelView extends View - @activate: (rootView, state) -> - if state? - @instance = @deserialize(state, rootView) - else - @instance = new CommandPanelView(rootView) - - @deserialize: (state, rootView) -> - commandPanel = new CommandPanelView(rootView, state.history) - commandPanel.attach(state.text, focus: false) if state.visible - commandPanel.miniEditor.focus() if state.miniEditorFocused - commandPanel - - @content: (rootView) -> + @content: -> @div class: 'command-panel tool-panel', => @div outlet: 'previewCount', class: 'preview-count' @subview 'previewList', new PreviewList(rootView) @@ -36,38 +23,42 @@ class CommandPanelView extends View historyIndex: 0 maxSerializedHistorySize: 100 - initialize: (@rootView, @history) -> - @commandInterpreter = new CommandInterpreter(@rootView.project) + initialize: (state={}) -> + @commandInterpreter = new CommandInterpreter(rootView.project) - @history ?= [] - @historyIndex = @history.length - - @command 'tool-panel:unfocus', => @rootView.focus() + @command 'tool-panel:unfocus', => rootView.focus() @command 'core:close', => @detach(); false @command 'core:confirm', => @execute() @command 'core:move-up', => @navigateBackwardInHistory() @command 'core:move-down', => @navigateForwardInHistory() + rootView.command 'command-panel:toggle', => @toggle() + rootView.command 'command-panel:toggle-preview', => @togglePreview() + rootView.command 'command-panel:find-in-file', => @attach('/') + rootView.command 'command-panel:find-in-project', => @attach('Xx/') + rootView.command 'command-panel:repeat-relative-address', => @repeatRelativeAddress() + rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddressInReverse() + rootView.command 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress() + @previewList.hide() @previewCount.hide() @errorMessages.hide() @prompt.iconSize(@miniEditor.getFontSize()) + @history = state.history ? [] + @historyIndex = @history.length + serialize: -> text: @miniEditor.getText() - visible: @hasParent() - miniEditorFocused: @miniEditor.isFocused history: @history[-@maxSerializedHistorySize..] - deactivate: -> @destroy() - destroy: -> @previewList.destroy() toggle: -> if @miniEditor.isFocused @detach() - @rootView.focus() + rootView.focus() else @attach() unless @hasParent() @miniEditor.focus() @@ -77,7 +68,7 @@ class CommandPanelView extends View @previewList.hide() @previewCount.hide() @detach() - @rootView.focus() + rootView.focus() else @attach() unless @hasParent() if @previewList.hasOperations() @@ -90,13 +81,13 @@ class CommandPanelView extends View @errorMessages.hide() focus = options.focus ? true - @rootView.vertical.append(this) + rootView.vertical.append(this) @miniEditor.focus() if focus @miniEditor.setText(text) @miniEditor.setCursorBufferPosition([0, Infinity]) detach: -> - @rootView.focus() + rootView.focus() @previewList.hide() @previewCount.hide() super @@ -108,7 +99,7 @@ class CommandPanelView extends View @errorMessages.empty() try - @commandInterpreter.eval(command, @rootView.getActiveEditSession()).done ({operationsToPreview, errorMessages}) => + @commandInterpreter.eval(command, rootView.getActiveEditSession()).done ({operationsToPreview, errorMessages}) => @history.push(command) @historyIndex = @history.length @@ -141,12 +132,12 @@ class CommandPanelView extends View @miniEditor.setText(@history[@historyIndex] or '') repeatRelativeAddress: -> - @commandInterpreter.repeatRelativeAddress(@rootView.getActiveEditSession()) + @commandInterpreter.repeatRelativeAddress(rootView.getActiveEditSession()) repeatRelativeAddressInReverse: -> - @commandInterpreter.repeatRelativeAddressInReverse(@rootView.getActiveEditSession()) + @commandInterpreter.repeatRelativeAddressInReverse(rootView.getActiveEditSession()) setSelectionAsLastRelativeAddress: -> - selection = @rootView.getActiveEditor().getSelectedText() + selection = rootView.getActiveEditor().getSelectedText() regex = _.escapeRegExp(selection) @commandInterpreter.lastRelativeAddress = new CompositeCommand([new RegexAddress(regex)]) diff --git a/src/packages/command-panel/lib/command-panel.coffee b/src/packages/command-panel/lib/command-panel.coffee new file mode 100644 index 000000000..1159e9ce4 --- /dev/null +++ b/src/packages/command-panel/lib/command-panel.coffee @@ -0,0 +1,17 @@ +CommandPanelView = require './command-panel-view' + +module.exports = + commandPanelView: null + + activate: (@state) -> + @commandPanelView = new CommandPanelView(@state) + + deactivate: -> + @commandPanelView?.destroy() + @commandPanelView = null + + serialize: -> + if @commandPanelView? + @commandPanelView.serialize() + else + @state diff --git a/src/packages/command-panel/commands.pegjs b/src/packages/command-panel/lib/commands.pegjs similarity index 70% rename from src/packages/command-panel/commands.pegjs rename to src/packages/command-panel/lib/commands.pegjs index 7536319dc..81f1b9c8e 100644 --- a/src/packages/command-panel/commands.pegjs +++ b/src/packages/command-panel/lib/commands.pegjs @@ -1,15 +1,15 @@ { - var CompositeCommand = require('command-panel/src/commands/composite-command') - var Substitution = require('command-panel/src/commands/substitution'); - var ZeroAddress = require('command-panel/src/commands/zero-address'); - var EofAddress = require('command-panel/src/commands/eof-address'); - var LineAddress = require('command-panel/src/commands/line-address'); - var AddressRange = require('command-panel/src/commands/address-range'); - var DefaultAddressRange = require('command-panel/src/commands/default-address-range'); - var CurrentSelectionAddress = require('command-panel/src/commands/current-selection-address') - var RegexAddress = require('command-panel/src/commands/regex-address') - var SelectAllMatches = require('command-panel/src/commands/select-all-matches') - var SelectAllMatchesInProject = require('command-panel/src/commands/select-all-matches-in-project') + var CompositeCommand = require('command-panel/lib/commands/composite-command') + var Substitution = require('command-panel/lib/commands/substitution'); + var ZeroAddress = require('command-panel/lib/commands/zero-address'); + var EofAddress = require('command-panel/lib/commands/eof-address'); + var LineAddress = require('command-panel/lib/commands/line-address'); + var AddressRange = require('command-panel/lib/commands/address-range'); + var DefaultAddressRange = require('command-panel/lib/commands/default-address-range'); + var CurrentSelectionAddress = require('command-panel/lib/commands/current-selection-address') + var RegexAddress = require('command-panel/lib/commands/regex-address') + var SelectAllMatches = require('command-panel/lib/commands/select-all-matches') + var SelectAllMatchesInProject = require('command-panel/lib/commands/select-all-matches-in-project') } start = _ commands:( selectAllMatchesInProject / textCommand ) { diff --git a/src/packages/command-panel/src/commands/address-range.coffee b/src/packages/command-panel/lib/commands/address-range.coffee similarity index 85% rename from src/packages/command-panel/src/commands/address-range.coffee rename to src/packages/command-panel/lib/commands/address-range.coffee index 3e4291011..8c4c976f2 100644 --- a/src/packages/command-panel/src/commands/address-range.coffee +++ b/src/packages/command-panel/lib/commands/address-range.coffee @@ -1,4 +1,4 @@ -Address = require 'command-panel/src/commands/address' +Address = require 'command-panel/lib/commands/address' Range = require 'range' module.exports = diff --git a/src/packages/command-panel/src/commands/address.coffee b/src/packages/command-panel/lib/commands/address.coffee similarity index 79% rename from src/packages/command-panel/src/commands/address.coffee rename to src/packages/command-panel/lib/commands/address.coffee index e5e4d66a9..7d0f88214 100644 --- a/src/packages/command-panel/src/commands/address.coffee +++ b/src/packages/command-panel/lib/commands/address.coffee @@ -1,5 +1,5 @@ -Command = require 'command-panel/src/commands/command' -Operation = require 'command-panel/src/operation' +Command = require './command' +Operation = require 'command-panel/lib/operation' $ = require 'jquery' module.exports = diff --git a/src/packages/command-panel/src/commands/command.coffee b/src/packages/command-panel/lib/commands/command.coffee similarity index 83% rename from src/packages/command-panel/src/commands/command.coffee rename to src/packages/command-panel/lib/commands/command.coffee index b07dd1a83..16a4e5a07 100644 --- a/src/packages/command-panel/src/commands/command.coffee +++ b/src/packages/command-panel/lib/commands/command.coffee @@ -1,5 +1,3 @@ -_ = require 'underscore' - module.exports = class Command isAddress: -> false diff --git a/src/packages/command-panel/src/commands/composite-command.coffee b/src/packages/command-panel/lib/commands/composite-command.coffee similarity index 100% rename from src/packages/command-panel/src/commands/composite-command.coffee rename to src/packages/command-panel/lib/commands/composite-command.coffee diff --git a/src/packages/command-panel/src/commands/current-selection-address.coffee b/src/packages/command-panel/lib/commands/current-selection-address.coffee similarity index 61% rename from src/packages/command-panel/src/commands/current-selection-address.coffee rename to src/packages/command-panel/lib/commands/current-selection-address.coffee index 6fd873123..2c01eb76e 100644 --- a/src/packages/command-panel/src/commands/current-selection-address.coffee +++ b/src/packages/command-panel/lib/commands/current-selection-address.coffee @@ -1,5 +1,4 @@ -Address = require 'command-panel/src/commands/address' -Range = require 'range' +Address = require './address' module.exports = class CurrentSelectionAddress extends Address diff --git a/src/packages/command-panel/src/commands/default-address-range.coffee b/src/packages/command-panel/lib/commands/default-address-range.coffee similarity index 69% rename from src/packages/command-panel/src/commands/default-address-range.coffee rename to src/packages/command-panel/lib/commands/default-address-range.coffee index 1af9c6a88..60400b321 100644 --- a/src/packages/command-panel/src/commands/default-address-range.coffee +++ b/src/packages/command-panel/lib/commands/default-address-range.coffee @@ -1,5 +1,4 @@ -Address = require 'command-panel/src/commands/address' -Range = require 'range' +Address = require './address' module.exports = class DefaultAddressRange extends Address diff --git a/src/packages/command-panel/src/commands/eof-address.coffee b/src/packages/command-panel/lib/commands/eof-address.coffee similarity index 77% rename from src/packages/command-panel/src/commands/eof-address.coffee rename to src/packages/command-panel/lib/commands/eof-address.coffee index c98dff4b3..aa13d6041 100644 --- a/src/packages/command-panel/src/commands/eof-address.coffee +++ b/src/packages/command-panel/lib/commands/eof-address.coffee @@ -1,4 +1,4 @@ -Address = require 'command-panel/src/commands/address' +Address = require './address' Range = require 'range' module.exports = diff --git a/src/packages/command-panel/src/commands/line-address.coffee b/src/packages/command-panel/lib/commands/line-address.coffee similarity index 79% rename from src/packages/command-panel/src/commands/line-address.coffee rename to src/packages/command-panel/lib/commands/line-address.coffee index 2c2e87b48..ab0f2ad92 100644 --- a/src/packages/command-panel/src/commands/line-address.coffee +++ b/src/packages/command-panel/lib/commands/line-address.coffee @@ -1,4 +1,4 @@ -Address = require 'command-panel/src/commands/address' +Address = require './address' Range = require 'range' module.exports = diff --git a/src/packages/command-panel/src/commands/regex-address.coffee b/src/packages/command-panel/lib/commands/regex-address.coffee similarity index 96% rename from src/packages/command-panel/src/commands/regex-address.coffee rename to src/packages/command-panel/lib/commands/regex-address.coffee index 58a46f749..d2828a6be 100644 --- a/src/packages/command-panel/src/commands/regex-address.coffee +++ b/src/packages/command-panel/lib/commands/regex-address.coffee @@ -1,4 +1,4 @@ -Address = require 'command-panel/src/commands/address' +Address = require './address' Range = require 'range' module.exports = diff --git a/src/packages/command-panel/src/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee similarity index 83% rename from src/packages/command-panel/src/commands/select-all-matches-in-project.coffee rename to src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 7ea56a70e..25acc5fbb 100644 --- a/src/packages/command-panel/src/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -1,5 +1,5 @@ -Command = require 'command-panel/src/commands/command' -Operation = require 'command-panel/src/operation' +Command = require './command' +Operation = require 'command-panel/lib/operation' $ = require 'jquery' module.exports = diff --git a/src/packages/command-panel/src/commands/select-all-matches.coffee b/src/packages/command-panel/lib/commands/select-all-matches.coffee similarity index 83% rename from src/packages/command-panel/src/commands/select-all-matches.coffee rename to src/packages/command-panel/lib/commands/select-all-matches.coffee index 78ebe9b8e..ad1b0bca9 100644 --- a/src/packages/command-panel/src/commands/select-all-matches.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches.coffee @@ -1,5 +1,5 @@ -Command = require 'command-panel/src/commands/command' -Operation = require 'command-panel/src/operation' +Command = require './command' +Operation = require 'command-panel/lib/operation' $ = require 'jquery' module.exports = diff --git a/src/packages/command-panel/src/commands/substitution.coffee b/src/packages/command-panel/lib/commands/substitution.coffee similarity index 87% rename from src/packages/command-panel/src/commands/substitution.coffee rename to src/packages/command-panel/lib/commands/substitution.coffee index 26231bd01..66a621bc2 100644 --- a/src/packages/command-panel/src/commands/substitution.coffee +++ b/src/packages/command-panel/lib/commands/substitution.coffee @@ -1,5 +1,5 @@ -Command = require 'command-panel/src/commands/command' -Operation = require 'command-panel/src/operation' +Command = require './command' +Operation = require 'command-panel/lib/operation' $ = require 'jquery' module.exports = diff --git a/src/packages/command-panel/src/commands/zero-address.coffee b/src/packages/command-panel/lib/commands/zero-address.coffee similarity index 72% rename from src/packages/command-panel/src/commands/zero-address.coffee rename to src/packages/command-panel/lib/commands/zero-address.coffee index 313f1cd5b..80928b29b 100644 --- a/src/packages/command-panel/src/commands/zero-address.coffee +++ b/src/packages/command-panel/lib/commands/zero-address.coffee @@ -1,4 +1,4 @@ -Address = require 'command-panel/src/commands/address' +Address = require './address' Range = require 'range' module.exports = diff --git a/src/packages/command-panel/src/operation.coffee b/src/packages/command-panel/lib/operation.coffee similarity index 96% rename from src/packages/command-panel/src/operation.coffee rename to src/packages/command-panel/lib/operation.coffee index 799649cb4..52e45d544 100644 --- a/src/packages/command-panel/src/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -1,5 +1,3 @@ -{$$$} = require 'space-pen' - module.exports = class Operation constructor: ({@project, @buffer, bufferRange, @newText, @preserveSelection, @errorMessage}) -> diff --git a/src/packages/command-panel/src/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee similarity index 100% rename from src/packages/command-panel/src/preview-list.coffee rename to src/packages/command-panel/lib/preview-list.coffee diff --git a/src/packages/command-panel/package.cson b/src/packages/command-panel/package.cson new file mode 100644 index 000000000..e5d8cf032 --- /dev/null +++ b/src/packages/command-panel/package.cson @@ -0,0 +1,10 @@ +'main': 'lib/command-panel' +'activationEvents': [ + 'command-panel:toggle' + 'command-panel:toggle-preview' + 'command-panel:find-in-file' + 'command-panel:find-in-project' + 'command-panel:repeat-relative-address' + 'command-panel:repeat-relative-address-in-reverse' + 'command-panel:set-selection-as-regex-address' +] diff --git a/src/packages/command-panel/spec/command-interpreter-spec.coffee b/src/packages/command-panel/spec/command-interpreter-spec.coffee index 792f1a529..289d87709 100644 --- a/src/packages/command-panel/spec/command-interpreter-spec.coffee +++ b/src/packages/command-panel/spec/command-interpreter-spec.coffee @@ -1,4 +1,4 @@ -CommandInterpreter = require 'command-panel/src/command-interpreter' +CommandInterpreter = require 'command-panel/lib/command-interpreter' Project = require 'project' Buffer = require 'buffer' EditSession = require 'edit-session' diff --git a/src/packages/command-panel/spec/command-panel-spec.coffee b/src/packages/command-panel/spec/command-panel-spec.coffee index c0d47fe45..99f886b03 100644 --- a/src/packages/command-panel/spec/command-panel-spec.coffee +++ b/src/packages/command-panel/spec/command-panel-spec.coffee @@ -1,19 +1,19 @@ RootView = require 'root-view' -CommandPanelView = require 'command-panel/src/command-panel-view' +CommandPanelView = require 'command-panel/lib/command-panel-view' _ = require 'underscore' describe "CommandPanel", -> - [rootView, editor, buffer, commandPanel, project, CommandPanel] = [] + [editor, buffer, commandPanel, project, CommandPanel] = [] beforeEach -> - rootView = new RootView + new RootView rootView.open(require.resolve 'fixtures/sample.js') rootView.enableKeymap() project = rootView.project editor = rootView.getActiveEditor() buffer = editor.activeEditSession.buffer - CommandPanel = atom.loadPackage('command-panel') - commandPanel = CommandPanel.getInstance() + commandPanelMain = atom.loadPackage('command-panel', activateImmediately: true).packageMain + commandPanel = commandPanelMain.commandPanelView commandPanel.history = [] commandPanel.historyIndex = 0 @@ -21,7 +21,7 @@ describe "CommandPanel", -> rootView.deactivate() describe "serialization", -> - it "preserves the command panel's mini-editor text, visibility, focus, and history across reloads", -> + it "preserves the command panel's history across reloads", -> rootView.attachToDom() rootView.trigger 'command-panel:toggle' expect(commandPanel.miniEditor.isFocused).toBeTruthy() @@ -31,29 +31,20 @@ describe "CommandPanel", -> expect(commandPanel.historyIndex).toBe(1) rootView.trigger 'command-panel:toggle' expect(commandPanel.miniEditor.isFocused).toBeTruthy() - commandPanel.miniEditor.insertText 'abc' - rootView2 = RootView.deserialize(rootView.serialize()) - rootView.deactivate() - rootView2.attachToDom() - commandPanel = rootView2.activatePackage('command-panel', CommandPanel).getInstance() - expect(rootView2.find('.command-panel')).toExist() - expect(commandPanel.miniEditor.getText()).toBe 'abc' - expect(commandPanel.miniEditor.isFocused).toBeTruthy() + rootViewState = rootView.serialize() + rootView.deactivate() + RootView.deserialize(rootViewState).attachToDom() + atom.loadPackage('command-panel') + + expect(rootView.find('.command-panel')).not.toExist() + rootView.trigger 'command-panel:toggle' + expect(rootView.find('.command-panel')).toExist() + commandPanel = rootView.find('.command-panel').view() expect(commandPanel.history.length).toBe(1) expect(commandPanel.history[0]).toBe('/.') expect(commandPanel.historyIndex).toBe(1) - rootView2.focus() - expect(commandPanel.miniEditor.isFocused).toBeFalsy() - rootView3 = RootView.deserialize(rootView2.serialize()) - rootView2.deactivate() - rootView3.attachToDom() - commandPanel = rootView3.activatePackage('command-panel', CommandPanel).getInstance() - - expect(commandPanel.miniEditor.isFocused).toBeFalsy() - rootView3.deactivate() - it "only retains the configured max serialized history size", -> rootView.attachToDom() @@ -67,18 +58,18 @@ describe "CommandPanel", -> expect(commandPanel.history[2]).toBe('/test3') expect(commandPanel.historyIndex).toBe(3) - rootView2 = RootView.deserialize(rootView.serialize()) + rootViewState = rootView.serialize() rootView.deactivate() - rootView2.attachToDom() + RootView.deserialize(rootViewState).attachToDom() + atom.loadPackage('command-panel') + rootView.trigger 'command-panel:toggle' - commandPanel = rootView2.activatePackage('command-panel', CommandPanel).getInstance() + commandPanel = rootView.find('.command-panel').view() expect(commandPanel.history.length).toBe(2) expect(commandPanel.history[0]).toBe('/test2') expect(commandPanel.history[1]).toBe('/test3') expect(commandPanel.historyIndex).toBe(2) - rootView2.deactivate() - describe "when core:close is triggered on the command panel", -> it "detaches the command panel, focuses the RootView and does not bubble the core:close event", -> commandPanel.attach() diff --git a/src/packages/editor-stats/index.coffee b/src/packages/editor-stats/index.coffee deleted file mode 100644 index e743c0f18..000000000 --- a/src/packages/editor-stats/index.coffee +++ /dev/null @@ -1,16 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' -Stats = require './src/stats' - -module.exports = -class EditorStats extends DeferredAtomPackage - loadEvents: ['editor-stats:toggle'] - instanceClass: 'editor-stats/src/editor-stats-view' - stats: new Stats - - activate: (rootView) -> - super - - rootView.on 'keydown', => @stats.track() - rootView.on 'mouseup', => @stats.track() - - onLoadEvent: (event, instance) -> instance.toggle(@stats) diff --git a/src/packages/editor-stats/src/editor-stats-view.coffee b/src/packages/editor-stats/lib/editor-stats-view.coffee similarity index 91% rename from src/packages/editor-stats/src/editor-stats-view.coffee rename to src/packages/editor-stats/lib/editor-stats-view.coffee index aea6ca303..c86ab18d8 100644 --- a/src/packages/editor-stats/src/editor-stats-view.coffee +++ b/src/packages/editor-stats/lib/editor-stats-view.coffee @@ -5,10 +5,10 @@ $ = require 'jquery' module.exports = class EditorStatsView extends ScrollView - @activate: (rootView, state) -> - @instance = new EditorStatsView(rootView) + @activate: -> + new EditorStatsView - @content: (rootView) -> + @content: -> @div class: 'editor-stats-wrapper', tabindex: -1, => @div class: 'editor-stats', outlet: 'editorStats' @@ -17,7 +17,7 @@ class EditorStatsView extends ScrollView pb: 3 pr: 25 - initialize: (@rootView) -> + initialize: -> super resizer = => @@ -30,7 +30,7 @@ class EditorStatsView extends ScrollView @editorStats.empty() @x ?= d3.scale.ordinal().domain d3.range(@stats.hours * 60) @y ?= d3.scale.linear() - w = @rootView.vertical.width() + w = rootView.vertical.width() h = @height() data = d3.entries @stats.eventLog max = d3.max data, (d) -> d.value @@ -94,10 +94,10 @@ class EditorStatsView extends ScrollView @attach() attach: -> - @rootView.vertical.append(@) + rootView.vertical.append(this) @draw() detach: -> - super() + super clearInterval(@updateInterval) - @rootView.focus() + rootView.focus() diff --git a/src/packages/editor-stats/lib/editor-stats.coffee b/src/packages/editor-stats/lib/editor-stats.coffee new file mode 100644 index 000000000..bdfdaa6b1 --- /dev/null +++ b/src/packages/editor-stats/lib/editor-stats.coffee @@ -0,0 +1,19 @@ +StatsTracker = require './stats-tracker' + +module.exports = + stats: null + editorStatsView: null + + activate: -> + @stats = new StatsTracker() + rootView.command 'editor-stats:toggle', => @createView().toggle(@stats) + + deactivate: -> + @editorStatsView = null + @stats = null + + createView: -> + unless @editorStatsView + EditorStatsView = require 'editor-stats/lib/editor-stats-view' + @editorStatsView = new EditorStatsView() + @editorStatsView diff --git a/src/packages/editor-stats/src/stats.coffee b/src/packages/editor-stats/lib/stats-tracker.coffee similarity index 85% rename from src/packages/editor-stats/src/stats.coffee rename to src/packages/editor-stats/lib/stats-tracker.coffee index 5e9c6700d..d7cc8d1d8 100644 --- a/src/packages/editor-stats/src/stats.coffee +++ b/src/packages/editor-stats/lib/stats-tracker.coffee @@ -1,5 +1,5 @@ module.exports = -class Stats +class StatsTracker startDate: new Date hours: 6 eventLog: [] @@ -12,6 +12,9 @@ class Stats while date < future @eventLog[@time(date)] = 0 + rootView.on 'keydown', => @track() + rootView.on 'mouseup', => @track() + clear: -> @eventLog = [] diff --git a/src/packages/editor-stats/package.cson b/src/packages/editor-stats/package.cson new file mode 100644 index 000000000..258a141f9 --- /dev/null +++ b/src/packages/editor-stats/package.cson @@ -0,0 +1 @@ +'main': 'lib/editor-stats' diff --git a/src/packages/editor-stats/spec/editor-stats-spec.coffee b/src/packages/editor-stats/spec/editor-stats-spec.coffee index ca274a252..0ba0b2032 100644 --- a/src/packages/editor-stats/spec/editor-stats-spec.coffee +++ b/src/packages/editor-stats/spec/editor-stats-spec.coffee @@ -1,9 +1,9 @@ $ = require 'jquery' RootView = require 'root-view' -EditorStats = require 'editor-stats/src/editor-stats-view' +EditorStats = require 'editor-stats/lib/editor-stats-view' describe "EditorStats", -> - [rootView, editorStats, time] = [] + [editorStats, time] = [] simulateKeyUp = (key) -> e = $.Event "keydown", keyCode: key.charCodeAt(0) @@ -23,10 +23,7 @@ describe "EditorStats", -> mins = if mins == 60 then '01' else mins + 1 time = "#{hours}:#{mins}" - editorStatsPackage = atom.loadPackage('editor-stats') - editorStatsPackage.getInstance() - editorStats = editorStatsPackage.stats - editorStats.clear() + editorStats = atom.loadPackage('editor-stats').packageMain.stats afterEach -> rootView.deactivate() diff --git a/src/packages/editor-stats/src/stats-tracker.coffee b/src/packages/editor-stats/src/stats-tracker.coffee new file mode 100644 index 000000000..d7cc8d1d8 --- /dev/null +++ b/src/packages/editor-stats/src/stats-tracker.coffee @@ -0,0 +1,32 @@ +module.exports = +class StatsTracker + startDate: new Date + hours: 6 + eventLog: [] + + constructor: -> + date = new Date(@startDate) + future = new Date(date.getTime() + (36e5 * @hours)) + @eventLog[@time(date)] = 0 + + while date < future + @eventLog[@time(date)] = 0 + + rootView.on 'keydown', => @track() + rootView.on 'mouseup', => @track() + + clear: -> + @eventLog = [] + + track: -> + date = new Date + times = @time date + @eventLog[times] ?= 0 + @eventLog[times] += 1 + @eventLog.shift() if @eventLog.length > (@hours * 60) + + time: (date) -> + date.setTime(date.getTime() + 6e4) + hour = date.getHours() + minute = date.getMinutes() + "#{hour}:#{minute}" diff --git a/src/packages/fuzzy-finder/index.coffee b/src/packages/fuzzy-finder/index.coffee deleted file mode 100644 index 4d213bcba..000000000 --- a/src/packages/fuzzy-finder/index.coffee +++ /dev/null @@ -1,32 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class FuzzyFinder extends DeferredAtomPackage - loadEvents: [ - 'fuzzy-finder:toggle-file-finder' - 'fuzzy-finder:toggle-buffer-finder' - 'fuzzy-finder:find-under-cursor' - ] - - instanceClass: 'fuzzy-finder/src/fuzzy-finder-view' - - activate: (rootView) -> - super - - if rootView.project.getPath()? - callback = (paths) => @projectPaths = paths - LoadPathsTask = require 'fuzzy-finder/src/load-paths-task' - new LoadPathsTask(rootView, callback).start() - - onLoadEvent: (event, instance) -> - if @projectPaths? and not instance.projectPaths? - instance.projectPaths = @projectPaths - instance.reloadProjectPaths = false - - switch event.type - when 'fuzzy-finder:toggle-file-finder' - instance.toggleFileFinder() - when 'fuzzy-finder:toggle-buffer-finder' - instance.toggleBufferFinder() - when 'fuzzy-finder:find-under-cursor' - instance.findUnderCursor() diff --git a/src/packages/fuzzy-finder/src/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee similarity index 84% rename from src/packages/fuzzy-finder/src/fuzzy-finder-view.coffee rename to src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 17165a8c1..ad1249ffb 100644 --- a/src/packages/fuzzy-finder/src/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -3,15 +3,12 @@ SelectList = require 'select-list' _ = require 'underscore' $ = require 'jquery' fs = require 'fs' -LoadPathsTask = require 'fuzzy-finder/src/load-paths-task' +LoadPathsTask = require './load-paths-task' module.exports = class FuzzyFinderView extends SelectList filenameRegex: /[\w\.\-\/\\]+/ - @activate: (rootView) -> - @instance = new FuzzyFinderView(rootView) - @viewClass: -> [super, 'fuzzy-finder', 'overlay', 'from-top'].join(' ') @@ -20,7 +17,7 @@ class FuzzyFinderView extends SelectList projectPaths: null reloadProjectPaths: true - initialize: (@rootView) -> + initialize: -> super @subscribe $(window), 'focus', => @reloadProjectPaths = true @@ -54,15 +51,15 @@ class FuzzyFinderView extends SelectList @span " - #{folder}/", class: 'directory' openPath: (path) -> - @rootView.open(path, {@allowActiveEditorChange}) if path + rootView.open(path, {@allowActiveEditorChange}) if path splitOpenPath: (fn) -> path = @getSelectedElement() return unless path - editor = @rootView.getActiveEditor() + editor = rootView.getActiveEditor() if editor - fn(editor, @rootView.project.buildEditSessionForPath(path)) + fn(editor, rootView.project.buildEditSessionForPath(path)) else @openPath(path) @@ -79,7 +76,7 @@ class FuzzyFinderView extends SelectList if @hasParent() @cancel() else - return unless @rootView.project.getPath()? + return unless rootView.project.getPath()? @allowActiveEditorChange = false @populateProjectPaths() @attach() @@ -96,9 +93,9 @@ class FuzzyFinderView extends SelectList if @hasParent() @cancel() else - return unless @rootView.project.getPath()? + return unless rootView.project.getPath()? @allowActiveEditorChange = false - editor = @rootView.getActiveEditor() + editor = rootView.getActiveEditor() currentWord = editor.getWordUnderCursor(wordRegex: @filenameRegex) if currentWord.length == 0 @@ -110,7 +107,7 @@ class FuzzyFinderView extends SelectList @attach() @setError("No files match '#{currentWord}'") else if paths.length == 1 - @rootView.open(paths[0]) + rootView.open(paths[0]) else @attach() @miniEditor.setText(currentWord) @@ -142,16 +139,21 @@ class FuzzyFinderView extends SelectList @setArray(listedItems) options.done(listedItems) if options.done? - @loadPathsTask = new LoadPathsTask(@rootView, callback) + @loadPathsTask = new LoadPathsTask(callback) @loadPathsTask.start() populateOpenBufferPaths: -> - @paths = @rootView.getOpenBufferPaths().map (path) => - @rootView.project.relativize(path) + @paths = rootView.getOpenBufferPaths().map (path) => + rootView.project.relativize(path) @setArray(@paths) + detach: -> + super + + @loadPathsTask?.terminate() + attach: -> super - @rootView.append(this) + rootView.append(this) @miniEditor.focus() diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee new file mode 100644 index 000000000..2bdd340a1 --- /dev/null +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder.coffee @@ -0,0 +1,32 @@ +module.exports = + projectPaths: null + fuzzyFinderView: null + + activate: -> + rootView.command 'fuzzy-finder:toggle-file-finder', => + @createView().toggleFileFinder() + rootView.command 'fuzzy-finder:toggle-buffer-finder', => + @createView().toggleBufferFinder() + rootView.command 'fuzzy-finder:find-under-cursor', => + @createView().findUnderCursor() + + if rootView.project.getPath()? + LoadPathsTask = require 'fuzzy-finder/lib/load-paths-task' + @loadPathsTask = new LoadPathsTask((paths) => @projectPaths = paths) + @loadPathsTask.start() + + deactivate: -> + @loadPathsTask?.terminate() + @fuzzyFinderView?.cancel() + @fuzzyFinderView = null + @projectPaths = null + @fuzzyFinderView = null + + createView: -> + unless @fuzzyFinderView + FuzzyFinderView = require 'fuzzy-finder/lib/fuzzy-finder-view' + @fuzzyFinderView = new FuzzyFinderView() + if @projectPaths? and not @fuzzyFinderView.projectPaths? + @fuzzyFinderView.projectPaths = @projectPaths + @fuzzyFinderView.reloadProjectPaths = false + @fuzzyFinderView diff --git a/src/packages/fuzzy-finder/src/load-paths-handler.coffee b/src/packages/fuzzy-finder/lib/load-paths-handler.coffee similarity index 82% rename from src/packages/fuzzy-finder/src/load-paths-handler.coffee rename to src/packages/fuzzy-finder/lib/load-paths-handler.coffee index 98c439d3d..723f302d1 100644 --- a/src/packages/fuzzy-finder/src/load-paths-handler.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-handler.coffee @@ -1,11 +1,13 @@ fs = require 'fs' _ = require 'underscore' -Git = require 'git' module.exports = loadPaths: (rootPath, ignoredNames, excludeGitIgnoredPaths) -> + if excludeGitIgnoredPaths + Git = require 'git' + repo = Git.open(rootPath, refreshIndexOnFocus: false) + paths = [] - repo = Git.open(rootPath, refreshIndexOnFocus: false) if excludeGitIgnoredPaths isIgnored = (path) -> for segment in path.split('/') return true if _.contains(ignoredNames, segment) @@ -15,5 +17,7 @@ module.exports = onDirectory = (path) -> not isIgnored(path) fs.traverseTree(rootPath, onFile, onDirectory) + repo?.destroy() + callTaskMethod('pathsLoaded', paths) diff --git a/src/packages/fuzzy-finder/src/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee similarity index 77% rename from src/packages/fuzzy-finder/src/load-paths-task.coffee rename to src/packages/fuzzy-finder/lib/load-paths-task.coffee index 64bf095b5..254c3673e 100644 --- a/src/packages/fuzzy-finder/src/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -2,14 +2,14 @@ Task = require 'task' module.exports = class LoadPathsTask extends Task - constructor: (@rootView, @callback)-> - super('fuzzy-finder/src/load-paths-handler') + constructor: (@callback) -> + super('fuzzy-finder/lib/load-paths-handler') started: -> ignoredNames = config.get('fuzzyFinder.ignoredNames') ? [] ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? []) excludeGitIgnoredPaths = config.get('core.hideGitIgnoredFiles') - rootPath = @rootView.project.getPath() + rootPath = rootView.project.getPath() @callWorkerMethod('loadPaths', rootPath, ignoredNames, excludeGitIgnoredPaths) pathsLoaded: (paths) -> diff --git a/src/packages/fuzzy-finder/package.cson b/src/packages/fuzzy-finder/package.cson new file mode 100644 index 000000000..d259d625b --- /dev/null +++ b/src/packages/fuzzy-finder/package.cson @@ -0,0 +1 @@ +'main': 'lib/fuzzy-finder' diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 9d90f26d3..67608bb4f 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -1,21 +1,20 @@ RootView = require 'root-view' -FuzzyFinder = require 'fuzzy-finder/src/fuzzy-finder-view' -LoadPathsTask = require 'fuzzy-finder/src/load-paths-task' +FuzzyFinder = require 'fuzzy-finder/lib/fuzzy-finder-view' +LoadPathsTask = require 'fuzzy-finder/lib/load-paths-task' $ = require 'jquery' {$$} = require 'space-pen' fs = require 'fs' describe 'FuzzyFinder', -> - [rootView, finder] = [] + [finderView] = [] beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) rootView.enableKeymap() - atom.loadPackage("fuzzy-finder").getInstance() - finder = FuzzyFinder.instance + finderView = atom.loadPackage("fuzzy-finder").packageMain.createView() afterEach -> - rootView.remove() + rootView.deactivate() describe "file-finder behavior", -> describe "toggling", -> @@ -26,12 +25,13 @@ describe 'FuzzyFinder', -> rootView.find('.editor').trigger 'editor:split-right' [editor1, editor2] = rootView.find('.editor').map -> $(this).view() + expect(rootView.find('.fuzzy-finder')).not.toExist() rootView.trigger 'fuzzy-finder:toggle-file-finder' expect(rootView.find('.fuzzy-finder')).toExist() - expect(finder.miniEditor.isFocused).toBeTruthy() + expect(finderView.miniEditor.isFocused).toBeTruthy() expect(editor1.isFocused).toBeFalsy() expect(editor2.isFocused).toBeFalsy() - finder.miniEditor.insertText('this should not show up next time we toggle') + finderView.miniEditor.insertText('this should not show up next time we toggle') rootView.trigger 'fuzzy-finder:toggle-file-finder' expect(editor1.isFocused).toBeFalsy() @@ -39,27 +39,27 @@ describe 'FuzzyFinder', -> expect(rootView.find('.fuzzy-finder')).not.toExist() rootView.trigger 'fuzzy-finder:toggle-file-finder' - expect(finder.miniEditor.getText()).toBe '' + expect(finderView.miniEditor.getText()).toBe '' it "shows all relative file paths for the current project and selects the first", -> rootView.attachToDom() - finder.maxItems = Infinity + finderView.maxItems = Infinity rootView.trigger 'fuzzy-finder:toggle-file-finder' paths = null - expect(finder.find(".loading")).toBeVisible() - expect(finder.find(".loading")).toHaveText "Indexing..." + expect(finderView.find(".loading")).toBeVisible() + expect(finderView.find(".loading")).toHaveText "Indexing..." waitsFor "all project paths to load", 5000, -> - if finder.projectPaths?.length > 0 - paths = finder.projectPaths + if finderView.projectPaths?.length > 0 + paths = finderView.projectPaths true runs -> - expect(finder.list.children('li').length).toBe paths.length + expect(finderView.list.children('li').length).toBe paths.length for path in paths - expect(finder.list.find("li:contains(#{fs.base(path)})")).toExist() - expect(finder.list.children().first()).toHaveClass 'selected' - expect(finder.find(".loading")).not.toBeVisible() + expect(finderView.list.find("li:contains(#{fs.base(path)})")).toExist() + expect(finderView.list.children().first()).toHaveClass 'selected' + expect(finderView.find(".loading")).not.toBeVisible() describe "when root view's project has no path", -> beforeEach -> @@ -78,10 +78,10 @@ describe 'FuzzyFinder', -> expect(rootView.getActiveEditor()).toBe editor2 rootView.trigger 'fuzzy-finder:toggle-file-finder' - finder.confirmed('dir/a') + finderView.confirmed('dir/a') expectedPath = fixturesProject.resolve('dir/a') - expect(finder.hasParent()).toBeFalsy() + expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).not.toBe expectedPath expect(editor2.getPath()).toBe expectedPath expect(editor2.isFocused).toBeTruthy() @@ -91,12 +91,12 @@ describe 'FuzzyFinder', -> rootView.attachToDom() path = rootView.getActiveEditor().getPath() rootView.trigger 'fuzzy-finder:toggle-file-finder' - finder.confirmed('dir/this/is/not/a/file.txt') - expect(finder.hasParent()).toBeTruthy() + finderView.confirmed('dir/this/is/not/a/file.txt') + expect(finderView.hasParent()).toBeTruthy() expect(rootView.getActiveEditor().getPath()).toBe path - expect(finder.find('.error').text().length).toBeGreaterThan 0 + expect(finderView.find('.error').text().length).toBeGreaterThan 0 advanceClock(2000) - expect(finder.find('.error').text().length).toBe 0 + expect(finderView.find('.error').text().length).toBe 0 describe "buffer-finder behavior", -> describe "toggling", -> @@ -113,7 +113,7 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-buffer-finder' expect(rootView.find('.fuzzy-finder')).toExist() expect(rootView.find('.fuzzy-finder input:focus')).toExist() - finder.miniEditor.insertText('this should not show up next time we toggle') + finderView.miniEditor.insertText('this should not show up next time we toggle') rootView.trigger 'fuzzy-finder:toggle-buffer-finder' expect(editor1.isFocused).toBeFalsy() @@ -121,14 +121,14 @@ describe 'FuzzyFinder', -> expect(rootView.find('.fuzzy-finder')).not.toExist() rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - expect(finder.miniEditor.getText()).toBe '' + expect(finderView.miniEditor.getText()).toBe '' it "lists the paths of the current open buffers", -> rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - expect(finder.list.children('li').length).toBe 2 - expect(finder.list.find("li:contains(sample.js)")).toExist() - expect(finder.list.find("li:contains(sample.txt)")).toExist() - expect(finder.list.children().first()).toHaveClass 'selected' + expect(finderView.list.children('li').length).toBe 2 + expect(finderView.list.find("li:contains(sample.js)")).toExist() + expect(finderView.list.find("li:contains(sample.txt)")).toExist() + expect(finderView.list.children().first()).toHaveClass 'selected' describe "when the active editor only contains edit sessions for anonymous buffers", -> it "does not open", -> @@ -162,9 +162,9 @@ describe 'FuzzyFinder', -> describe "when there is an edit session for the confirmed path in the active editor", -> it "switches the active editor to the edit session for the selected path", -> expectedPath = fixturesProject.resolve('sample.txt') - finder.confirmed('sample.txt') + finderView.confirmed('sample.txt') - expect(finder.hasParent()).toBeFalsy() + expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).not.toBe expectedPath expect(editor2.getPath()).toBe expectedPath expect(editor2.isFocused).toBeTruthy() @@ -178,9 +178,9 @@ describe 'FuzzyFinder', -> expect(rootView.getActiveEditor()).toBe editor1 expectedPath = fixturesProject.resolve('sample.txt') - finder.confirmed('sample.txt') + finderView.confirmed('sample.txt') - expect(finder.hasParent()).toBeFalsy() + expect(finderView.hasParent()).toBeFalsy() expect(editor1.getPath()).not.toBe expectedPath expect(editor2.getPath()).toBe expectedPath expect(editor2.isFocused).toBeTruthy() @@ -194,31 +194,31 @@ describe 'FuzzyFinder', -> activeEditor.focus() rootView.trigger 'fuzzy-finder:toggle-file-finder' - expect(finder.hasParent()).toBeTruthy() + expect(finderView.hasParent()).toBeTruthy() expect(activeEditor.isFocused).toBeFalsy() - expect(finder.miniEditor.isFocused).toBeTruthy() + expect(finderView.miniEditor.isFocused).toBeTruthy() - finder.cancel() + finderView.cancel() - expect(finder.hasParent()).toBeFalsy() + expect(finderView.hasParent()).toBeFalsy() expect(activeEditor.isFocused).toBeTruthy() - expect(finder.miniEditor.isFocused).toBeFalsy() + expect(finderView.miniEditor.isFocused).toBeFalsy() describe "when no editors are open", -> - it "detaches the finder and surrenders focus to the body", -> + it "detaches the finder and focuses the previously focused element", -> rootView.attachToDom() rootView.getActiveEditor().destroyActiveEditSession() rootView.trigger 'fuzzy-finder:toggle-file-finder' - expect(finder.hasParent()).toBeTruthy() + expect(finderView.hasParent()).toBeTruthy() expect(rootView.isFocused).toBeFalsy() - expect(finder.miniEditor.isFocused).toBeTruthy() + expect(finderView.miniEditor.isFocused).toBeTruthy() - finder.cancel() + finderView.cancel() - expect(finder.hasParent()).toBeFalsy() - expect(document.activeElement).toBe $('body')[0] - expect(finder.miniEditor.isFocused).toBeFalsy() + expect(finderView.hasParent()).toBeFalsy() + expect($(document.activeElement).view()).toBe rootView + expect(finderView.miniEditor.isFocused).toBeFalsy() describe "cached file paths", -> it "caches file paths after first time", -> @@ -226,26 +226,26 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-file-finder' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> - expect(finder.loadPathsTask.start).toHaveBeenCalled() - finder.loadPathsTask.start.reset() + expect(finderView.loadPathsTask.start).toHaveBeenCalled() + finderView.loadPathsTask.start.reset() rootView.trigger 'fuzzy-finder:toggle-file-finder' rootView.trigger 'fuzzy-finder:toggle-file-finder' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> - expect(finder.loadPathsTask.start).not.toHaveBeenCalled() + expect(finderView.loadPathsTask.start).not.toHaveBeenCalled() it "doesn't cache buffer paths", -> spyOn(rootView, "getOpenBufferPaths").andCallThrough() rootView.trigger 'fuzzy-finder:toggle-buffer-finder' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> expect(rootView.getOpenBufferPaths).toHaveBeenCalled() @@ -254,7 +254,7 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-buffer-finder' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> expect(rootView.getOpenBufferPaths).toHaveBeenCalled() @@ -264,27 +264,27 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-file-finder' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> - expect(finder.loadPathsTask.start).toHaveBeenCalled() - finder.loadPathsTask.start.reset() + expect(finderView.loadPathsTask.start).toHaveBeenCalled() + finderView.loadPathsTask.start.reset() $(window).trigger 'focus' rootView.trigger 'fuzzy-finder:toggle-file-finder' rootView.trigger 'fuzzy-finder:toggle-file-finder' - expect(finder.loadPathsTask.start).toHaveBeenCalled() + expect(finderView.loadPathsTask.start).toHaveBeenCalled() describe "path ignoring", -> it "ignores paths that match entries in config.fuzzyFinder.ignoredNames", -> config.set("fuzzyFinder.ignoredNames", ["tree-view.js"]) rootView.trigger 'fuzzy-finder:toggle-file-finder' - finder.maxItems = Infinity + finderView.maxItems = Infinity waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> - expect(finder.list.find("li:contains(tree-view.js)")).not.toExist() + expect(finderView.list.find("li:contains(tree-view.js)")).not.toExist() describe "fuzzy find by content under cursor", -> editor = null @@ -298,10 +298,10 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:find-under-cursor' waitsFor -> - finder.list.children('li').length > 0 + finderView.list.children('li').length > 0 runs -> - expect(finder).toBeVisible() + expect(finderView).toBeVisible() expect(rootView.find('.fuzzy-finder input:focus')).toExist() it "opens a file directly when there is a single match", -> @@ -316,7 +316,7 @@ describe 'FuzzyFinder', -> openedPath != null runs -> - expect(finder).not.toBeVisible() + expect(finderView).not.toBeVisible() expect(openedPath).toBe "sample.txt" it "displays error when the word under the cursor doesn't match any files", -> @@ -326,10 +326,10 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:find-under-cursor' waitsFor -> - finder.is(':visible') + finderView.is(':visible') runs -> - expect(finder.find('.error').text().length).toBeGreaterThan 0 + expect(finderView.find('.error').text().length).toBeGreaterThan 0 it "displays error when there is no word under the cursor", -> editor.setText("&&&&&&&&&&&&&&& sample") @@ -338,10 +338,10 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:find-under-cursor' waitsFor -> - finder.is(':visible') + finderView.is(':visible') runs -> - expect(finder.find('.error').text().length).toBeGreaterThan 0 + expect(finderView.find('.error').text().length).toBeGreaterThan 0 describe "opening a path into a split", -> @@ -354,7 +354,7 @@ describe 'FuzzyFinder', -> spyOn(editor, "splitLeft").andCallThrough() expect(rootView.find('.editor').length).toBe 1 rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - finder.miniEditor.trigger 'editor:split-left' + finderView.miniEditor.trigger 'editor:split-left' expect(rootView.find('.editor').length).toBe 2 expect(editor.splitLeft).toHaveBeenCalled() expect(rootView.getActiveEditor()).not.toBe editor @@ -365,7 +365,7 @@ describe 'FuzzyFinder', -> spyOn(editor, "splitRight").andCallThrough() expect(rootView.find('.editor').length).toBe 1 rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - finder.miniEditor.trigger 'editor:split-right' + finderView.miniEditor.trigger 'editor:split-right' expect(rootView.find('.editor').length).toBe 2 expect(editor.splitRight).toHaveBeenCalled() expect(rootView.getActiveEditor()).not.toBe editor @@ -376,7 +376,7 @@ describe 'FuzzyFinder', -> spyOn(editor, "splitDown").andCallThrough() expect(rootView.find('.editor').length).toBe 1 rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - finder.miniEditor.trigger 'editor:split-down' + finderView.miniEditor.trigger 'editor:split-down' expect(rootView.find('.editor').length).toBe 2 expect(editor.splitDown).toHaveBeenCalled() expect(rootView.getActiveEditor()).not.toBe editor @@ -387,8 +387,8 @@ describe 'FuzzyFinder', -> spyOn(editor, "splitUp").andCallThrough() expect(rootView.find('.editor').length).toBe 1 rootView.trigger 'fuzzy-finder:toggle-buffer-finder' - finder.miniEditor.trigger 'editor:split-up' + finderView.miniEditor.trigger 'editor:split-up' expect(rootView.find('.editor').length).toBe 2 expect(editor.splitUp).toHaveBeenCalled() expect(rootView.getActiveEditor()).not.toBe editor - expect(rootView.getActiveEditor().getPath()).toBe editor.getPath() + expect(rootView.getActiveEditor().getPath()).toBe editor.getPath() \ No newline at end of file diff --git a/src/packages/gfm.tmbundle/Syntaxes/gfm.cson b/src/packages/gfm.tmbundle/Syntaxes/gfm.cson new file mode 100644 index 000000000..30a11c07b --- /dev/null +++ b/src/packages/gfm.tmbundle/Syntaxes/gfm.cson @@ -0,0 +1,62 @@ +'name': 'GitHub Markdown' +'scopeName': 'source.gfm' +'fileTypes': [ + 'markdown' + 'md' + 'mkd' + 'mkdown' + 'ron' +] +'patterns': [ + { + 'match': '\\*\\*[^\\*]+\\*\\*' + 'name': 'markup.bold.gfm' + } + { + 'match': '__[^_]+__' + 'name': 'markup.bold.gfm' + } + { + 'match': '\\*[^\\*]+\\*' + 'name': 'markup.italic.gfm' + } + { + 'match': '_[^_]+_' + 'name': 'markup.italic.gfm' + } + { + 'match': '^#{1,6}\\s+.+$' + 'name': 'markup.heading.gfm' + } + { + 'match': '\\:[^\\:]+\\:' + 'name': 'variable.emoji.gfm' + } + { + 'begin': '^```.*$' + 'beginCaptures': + '0': 'name': 'support.gfm' + 'end': '^```$' + 'endCaptures': + '0': 'name': 'support.gfm' + 'patterns': [ + 'match': '.*' + 'name': 'markup.raw.gfm' + ] + } + { + 'match': '`[^`]+`' + 'name': 'markup.raw.gfm' + } + { + 'match': '\\!?\\[([^\\]]*)\\]\\(([^\\)]+)\\)' + 'captures': + '1': 'name': 'entity.gfm' + '2': 'name': 'markup.underline.gfm' + } + { + 'match': '^\\s*([\\*\\+-])[ \\t]+' + 'captures': + '1': 'name': 'constant.gfm' + } +] diff --git a/src/packages/gfm.tmbundle/spec/gfm-spec.coffee b/src/packages/gfm.tmbundle/spec/gfm-spec.coffee new file mode 100644 index 000000000..70e24d913 --- /dev/null +++ b/src/packages/gfm.tmbundle/spec/gfm-spec.coffee @@ -0,0 +1,106 @@ +TextMatePackage = require 'text-mate-package' + +describe "GitHub Flavored Markdown grammar", -> + grammar = null + + beforeEach -> + spyOn(syntax, "addGrammar") + pack = new TextMatePackage(require.resolve("gfm.tmbundle")) + pack.load() + grammar = pack.grammars[0] + + it "parses the grammar", -> + expect(grammar).toBeTruthy() + expect(grammar.scopeName).toBe "source.gfm" + + it "tokenizes **bold** text", -> + {tokens} = grammar.tokenizeLine("****") + expect(tokens[0]).toEqual value: "****", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine("this is **bold** text") + expect(tokens[0]).toEqual value: "this is ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "**bold**", scopes: ["source.gfm", "markup.bold.gfm"] + expect(tokens[2]).toEqual value: " text", scopes: ["source.gfm"] + + it "tokenizes __bold__ text", -> + {tokens} = grammar.tokenizeLine("____") + expect(tokens[0]).toEqual value: "____", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine("this is __bold__ text") + expect(tokens[0]).toEqual value: "this is ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "__bold__", scopes: ["source.gfm", "markup.bold.gfm"] + expect(tokens[2]).toEqual value: " text", scopes: ["source.gfm"] + + it "tokenizes *italic* text", -> + {tokens} = grammar.tokenizeLine("**") + expect(tokens[0]).toEqual value: "**", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine("this is *italic* text") + expect(tokens[0]).toEqual value: "this is ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "*italic*", scopes: ["source.gfm", "markup.italic.gfm"] + expect(tokens[2]).toEqual value: " text", scopes: ["source.gfm"] + + it "tokenizes _italic_ text", -> + {tokens} = grammar.tokenizeLine("__") + expect(tokens[0]).toEqual value: "__", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine("this is _italic_ text") + expect(tokens[0]).toEqual value: "this is ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "_italic_", scopes: ["source.gfm", "markup.italic.gfm"] + expect(tokens[2]).toEqual value: " text", scopes: ["source.gfm"] + + it "tokenizes a ## Heading", -> + {tokens} = grammar.tokenizeLine("# Heading 1") + expect(tokens[0]).toEqual value: "# Heading 1", scopes: ["source.gfm", "markup.heading.gfm"] + {tokens} = grammar.tokenizeLine("### Heading 3") + expect(tokens[0]).toEqual value: "### Heading 3", scopes: ["source.gfm", "markup.heading.gfm"] + + it "tokenizies an :emoji:", -> + {tokens} = grammar.tokenizeLine("this is :no_good:") + expect(tokens[0]).toEqual value: "this is ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: ":no_good:", scopes: ["source.gfm", "variable.emoji.gfm"] + + it "tokenizes a ``` code block```", -> + {tokens, ruleStack} = grammar.tokenizeLine("```coffeescript") + expect(tokens[0]).toEqual value: "```coffeescript", scopes: ["source.gfm", "support.gfm"] + {tokens, ruleStack} = grammar.tokenizeLine("-> 'hello'", ruleStack) + expect(tokens[0]).toEqual value: "-> 'hello'", scopes: ["source.gfm", "markup.raw.gfm"] + {tokens} = grammar.tokenizeLine("```", ruleStack) + expect(tokens[0]).toEqual value: "```", scopes: ["source.gfm", "support.gfm"] + + it "tokenizes inline `code` blocks", -> + {tokens} = grammar.tokenizeLine("`this` is `code`") + expect(tokens[0]).toEqual value: "`this`", scopes: ["source.gfm", "markup.raw.gfm"] + expect(tokens[1]).toEqual value: " is ", scopes: ["source.gfm"] + expect(tokens[2]).toEqual value: "`code`", scopes: ["source.gfm", "markup.raw.gfm"] + + it "tokenizes [links](links)", -> + {tokens} = grammar.tokenizeLine("please click [this link](website)") + expect(tokens[0]).toEqual value: "please click ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "[", scopes: ["source.gfm"] + expect(tokens[2]).toEqual value: "this link", scopes: ["source.gfm", "entity.gfm"] + expect(tokens[3]).toEqual value: "](", scopes: ["source.gfm"] + expect(tokens[4]).toEqual value: "website", scopes: ["source.gfm", "markup.underline.gfm"] + expect(tokens[5]).toEqual value: ")", scopes: ["source.gfm"] + + it "tokenizes lists", -> + {tokens} = grammar.tokenizeLine("*Item 1") + expect(tokens[0]).toEqual value: "*Item 1", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine(" * Item 1") + expect(tokens[0]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "*", scopes: ["source.gfm", "constant.gfm"] + expect(tokens[2]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[3]).toEqual value: "Item 1", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine(" + Item 2") + expect(tokens[0]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "+", scopes: ["source.gfm", "constant.gfm"] + expect(tokens[2]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[3]).toEqual value: "Item 2", scopes: ["source.gfm"] + + {tokens} = grammar.tokenizeLine(" - Item 3") + expect(tokens[0]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[1]).toEqual value: "-", scopes: ["source.gfm", "constant.gfm"] + expect(tokens[2]).toEqual value: " ", scopes: ["source.gfm"] + expect(tokens[3]).toEqual value: "Item 3", scopes: ["source.gfm"] diff --git a/src/packages/gists/index.coffee b/src/packages/gists/index.coffee deleted file mode 100644 index 6205cb366..000000000 --- a/src/packages/gists/index.coffee +++ /dev/null @@ -1,12 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class GistsPackage extends DeferredAtomPackage - - loadEvents: - 'gist:create': '.editor' - - instanceClass: 'gists/lib/gists' - - onLoadEvent: (event, instance) -> - instance.createGist(event.currentTargetView()) diff --git a/src/packages/gists/lib/gists.coffee b/src/packages/gists/lib/gists.coffee index b46e5beab..4cc241fcd 100644 --- a/src/packages/gists/lib/gists.coffee +++ b/src/packages/gists/lib/gists.coffee @@ -3,12 +3,15 @@ $ = require 'jquery' module.exports = class Gists + @activate: -> new Gists - @activate: (rootView) -> new Gists(rootView) - - constructor: (@rootView) -> + constructor: -> + rootView.command 'gist:create', '.editor', => @createGist() createGist: (editor) -> + editor = rootView.getActiveEditor() + return unless editor? + gist = { public: false, files: {} } gist.files[editor.getBuffer().getBaseName()] = content: editor.getSelectedText() or editor.getText() @@ -27,5 +30,5 @@ class Gists @div class: 'content', => @h3 "Gist #{response.id} created", class: 'title' @p "The url is on your clipboard", class: 'message' - @rootView.append(notification.hide()) + rootView.append(notification.hide()) notification.fadeIn().delay(2000).fadeOut(complete: -> $(this).remove()) diff --git a/src/packages/gists/package.cson b/src/packages/gists/package.cson new file mode 100644 index 000000000..45603d61f --- /dev/null +++ b/src/packages/gists/package.cson @@ -0,0 +1,3 @@ +'main': 'lib/gists' +'activationEvents': + 'gist:create': '.editor' diff --git a/src/packages/gists/spec/gists-spec.coffee b/src/packages/gists/spec/gists-spec.coffee index 056d6e412..5382fb33f 100644 --- a/src/packages/gists/spec/gists-spec.coffee +++ b/src/packages/gists/spec/gists-spec.coffee @@ -2,12 +2,11 @@ RootView = require 'root-view' $ = require 'jquery' describe "Gists package", -> - - [rootView, editor] = [] + [editor] = [] beforeEach -> rootView = new RootView(fixturesProject.resolve('sample.js')) - atom.loadPackage('gists').getInstance() + atom.loadPackage('gists') editor = rootView.getActiveEditor() spyOn($, 'ajax') diff --git a/src/packages/go-to-line/index.coffee b/src/packages/go-to-line/index.coffee deleted file mode 100644 index 12601c00e..000000000 --- a/src/packages/go-to-line/index.coffee +++ /dev/null @@ -1,12 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class GoToLinePackage extends DeferredAtomPackage - - loadEvents: - 'editor:go-to-line': '.editor' - - instanceClass: 'go-to-line/lib/go-to-line-view' - - onLoadEvent: (event, instance) -> - instance.toggle(event.currentTargetView()) diff --git a/src/packages/go-to-line/lib/go-to-line-view.coffee b/src/packages/go-to-line/lib/go-to-line-view.coffee index f207e11eb..1f24afd39 100644 --- a/src/packages/go-to-line/lib/go-to-line-view.coffee +++ b/src/packages/go-to-line/lib/go-to-line-view.coffee @@ -6,14 +6,15 @@ Point = require 'point' module.exports = class GoToLineView extends View - @activate: (rootView) -> new GoToLineView(rootView) + @activate: -> new GoToLineView @content: -> @div class: 'go-to-line overlay from-top mini', => @subview 'miniEditor', new Editor(mini: true) @div class: 'message', outlet: 'message' - initialize: (@rootView) -> + initialize: -> + rootView.command 'editor:go-to-line', '.editor', => @toggle() @miniEditor.on 'focusout', => @detach() @on 'core:confirm', => @confirm() @on 'core:cancel', => @detach() @@ -42,13 +43,13 @@ class GoToLineView extends View @detach() return unless editor and lineNumber.length - position = new Point(parseInt(lineNumber - 1, 0)) + position = new Point(parseInt(lineNumber - 1)) editor.scrollToBufferPosition(position, center: true) editor.setCursorBufferPosition(position) editor.moveCursorToFirstCharacterOfLine() attach: -> @previouslyFocusedElement = $(':focus') - @rootView.append(this) - @message.text("Enter a line number 1-#{@rootView.getActiveEditor().getLineCount()}") + rootView.append(this) + @message.text("Enter a line number 1-#{rootView.getActiveEditor().getLineCount()}") @miniEditor.focus() diff --git a/src/packages/go-to-line/package.cson b/src/packages/go-to-line/package.cson new file mode 100644 index 000000000..4711ed56c --- /dev/null +++ b/src/packages/go-to-line/package.cson @@ -0,0 +1,3 @@ +'activationEvents': + 'editor:go-to-line': '.editor' +'main': './lib/go-to-line-view' diff --git a/src/packages/go-to-line/spec/go-to-line-spec.coffee b/src/packages/go-to-line/spec/go-to-line-spec.coffee index 775bde131..8c9d13d90 100644 --- a/src/packages/go-to-line/spec/go-to-line-spec.coffee +++ b/src/packages/go-to-line/spec/go-to-line-spec.coffee @@ -1,13 +1,14 @@ RootView = require 'root-view' +GoToLineView = require 'go-to-line/lib/go-to-line-view' describe 'GoToLine', -> - [rootView, goToLine, editor] = [] + [goToLine, editor] = [] beforeEach -> - rootView = new RootView(require.resolve('fixtures/sample.js')) + new RootView(require.resolve('fixtures/sample.js')) rootView.enableKeymap() - goToLine = atom.loadPackage("go-to-line").getInstance() editor = rootView.getActiveEditor() + goToLine = GoToLineView.activate() editor.setCursorBufferPosition([1,0]) afterEach -> diff --git a/src/packages/markdown-preview/index.coffee b/src/packages/markdown-preview/index.coffee deleted file mode 100644 index 7cbba03f0..000000000 --- a/src/packages/markdown-preview/index.coffee +++ /dev/null @@ -1,9 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class MarkdownPreview extends DeferredAtomPackage - loadEvents: ['markdown-preview:toggle'] - - instanceClass: 'markdown-preview/src/markdown-preview-view' - - onLoadEvent: (event, instance) -> instance.toggle() diff --git a/src/packages/markdown-preview/src/markdown-preview-view.coffee b/src/packages/markdown-preview/lib/markdown-preview-view.coffee similarity index 75% rename from src/packages/markdown-preview/src/markdown-preview-view.coffee rename to src/packages/markdown-preview/lib/markdown-preview-view.coffee index c044623ab..f112d58e1 100644 --- a/src/packages/markdown-preview/src/markdown-preview-view.coffee +++ b/src/packages/markdown-preview/lib/markdown-preview-view.coffee @@ -5,19 +5,19 @@ $ = require 'jquery' module.exports = class MarkdownPreviewView extends ScrollView - @activate: (rootView, state) -> - @instance = new this(rootView) + @activate: -> + @instance = new MarkdownPreviewView - @content: (rootView) -> + @content: -> @div class: 'markdown-preview', tabindex: -1, => @div class: 'markdown-body', outlet: 'markdownBody' - initialize: (@rootView) -> + initialize: -> super - @editor = @rootView.getActiveEditor() - @subscribe @editor, 'focus', => @detach() unless @detaching - @command 'core:cancel', => @detach() unless @detaching + rootView.command 'markdown-preview:toggle', => @toggle() + @on 'blur', => @detach() unless document.activeElement is this[0] + @command 'core:cancel', => @detach() toggle: -> if @hasParent() @@ -27,22 +27,23 @@ class MarkdownPreviewView extends ScrollView attach: -> return unless @isMarkdownFile(@getActivePath()) - @rootView.append(this) + rootView.append(this) @markdownBody.html(@getLoadingHtml()) @loadHtml() @focus() detach: -> + return if @detaching @detaching = true super - @rootView.focus() + rootView.focus() @detaching = false getActivePath: -> - @editor.getPath() + rootView.getActiveEditor()?.getPath() getActiveText: -> - @editor.getText() + rootView.getActiveEditor()?.getText() getErrorHtml: (error) -> $$$ -> @@ -76,4 +77,4 @@ class MarkdownPreviewView extends ScrollView @markdownBody.html(html) if @hasParent() isMarkdownFile: (path) -> - fs.isMarkdownExtension(fs.extension(path)) + path and fs.isMarkdownExtension(fs.extension(path)) diff --git a/src/packages/markdown-preview/package.cson b/src/packages/markdown-preview/package.cson new file mode 100644 index 000000000..deea08f07 --- /dev/null +++ b/src/packages/markdown-preview/package.cson @@ -0,0 +1,3 @@ +'main': 'lib/markdown-preview-view' +'activationEvents': + 'markdown-preview:toggle': '.editor' diff --git a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee index 49066159b..6917e8240 100644 --- a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee +++ b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee @@ -1,12 +1,12 @@ $ = require 'jquery' RootView = require 'root-view' -MarkdownPreview = require 'markdown-preview/src/markdown-preview-view' +MarkdownPreview = require 'markdown-preview/lib/markdown-preview-view' describe "MarkdownPreview", -> - [rootView, markdownPreview] = [] - beforeEach -> rootView = new RootView(require.resolve('fixtures/markdown')) + atom.loadPackage("markdown-preview") + spyOn(MarkdownPreview.prototype, 'loadHtml') afterEach -> rootView.deactivate() @@ -15,44 +15,37 @@ describe "MarkdownPreview", -> it "toggles on/off a preview for a .md file", -> rootView.open('file.md') editor = rootView.getActiveEditor() - markdownPreview = atom.loadPackage("markdown-preview").getInstance() expect(rootView.find('.markdown-preview')).not.toExist() - spyOn(markdownPreview, 'loadHtml') editor.trigger('markdown-preview:toggle') markdownPreviewView = rootView.find('.markdown-preview')?.view() expect(rootView.find('.markdown-preview')).toExist() - expect(markdownPreview.loadHtml).toHaveBeenCalled(); + expect(markdownPreviewView.loadHtml).toHaveBeenCalled() markdownPreviewView.trigger('markdown-preview:toggle') expect(rootView.find('.markdown-preview')).not.toExist() it "displays a preview for a .markdown file", -> rootView.open('file.markdown') editor = rootView.getActiveEditor() - markdownPreview = atom.loadPackage("markdown-preview").getInstance() expect(rootView.find('.markdown-preview')).not.toExist() - spyOn(markdownPreview, 'loadHtml') editor.trigger('markdown-preview:toggle') expect(rootView.find('.markdown-preview')).toExist() - expect(markdownPreview.loadHtml).toHaveBeenCalled(); + markdownPreviewView = rootView.find('.markdown-preview')?.view() + expect(markdownPreviewView.loadHtml).toHaveBeenCalled() it "does not display a preview for non-markdown file", -> rootView.open('file.js') editor = rootView.getActiveEditor() - markdownPreview = atom.loadPackage("markdown-preview").getInstance() expect(rootView.find('.markdown-preview')).not.toExist() - spyOn(markdownPreview, 'loadHtml') editor.trigger('markdown-preview:toggle') expect(rootView.find('.markdown-preview')).not.toExist() - expect(markdownPreview.loadHtml).not.toHaveBeenCalled(); + expect(MarkdownPreview.prototype.loadHtml).not.toHaveBeenCalled() describe "core:cancel event", -> it "removes markdown preview", -> rootView.open('file.md') editor = rootView.getActiveEditor() - markdownPreview = atom.loadPackage("markdown-preview").getInstance() expect(rootView.find('.markdown-preview')).not.toExist() - spyOn(markdownPreview, 'loadHtml') editor.trigger('markdown-preview:toggle') markdownPreviewView = rootView.find('.markdown-preview')?.view() @@ -62,14 +55,19 @@ describe "MarkdownPreview", -> describe "when the editor receives focus", -> it "removes the markdown preview view", -> + rootView.attachToDom() rootView.open('file.md') editor = rootView.getActiveEditor() - markdownPreview = atom.loadPackage("markdown-preview").getInstance() expect(rootView.find('.markdown-preview')).not.toExist() - spyOn(markdownPreview, 'loadHtml') editor.trigger('markdown-preview:toggle') markdownPreviewView = rootView.find('.markdown-preview') - expect(markdownPreviewView).toExist() editor.focus() + expect(markdownPreviewView).toExist() + expect(rootView.find('.markdown-preview')).not.toExist() + + describe "when no editor is open", -> + it "does not attach", -> + expect(rootView.getActiveEditor()).toBeFalsy() + rootView.trigger('markdown-preview:toggle') expect(rootView.find('.markdown-preview')).not.toExist() diff --git a/src/packages/snippets/src/load-snippets-handler.coffee b/src/packages/snippets/lib/load-snippets-handler.coffee similarity index 100% rename from src/packages/snippets/src/load-snippets-handler.coffee rename to src/packages/snippets/lib/load-snippets-handler.coffee diff --git a/src/packages/snippets/src/load-snippets-task.coffee b/src/packages/snippets/lib/load-snippets-task.coffee similarity index 90% rename from src/packages/snippets/src/load-snippets-task.coffee rename to src/packages/snippets/lib/load-snippets-task.coffee index e32d70dde..b664c0b51 100644 --- a/src/packages/snippets/src/load-snippets-task.coffee +++ b/src/packages/snippets/lib/load-snippets-task.coffee @@ -4,8 +4,8 @@ TextMatePackage = require 'text-mate-package' module.exports = class LoadSnippetsTask extends Task constructor: (@snippets) -> - super('snippets/src/load-snippets-handler') - @packages = atom.getPackages() + super('snippets/lib/load-snippets-handler') + @packages = atom.getLoadedPackages() @packages.push(path: config.configDirPath) started: -> diff --git a/src/packages/snippets/src/snippet-body-parser.coffee b/src/packages/snippets/lib/snippet-body-parser.coffee similarity index 100% rename from src/packages/snippets/src/snippet-body-parser.coffee rename to src/packages/snippets/lib/snippet-body-parser.coffee diff --git a/src/packages/snippets/src/snippet-body.pegjs b/src/packages/snippets/lib/snippet-body.pegjs similarity index 100% rename from src/packages/snippets/src/snippet-body.pegjs rename to src/packages/snippets/lib/snippet-body.pegjs diff --git a/src/packages/snippets/src/snippet-expansion.coffee b/src/packages/snippets/lib/snippet-expansion.coffee similarity index 100% rename from src/packages/snippets/src/snippet-expansion.coffee rename to src/packages/snippets/lib/snippet-expansion.coffee diff --git a/src/packages/snippets/src/snippet.coffee b/src/packages/snippets/lib/snippet.coffee similarity index 100% rename from src/packages/snippets/src/snippet.coffee rename to src/packages/snippets/lib/snippet.coffee diff --git a/src/packages/snippets/index.coffee b/src/packages/snippets/lib/snippets.coffee similarity index 81% rename from src/packages/snippets/index.coffee rename to src/packages/snippets/lib/snippets.coffee index 6778eb145..fb514f11b 100644 --- a/src/packages/snippets/index.coffee +++ b/src/packages/snippets/lib/snippets.coffee @@ -1,22 +1,25 @@ AtomPackage = require 'atom-package' fs = require 'fs' _ = require 'underscore' -SnippetExpansion = require './src/snippet-expansion' -Snippet = require './src/snippet' -LoadSnippetsTask = require './src/load-snippets-task' +SnippetExpansion = require './snippet-expansion' +Snippet = require './snippet' +LoadSnippetsTask = require './load-snippets-task' module.exports = -class Snippets extends AtomPackage snippetsByExtension: {} loaded: false - activate: (@rootView) -> + activate: -> window.snippets = this @loadAll() - @rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor) + rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor) + + deactivate: -> + @loadSnippetsTask?.terminate() loadAll: -> - new LoadSnippetsTask(this).start() + @loadSnippetsTask = new LoadSnippetsTask(this) + @loadSnippetsTask.start() loadDirectory: (snippetsDirPath) -> for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0 @@ -41,7 +44,7 @@ class Snippets extends AtomPackage syntax.addProperties(selector, snippets: snippetsByPrefix) getBodyParser: -> - require 'snippets/src/snippet-body-parser' + require 'snippets/lib/snippet-body-parser' enableSnippetsInEditor: (editor) -> editor.command 'snippets:expand', (e) => diff --git a/src/packages/snippets/package.cson b/src/packages/snippets/package.cson new file mode 100644 index 000000000..94cd77cc9 --- /dev/null +++ b/src/packages/snippets/package.cson @@ -0,0 +1 @@ +'main': 'lib/snippets' diff --git a/src/packages/snippets/spec/snippets-spec.coffee b/src/packages/snippets/spec/snippets-spec.coffee index e8499248b..ac3a2c441 100644 --- a/src/packages/snippets/spec/snippets-spec.coffee +++ b/src/packages/snippets/spec/snippets-spec.coffee @@ -1,6 +1,5 @@ -Snippets = require 'snippets' -Snippet = require 'snippets/src/snippet' -LoadSnippetsTask = require 'snippets/src/load-snippets-task' +Snippet = require 'snippets/lib/snippet' +LoadSnippetsTask = require 'snippets/lib/load-snippets-task' RootView = require 'root-view' Buffer = require 'buffer' Editor = require 'editor' @@ -12,7 +11,14 @@ describe "Snippets extension", -> beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) spyOn(LoadSnippetsTask.prototype, 'start') + + packageWithSnippets = atom.loadPackage("package-with-snippets") + + spyOn(atom, "getLoadedPackages").andCallFake -> + window.textMatePackages.concat([packageWithSnippets]) + atom.loadPackage("snippets") + editor = rootView.getActiveEditor() editSession = rootView.getActiveEditSession() buffer = editor.getBuffer() @@ -20,7 +26,7 @@ describe "Snippets extension", -> rootView.enableKeymap() afterEach -> - rootView.remove() + rootView.deactivate() delete window.snippets describe "when 'tab' is triggered on the editor", -> @@ -232,7 +238,6 @@ describe "Snippets extension", -> describe "snippet loading", -> beforeEach -> - atom.packages = null jasmine.unspy(LoadSnippetsTask.prototype, 'start') spyOn(LoadSnippetsTask.prototype, 'loadAtomSnippets').andCallFake -> @snippetsLoaded({}) spyOn(LoadSnippetsTask.prototype, 'loadTextMateSnippets').andCallFake -> @snippetsLoaded({}) @@ -271,7 +276,7 @@ describe "Snippets extension", -> } """ - # warn about junk-file, but don't even try to parse a hidden file + # warn about invalid.plist expect(console.warn).toHaveBeenCalled() expect(console.warn.calls.length).toBe 1 diff --git a/src/packages/status-bar/index.coffee b/src/packages/status-bar/index.coffee deleted file mode 100644 index 7d05f5962..000000000 --- a/src/packages/status-bar/index.coffee +++ /dev/null @@ -1,6 +0,0 @@ -AtomPackage = require 'atom-package' -StatusBarView = require './src/status-bar-view' - -module.exports = -class StatusBar extends AtomPackage - activate: (rootView) -> StatusBarView.activate(rootView) diff --git a/src/packages/status-bar/src/status-bar-view.coffee b/src/packages/status-bar/lib/status-bar-view.coffee similarity index 96% rename from src/packages/status-bar/src/status-bar-view.coffee rename to src/packages/status-bar/lib/status-bar-view.coffee index 565532679..1e7e38694 100644 --- a/src/packages/status-bar/src/status-bar-view.coffee +++ b/src/packages/status-bar/lib/status-bar-view.coffee @@ -4,7 +4,7 @@ $ = require 'jquery' module.exports = class StatusBarView extends View - @activate: (rootView) -> + @activate: -> rootView.eachEditor (editor) => @appendToEditorPane(rootView, editor) if editor.attached @@ -24,7 +24,7 @@ class StatusBarView extends View @span class: 'cursor-position', outlet: 'cursorPosition' @span class: 'grammar-name', outlet: 'grammarName' - initialize: (@rootView, @editor) -> + initialize: (rootView, @editor) -> @updatePathText() @editor.on 'editor:path-changed', => @subscribeToBuffer() @@ -99,7 +99,7 @@ class StatusBarView extends View updatePathText: -> if path = @editor.getPath() - @currentPath.text(@rootView.project.relativize(path)) + @currentPath.text(rootView.project.relativize(path)) else @currentPath.text('untitled') diff --git a/src/packages/status-bar/package.cson b/src/packages/status-bar/package.cson new file mode 100644 index 000000000..31e9f7795 --- /dev/null +++ b/src/packages/status-bar/package.cson @@ -0,0 +1 @@ +'main': 'lib/status-bar-view' diff --git a/src/packages/status-bar/spec/status-bar-spec.coffee b/src/packages/status-bar/spec/status-bar-spec.coffee index 16e4bcc20..fc702734e 100644 --- a/src/packages/status-bar/spec/status-bar-spec.coffee +++ b/src/packages/status-bar/spec/status-bar-spec.coffee @@ -1,7 +1,7 @@ $ = require 'jquery' _ = require 'underscore' RootView = require 'root-view' -StatusBar = require 'status-bar/src/status-bar-view' +StatusBar = require 'status-bar/lib/status-bar-view' fs = require 'fs' describe "StatusBar", -> @@ -10,7 +10,7 @@ describe "StatusBar", -> beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) rootView.simulateDomAttachment() - StatusBar.activate(rootView) + StatusBar.activate() editor = rootView.getActiveEditor() statusBar = rootView.find('.status-bar').view() buffer = editor.getBuffer() @@ -38,7 +38,7 @@ describe "StatusBar", -> rootView = new RootView rootView.open() rootView.simulateDomAttachment() - StatusBar.activate(rootView) + StatusBar.activate() statusBar = rootView.find('.status-bar').view() expect(statusBar.currentPath.text()).toBe 'untitled' expect(statusBar.cursorPosition.text()).toBe '1,1' diff --git a/src/packages/strip-trailing-whitespace/index.coffee b/src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee similarity index 83% rename from src/packages/strip-trailing-whitespace/index.coffee rename to src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee index 20b3e6d74..1f93f4a68 100644 --- a/src/packages/strip-trailing-whitespace/index.coffee +++ b/src/packages/strip-trailing-whitespace/lib/strip-trailing-whitespace.coffee @@ -1,8 +1,5 @@ -AtomPackage = require 'atom-package' - module.exports = -class StripTrailingWhitespace extends AtomPackage - activate: (rootView) -> + activate: -> rootView.eachBuffer (buffer) => @stripTrailingWhitespaceBeforeSave(buffer) stripTrailingWhitespaceBeforeSave: (buffer) -> diff --git a/src/packages/strip-trailing-whitespace/package.cson b/src/packages/strip-trailing-whitespace/package.cson new file mode 100644 index 000000000..0daeeb412 --- /dev/null +++ b/src/packages/strip-trailing-whitespace/package.cson @@ -0,0 +1 @@ +'main': './lib/strip-trailing-whitespace' diff --git a/src/packages/symbols-view/index.coffee b/src/packages/symbols-view/index.coffee deleted file mode 100644 index 50d9a89c0..000000000 --- a/src/packages/symbols-view/index.coffee +++ /dev/null @@ -1,21 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class Symbols extends DeferredAtomPackage - - loadEvents: [ - 'symbols-view:toggle-file-symbols' - 'symbols-view:toggle-project-symbols' - 'symbols-view:go-to-declaration' - ] - - instanceClass: 'symbols-view/src/symbols-view' - - onLoadEvent: (event, instance) -> - switch event.type - when 'symbols-view:toggle-file-symbols' - instance.toggleFileSymbols() - when 'symbols-view:toggle-project-symbols' - instance.toggleProjectSymbols() - when 'symbols-view:go-to-declaration' - instance.goToDeclaration() diff --git a/src/packages/symbols-view/src/symbols-view.coffee b/src/packages/symbols-view/lib/symbols-view.coffee similarity index 78% rename from src/packages/symbols-view/src/symbols-view.coffee rename to src/packages/symbols-view/lib/symbols-view.coffee index acf72cb78..c539eb5fe 100644 --- a/src/packages/symbols-view/src/symbols-view.coffee +++ b/src/packages/symbols-view/lib/symbols-view.coffee @@ -1,7 +1,7 @@ {$$} = require 'space-pen' SelectList = require 'select-list' -TagGenerator = require 'symbols-view/src/tag-generator' -TagReader = require 'symbols-view/src/tag-reader' +TagGenerator = require './tag-generator' +TagReader = require './tag-reader' Point = require 'point' fs = require 'fs' $ = require 'jquery' @@ -9,16 +9,20 @@ $ = require 'jquery' module.exports = class SymbolsView extends SelectList - @activate: (rootView) -> - @instance = new SymbolsView(rootView) + @activate: -> + new SymbolsView @viewClass: -> "#{super} symbols-view overlay from-top" filterKey: 'name' - initialize: (@rootView) -> + initialize: -> super + rootView.command 'symbols-view:toggle-file-symbols', => @toggleFileSymbols() + rootView.command 'symbols-view:toggle-project-symbols', => @toggleProjectSymbols() + rootView.command 'symbols-view:go-to-declaration', => @goToDeclaration() + itemForElement: ({position, name, file}) -> $$ -> @li => @@ -40,7 +44,8 @@ class SymbolsView extends SelectList populateFileSymbols: -> tags = [] callback = (tag) -> tags.push tag - path = @rootView.getActiveEditor().getPath() + path = rootView.getActiveEditor().getPath() + @list.empty() @setLoading("Generating symbols...") new TagGenerator(path, callback).generate().done => if tags.length > 0 @@ -60,8 +65,9 @@ class SymbolsView extends SelectList @attach() populateProjectSymbols: -> + @list.empty() @setLoading("Loading symbols...") - TagReader.getAllTags(@rootView.project).done (tags) => + TagReader.getAllTags(rootView.project).done (tags) => if tags.length > 0 @miniEditor.show() @maxItems = 10 @@ -78,11 +84,11 @@ class SymbolsView extends SelectList openTag: (tag) -> position = tag.position position = @getTagLine(tag) unless position - @rootView.open(tag.file, {changeFocus: true, allowActiveEditorChange:true}) if tag.file + rootView.open(tag.file, {changeFocus: true, allowActiveEditorChange:true}) if tag.file @moveToPosition(position) if position moveToPosition: (position) -> - editor = @rootView.getActiveEditor() + editor = rootView.getActiveEditor() editor.scrollToBufferPosition(position, center: true) editor.setCursorBufferPosition(position) editor.moveCursorToFirstCharacterOfLine() @@ -90,19 +96,19 @@ class SymbolsView extends SelectList attach: -> super - @rootView.append(this) + rootView.append(this) @miniEditor.focus() getTagLine: (tag) -> pattern = $.trim(tag.pattern?.replace(/(^^\/\^)|(\$\/$)/g, '')) # Remove leading /^ and trailing $/ return unless pattern - file = @rootView.project.resolve(tag.file) + file = rootView.project.resolve(tag.file) return unless fs.isFile(file) for line, index in fs.read(file).split('\n') return new Point(index, 0) if pattern is $.trim(line) goToDeclaration: -> - editor = @rootView.getActiveEditor() + editor = rootView.getActiveEditor() matches = TagReader.find(editor) return unless matches.length diff --git a/src/packages/symbols-view/src/tag-generator.coffee b/src/packages/symbols-view/lib/tag-generator.coffee similarity index 100% rename from src/packages/symbols-view/src/tag-generator.coffee rename to src/packages/symbols-view/lib/tag-generator.coffee diff --git a/src/packages/symbols-view/src/tag-reader.coffee b/src/packages/symbols-view/lib/tag-reader.coffee similarity index 100% rename from src/packages/symbols-view/src/tag-reader.coffee rename to src/packages/symbols-view/lib/tag-reader.coffee diff --git a/src/packages/symbols-view/package.cson b/src/packages/symbols-view/package.cson new file mode 100644 index 000000000..ceffedd45 --- /dev/null +++ b/src/packages/symbols-view/package.cson @@ -0,0 +1,5 @@ +'main': 'lib/symbols-view' +'activationEvents': + 'symbols-view:toggle-file-symbols': '.editor' + 'symbols-view:toggle-project-symbols': null + 'symbols-view:go-to-declaration': '.editor' diff --git a/src/packages/symbols-view/spec/symbols-view-spec.coffee b/src/packages/symbols-view/spec/symbols-view-spec.coffee index 3e0724795..5a8eff02c 100644 --- a/src/packages/symbols-view/spec/symbols-view-spec.coffee +++ b/src/packages/symbols-view/spec/symbols-view-spec.coffee @@ -1,16 +1,17 @@ RootView = require 'root-view' -SymbolsView = require 'symbols-view/src/symbols-view' -TagGenerator = require 'symbols-view/src/tag-generator' +SymbolsView = require 'symbols-view/lib/symbols-view' +TagGenerator = require 'symbols-view/lib/tag-generator' fs = require 'fs' describe "SymbolsView", -> - [rootView, symbolsView, setArraySpy] = [] + [symbolsView, setArraySpy] = [] beforeEach -> rootView = new RootView(require.resolve('fixtures')) - symbolsView = atom.loadPackage("symbols-view").getInstance() + atom.loadPackage("symbols-view") + rootView.attachToDom() - setArraySpy = spyOn(symbolsView, 'setArray').andCallThrough() + setArraySpy = spyOn(SymbolsView.prototype, 'setArray').andCallThrough() afterEach -> rootView.deactivate() @@ -19,8 +20,8 @@ describe "SymbolsView", -> describe "when tags can be generated for a file", -> it "initially displays all JavaScript functions with line numbers", -> rootView.open('sample.js') - expect(rootView.find('.symbols-view')).not.toExist() rootView.getActiveEditor().trigger "symbols-view:toggle-file-symbols" + symbolsView = rootView.find('.symbols-view').view() expect(symbolsView.find('.loading')).toHaveText 'Generating symbols...' waitsFor -> @@ -39,8 +40,8 @@ describe "SymbolsView", -> it "displays error when no tags match text in mini-editor", -> rootView.open('sample.js') - expect(rootView.find('.symbols-view')).not.toExist() rootView.getActiveEditor().trigger "symbols-view:toggle-file-symbols" + symbolsView = rootView.find('.symbols-view').view() waitsFor -> setArraySpy.callCount > 0 @@ -66,8 +67,8 @@ describe "SymbolsView", -> describe "when tags can't be generated for a file", -> it "shows an error message when no matching tags are found", -> rootView.open('sample.txt') - expect(rootView.find('.symbols-view')).not.toExist() rootView.getActiveEditor().trigger "symbols-view:toggle-file-symbols" + symbolsView = rootView.find('.symbols-view').view() setErrorSpy = spyOn(symbolsView, "setError").andCallThrough() waitsFor -> @@ -95,6 +96,7 @@ describe "SymbolsView", -> rootView.open('sample.js') expect(rootView.getActiveEditor().getCursorBufferPosition()).toEqual [0,0] expect(rootView.find('.symbols-view')).not.toExist() + symbolsView = SymbolsView.activate() symbolsView.setArray(tags) symbolsView.attach() expect(rootView.find('.symbols-view')).toExist() @@ -146,6 +148,7 @@ describe "SymbolsView", -> editor = rootView.getActiveEditor() editor.setCursorBufferPosition([8,14]) editor.trigger 'symbols-view:go-to-declaration' + symbolsView = rootView.find('.symbols-view').view() expect(symbolsView.list.children('li').length).toBe 2 expect(symbolsView).toBeVisible() symbolsView.confirmed(symbolsView.array[0]) @@ -164,6 +167,7 @@ describe "SymbolsView", -> editor = rootView.getActiveEditor() editor.setCursorBufferPosition([8,14]) editor.trigger 'symbols-view:go-to-declaration' + symbolsView = rootView.find('.symbols-view').view() expect(symbolsView.list.children('li').length).toBe 1 expect(symbolsView.list.children('li:first').find('.label')).toHaveText 'tagged.js' @@ -172,6 +176,7 @@ describe "SymbolsView", -> rootView.open("tagged.js") expect(rootView.find('.symbols-view')).not.toExist() rootView.trigger "symbols-view:toggle-project-symbols" + symbolsView = rootView.find('.symbols-view').view() expect(symbolsView.find('.loading')).toHaveText 'Loading symbols...' waitsFor -> diff --git a/src/packages/tabs/index.coffee b/src/packages/tabs/index.coffee deleted file mode 100644 index 58a88c08a..000000000 --- a/src/packages/tabs/index.coffee +++ /dev/null @@ -1,6 +0,0 @@ -AtomPackage = require 'atom-package' -TabsView = require './src/tabs-view' - -module.exports = -class Tabs extends AtomPackage - activate: (rootView) -> TabsView.activate(rootView) diff --git a/src/packages/tabs/src/tab.coffee b/src/packages/tabs/lib/tab.coffee similarity index 100% rename from src/packages/tabs/src/tab.coffee rename to src/packages/tabs/lib/tab.coffee diff --git a/src/packages/tabs/src/tabs-view.coffee b/src/packages/tabs/lib/tabs-view.coffee similarity index 97% rename from src/packages/tabs/src/tabs-view.coffee rename to src/packages/tabs/lib/tabs-view.coffee index 35c3bee57..8ce1f8243 100644 --- a/src/packages/tabs/src/tabs-view.coffee +++ b/src/packages/tabs/lib/tabs-view.coffee @@ -3,8 +3,8 @@ SortableList = require 'sortable-list' Tab = require 'tabs/src/tab' module.exports = -class Tabs extends SortableList - @activate: (rootView) -> +class Tabs extends View + @activate: -> rootView.eachEditor (editor) => @prependToEditorPane(rootView, editor) if editor.attached diff --git a/src/packages/tabs/package.cson b/src/packages/tabs/package.cson new file mode 100644 index 000000000..6cc9d8565 --- /dev/null +++ b/src/packages/tabs/package.cson @@ -0,0 +1 @@ +'main': 'lib/tabs-view' diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index e5b821204..a9bc5b358 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -1,11 +1,10 @@ $ = require 'jquery' _ = require 'underscore' RootView = require 'root-view' -Tabs = require 'tabs' fs = require 'fs' describe "Tabs", -> - [rootView, editor, buffer, tabs] = [] + [editor, buffer, tabs] = [] beforeEach -> rootView = new RootView(require.resolve('fixtures/sample.js')) diff --git a/src/packages/tree-view/index.coffee b/src/packages/tree-view/index.coffee deleted file mode 100644 index b2228f183..000000000 --- a/src/packages/tree-view/index.coffee +++ /dev/null @@ -1,26 +0,0 @@ -DeferredAtomPackage = require 'deferred-atom-package' - -module.exports = -class Tree extends DeferredAtomPackage - - loadEvents: [ - 'tree-view:toggle' - 'tree-view:reveal-active-file' - ] - - instanceClass: 'tree-view/src/tree-view' - - activate: (rootView, state) -> - super - - if state - @getInstance().attach() if state.attached - else if rootView.project.getPath() and not rootView.pathToOpenIsFile - @getInstance().attach() - - onLoadEvent: (event, instance) -> - switch event.type - when 'tree-view:toggle' - instance.toggle() - when 'tree-view:reveal-active-file' - instance.revealActiveFile() diff --git a/src/packages/tree-view/src/dialog.coffee b/src/packages/tree-view/lib/dialog.coffee similarity index 100% rename from src/packages/tree-view/src/dialog.coffee rename to src/packages/tree-view/lib/dialog.coffee diff --git a/src/packages/tree-view/src/directory-view.coffee b/src/packages/tree-view/lib/directory-view.coffee similarity index 98% rename from src/packages/tree-view/src/directory-view.coffee rename to src/packages/tree-view/lib/directory-view.coffee index 3614f79da..29bd8e767 100644 --- a/src/packages/tree-view/src/directory-view.coffee +++ b/src/packages/tree-view/lib/directory-view.coffee @@ -1,5 +1,5 @@ {View, $$} = require 'space-pen' -FileView = require 'tree-view/src/file-view' +FileView = require './file-view' Directory = require 'directory' $ = require 'jquery' Git = require 'git' diff --git a/src/packages/tree-view/src/file-view.coffee b/src/packages/tree-view/lib/file-view.coffee similarity index 100% rename from src/packages/tree-view/src/file-view.coffee rename to src/packages/tree-view/lib/file-view.coffee diff --git a/src/packages/tree-view/src/tree-view.coffee b/src/packages/tree-view/lib/tree-view.coffee similarity index 86% rename from src/packages/tree-view/src/tree-view.coffee rename to src/packages/tree-view/lib/tree-view.coffee index 67bd3dcec..c48400895 100644 --- a/src/packages/tree-view/src/tree-view.coffee +++ b/src/packages/tree-view/lib/tree-view.coffee @@ -1,34 +1,28 @@ {View, $$} = require 'space-pen' ScrollView = require 'scroll-view' Directory = require 'directory' -DirectoryView = require 'tree-view/src/directory-view' -FileView = require 'tree-view/src/file-view' -Dialog = require 'tree-view/src/dialog' +DirectoryView = require './directory-view' +FileView = require './file-view' +Dialog = require './dialog' fs = require 'fs' $ = require 'jquery' _ = require 'underscore' module.exports = class TreeView extends ScrollView - @activate: (rootView, state) -> + @activate: (state) -> if state - @instance = TreeView.deserialize(state, rootView) + TreeView.deserialize(state) else - @instance = new TreeView(rootView) - - @deactivate: -> - @instance.deactivate() - - @serialize: -> - @instance.serialize() + new TreeView @content: (rootView) -> @div class: 'tree-view-wrapper', => @ol class: 'tree-view tool-panel', tabindex: -1, outlet: 'treeViewList' @div class: 'tree-view-resizer', outlet: 'resizer' - @deserialize: (state, rootView) -> - treeView = new TreeView(rootView) + @deserialize: (state) -> + treeView = new TreeView treeView.root.deserializeEntryExpansionStates(state.directoryExpansionStates) treeView.selectEntryForPath(state.selectedPath) treeView.focusAfterAttach = state.hasFocus @@ -42,7 +36,7 @@ class TreeView extends ScrollView scrollTopAfterAttach: -1 selectedPath: null - initialize: (@rootView) -> + initialize: -> super @on 'click', '.entry', (e) => @entryClicked(e) @on 'mousedown', '.tree-view-resizer', (e) => @resizeStarted(e) @@ -55,15 +49,15 @@ class TreeView extends ScrollView @command 'tree-view:move', => @moveSelectedEntry() @command 'tree-view:add', => @add() @command 'tree-view:remove', => @removeSelectedEntry() - @command 'tool-panel:unfocus', => @rootView.focus() + @command 'tool-panel:unfocus', => rootView.focus() @command 'tree-view:directory-modified', => if @hasFocus() @selectEntryForPath(@selectedPath) if @selectedPath else @selectActiveFile() - @rootView.on 'root-view:active-path-changed', => @selectActiveFile() - @rootView.project.on 'path-changed', => @updateRoot() + rootView.on 'root-view:active-path-changed', => @selectActiveFile() + rootView.project.on 'path-changed', => @updateRoot() @observeConfig 'core.hideGitIgnoredFiles', => @updateRoot() @selectEntry(@root) if @root @@ -94,13 +88,13 @@ class TreeView extends ScrollView attach: -> return unless rootView.project.getPath() - @rootView.horizontal.prepend(this) + rootView.horizontal.prepend(this) @focus() detach: -> @scrollTopAfterAttach = @scrollTop() super - @rootView.focus() + rootView.focus() focus: -> @treeViewList.focus() @@ -116,7 +110,7 @@ class TreeView extends ScrollView @openSelectedEntry(false) if entry instanceof FileView when 2 if entry.is('.selected.file') - @rootView.getActiveEditor().focus() + rootView.getActiveEditor().focus() else if entry.is('.selected.directory') entry.toggleExpansion() @@ -137,22 +131,22 @@ class TreeView extends ScrollView updateRoot: -> @root?.remove() - if rootDirectory = @rootView.project.getRootDirectory() - @root = new DirectoryView(directory: rootDirectory, isExpanded: true, project: @rootView.project) + if rootDirectory = rootView.project.getRootDirectory() + @root = new DirectoryView(directory: rootDirectory, isExpanded: true, project: rootView.project) @treeViewList.append(@root) else @root = null selectActiveFile: -> - activeFilePath = @rootView.getActiveEditor()?.getPath() + activeFilePath = rootView.getActiveEditor()?.getPath() @selectEntryForPath(activeFilePath) if activeFilePath revealActiveFile: -> @attach() - return unless activeFilePath = @rootView.getActiveEditor()?.getPath() + return unless activeFilePath = rootView.getActiveEditor()?.getPath() - project = @rootView.project + project = rootView.project activePathComponents = project.relativize(activeFilePath).split('/') currentPath = project.getPath().replace(/\/$/, '') for pathComponent in activePathComponents @@ -219,7 +213,7 @@ class TreeView extends ScrollView if selectedEntry instanceof DirectoryView selectedEntry.view().toggleExpansion() else if selectedEntry instanceof FileView - @rootView.open(selectedEntry.getPath(), { changeFocus }) + rootView.open(selectedEntry.getPath(), { changeFocus }) moveSelectedEntry: -> entry = @selectedEntry() @@ -232,11 +226,11 @@ class TreeView extends ScrollView dialog = new Dialog prompt: prompt - path: @rootView.project.relativize(oldPath) + path: rootView.project.relativize(oldPath) select: true iconClass: 'move' onConfirm: (newPath) => - newPath = @rootView.project.resolve(newPath) + newPath = rootView.project.resolve(newPath) directoryPath = fs.directory(newPath) try fs.makeTree(directoryPath) unless fs.exists(directoryPath) @@ -245,7 +239,7 @@ class TreeView extends ScrollView catch e dialog.showError("Error: #{e.message} Try a different path.") - @rootView.append(dialog) + rootView.append(dialog) removeSelectedEntry: -> entry = @selectedEntry() @@ -264,7 +258,7 @@ class TreeView extends ScrollView selectedEntry = @selectedEntry() or @root selectedPath = selectedEntry.getPath() directoryPath = if fs.isFile(selectedPath) then fs.directory(selectedPath) else selectedPath - relativeDirectoryPath = @rootView.project.relativize(directoryPath) + relativeDirectoryPath = rootView.project.relativize(directoryPath) relativeDirectoryPath += '/' if relativeDirectoryPath.length > 0 dialog = new Dialog @@ -274,7 +268,7 @@ class TreeView extends ScrollView iconClass: 'add' onConfirm: (relativePath) => endsWithDirectorySeparator = /\/$/.test(relativePath) - path = @rootView.project.resolve(relativePath) + path = rootView.project.resolve(relativePath) try if fs.exists(path) pathType = if fs.isFile(path) then "file" else "directory" @@ -286,12 +280,12 @@ class TreeView extends ScrollView @selectEntryForPath(path) else fs.write(path, "") - @rootView.open(path) + rootView.open(path) dialog.close() catch e dialog.showError("Error: #{e.message} Try a different path.") - @rootView.append(dialog) + rootView.append(dialog) selectedEntry: -> @treeViewList.find('.selected')?.view() diff --git a/src/packages/tree-view/lib/tree.coffee b/src/packages/tree-view/lib/tree.coffee new file mode 100644 index 000000000..86b4f8fb4 --- /dev/null +++ b/src/packages/tree-view/lib/tree.coffee @@ -0,0 +1,27 @@ +module.exports = + treeView: null + + activate: (@state) -> + if state + @createView().attach() if state.attached + else if rootView.project.getPath() and not rootView.pathToOpenIsFile + @createView().attach() + + rootView.command 'tree-view:toggle', => @createView().toggle() + rootView.command 'tree-view:reveal-active-file', => @createView().revealActiveFile() + + deactivate: -> + @treeView?.deactivate() + @treeView = null + + serialize: -> + if @treeView? + @treeView.serialize() + else + @state + + createView: -> + unless @treeView? + TreeView = require 'tree-view/lib/tree-view' + @treeView = TreeView.activate(@state) + @treeView diff --git a/src/packages/tree-view/package.cson b/src/packages/tree-view/package.cson new file mode 100644 index 000000000..4bbfd3695 --- /dev/null +++ b/src/packages/tree-view/package.cson @@ -0,0 +1 @@ +'main': 'lib/tree' diff --git a/src/packages/tree-view/spec/tree-view-spec.coffee b/src/packages/tree-view/spec/tree-view-spec.coffee index 20b283267..8459a06ae 100644 --- a/src/packages/tree-view/spec/tree-view-spec.coffee +++ b/src/packages/tree-view/spec/tree-view-spec.coffee @@ -1,18 +1,18 @@ $ = require 'jquery' _ = require 'underscore' -TreeView = require 'tree-view/src/tree-view' +TreeView = require 'tree-view/lib/tree-view' RootView = require 'root-view' Directory = require 'directory' fs = require 'fs' describe "TreeView", -> - [rootView, project, treeView, sampleJs, sampleTxt] = [] + [project, treeView, sampleJs, sampleTxt] = [] beforeEach -> - rootView = new RootView(require.resolve('fixtures/tree-view')) + new RootView(require.resolve('fixtures/tree-view')) project = rootView.project - treeView = atom.loadPackage("tree-view").getInstance() + treeView = atom.loadPackage("tree-view").packageMain.createView() treeView.root = treeView.find('ol > li:first').view() sampleJs = treeView.find('.file:contains(tree-view.js)') sampleTxt = treeView.find('.file:contains(tree-view.txt)') @@ -20,7 +20,7 @@ describe "TreeView", -> expect(treeView.root.directory.subscriptionCount()).toBeGreaterThan 0 afterEach -> - rootView?.deactivate() + rootView.deactivate() describe ".initialize(project)", -> it "renders the root of the project and its contents alphabetically with subdirectories first in a collapsed state", -> @@ -48,8 +48,8 @@ describe "TreeView", -> beforeEach -> rootView.deactivate() - rootView = new RootView - treeView = atom.loadPackage("tree-view").getInstance() + new RootView + treeView = atom.loadPackage("tree-view").packageMain.createView() it "does not attach to the root view or create a root node when initialized", -> expect(treeView.hasParent()).toBeFalsy() @@ -74,8 +74,8 @@ describe "TreeView", -> beforeEach -> rootView.deactivate() - rootView = new RootView(require.resolve('fixtures/tree-view/tree-view.js')) - treeView = atom.loadPackage("tree-view").getInstance() + new RootView(require.resolve('fixtures/tree-view/tree-view.js')) + treeView = atom.loadPackage("tree-view").packageMain.createView() it "does not attach to the root view but does create a root node when initialized", -> expect(treeView.hasParent()).toBeFalsy() @@ -90,10 +90,11 @@ describe "TreeView", -> it "restores expanded directories and selected file when deserialized", -> treeView.find('.directory:contains(dir1)').click() sampleJs.click() + oldRootView = rootView newRootView = RootView.deserialize(rootView.serialize()) - rootView.deactivate() # Deactivates previous TreeView + oldRootView.deactivate() # Deactivates previous TreeView - newRootView.activatePackage('tree-view', TreeView) + atom.loadPackage('tree-view') newTreeView = newRootView.find(".tree-view").view() @@ -106,11 +107,12 @@ describe "TreeView", -> treeView.focus() expect(treeView.find(".tree-view")).toMatchSelector ':focus' + oldRootView = rootView newRootView = RootView.deserialize(rootView.serialize()) - rootView.deactivate() # Deactivates previous TreeView + oldRootView.deactivate() # Deactivates previous TreeView newRootView.attachToDom() - newRootView.activatePackage('tree-view', TreeView) + atom.loadPackage('tree-view') newTreeView = newRootView.find(".tree-view").view() expect(newTreeView.find(".tree-view")).toMatchSelector ':focus' @@ -604,7 +606,7 @@ describe "TreeView", -> fs.makeDirectory(dirPath) fs.write(filePath, "doesn't matter") - rootView = new RootView(rootDirPath) + new RootView(rootDirPath) project = rootView.project atom.loadPackage('tree-view') treeView = rootView.find(".tree-view").view() @@ -614,7 +616,6 @@ describe "TreeView", -> afterEach -> rootView.deactivate() - rootView = null fs.remove(rootDirPath) if fs.exists(rootDirPath) describe "tree-view:add", -> diff --git a/src/packages/wrap-guide/index.coffee b/src/packages/wrap-guide/index.coffee deleted file mode 100644 index 1f9ab8ffd..000000000 --- a/src/packages/wrap-guide/index.coffee +++ /dev/null @@ -1,6 +0,0 @@ -AtomPackage = require 'atom-package' -WrapGuideView = require './src/wrap-guide-view' - -module.exports = -class WrapGuide extends AtomPackage - activate: (rootView) -> WrapGuideView.activate(rootView) diff --git a/src/packages/wrap-guide/src/wrap-guide-view.coffee b/src/packages/wrap-guide/lib/wrap-guide-view.coffee similarity index 91% rename from src/packages/wrap-guide/src/wrap-guide-view.coffee rename to src/packages/wrap-guide/lib/wrap-guide-view.coffee index 35dd192d8..2035e8157 100644 --- a/src/packages/wrap-guide/src/wrap-guide-view.coffee +++ b/src/packages/wrap-guide/lib/wrap-guide-view.coffee @@ -4,20 +4,20 @@ _ = require 'underscore' module.exports = class WrapGuideView extends View - @activate: (rootView, state) -> + @activate: -> rootView.eachEditor (editor) => @appendToEditorPane(rootView, editor) if editor.attached @appendToEditorPane: (rootView, editor) -> if underlayer = editor.pane()?.find('.underlayer') - underlayer.append(new WrapGuideView(rootView, editor)) + underlayer.append(new WrapGuideView(editor)) @content: -> @div class: 'wrap-guide' defaultColumn: 80 - initialize: (@rootView, @editor) => + initialize: (@editor) => @observeConfig 'editor.fontSize', => @updateGuide() @subscribe @editor, 'editor:path-changed', => @updateGuide() @subscribe @editor, 'editor:min-width-changed', => @updateGuide() diff --git a/src/packages/wrap-guide/package.cson b/src/packages/wrap-guide/package.cson new file mode 100644 index 000000000..874306120 --- /dev/null +++ b/src/packages/wrap-guide/package.cson @@ -0,0 +1 @@ +'main': 'lib/wrap-guide-view' diff --git a/src/stdlib/task.coffee b/src/stdlib/task.coffee index 4a66a7042..146a62928 100644 --- a/src/stdlib/task.coffee +++ b/src/stdlib/task.coffee @@ -1,10 +1,15 @@ module.exports = class Task + terminated: false + constructor: (@path) -> start: -> + throw new Error("Task already started") if @worker? + @worker = new Worker(require.getPath('task-shell')) @worker.onmessage = ({data}) => + return if @terminated if data.method and this[data.method] this[data.method](data.args...) else @@ -35,4 +40,7 @@ class Task @worker.postMessage(data) terminate: -> - @worker.terminate() + unless @terminated + @terminated = true + @worker?.terminate() + @worker = null