diff --git a/package.json b/package.json index e240ed467..30343766f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.86.0", + "version": "0.90.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "All Rights Reserved", - "atomShellVersion": "0.11.9", + "atomShellVersion": "0.11.10", "dependencies": { "async": "0.2.6", "atom-keymap": "^0.17.0", @@ -23,14 +23,13 @@ "delegato": "1.x", "emissary": "^1.2.1", "first-mate": "^1.5.1", - "fs-plus": "^2.2.1", + "fs-plus": "^2.2.2", "fstream": "0.1.24", "fuzzaldrin": "~1.1", "git-utils": "^1.2.2", "grim": "0.7.0", "guid": "0.0.10", "jasmine-tagged": "^1.1.1", - "keytar": "1.x", "less-cache": "0.12.0", "mixto": "1.x", "mkdirp": "0.3.5", @@ -50,7 +49,7 @@ "serializable": "1.x", "space-pen": "3.1.1", "temp": "0.5.0", - "text-buffer": "^2.0.1", + "text-buffer": "^2.0.2", "theorist": "1.x", "underscore-plus": "^1.1.2", "vm-compatibility-layer": "0.1.0" @@ -67,36 +66,36 @@ "autocomplete": "0.27.0", "autoflow": "0.16.0", "autosave": "0.13.0", - "background-tips": "0.10.0", + "background-tips": "0.12.0", "bookmarks": "0.22.0", - "bracket-matcher": "0.30.0", + "bracket-matcher": "0.31.0", "command-palette": "0.20.0", "deprecation-cop": "0.2.0", "dev-live-reload": "0.30.0", "exception-reporting": "0.17.0", - "feedback": "0.29.0", - "find-and-replace": "0.97.0", - "fuzzy-finder": "0.47.0", + "feedback": "0.30.0", + "find-and-replace": "0.98.0", + "fuzzy-finder": "0.50.0", "git-diff": "0.28.0", "go-to-line": "0.19.0", "grammar-selector": "0.24.0", - "image-view": "0.32.0", + "image-view": "0.33.0", "keybinding-resolver": "0.17.0", - "link": "0.21.0", - "markdown-preview": "0.61.0", + "link": "0.22.0", + "markdown-preview": "0.64.0", "metrics": "0.32.0", "open-on-github": "0.28.0", "package-generator": "0.30.0", - "release-notes": "0.26.0", - "settings-view": "0.107.0", - "snippets": "0.40.0", + "release-notes": "0.27.0", + "settings-view": "0.110.0", + "snippets": "0.41.0", "spell-check": "0.33.0", - "status-bar": "0.39.0", - "styleguide": "0.28.0", + "status-bar": "0.40.0", + "styleguide": "0.29.0", "symbols-view": "0.49.0", - "tabs": "0.34.0", - "timecop": "0.17.0", - "tree-view": "0.89.0", + "tabs": "0.36.0", + "timecop": "0.18.0", + "tree-view": "0.90.0", "update-package-dependencies": "0.6.0", "welcome": "0.12.0", "whitespace": "0.22.0", @@ -106,7 +105,7 @@ "language-css": "0.13.0", "language-gfm": "0.31.0", "language-git": "0.9.0", - "language-go": "0.8.0", + "language-go": "0.9.0", "language-html": "0.19.0", "language-hyperlink": "0.9.0", "language-java": "0.9.0", @@ -119,14 +118,14 @@ "language-php": "0.14.0", "language-property-list": "0.7.0", "language-python": "0.15.0", - "language-ruby": "0.21.0", + "language-ruby": "0.22.0", "language-ruby-on-rails": "0.12.0", "language-sass": "0.8.0", "language-shellscript": "0.8.0", "language-source": "0.7.0", "language-sql": "0.8.0", "language-text": "0.6.0", - "language-todo": "0.9.0", + "language-todo": "0.10.0", "language-toml": "0.12.0", "language-xml": "0.11.0", "language-yaml": "0.6.0" diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 2d74d69f0..95ef05760 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -36,15 +36,53 @@ describe "Config", -> describe "when the value equals the default value", -> it "does not store the value", -> - atom.config.setDefaults("foo", same: 1, changes: 1) + atom.config.setDefaults "foo", + same: 1 + changes: 1 + sameArray: [1, 2, 3] + sameObject: {a: 1, b: 2} + null: null + undefined: undefined expect(atom.config.settings.foo).toBeUndefined() + atom.config.set('foo.same', 1) atom.config.set('foo.changes', 2) + atom.config.set('foo.sameArray', [1, 2, 3]) + atom.config.set('foo.null', undefined) + atom.config.set('foo.undefined', null) + atom.config.set('foo.sameObject', {b: 2, a: 1}) expect(atom.config.settings.foo).toEqual {changes: 2} atom.config.set('foo.changes', 1) expect(atom.config.settings.foo).toEqual {} + describe ".getDefault(keyPath)", -> + it "returns a clone of the default value", -> + atom.config.setDefaults("foo", same: 1, changes: 1) + expect(atom.config.getDefault('foo.same')).toBe 1 + expect(atom.config.getDefault('foo.changes')).toBe 1 + + atom.config.set('foo.same', 2) + atom.config.set('foo.changes', 3) + expect(atom.config.getDefault('foo.same')).toBe 1 + expect(atom.config.getDefault('foo.changes')).toBe 1 + + initialDefaultValue = [1, 2, 3] + atom.config.setDefaults("foo", bar: initialDefaultValue) + expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue + expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue + + describe ".isDefault(keyPath)", -> + it "returns true when the value of the key path is its default value", -> + atom.config.setDefaults("foo", same: 1, changes: 1) + expect(atom.config.isDefault('foo.same')).toBe true + expect(atom.config.isDefault('foo.changes')).toBe true + + atom.config.set('foo.same', 2) + atom.config.set('foo.changes', 3) + expect(atom.config.isDefault('foo.same')).toBe false + expect(atom.config.isDefault('foo.changes')).toBe false + describe ".toggle(keyPath)", -> it "negates the boolean value of the current key path value", -> atom.config.set('foo.a', 1) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 0a65cc712..0abd31d05 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -33,21 +33,7 @@ describe "Project", -> deserializedProject.getBuffers()[0].destroy() expect(deserializedProject.getBuffers().length).toBe 0 - describe "when an edit session is destroyed", -> - it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", -> - editor = atom.project.openSync("a") - anotherEditor = atom.project.openSync("a") - - expect(atom.project.editors.length).toBe 2 - expect(editor.buffer).toBe anotherEditor.buffer - - editor.destroy() - expect(atom.project.editors.length).toBe 1 - - anotherEditor.destroy() - expect(atom.project.editors.length).toBe 0 - - describe "when an edit session is saved and the project has no path", -> + describe "when an editor is saved and the project has no path", -> it "sets the project's path to the saved file's parent directory", -> tempFile = temp.openSync().path atom.project.setPath(undefined) @@ -56,73 +42,49 @@ describe "Project", -> editor.saveAs(tempFile) expect(atom.project.getPath()).toBe path.dirname(tempFile) - describe "when an edit session is copied", -> - it "emits an 'editor-created' event and stores the edit session", -> - handler = jasmine.createSpy('editorCreatedHandler') - atom.project.on 'editor-created', handler - - editor1 = atom.project.openSync("a") - expect(handler.callCount).toBe 1 - expect(atom.project.getEditors().length).toBe 1 - expect(atom.project.getEditors()[0]).toBe editor1 - - editor2 = editor1.copy() - expect(handler.callCount).toBe 2 - expect(atom.project.getEditors().length).toBe 2 - expect(atom.project.getEditors()[0]).toBe editor1 - expect(atom.project.getEditors()[1]).toBe editor2 - describe ".openSync(path)", -> - [absolutePath, newBufferHandler, newEditorHandler] = [] + [absolutePath, newBufferHandler] = [] beforeEach -> absolutePath = require.resolve('./fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') atom.project.on 'buffer-created', newBufferHandler - newEditorHandler = jasmine.createSpy('newEditorHandler') - atom.project.on 'editor-created', newEditorHandler describe "when given an absolute path that hasn't been opened previously", -> - it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session for the given path and emits 'buffer-created'", -> editor = atom.project.openSync(absolutePath) expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when given a relative path that hasn't been opened previously", -> - it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", -> editor = atom.project.openSync('a') expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when passed the path to a buffer that has already been opened", -> - it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", -> + it "returns a new edit session containing previously opened buffer", -> editor = atom.project.openSync(absolutePath) newBufferHandler.reset() expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer expect(atom.project.openSync('a').buffer).toBe editor.buffer expect(newBufferHandler).not.toHaveBeenCalled() - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when not passed a path", -> - it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session and emits 'buffer-created'", -> editor = atom.project.openSync() expect(editor.buffer.getPath()).toBeUndefined() expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer) - expect(newEditorHandler).toHaveBeenCalledWith editor describe ".open(path)", -> - [absolutePath, newBufferHandler, newEditorHandler] = [] + [absolutePath, newBufferHandler] = [] beforeEach -> absolutePath = require.resolve('./fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') atom.project.on 'buffer-created', newBufferHandler - newEditorHandler = jasmine.createSpy('newEditorHandler') - atom.project.on 'editor-created', newEditorHandler describe "when given an absolute path that isn't currently open", -> - it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session for the given path and emits 'buffer-created'", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -130,10 +92,9 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when given a relative path that isn't currently opened", -> - it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -141,10 +102,9 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when passed the path to a buffer that is currently opened", -> - it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", -> + it "returns a new edit session containing currently opened buffer", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -154,10 +114,9 @@ describe "Project", -> expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer expect(atom.project.openSync('a').buffer).toBe editor.buffer expect(newBufferHandler).not.toHaveBeenCalled() - expect(newEditorHandler).toHaveBeenCalledWith editor describe "when not passed a path", -> - it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", -> + it "returns a new edit session and emits 'buffer-created'", -> editor = null waitsForPromise -> atom.project.open().then (o) -> editor = o @@ -165,7 +124,6 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBeUndefined() expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer) - expect(newEditorHandler).toHaveBeenCalledWith editor it "returns number of read bytes as progress indicator", -> filePath = atom.project.resolve 'a' diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index d3e3bd434..cbf201913 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -36,6 +36,22 @@ describe "ThemeManager", -> themes = themeManager.getActiveThemes() expect(themes).toHaveLength(names.length) + describe "when the core.themes config value contains invalid entry", -> + fit "ignores theme", -> + atom.config.set 'core.themes', [ + 'atom-light-ui' + null + undefined + '' + false + 4 + {} + [] + 'atom-dark-ui' + ] + + expect(themeManager.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui'] + describe "getImportPaths()", -> it "returns the theme directories before the themes are loaded", -> atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui']) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index ffc1667c3..538907c02 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -5,7 +5,7 @@ describe "Workspace", -> beforeEach -> atom.project.setPath(atom.project.resolve('dir')) - workspace = new Workspace + atom.workspace = workspace = new Workspace describe "::open(uri, options)", -> beforeEach -> @@ -152,6 +152,18 @@ describe "Workspace", -> workspace.open("bar://baz").then (item) -> expect(item).toEqual { bar: "bar://baz" } + it "emits an 'editor-created' event", -> + absolutePath = require.resolve('./fixtures/dir/a') + newEditorHandler = jasmine.createSpy('newEditorHandler') + workspace.on 'editor-created', newEditorHandler + + editor = null + waitsForPromise -> + workspace.open(absolutePath).then (e) -> editor = e + + runs -> + expect(newEditorHandler).toHaveBeenCalledWith editor + describe "::openSync(uri, options)", -> [activePane, initialItemCount] = [] @@ -245,3 +257,26 @@ describe "Workspace", -> it "opens the license as plain-text in a buffer", -> waitsForPromise -> workspace.openLicense() runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/ + + describe "when an editor is destroyed", -> + it "removes the editor", -> + editor = null + + waitsForPromise -> + workspace.open("a").then (e) -> editor = e + + runs -> + expect(workspace.getEditors()).toHaveLength 1 + editor.destroy() + expect(workspace.getEditors()).toHaveLength 0 + + describe "when an editor is copied", -> + it "emits an 'editor-created' event", -> + handler = jasmine.createSpy('editorCreatedHandler') + workspace.on 'editor-created', handler + + editor1 = workspace.openSync("a") + expect(handler.callCount).toBe 1 + + editor2 = editor1.copy() + expect(handler.callCount).toBe 2 diff --git a/src/atom.coffee b/src/atom.coffee index 59c3d1578..6a096a22f 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -1,6 +1,5 @@ crypto = require 'crypto' ipc = require 'ipc' -keytar = require 'keytar' os = require 'os' path = require 'path' remote = require 'remote' @@ -484,17 +483,6 @@ class Atom extends Model isReleasedVersion: -> @constructor.isReleasedVersion() - getGitHubAuthTokenName: -> - 'Atom GitHub API Token' - - # Public: Set the the github token in the keychain - setGitHubAuthToken: (token) -> - keytar.replacePassword(@getGitHubAuthTokenName(), 'github', token) - - # Public: Get the github token from the keychain - getGitHubAuthToken: -> - keytar.getPassword(@getGitHubAuthTokenName(), 'github') - # Public: Get the directory path to Atom's configuration area. # # Returns the absolute path to ~/.atom diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 20d5e1e5b..01ca799b7 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -140,7 +140,10 @@ class AtomApplication @on 'application:open-file', -> @promptForPath(type: 'file') @on 'application:open-folder', -> @promptForPath(type: 'folder') @on 'application:open-dev', -> @promptForPath(devMode: true) - @on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y) + @on 'application:inspect', ({x,y, atomWindow}) -> + atomWindow ?= @focusedWindow() + atomWindow?.browserWindow.inspectElement(x, y) + @on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app') @on 'application:install-update', -> @autoUpdateManager.install() @on 'application:check-for-update', => @autoUpdateManager.check() @@ -228,6 +231,18 @@ class AtomApplication else @sendCommandToFirstResponder(command) + # Public: Executes the given command on the given window. + # + # command - The string representing the command. + # atomWindow - The {AtomWindow} to send the command to. + # args - The optional arguments to pass along. + sendCommandToWindow: (command, atomWindow, args...) -> + unless @emit(command, args...) + if atomWindow? + atomWindow.sendCommand(command, args...) + else + @sendCommandToFirstResponder(command) + # Translates the command into OS X action and sends it to application's first # responder. sendCommandToFirstResponder: (command) -> diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 4d4f71a93..744ae9a50 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -107,7 +107,7 @@ class AtomWindow when 1 then @browserWindow.restart() @browserWindow.on 'context-menu', (menuTemplate) => - new ContextMenu(menuTemplate, @browserWindow) + new ContextMenu(menuTemplate, this) if @isSpec # Spec window's web view should always have focus diff --git a/src/browser/context-menu.coffee b/src/browser/context-menu.coffee index e3044b30d..1f8377637 100644 --- a/src/browser/context-menu.coffee +++ b/src/browser/context-menu.coffee @@ -2,10 +2,10 @@ Menu = require 'menu' module.exports = class ContextMenu - constructor: (template, browserWindow) -> + constructor: (template, @atomWindow) -> template = @createClickHandlers(template) menu = Menu.buildFromTemplate(template) - menu.popup(browserWindow) + menu.popup(@atomWindow.browserWindow) # It's necessary to build the event handlers in this process, otherwise # closures are drug across processes and failed to be garbage collected @@ -13,7 +13,10 @@ class ContextMenu createClickHandlers: (template) -> for item in template if item.command - (item.commandOptions ?= {}).contextCommand = true - item.click = do (item) -> - => global.atomApplication.sendCommand(item.command, item.commandOptions) + item.commandOptions ?= {} + item.commandOptions.contextCommand = true + item.commandOptions.atomWindow = @atomWindow + do (item) => + item.click = => + global.atomApplication.sendCommandToWindow(item.command, @atomWindow, item.commandOptions) item diff --git a/src/config.coffee b/src/config.coffee index 0c0e558aa..551240f31 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -18,9 +18,9 @@ pathWatcher = require 'pathwatcher' # ## Example # # ```coffeescript -# atom.config.set('myplugin.key', 'value') -# atom.config.observe 'myplugin.key', -> -# console.log 'My configuration changed:', atom.config.get('myplugin.key') +# atom.config.set('my-package.key', 'value') +# atom.config.observe 'my-package.key', -> +# console.log 'My configuration changed:', atom.config.get('my-package.key') # ``` module.exports = class Config @@ -88,7 +88,7 @@ class Config _.extend hash, defaults @emit 'updated' - # Public: Get the path to the config file being used. + # Public: Get the {String} path to the config file being used. getUserConfigPath: -> @configFilePath @@ -98,10 +98,10 @@ class Config # Public: Retrieves the setting for the given key. # - # keyPath - The {String} name of the key to retrieve + # keyPath - The {String} name of the key to retrieve. # - # Returns the value from Atom's default settings, the user's configuration file, - # or `null` if the key doesn't exist in either. + # Returns the value from Atom's default settings, the user's configuration + # file, or `null` if the key doesn't exist in either. get: (keyPath) -> value = _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath) _.deepClone(value) @@ -110,8 +110,8 @@ class Config # # keyPath - The {String} name of the key to retrieve # - # Returns the value from Atom's default settings, the user's configuration file, - # or `NaN` if the key doesn't exist in either. + # Returns the value from Atom's default settings, the user's configuration + # file, or `NaN` if the key doesn't exist in either. getInt: (keyPath) -> parseInt(@get(keyPath)) @@ -121,8 +121,8 @@ class Config # defaultValue - The integer {Number} to fall back to if the value isn't # positive, defaults to 0. # - # Returns the value from Atom's default settings, the user's configuration file, - # or `defaultValue` if the key value isn't greater than zero. + # Returns the value from Atom's default settings, the user's configuration + # file, or `defaultValue` if the key value isn't greater than zero. getPositiveInt: (keyPath, defaultValue=0) -> Math.max(@getInt(keyPath), 0) or defaultValue @@ -130,13 +130,14 @@ class Config # # This value is stored in Atom's internal configuration file. # - # keyPath - The {String} name of the key - # value - The value of the setting + # keyPath - The {String} name of the key. + # value - The value of the setting. # # Returns the `value`. set: (keyPath, value) -> - if @get(keyPath) != value - value = undefined if _.valueForKeyPath(@defaultSettings, keyPath) == value + if @get(keyPath) isnt value + defaultValue = _.valueForKeyPath(@defaultSettings, keyPath) + value = undefined if _.isEqual(defaultValue, value) _.setValueForKeyPath(@settings, keyPath, value) @update() value @@ -146,6 +147,8 @@ class Config # The new value will be `true` if the value is currently falsy and will be # `false` if the value is currently truthy. # + # keyPath - The {String} name of the key. + # # Returns the new value. toggle: (keyPath) -> @set(keyPath, !@get(keyPath)) @@ -158,12 +161,30 @@ class Config restoreDefault: (keyPath) -> @set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath)) + # Public: Get the default value of the key path. + # + # keyPath - The {String} name of the key. + # + # Returns the default value. + getDefault: (keyPath) -> + defaultValue = _.valueForKeyPath(@defaultSettings, keyPath) + _.deepClone(defaultValue) + + # Public: Is the key path value its default value? + # + # keyPath - The {String} name of the key. + # + # Returns a {Boolean}, `true` if the current value is the default, `false` + # otherwise. + isDefault: (keyPath) -> + not _.valueForKeyPath(@settings, keyPath)? + # Public: Push the value to the array at the key path. # # keyPath - The {String} key path. # value - The value to push to the array. # - # Returns the new array length of the setting. + # Returns the new array length {Number} of the setting. pushAtKeyPath: (keyPath, value) -> arrayValue = @get(keyPath) ? [] result = arrayValue.push(value) @@ -175,7 +196,7 @@ class Config # keyPath - The {String} key path. # value - The value to shift onto the array. # - # Returns the new array length of the setting. + # Returns the new array length {Number} of the setting. unshiftAtKeyPath: (keyPath, value) -> arrayValue = @get(keyPath) ? [] result = arrayValue.unshift(value) @@ -225,9 +246,9 @@ class Config callback(value) if options.callNow ? true subscription - # Public: Unobserve all callbacks on a given key + # Public: Unobserve all callbacks on a given key. # - # keyPath - The {String} name of the key to unobserve + # keyPath - The {String} name of the key to unobserve. unobserve: (keyPath) -> @off("updated.#{keyPath.replace(/\./, '-')}") diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 40228156b..c310c9c48 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -18,7 +18,7 @@ class ContextMenuManager label: 'Inspect Element' command: 'application:inspect' executeAtBuild: (e) -> - @.commandOptions = x: e.pageX, y: e.pageY + @commandOptions = x: e.pageX, y: e.pageY ] # Public: Creates menu definitions from the object specified by the menu diff --git a/src/editor-view.coffee b/src/editor-view.coffee index b07eb3424..28b50bf05 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -50,7 +50,7 @@ class EditorView extends View showLineNumbers: true autoIndent: true normalizeIndentOnPaste: true - nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-" + nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" preferredLineLength: 80 tabLength: 2 softWrap: false @@ -527,7 +527,7 @@ class EditorView extends View if @editor @saveScrollPositionForEditor() - @editor.off(".editor") + @unsubscribe(@editor) @editor = editor @@ -535,34 +535,34 @@ class EditorView extends View @editor.setVisible(true) - @editor.on "destroyed", => + @subscribe @editor, "destroyed", => @remove() - @editor.on "contents-conflicted.editor", => + @subscribe @editor, "contents-conflicted", => @showBufferConflictAlert(@editor) - @editor.on "path-changed.editor", => + @subscribe @editor, "path-changed", => @editor.reloadGrammar() @trigger 'editor:path-changed' - @editor.on "grammar-changed.editor", => + @subscribe @editor, "grammar-changed", => @trigger 'editor:grammar-changed' - @editor.on 'selection-added.editor', (selection) => + @subscribe @editor, 'selection-added', (selection) => @newCursors.push(selection.cursor) @newSelections.push(selection) @requestDisplayUpdate() - @editor.on 'screen-lines-changed.editor', (e) => + @subscribe @editor, 'screen-lines-changed', (e) => @handleScreenLinesChange(e) - @editor.on 'scroll-top-changed.editor', (scrollTop) => + @subscribe @editor, 'scroll-top-changed', (scrollTop) => @scrollTop(scrollTop) - @editor.on 'scroll-left-changed.editor', (scrollLeft) => + @subscribe @editor, 'scroll-left-changed', (scrollLeft) => @scrollLeft(scrollLeft) - @editor.on 'soft-wrap-changed.editor', (softWrap) => + @subscribe @editor, 'soft-wrap-changed', (softWrap) => @setSoftWrap(softWrap) @trigger 'editor:path-changed' diff --git a/src/editor.coffee b/src/editor.coffee index aa492db18..d286e187f 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -182,7 +182,7 @@ class Editor extends Model @subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop @subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft - atom.project.addEditor(this) if registerEditor + atom.workspace?.editorAdded(this) if registerEditor serializeParams: -> id: @id @@ -225,19 +225,17 @@ class Editor extends Model @buffer.release() @displayBuffer.destroy() @languageMode.destroy() - atom.project?.removeEditor(this) # Create an {Editor} with its initial state based on this object copy: -> tabLength = @getTabLength() displayBuffer = @displayBuffer.copy() softTabs = @getSoftTabs() - newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true}) + newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true, registerEditor: true}) newEditor.setScrollTop(@getScrollTop()) newEditor.setScrollLeft(@getScrollLeft()) for marker in @findMarkers(editorId: @id) marker.copy(editorId: newEditor.id, preserveFolds: true) - atom.project.addEditor(newEditor) newEditor # Public: Get the title the editor's title for display in other parts of the @@ -915,7 +913,7 @@ class Editor extends Model for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() @foldBufferRow(foldedRow) - @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true) + @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true, autoscroll: true) # Move lines intersecting the most recent selection down by one row in screen # coordinates. @@ -971,7 +969,7 @@ class Editor extends Model for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() @foldBufferRow(foldedRow) - @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true) + @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) # Duplicate the most recent cursor's current line. duplicateLines: -> diff --git a/src/package.coffee b/src/package.coffee index f51d9aea4..6ef7bc3b4 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -7,7 +7,7 @@ fs = require 'fs-plus' {Emitter} = require 'emissary' Q = require 'q' -{$} = require './space-pen-extensions' +$ = null # Defer require in case this is in the window-less browser process ScopedProperties = require './scoped-properties' # Loads and activates a package's main module and resources such as @@ -286,6 +286,7 @@ class Package handleActivationEvent: (event) => bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event) @activateNow() + $ ?= require('./space-pen-extensions').$ $(event.target).trigger(event) @restoreEventHandlersOnBubblePath(bubblePathEventHandlers) @unsubscribeFromActivationEvents() @@ -303,6 +304,7 @@ class Package disableEventHandlersOnBubblePath: (event) -> bubblePathEventHandlers = [] disabledHandler = -> + $ ?= require('./space-pen-extensions').$ element = $(event.target) while element.length if eventHandlers = element.handlers()?[event.type] diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 1421b1283..1849441a2 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -81,6 +81,10 @@ class PaneContainerView extends View @find('.pane:has(:focus)').view() getActivePane: -> + deprecate("Use PaneContainerView::getActivePaneView instead.") + @getActivePaneView() + + getActivePaneView: -> @viewForModel(@model.activePane) getActivePaneItem: -> diff --git a/src/pane-container.coffee b/src/pane-container.coffee index a02afed9e..535e55607 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -39,6 +39,9 @@ class PaneContainer extends Model getPanes: -> @root?.getPanes() ? [] + getActivePane: -> + @activePane + paneForUri: (uri) -> find @getPanes(), (pane) -> pane.itemForUri(uri)? diff --git a/src/project.coffee b/src/project.coffee index 81f7eaa8c..f3acf0b1a 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -31,13 +31,11 @@ class Project extends Model constructor: ({path, @buffers}={}) -> @buffers ?= [] - @openers = [] for buffer in @buffers do (buffer) => buffer.once 'destroyed', => @removeBuffer(buffer) - @editors = [] @setPath(path) serializeParams: -> @@ -49,7 +47,6 @@ class Project extends Model params destroyed: -> - editor.destroy() for editor in @getEditors() buffer.destroy() for buffer in @getBuffers() @destroyRepo() @@ -132,15 +129,6 @@ class Project extends Model filePath = @resolve(filePath) @buildEditorForBuffer(@bufferForPathSync(filePath), options) - # Add the given {Editor}. - addEditor: (editor) -> - @editors.push editor - @emit 'editor-created', editor - - # Return and removes the given {Editor}. - removeEditor: (editor) -> - _.remove(@editors, editor) - # Retrieves all the {TextBuffer}s in the project; that is, the # buffers for all open files. # @@ -153,7 +141,7 @@ class Project extends Model @findBufferForPath(@resolve(filePath))?.isModified() findBufferForPath: (filePath) -> - _.find @buffers, (buffer) -> buffer.getPath() == filePath + _.find @buffers, (buffer) -> buffer.getPath() == filePath # Only to be used in specs bufferForPathSync: (filePath) -> @@ -304,8 +292,7 @@ class Project extends Model deferred.promise buildEditorForBuffer: (buffer, editorOptions) -> - editor = new Editor(_.extend({buffer}, editorOptions)) - @addEditor(editor) + editor = new Editor(_.extend({buffer, registerEditor: true}, editorOptions)) editor eachBuffer: (args...) -> @@ -321,20 +308,19 @@ class Project extends Model # Deprecated: delegate registerOpener: (opener) -> deprecate("Use Workspace::registerOpener instead") - @openers.push(opener) + atom.workspace.registerOpener(opener) # Deprecated: delegate unregisterOpener: (opener) -> deprecate("Use Workspace::unregisterOpener instead") - _.remove(@openers, opener) + atom.workspace.unregisterOpener(opener) # Deprecated: delegate eachEditor: (callback) -> deprecate("Use Workspace::eachEditor instead") - callback(editor) for editor in @getEditors() - @on 'editor-created', (editor) -> callback(editor) + atom.workspace.eachEditor(callback) # Deprecated: delegate getEditors: -> deprecate("Use Workspace::getEditors instead") - new Array(@editors...) + atom.workspace.getEditors() diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 320e1e8f9..3570bef69 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -48,6 +48,8 @@ class ThemeManager getEnabledThemeNames: -> themeNames = atom.config.get('core.themes') ? [] themeNames = [themeNames] unless _.isArray(themeNames) + themeNames = themeNames.filter (themeName) -> + themeName and typeof themeName is 'string' # Reverse so the first (top) theme is loaded after the others. We want # the first/top theme to override later themes in the stack. diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 52803a8ef..c4bcaad36 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -151,7 +151,7 @@ class WorkspaceView extends View @command 'pane:reopen-closed-item', => @reopenItemSync() - @command 'core:close', => if @getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane() + @command 'core:close', => if @getModel().getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane() @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() @@ -182,8 +182,8 @@ class WorkspaceView extends View detailedMessage: "The shell commands `atom` and `apm` are installed." handleFocus: -> - if @getActivePane() - @getActivePane().focus() + if @getActivePaneView() + @getActivePaneView().focus() false else @updateTitle() @@ -205,7 +205,7 @@ class WorkspaceView extends View # Updates the application's title, based on whichever file is open. updateTitle: -> if projectPath = atom.project.getPath() - if item = @getActivePaneItem() + if item = @getModel().getActivePaneItem() @setTitle("#{item.getTitle?() ? 'untitled'} - #{projectPath}") else @setTitle(projectPath) @@ -272,7 +272,7 @@ class WorkspaceView extends View # # Returns a {PaneView}. getActivePaneView: -> - @panes.getActivePane() + @panes.getActivePaneView() # Public: Get the view associated with the active pane item. # @@ -316,7 +316,7 @@ class WorkspaceView extends View # # Returns an Array of all open {PaneView}s. getPaneViews: -> - @panes.getPanes() + @panes.getPaneViews() # Public: Register a function to be called for every current and future # editor view in the workspace. diff --git a/src/workspace.coffee b/src/workspace.coffee index 18b253e1b..cc29853b0 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1,10 +1,11 @@ {deprecate} = require 'grim' -{remove, last} = require 'underscore-plus' +_ = require 'underscore-plus' {join} = require 'path' {Model} = require 'theorist' Q = require 'q' Serializable = require 'serializable' Delegator = require 'delegato' +Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' @@ -28,6 +29,9 @@ class Workspace extends Model constructor: -> super + + @openers = [] + @subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed @registerOpener (filePath) => switch filePath @@ -50,6 +54,9 @@ class Workspace extends Model paneContainer: @paneContainer.serialize() fullScreen: atom.isFullScreen() + editorAdded: (editor) -> + @emit 'editor-created', editor + # Public: Register a function to be called for every current and future # {Editor} in the workspace. # @@ -58,13 +65,18 @@ class Workspace extends Model # Returns a subscription object with an `.off` method that you can call to # unregister the callback. eachEditor: (callback) -> - atom.project.eachEditor(callback) + callback(editor) for editor in @getEditors() + @subscribe this, 'editor-created', (editor) -> callback(editor) # Public: Get all current editors in the workspace. # # Returns an {Array} of {Editor}s. getEditors: -> - atom.project.getEditors() + editors = [] + for pane in @paneContainer.getPanes() + editors.push(item) for item in pane.getItems() when item instanceof Editor + + editors # Public: Open a given a URI in Atom asynchronously. # @@ -172,14 +184,14 @@ class Workspace extends Model # # opener - A {Function} to be called when a path is being opened. registerOpener: (opener) -> - atom.project.registerOpener(opener) + @openers.push(opener) # Public: Unregister an opener registered with {::registerOpener}. unregisterOpener: (opener) -> - atom.project.unregisterOpener(opener) + _.remove(@openers, opener) getOpeners: -> - atom.project.openers + @openers # Public: Get the active {Pane}. # @@ -211,6 +223,12 @@ class Workspace extends Model paneForUri: (uri) -> @paneContainer.paneForUri(uri) + # Public: Get the active {Pane}'s active item. + # + # Returns an pane item {Object}. + getActivePaneItem: -> + @paneContainer.getActivePane().getActiveItem() + # Public: Save the active pane item. # # If the active pane item currently has a URI according to the item's @@ -262,7 +280,7 @@ class Workspace extends Model # Removes the item's uri from the list of potential items to reopen. itemOpened: (item) -> if uri = item.getUri?() - remove(@destroyedItemUris, uri) + _.remove(@destroyedItemUris, uri) # Adds the destroyed item's uri to the list of items to reopen. onPaneItemDestroyed: (item) => diff --git a/vendor/apm b/vendor/apm index 2ce1206e6..b0fd602b4 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 2ce1206e6720c133fd2acf53bebeb9b37de2b65a +Subproject commit b0fd602b4ffd0dac68cdc7b012666faebba0a0f8