diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..4e9918e86 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +v0.10.21 diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index a014dc3c9..7e70d9753 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -227,10 +227,12 @@ module.exports = (grunt) -> grunt.registerTask('test', ['shell:kill-atom', 'run-specs']) grunt.registerTask('docs', ['markdown:guides', 'build-docs']) - ciTasks = ['output-disk-space', 'download-atom-shell', 'build'] - ciTasks.push('dump-symbols') unless process.platform is 'win32' - ciTasks.push('set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build') + ciTasks.push('dump-symbols') if process.platform isnt 'win32' + ciTasks.push('mkdeb') if process.platform is 'linux' + ciTasks.push('set-version', 'check-licenses', 'lint') + ciTasks.push('test') if process.platform isnt 'linux' + ciTasks.push('codesign', 'publish-build') grunt.registerTask('ci', ciTasks) defaultTasks = ['download-atom-shell', 'build', 'set-version'] diff --git a/build/package.json b/build/package.json index e77040261..422a48693 100644 --- a/build/package.json +++ b/build/package.json @@ -8,7 +8,7 @@ "dependencies": { "async": "~0.2.9", "donna": "1.0.1", - "tello": "1.0.2", + "tello": "1.0.3", "formidable": "~1.0.14", "fs-plus": "2.x", "github-releases": "~0.2.0", diff --git a/build/tasks/docs-task.coffee b/build/tasks/docs-task.coffee index 605269933..75e21de8a 100644 --- a/build/tasks/docs-task.coffee +++ b/build/tasks/docs-task.coffee @@ -11,6 +11,7 @@ module.exports = (grunt) -> modulesPath = path.resolve(__dirname, '..', '..', 'node_modules') classes = {} fs.traverseTreeSync modulesPath, (modulePath) -> + return false if modulePath.match(/node_modules/g).length > 1 # dont need the dependencies of the dependencies return true unless path.basename(modulePath) is 'package.json' return true unless fs.isFileSync(modulePath) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 69ba1b20b..b1d8a5802 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -28,7 +28,7 @@ module.exports = (gruntObject) -> grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', -> docsOutputDir = grunt.config.get('docsOutputDir') buildDir = grunt.config.get('atom.buildDir') - cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json') + cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json') grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', -> done = @async() @@ -45,16 +45,32 @@ module.exports = (gruntObject) -> uploadAssets(release, buildDir, assets, done) getAssets = -> - if process.platform is 'darwin' - [ - {assetName: 'atom-mac.zip', sourcePath: 'Atom.app'} - {assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'} - {assetName: 'atom-api.json', sourcePath: 'atom-api.json'} - ] - else - [ - {assetName: 'atom-windows.zip', sourcePath: 'Atom'} - ] + switch process.platform + when 'darwin' + [ + {assetName: 'atom-mac.zip', sourcePath: 'Atom.app'} + {assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'} + {assetName: 'atom-api.json', sourcePath: 'atom-api.json'} + ] + when 'win32' + [ + {assetName: 'atom-windows.zip', sourcePath: 'Atom'} + ] + when 'linux' + buildDir = grunt.config.get('atom.buildDir') + sourcePath = fs.listSync(buildDir, ['.deb'])[0] + if process.arch is 'ia32g' + arch = 'i386' + else + arch = 'amd64' + assetName = "atom-#{arch}.deb" + + {cp} = require('./task-helpers')(grunt) + cp sourcePath, path.join(buildDir, assetName) + + [ + {assetName, sourcePath} + ] logError = (message, error, details) -> grunt.log.error(message) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index e5d54069e..6230f9b96 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -33,26 +33,41 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. If you have problems with permissions don't forget to prefix with `sudo` -From the cloned repository directory: +1. Clone the Atom repository: - 1. Build: + ```sh + git clone https://github.com/atom/atom + cd atom + ``` - ```sh - $ script/build - ``` - This will create the atom application at `$TMPDIR/atom-build/Atom`. - 2. Install the `atom` and `apm` commands to `/usr/local/bin` by executing: +2. Checkout the latest Atom release: - ```sh - $ sudo script/grunt install - ``` - 3. *Optionally*, you may generate a `.deb` package at `$TMPDIR/atom-build`: + ```sh + git fetch + git checkout $(git describe --tags `git rev-list --tags --max-count=1`) + ``` - ```sh - $ script/grunt mkdeb - ``` +3. Build Atom: -Use the newly installed atom by restarting any running atom instances. + ```sh + script/build + ``` + + This will create the atom application at `$TMPDIR/atom-build/Atom`. + +4. Install the `atom` and `apm` commands to `/usr/local/bin` by executing: + + ```sh + sudo script/grunt install + ``` + +5. *Optionally*, you may generate a `.deb` package at `$TMPDIR/atom-build`: + + ```sh + script/grunt mkdeb + ``` + +Use the newly installed Atom by fully quitting Atom and then reopening. ## Advanced Options diff --git a/docs/customizing-atom.md b/docs/customizing-atom.md index 0b86298d6..d83e960be 100644 --- a/docs/customizing-atom.md +++ b/docs/customizing-atom.md @@ -123,7 +123,7 @@ You can open this file in an editor from the _Atom > Open Your Config_ menu. - `showInvisibles`: Whether to render placeholders for invisible characters (defaults to `false`) - `showIndentGuide`: Show/hide indent indicators within the editor - `showLineNumbers`: Show/hide line numbers within the gutter - - `softWrapped`: Enable/disable soft wrapping of text within the editor + - `softWrap`: Enable/disable soft wrapping of text within the editor - `softWrapAtPreferredLineLength`: Enable/disable soft line wrapping at `preferredLineLength` - `tabLength`: Number of spaces within a tab (defaults to `2`) - `fuzzyFinder` diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 9c164c9c0..34f977209 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -119,7 +119,7 @@ 'cmd-shift-right': 'editor:select-to-end-of-line' 'alt-backspace': 'editor:delete-to-beginning-of-word' 'alt-delete': 'editor:delete-to-end-of-word' - 'ctrl-a': 'editor:move-to-beginning-of-line' + 'ctrl-a': 'editor:move-to-first-character-of-line' 'ctrl-e': 'editor:move-to-end-of-line' 'ctrl-k': 'editor:cut-to-end-of-line' diff --git a/menus/darwin.cson b/menus/darwin.cson index 6e079ad0d..b892c56cc 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -166,7 +166,7 @@ ] } { type: 'separator' } - { label: 'Toggle Soft Wrap', command: 'editor:toggle-soft-wrapped' } + { label: 'Toggle Soft Wrap', command: 'editor:toggle-soft-wrap' } ] } diff --git a/menus/linux.cson b/menus/linux.cson index f0320fd23..ba87732dd 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -104,7 +104,7 @@ ] } { type: 'separator' } - { label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrapped' } + { label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' } ] } diff --git a/menus/win32.cson b/menus/win32.cson index f428acf4c..d002c2428 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -122,7 +122,7 @@ ] } { type: 'separator' } - { label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrapped' } + { label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' } ] } diff --git a/package.json b/package.json index cd7656c85..d5e5a91f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.127.0", + "version": "0.128.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { @@ -20,15 +20,15 @@ "atomShellVersion": "0.16.2", "dependencies": { "async": "0.2.6", - "atom-keymap": "^2.1.0", + "atom-keymap": "^2.1.1", "bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "clear-cut": "0.4.0", "coffee-script": "1.7.0", "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.1", - "event-kit": "0.7.1", - "first-mate": "^2.1.0", + "event-kit": "0.7.2", + "first-mate": "^2.1.1", "fs-plus": "^2.2.6", "fstream": "0.1.24", "fuzzaldrin": "^2.1", @@ -42,7 +42,7 @@ "nslog": "^1.0.1", "oniguruma": "^3.0.4", "optimist": "0.4.0", - "pathwatcher": "^2.1.1", + "pathwatcher": "^2.1.2", "property-accessors": "^1", "q": "^1.0.1", "random-words": "0.0.1", @@ -64,14 +64,14 @@ }, "packageDependencies": { "atom-dark-syntax": "0.19.0", - "atom-dark-ui": "0.34.0", + "atom-dark-ui": "0.35.0", "atom-light-syntax": "0.20.0", - "atom-light-ui": "0.29.0", + "atom-light-ui": "0.30.0", "base16-tomorrow-dark-theme": "0.21.0", "base16-tomorrow-light-theme": "0.4.0", "solarized-dark-syntax": "0.22.0", "solarized-light-syntax": "0.12.0", - "archive-view": "0.36.0", + "archive-view": "0.37.0", "autocomplete": "0.32.0", "autoflow": "0.18.0", "autosave": "0.17.0", @@ -83,16 +83,16 @@ "dev-live-reload": "0.34.0", "exception-reporting": "0.20.0", "feedback": "0.33.0", - "find-and-replace": "0.134.0", + "find-and-replace": "0.138.0", "fuzzy-finder": "0.58.0", "git-diff": "0.39.0", "go-to-line": "0.25.0", "grammar-selector": "0.29.0", "image-view": "0.36.0", "incompatible-packages": "0.9.0", - "keybinding-resolver": "0.19.0", + "keybinding-resolver": "0.20.0", "link": "0.25.0", - "markdown-preview": "0.101.0", + "markdown-preview": "0.102.0", "metrics": "0.34.0", "open-on-github": "0.30.0", "package-generator": "0.31.0", @@ -105,15 +105,15 @@ "symbols-view": "0.63.0", "tabs": "0.50.0", "timecop": "0.22.0", - "tree-view": "0.115.0", + "tree-view": "0.124.0", "update-package-dependencies": "0.6.0", "welcome": "0.18.0", "whitespace": "0.25.0", - "wrap-guide": "0.21.0", + "wrap-guide": "0.22.0", "language-c": "0.28.0", "language-coffee-script": "0.30.0", "language-css": "0.17.0", - "language-gfm": "0.48.0", + "language-gfm": "0.50.0", "language-git": "0.9.0", "language-go": "0.17.0", "language-html": "0.26.0", @@ -126,10 +126,10 @@ "language-mustache": "0.10.0", "language-objective-c": "0.11.0", "language-perl": "0.9.0", - "language-php": "0.15.0", + "language-php": "0.16.0", "language-property-list": "0.7.0", "language-python": "0.19.0", - "language-ruby": "0.35.0", + "language-ruby": "0.37.0", "language-ruby-on-rails": "0.18.0", "language-sass": "0.21.0", "language-shellscript": "0.8.0", diff --git a/script/cibuild b/script/cibuild index 6e76d8831..863478ec1 100755 --- a/script/cibuild +++ b/script/cibuild @@ -5,9 +5,6 @@ var path = require('path'); process.chdir(path.dirname(__dirname)); -if (process.platform == 'linux') - throw new Error('cibuild can not run on linux yet!'); - var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME; function loadEnvironmentVariables(filePath) { @@ -27,7 +24,7 @@ function loadEnvironmentVariables(filePath) { function readEnvironmentVariables() { if (process.platform === 'win32') loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials')); - else { + else if (process.platform === 'darwin') { loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials'); loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain'); } diff --git a/script/cibuild-atom-linux b/script/cibuild-atom-linux new file mode 100755 index 000000000..c4e957189 --- /dev/null +++ b/script/cibuild-atom-linux @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +export ATOM_ACCESS_TOKEN=$BUILD_ATOM_LINUX_ACCESS_TOKEN + +if [ -d /usr/local/share/nodenv ]; then + export NODENV_ROOT=/usr/local/share/nodenv + export PATH=/usr/local/share/nodenv/bin:/usr/local/share/nodenv/shims:$PATH + export NODENV_VERSION="v0.10.21" +fi + +script/cibuild diff --git a/script/utils/verify-requirements.js b/script/utils/verify-requirements.js index fa65a339e..554c27dd0 100644 --- a/script/utils/verify-requirements.js +++ b/script/utils/verify-requirements.js @@ -31,7 +31,7 @@ function verifyNode(cb) { var nodeMajorVersion = +versionArray[0]; var nodeMinorVersion = +versionArray[1]; if (nodeMajorVersion === 0 && nodeMinorVersion < 10) { - error = "node v0.10 is required to build Atom."; + error = "node v0.10 is required to build Atom, node " + nodeVersion + " is installed."; cb(error); } else { diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index 54a94c9a8..7b1ae1618 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -350,7 +350,7 @@ describe "the `atom` global", -> atom.packages.deactivatePackage("package-that-throws-on-activate") expect(badPack.mainModule.serialize).not.toHaveBeenCalled() - it "absorbs exceptions that are thrown by the package module's serialize methods", -> + it "absorbs exceptions that are thrown by the package module's serialize method", -> spyOn(console, 'error') waitsForPromise -> @@ -365,6 +365,16 @@ describe "the `atom` global", -> expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 expect(console.error).toHaveBeenCalled() + it "absorbs exceptions that are thrown by the package module's deactivate method", -> + spyOn(console, 'error') + + waitsForPromise -> + atom.packages.activatePackage("package-that-throws-on-deactivate") + + runs -> + expect(-> atom.packages.deactivatePackage("package-that-throws-on-deactivate")).not.toThrow() + expect(console.error).toHaveBeenCalled() + it "removes the package's grammars", -> waitsForPromise -> atom.packages.activatePackage('package-with-grammars') @@ -520,7 +530,7 @@ describe "the `atom` global", -> reloadedHandler = jasmine.createSpy('reloadedHandler') reloadedHandler.reset() - atom.themes.on('reloaded', reloadedHandler) + atom.themes.onDidReloadAll reloadedHandler pack = atom.packages.disablePackage(packageName) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index d54ff5598..6ff0aa304 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -98,11 +98,11 @@ describe "Editor", -> expect(editor2.isFoldedAtBufferRow(4)).not.toBe editor.isFoldedAtBufferRow(4) describe "config defaults", -> - it "uses the `editor.tabLength`, `editor.softWrapped`, and `editor.softTabs` config values", -> + it "uses the `editor.tabLength`, `editor.softWrap`, and `editor.softTabs` config values", -> editor1 = null editor2 = null atom.config.set('editor.tabLength', 4) - atom.config.set('editor.softWrapped', true) + atom.config.set('editor.softWrap', true) atom.config.set('editor.softTabs', false) waitsForPromise -> @@ -114,7 +114,7 @@ describe "Editor", -> expect(editor1.getSoftTabs()).toBe false atom.config.set('editor.tabLength', 100) - atom.config.set('editor.softWrapped', false) + atom.config.set('editor.softWrap', false) atom.config.set('editor.softTabs', true) waitsForPromise -> diff --git a/spec/fixtures/packages/package-that-throws-on-deactivate/index.coffee b/spec/fixtures/packages/package-that-throws-on-deactivate/index.coffee new file mode 100644 index 000000000..5def0ed2d --- /dev/null +++ b/spec/fixtures/packages/package-that-throws-on-deactivate/index.coffee @@ -0,0 +1,4 @@ +module.exports = + activate: -> + deactivate: -> throw new Error('Top that') + serialize: -> diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 63c812621..56cb6ddc8 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -1,6 +1,7 @@ PaneContainerView = require '../src/pane-container-view' PaneView = require '../src/pane-view' fs = require 'fs-plus' +{Emitter} = require 'event-kit' {$, View} = require 'atom' path = require 'path' temp = require 'temp' @@ -12,9 +13,14 @@ describe "PaneView", -> @deserialize: ({id, text}) -> new TestView({id, text}) @content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text initialize: ({@id, @text}) -> + @emitter = new Emitter serialize: -> { deserializer: 'TestView', @id, @text } getUri: -> @id isEqual: (other) -> other? and @id == other.id and @text == other.text + changeTitle: -> + @emitter.emit 'did-change-title', 'title' + onDidChangeTitle: (callback) -> + @emitter.on 'did-change-title', callback beforeEach -> atom.deserializers.add(TestView) @@ -145,22 +151,47 @@ describe "PaneView", -> expect(view1.data('preservative')).toBe 1234 describe "when the title of the active item changes", -> - it "emits pane:active-item-title-changed", -> - activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") - pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler + describe 'when there is no onDidChangeTitle method', -> + beforeEach -> + view1.onDidChangeTitle = null + view2.onDidChangeTitle = null - expect(pane.getActiveItem()).toBe view1 + pane.activateItem(view2) + pane.activateItem(view1) - view2.trigger 'title-changed' - expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() + it "emits pane:active-item-title-changed", -> + activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") + pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler - view1.trigger 'title-changed' - expect(activeItemTitleChangedHandler).toHaveBeenCalled() - activeItemTitleChangedHandler.reset() + expect(pane.getActiveItem()).toBe view1 - pane.activateItem(view2) - view2.trigger 'title-changed' - expect(activeItemTitleChangedHandler).toHaveBeenCalled() + view2.trigger 'title-changed' + expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() + + view1.trigger 'title-changed' + expect(activeItemTitleChangedHandler).toHaveBeenCalled() + activeItemTitleChangedHandler.reset() + + pane.activateItem(view2) + view2.trigger 'title-changed' + expect(activeItemTitleChangedHandler).toHaveBeenCalled() + + describe 'when there is a onDidChangeTitle method', -> + it "emits pane:active-item-title-changed", -> + activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") + pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler + + expect(pane.getActiveItem()).toBe view1 + view2.changeTitle() + expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() + + view1.changeTitle() + expect(activeItemTitleChangedHandler).toHaveBeenCalled() + activeItemTitleChangedHandler.reset() + + pane.activateItem(view2) + view2.changeTitle() + expect(activeItemTitleChangedHandler).toHaveBeenCalled() describe "when an unmodifed buffer's path is deleted", -> it "removes the pane item", -> diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 7f359536b..48fad5c8c 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -21,6 +21,7 @@ clipboard = require 'clipboard' atom.themes.loadBaseStylesheets() atom.themes.requireStylesheet '../static/jasmine' +atom.themes.initialLoadComplete = true fixturePackagesPath = path.resolve(__dirname, './fixtures/packages') atom.packages.packageDirPaths.unshift(fixturePackagesPath) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index c6aa502b3..8adbc69e5 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -42,7 +42,7 @@ describe "WorkspaceView", -> runs -> editorView1 = atom.workspaceView.getActiveView() buffer = editorView1.getEditor().getBuffer() - editorView1.splitRight() + editorView1.getPaneView().getModel().splitRight(copyActiveItem: true) expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1] simulateReload() @@ -196,7 +196,8 @@ describe "WorkspaceView", -> atom.workspaceView.attachToDom() rightEditorView = atom.workspaceView.getActiveView() rightEditorView.getEditor().setText("\t \n") - leftEditorView = rightEditorView.splitLeft() + rightEditorView.getPaneView().getModel().splitLeft(copyActiveItem: true) + leftEditorView = atom.workspaceView.getActiveView() expect(rightEditorView.find(".line:first").text()).toBe " " expect(leftEditorView.find(".line:first").text()).toBe " " @@ -207,14 +208,16 @@ describe "WorkspaceView", -> expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing - lowerLeftEditorView = leftEditorView.splitDown() + leftEditorView.getPaneView().getModel().splitDown(copyActiveItem: true) + lowerLeftEditorView = atom.workspaceView.getActiveView() expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing atom.workspaceView.trigger "window:toggle-invisibles" expect(rightEditorView.find(".line:first").text()).toBe " " expect(leftEditorView.find(".line:first").text()).toBe " " - lowerRightEditorView = rightEditorView.splitDown() + rightEditorView.getPaneView().getModel().splitDown(copyActiveItem: true) + lowerRightEditorView = atom.workspaceView.getActiveView() expect(lowerRightEditorView.find(".line:first").text()).toBe " " describe ".eachEditorView(callback)", -> @@ -241,7 +244,7 @@ describe "WorkspaceView", -> atom.workspaceView.eachEditorView(callback) count = 0 callbackEditor = null - atom.workspaceView.getActiveView().splitRight() + atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) expect(count).toBe 1 expect(callbackEditor).toBe atom.workspaceView.getActiveView() @@ -259,10 +262,10 @@ describe "WorkspaceView", -> subscription = atom.workspaceView.eachEditorView(callback) expect(count).toBe 1 - atom.workspaceView.getActiveView().splitRight() + atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) expect(count).toBe 2 subscription.off() - atom.workspaceView.getActiveView().splitRight() + atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) expect(count).toBe 2 describe "core:close", -> @@ -271,7 +274,7 @@ describe "WorkspaceView", -> paneView1 = atom.workspaceView.getActivePaneView() editorView = atom.workspaceView.getActiveView() - editorView.splitRight() + editorView.getPaneView().getModel().splitRight(copyActiveItem: true) paneView2 = atom.workspaceView.getActivePaneView() expect(paneView1).not.toBe paneView2 diff --git a/src/atom.coffee b/src/atom.coffee index 0432024c9..731131d22 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -378,7 +378,7 @@ class Atom extends Model @themes.load() watchThemes: -> - @themes.on 'reloaded', => + @themes.onDidReloadAll => # Only reload stylesheets from non-theme packages for pack in @packages.getActivePackages() when pack.getType() isnt 'theme' pack.reloadStylesheets?() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 1011a660b..7460952a1 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -484,6 +484,14 @@ class AtomApplication when 'all' then ['openFile', 'openDirectory'] else throw new Error("#{type} is an invalid type for promptForPath") + # Show the open dialog as child window on Windows and Linux, and as + # independent dialog on OS X. This matches most native apps. + parentWindow = + if process.platform is 'darwin' + null + else + BrowserWindow.getFocusedWindow() + dialog = require 'dialog' - dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) => + dialog.showOpenDialog parentWindow, title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) => @openPaths({pathsToOpen, devMode, safeMode, window}) diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 91cfe04db..5aa8392a6 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -24,7 +24,7 @@ class ContextMenuManager @commandOptions = x: e.pageX, y: e.pageY ] - atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems() + atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems() loadPlatformItems: -> menusDirPath = path.join(@resourcePath, 'menus') diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index a2c9817a3..c86324b35 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -45,7 +45,7 @@ class DisplayBuffer extends Model @emitter = new Emitter - @softWrapped ?= atom.config.get('editor.softWrapped') ? false + @softWrapped ?= atom.config.get('editor.softWrap') ? false @tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, @invisibles}) @buffer = @tokenizedBuffer.buffer @charWidthsByScope = {} diff --git a/src/editor-component.coffee b/src/editor-component.coffee index d70cf9a14..646ff690c 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -176,10 +176,16 @@ EditorComponent = React.createClass @listenForDOMEvents() @listenForCommands() - @subscribe atom.themes, 'stylesheet-added stylesheet-removed stylesheet-updated', @onStylesheetsChanged + @subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged + @subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged + @subscribe atom.themes.onDidRemoveStylesheet @onStylesheetsChanged + unless atom.themes.isInitialLoadComplete() + @subscribe atom.themes.onDidReloadAll @onStylesheetsChanged @subscribe scrollbarStyle.changes, @refreshScrollbars @domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval) + @updateParentViewFocusedClassIfNeeded({}) + @updateParentViewMiniClassIfNeeded({}) @checkForVisibilityChange() componentWillUnmount: -> @@ -474,7 +480,7 @@ EditorComponent = React.createClass 'editor:add-selection-above': -> editor.addSelectionAbove() 'editor:split-selections-into-lines': -> editor.splitSelectionsIntoLines() 'editor:toggle-soft-tabs': -> editor.toggleSoftTabs() - 'editor:toggle-soft-wrapped': -> editor.toggleSoftWrapped() + 'editor:toggle-soft-wrap': -> editor.toggleSoftWrapped() 'editor:fold-all': -> editor.foldAll() 'editor:unfold-all': -> editor.unfoldAll() 'editor:fold-current-row': -> editor.foldCurrentRow() @@ -706,8 +712,9 @@ EditorComponent = React.createClass onStylesheetsChanged: (stylesheet) -> return unless @performedInitialMeasurement + return unless atom.themes.isInitialLoadComplete() - @refreshScrollbars() if @containsScrollbarSelector(stylesheet) + @refreshScrollbars() if not stylesheet? or @containsScrollbarSelector(stylesheet) @sampleFontStyling() @sampleBackgroundColors() @remeasureCharacterWidths() diff --git a/src/editor-view.coffee b/src/editor-view.coffee index dc0e42364..e91d69caa 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -47,7 +47,7 @@ class EditorView extends View nonWordCharacters: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" preferredLineLength: 80 tabLength: 2 - softWrapped: false + softWrap: false softTabs: true softWrapAtPreferredLineLength: false scrollSensitivity: 40 @@ -282,56 +282,39 @@ class EditorView extends View deprecate 'Use Editor::getLastVisibleScreenRow instead. You can get the editor via editorView.getModel()' @editor.getLastVisibleScreenRow() - # Public: Gets the font family for the editor. - # - # Returns a {String} identifying the CSS `font-family`. getFontFamily: -> + deprecate 'This is going away. Use atom.config.get("editor.fontFamily") instead' @component?.getFontFamily() - # Public: Sets the font family for the editor. - # - # * `fontFamily` A {String} identifying the CSS `font-family`. setFontFamily: (fontFamily) -> + deprecate 'This is going away. Use atom.config.set("editor.fontFamily", "my-font") instead' @component?.setFontFamily(fontFamily) - # Public: Retrieves the font size for the editor. - # - # Returns a {Number} indicating the font size in pixels. getFontSize: -> + deprecate 'This is going away. Use atom.config.get("editor.fontSize") instead' @component?.getFontSize() - # Public: Sets the font size for the editor. - # - # * `fontSize` A {Number} indicating the font size in pixels. setFontSize: (fontSize) -> + deprecate 'This is going away. Use atom.config.set("editor.fontSize", 12) instead' @component?.setFontSize(fontSize) + setLineHeight: (lineHeight) -> + deprecate 'This is going away. Use atom.config.set("editor.lineHeight", 1.5) instead' + @component.setLineHeight(lineHeight) + setWidthInChars: (widthInChars) -> @component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px' - # Public: Sets the line height of the editor. - # - # Calling this method has no effect when called on a mini editor. - # - # * `lineHeight` A {Number} without a unit suffix identifying the CSS `line-height`. - setLineHeight: (lineHeight) -> - @component.setLineHeight(lineHeight) - - # Public: Sets whether you want to show the indentation guides. - # - # * `showIndentGuide` A {Boolean} you can set to `true` if you want to see the - # indentation guides. setShowIndentGuide: (showIndentGuide) -> + deprecate 'This is going away. Use atom.config.set("editor.showIndentGuide", true|false) instead' @component.setShowIndentGuide(showIndentGuide) setSoftWrap: (softWrapped) -> deprecate 'Use Editor::setSoftWrapped instead. You can get the editor via editorView.getModel()' @editor.setSoftWrapped(softWrapped) - # Public: Set whether invisible characters are shown. - # - # * `showInvisibles` A {Boolean} which, if `true`, show invisible characters. setShowInvisibles: (showInvisibles) -> + deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead' @component.setShowInvisibles(showInvisibles) getText: -> diff --git a/src/editor.coffee b/src/editor.coffee index 92af3ed11..e66110d8e 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -257,6 +257,16 @@ class Editor extends Model onDidInsertText: (callback) -> @emitter.on 'did-insert-text', callback + # Public: Invoke the given callback after the buffer is saved to disk. + # + # * `callback` {Function} to be called after the buffer is saved. + # * `event` {Object} with the following keys: + # * `path` The path to which the buffer was saved. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidSave: (callback) -> + @getBuffer().onDidSave(callback) + # Extended: Calls your `callback` when a {Cursor} is added to the editor. # Immediately calls your callback for each existing cursor. # @@ -1365,7 +1375,8 @@ class Editor extends Model isBufferRowCommented: (bufferRow) -> if match = @lineTextForBufferRow(bufferRow).match(/\S/) scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes - new TextMateScopeSelector('comment.*').matches(scopes) + @commentScopeSelector ?= new TextMateScopeSelector('comment.*') + @commentScopeSelector.matches(scopes) # Public: Toggle line comments for rows intersecting selections. # diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index a4153de92..754bd502a 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -4,9 +4,13 @@ KeymapManager = require 'atom-keymap' CSON = require 'season' {jQuery} = require 'space-pen' +KeymapManager::onDidLoadBundledKeymaps = (callback) -> + @emitter.on 'did-load-bundled-keymaps', callback + KeymapManager::loadBundledKeymaps = -> @loadKeymap(path.join(@resourcePath, 'keymaps')) - @emit('bundled-keymaps-loaded') + @emit 'bundled-keymaps-loaded' + @emitter.emit 'did-load-bundled-keymaps' KeymapManager::getUserKeymapPath = -> if userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap')) diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index 5c7d955d0..da5ae6507 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -14,7 +14,7 @@ class MenuManager constructor: ({@resourcePath}) -> @pendingUpdateOperation = null @template = [] - atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems() + atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems() atom.packages.onDidActivateAll => @sortPackagesMenu() # Public: Adds the given items to the application menu. diff --git a/src/package.coffee b/src/package.coffee index 6a104eccb..c005ae664 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -273,7 +273,11 @@ class Package @unsubscribeFromActivationEvents() @deactivateResources() @deactivateConfig() - @mainModule?.deactivate?() if @mainActivated + if @mainActivated + try + @mainModule?.deactivate?() + catch e + console.error "Error deactivating package '#{@name}'", e.stack @emit 'deactivated' @emitter.emit 'did-deactivate' diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 0de2ca7f1..21f4c8cfa 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -147,6 +147,9 @@ class PaneView extends View @activeItem onActiveItemChanged: (item) => + @activeItemDisposables.dispose() if @activeItemDisposables? + @activeItemDisposables = new CompositeDisposable() + if @previousActiveItem?.off? @previousActiveItem.off 'title-changed', @activeItemTitleChanged @previousActiveItem.off 'modified-status-changed', @activeItemModifiedChanged @@ -154,15 +157,29 @@ class PaneView extends View return unless item? - hasFocus = @hasFocus() - if item.on? - item.on 'title-changed', @activeItemTitleChanged - item.on 'modified-status-changed', @activeItemModifiedChanged + if item.onDidChangeTitle? + disposable = item.onDidChangeTitle(@activeItemTitleChanged) + deprecate 'Please return a Disposable object from your ::onDidChangeTitle method!' unless disposable?.dispose? + @activeItemDisposables.add(disposable) if disposable?.dispose? + else if item.on? + deprecate '::on methods for items are no longer supported. If you would like your item to title change behavior, please implement a ::onDidChangeTitle() method.' + disposable = item.on('title-changed', @activeItemTitleChanged) + @activeItemDisposables.add(disposable) if disposable?.dispose? + + if item.onDidChangeModified? + disposable = item.onDidChangeModified(@activeItemModifiedChanged) + deprecate 'Please return a Disposable object from your ::onDidChangeModified method!' unless disposable?.dispose? + @activeItemDisposables.add(disposable) if disposable?.dispose? + else if item.on? + deprecate '::on methods for items are no longer supported. If you would like your item to support modified behavior, please implement a ::onDidChangeModified() method.' + item.on('modified-status-changed', @activeItemModifiedChanged) + @activeItemDisposables.add(disposable) if disposable?.dispose? + view = @viewForItem(item) otherView.hide() for otherView in @itemViews.children().not(view).views() @itemViews.append(view) unless view.parent().is(@itemViews) view.show() if @attached - view.focus() if hasFocus + view.focus() if @hasFocus() @trigger 'pane:active-item-changed', [item] diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index ae528607d..23c630043 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -20,6 +20,7 @@ class ThemeManager constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode}) -> @emitter = new Emitter @lessCache = null + @initialLoadComplete = false @packageManager.registerPackageActivator(this, ['theme']) ### @@ -50,6 +51,15 @@ class ThemeManager onDidRemoveStylesheet: (callback) -> @emitter.on 'did-remove-stylesheet', callback + # Essential: Invoke `callback` when a stylesheet has been updated. + # + # * `callback` {Function} + # * `stylesheet` {StyleSheet} the style node + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidUpdateStylesheet: (callback) -> + @emitter.on 'did-update-stylesheet', callback + # Essential: Invoke `callback` when any stylesheet has been updated, added, or removed. # # * `callback` {Function} @@ -66,6 +76,8 @@ class ThemeManager deprecate 'Use ThemeManager::onDidAddStylesheet instead' when 'stylesheet-removed' deprecate 'Use ThemeManager::onDidRemoveStylesheet instead' + when 'stylesheet-updated' + deprecate 'Use ThemeManager::onDidUpdateStylesheet instead' when 'stylesheets-changed' deprecate 'Use ThemeManager::onDidChangeStylesheets instead' else @@ -73,7 +85,7 @@ class ThemeManager EmitterMixin::on.apply(this, arguments) ### - Section: Methods + Section: Instance Methods ### getAvailableNames: -> @@ -96,7 +108,7 @@ class ThemeManager getLoadedThemes: -> pack for pack in @packageManager.getLoadedPackages() when pack.isTheme() - activatePackages: (themePackages) -> @activateThemes() + activatePackages: -> @activateThemes() # Get the enabled theme names from the config. # @@ -154,6 +166,7 @@ class ThemeManager @refreshLessCache() # Update cache again now that @getActiveThemes() is populated @loadUserStylesheet() @reloadBaseStylesheets() + @initialLoadComplete = true @emit 'reloaded' @emitter.emit 'did-reload-all' deferred.resolve() @@ -166,6 +179,8 @@ class ThemeManager @packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes() null + isInitialLoadComplete: -> @initialLoadComplete + addActiveThemeClasses: -> for pack in @getActiveThemes() atom.workspaceView?[0]?.classList.add("theme-#{pack.name}") @@ -323,3 +338,17 @@ class ThemeManager @emitter.emit 'did-add-stylesheet', styleElement.sheet @emit 'stylesheets-changed' @emitter.emit 'did-change-stylesheets' + + updateGlobalEditorStyle: (property, value) -> + unless styleNode = @stylesheetElementForId('global-editor-styles') + @applyStylesheet('global-editor-styles', '.editor {}') + styleNode = @stylesheetElementForId('global-editor-styles') + + {sheet} = styleNode + editorRule = sheet.cssRules[0] + editorRule.style[property] = value + + @emit 'stylesheet-updated', sheet + @emitter.emit 'did-update-stylesheet', sheet + @emit 'stylesheets-changed' + @emitter.emit 'did-change-stylesheets' diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index b97c178c6..71901b898 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -376,25 +376,14 @@ class WorkspaceView extends View beforeRemove: -> @model.destroy() - setEditorFontSize: (fontSize) => - @setEditorStyle('font-size', fontSize + 'px') + setEditorFontSize: (fontSize) -> + atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px') - setEditorFontFamily: (fontFamily) => - @setEditorStyle('font-family', fontFamily) + setEditorFontFamily: (fontFamily) -> + atom.themes.updateGlobalEditorStyle('font-family', fontFamily) - setEditorLineHeight: (lineHeight) => - @setEditorStyle('line-height', lineHeight) - - setEditorStyle: (property, value) -> - unless styleNode = atom.themes.stylesheetElementForId('global-editor-styles') - atom.themes.applyStylesheet('global-editor-styles', '.editor {}') - styleNode = atom.themes.stylesheetElementForId('global-editor-styles') - - {sheet} = styleNode - editorRule = sheet.cssRules[0] - editorRule.style[property] = value - atom.themes.emit 'stylesheet-updated', sheet - atom.themes.emit 'stylesheets-changed' + setEditorLineHeight: (lineHeight) -> + atom.themes.updateGlobalEditorStyle('line-height', lineHeight) # Deprecated eachPane: (callback) -> diff --git a/src/workspace.coffee b/src/workspace.coffee index 47d0cbe17..64ea6404b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -110,10 +110,28 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePanes: (callback) -> @paneContainer.observePanes(callback) + # Extended: Invoke the given callback when the active pane changes. + # + # * `callback` {Function} to be called when the active pane changes. + # * `pane` A {Pane} that is the current return value of {::getActivePane}. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActivePane: (callback) -> @paneContainer.onDidChangeActivePane(callback) + + # Extended: Invoke the given callback with the current active pane and when + # the active pane changes. + # + # * `callback` {Function} to be called with the current and future active# + # panes. + # * `pane` A {Pane} that is the current return value of {::getActivePane}. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActivePane: (callback) -> @paneContainer.observeActivePane(callback) + # Extended: Invoke the given callback when a pane item is added to the # workspace. # - # * `callback` {Function} to be called panes are added. + # * `callback` {Function} to be called when panes are added. # * `event` {Object} with the following keys: # * `item` The added pane item. # * `pane` {Pane} containing the added item. @@ -122,6 +140,15 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback) + # Extended: Invoke the given callback when the active pane item changes. + # + # * `callback` {Function} to be called when the active pane item changes. + # * `event` {Object} with the following keys: + # * `activeItem` The active pane item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback) + # Extended: Invoke the given callback with all current and future panes items in # the workspace. # @@ -362,7 +389,7 @@ class Workspace extends Model # Returns an {Editor} or `undefined` if the current active item is not an # {Editor}. getActiveTextEditor: -> - activeItem = @getActiveItem() + activeItem = @getActivePaneItem() activeItem if activeItem instanceof Editor # Deprecated: