diff --git a/.gitignore b/.gitignore index c3468df01..b2b4cf15a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.swp +*~ .DS_Store .project .svn diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c04a194d..823deb597 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,7 @@ in the proper package's repository. * :racehorse: when improving performance * :non-potable_water: when plugging memory leaks * :memo: when writing docs + * :penguin: when fixing something on Linux ## CoffeeScript Styleguide diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 46df41855..e4ff2578c 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -47,6 +47,7 @@ module.exports = (grunt) -> path.join('less', 'test') path.join('bootstrap', 'docs') path.join('bootstrap', 'examples') + path.join('pegjs', 'examples') # Add .* to avoid matching hunspell_dictionaries. path.join('spellchecker', 'vendor', 'hunspell', '.*') path.join('xmldom', 'test') diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 6ad119b83..9c7617684 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -1,4 +1,6 @@ +fs = require 'fs' path = require 'path' +fs = require 'fs-plus' runas = null module.exports = (grunt) -> @@ -28,3 +30,10 @@ module.exports = (grunt) -> rm shareDir mkdir path.dirname(shareDir) cp shellAppDir, shareDir + + # Create relative symbol link for apm. + process.chdir(binDir) + rm('apm') + fs.symlinkSync(path.join('..', 'share', 'atom', 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), 'apm') + + fs.chmodSync(path.join(shareDir, 'atom'), "755") diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 2d503ac7f..5adf92574 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -16,7 +16,7 @@ module.exports = (grunt) -> {name, version, description} = grunt.file.readJSON('package.json') section = 'devel' arch = 'amd64' - maintainer = 'GitHub ' + maintainer = 'GitHub ' data = {name, version, description, section, arch, maintainer} control = path.join('resources', 'linux', 'debian', 'control') diff --git a/docs/advanced/keymaps.md b/docs/advanced/keymaps.md index aaa5d398b..723369253 100644 --- a/docs/advanced/keymaps.md +++ b/docs/advanced/keymaps.md @@ -83,6 +83,22 @@ file. For now, we've opted to handle cases where selector ordering is critical by breaking the keymap into two separate files, such as `snippets-1.cson` and `snippets-2.cson`. +## Removing Bindings + +When the keymap system encounters a binding with the `unset!` directive as its +command, it will treat the current element as if it had no key bindings matching +the current keystroke sequence and continue searching from its parent. If you +want to remove a binding from a keymap you don't control, such as keymaps in +Atom core or in packages, use the `unset!` directive. + +## Forcing Chromium's Native Keystroke Handling + +If you want to force the native browser behavior for a given keystroke, use the +`native!` directive as the command of a binding. This can be useful to enable +the correct behavior in native input elements, for example. If you apply the +`.native-key-bindings` class to an element, all the keystrokes typically handled +by the browser will be assigned the `native!` directive. + ## Overloading Key Bindings Occasionally, it makes sense to layer multiple actions on top of the same key diff --git a/docs/getting-started.md b/docs/getting-started.md index b94026479..5d443cb64 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -73,7 +73,7 @@ from. You can split any editor pane horizontally or vertically by using `cmd-k right` or `cmd-k down`. Once you have a split pane, you can move focus between them with `cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all its -editors with `meta-w`, then press `meta-w` one more time to close the pane. You +editors with `cmd-w`, then press `cmd-w` one more time to close the pane. You can configure panes to auto-close when empty in the Settings view. ### Folding diff --git a/keymaps/base.cson b/keymaps/base.cson index 4fe2e868c..b532244af 100644 --- a/keymaps/base.cson +++ b/keymaps/base.cson @@ -9,9 +9,6 @@ 'shift-home': 'editor:select-to-first-character-of-line' 'shift-end': 'editor:select-to-end-of-line' - # Sublime Parity - 'ctrl-t': 'editor:transpose' - '.editor:not(.mini)': # Atom Specific 'ctrl-C': 'editor:copy-path' diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index a5b74356c..1fd2bfab4 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -119,6 +119,7 @@ 'cmd-k cmd-u': 'editor:upper-case' 'cmd-k cmd-l': 'editor:lower-case' 'cmd-l': 'editor:select-line' + 'ctrl-t': 'editor:transpose' '.workspace .editor:not(.mini)': # Atom specific diff --git a/keymaps/linux.cson b/keymaps/linux.cson index b9a3364da..48103ebba 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -10,11 +10,13 @@ 'ctrl-alt-i': 'window:toggle-dev-tools' 'ctrl-alt-p': 'window:run-package-specs' 'ctrl-alt-s': 'application:run-all-specs' + 'ctrl-shift-o': 'application:open-dev' # Sublime Parity 'ctrl-N': 'application:new-window' 'ctrl-W': 'window:close' 'ctrl-o': 'application:open' + 'ctrl-q': 'application:quit' 'ctrl-T': 'pane:reopen-closed-item' 'ctrl-n': 'application:new-file' 'ctrl-s': 'core:save' diff --git a/menus/linux.cson b/menus/linux.cson index f69ab563c..2b5658112 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -17,7 +17,7 @@ { label: 'Close All &Buffers', command: 'pane:close' } { label: 'Clos&e Window', command: 'window:close' } { type: 'separator' } - { label: 'E&xit', command: 'application:quit' } + { label: 'Quit', command: 'application:quit' } ] } @@ -144,10 +144,8 @@ { label: '&Help' submenu: [ - { label: '&About Atom...', command: 'application:about' } { label: 'View &License', command: 'application:open-license' } { label: "VERSION", enabled: false } - { label: "Install &update", command: 'application:install-update', visible: false } { type: 'separator' } { label: '&Documentation', command: 'application:open-documentation' } { type: 'separator' } diff --git a/package.json b/package.json index 0297a1842..c1e8206c9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.82.0", + "version": "0.85.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { @@ -12,30 +12,30 @@ "url": "https://github.com/atom/atom/issues" }, "license": "All Rights Reserved", - "atomShellVersion": "0.11.4", + "atomShellVersion": "0.11.5", "dependencies": { "async": "0.2.6", - "atom-keymap": "^0.13.0", + "atom-keymap": "^0.16.0", "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "clear-cut": "0.4.0", "coffee-script": "1.7.0", "coffeestack": "0.7.0", "delegato": "1.x", - "emissary": "^1.2", - "first-mate": ">=1.4.2 <2.0", + "emissary": "^1.2.1", + "first-mate": "^1.5.1", "fs-plus": "^2.2", "fstream": "0.1.24", "fuzzaldrin": "~1.1", - "git-utils": "^1.2.1", + "git-utils": "^1.2.2", "grim": "0.6.0", "guid": "0.0.10", - "jasmine-tagged": ">=1.1.1 <2.0", + "jasmine-tagged": "^1.1.1", "keytar": "1.x", "less-cache": "0.12.0", "mixto": "1.x", "mkdirp": "0.3.5", "nslog": "0.5.0", - "oniguruma": "^1.0.4", + "oniguruma": "^1.0.6", "optimist": "0.4.0", "pathwatcher": "^1.1.1", "property-accessors": "1.x", @@ -43,9 +43,9 @@ "random-words": "0.0.1", "runas": "0.5.x", "scandal": "0.15.2", - "scoped-property-store": "^0.7.0", + "scoped-property-store": "^0.8.0", "scrollbar-style": "^0.1.0", - "season": ">=1.0.2 <2.0", + "season": "^1.0.2", "semver": "1.1.4", "serializable": "1.x", "space-pen": "3.1.1", @@ -67,46 +67,46 @@ "autocomplete": "0.27.0", "autoflow": "0.15.0", "autosave": "0.13.0", - "background-tips": "0.9.0", + "background-tips": "0.10.0", "bookmarks": "0.22.0", "bracket-matcher": "0.29.0", - "command-palette": "0.19.0", + "command-palette": "0.20.0", "dev-live-reload": "0.30.0", "exception-reporting": "0.17.0", "feedback": "0.28.0", - "find-and-replace": "0.93.0", - "fuzzy-finder": "0.44.0", + "find-and-replace": "0.94.0", + "fuzzy-finder": "0.46.0", "git-diff": "0.27.0", "go-to-line": "0.18.0", "grammar-selector": "0.23.0", "image-view": "0.32.0", "keybinding-resolver": "0.15.0", "link": "0.20.0", - "markdown-preview": "0.54.0", + "markdown-preview": "0.59.0", "metrics": "0.32.0", - "open-on-github": "0.23.0", + "open-on-github": "0.26.0", "package-generator": "0.30.0", "release-notes": "0.26.0", - "settings-view": "0.104.0", + "settings-view": "0.107.0", "snippets": "0.40.0", - "spell-check": "0.31.0", - "status-bar": "0.38.0", - "styleguide": "0.27.0", - "symbols-view": "0.46.0", - "tabs": "0.32.0", + "spell-check": "0.32.0", + "status-bar": "0.39.0", + "styleguide": "0.28.0", + "symbols-view": "0.49.0", + "tabs": "0.34.0", "timecop": "0.17.0", - "tree-view": "0.85.0", + "tree-view": "0.86.0", "update-package-dependencies": "0.6.0", "welcome": "0.12.0", - "whitespace": "0.21.0", + "whitespace": "0.22.0", "wrap-guide": "0.18.0", - "language-c": "0.13.0", - "language-coffee-script": "0.17.0", + "language-c": "0.14.0", + "language-coffee-script": "0.19.0", "language-css": "0.13.0", - "language-gfm": "0.28.0", + "language-gfm": "0.29.0", "language-git": "0.9.0", - "language-go": "0.7.0", - "language-html": "0.17.0", + "language-go": "0.8.0", + "language-html": "0.18.0", "language-hyperlink": "0.9.0", "language-java": "0.9.0", "language-javascript": "0.21.0", @@ -115,10 +115,10 @@ "language-make": "0.9.0", "language-objective-c": "0.11.0", "language-perl": "0.8.0", - "language-php": "0.13.0", + "language-php": "0.14.0", "language-property-list": "0.7.0", - "language-python": "0.13.0", - "language-ruby": "0.19.0", + "language-python": "0.14.0", + "language-ruby": "0.21.0", "language-ruby-on-rails": "0.12.0", "language-sass": "0.8.0", "language-shellscript": "0.8.0", diff --git a/resources/atom.png b/resources/atom.png index d6cd3a5ea..45548069f 100644 Binary files a/resources/atom.png and b/resources/atom.png differ diff --git a/script/mkdeb b/script/mkdeb index 66ce9686b..0bcc84926 100755 --- a/script/mkdeb +++ b/script/mkdeb @@ -12,7 +12,7 @@ ICON_FILE="$4" DEB_PATH="$5" TARGET_ROOT="`mktemp -d`" -TARGET="$TARGET_ROOT/atom-$VERSION" +TARGET="$TARGET_ROOT/atom-$VERSION-amd64" mkdir -p "$TARGET/usr" env INSTALL_PREFIX="$TARGET/usr" script/grunt install @@ -27,5 +27,5 @@ mkdir -p "$TARGET/usr/share/pixmaps" cp "$ICON_FILE" "$TARGET/usr/share/pixmaps" dpkg-deb -b "$TARGET" -mv "$TARGET_ROOT/atom-$VERSION.deb" "$DEB_PATH" +mv "$TARGET_ROOT/atom-$VERSION-amd64.deb" "$DEB_PATH" rm -rf $TARGET_ROOT diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index 38d649ae2..9adc77ab1 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -170,28 +170,28 @@ describe "the `atom` global", -> element2 = $$ -> @div class: 'test-2' element3 = $$ -> @div class: 'test-3' - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0 - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0 - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0 atom.packages.activatePackage("package-with-keymaps") - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1" - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2" - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1" + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2" + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0 describe "when the metadata contains a 'keymaps' manifest", -> it "loads only the keymaps specified by the manifest, in the specified order", -> element1 = $$ -> @div class: 'test-1' element3 = $$ -> @div class: 'test-3' - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0 atom.packages.activatePackage("package-with-keymaps-manifest") - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1' - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2' - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1' + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2' + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0 describe "menu loading", -> beforeEach -> @@ -377,8 +377,8 @@ describe "the `atom` global", -> runs -> atom.packages.deactivatePackage('package-with-keymaps') - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0 - expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0 + expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0 it "removes the package's stylesheets", -> waitsForPromise -> diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index ea5745d52..2d74d69f0 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -170,6 +170,13 @@ describe "Config", -> expect(atom.config.get("foo.quux.x")).toBe 0 expect(atom.config.get("foo.quux.y")).toBe 1 + it "emits an updated event", -> + updatedCallback = jasmine.createSpy('updated') + atom.config.observe('foo.bar.baz.a', callNow: false, updatedCallback) + expect(updatedCallback.callCount).toBe 0 + atom.config.setDefaults("foo.bar.baz", a: 2) + expect(updatedCallback.callCount).toBe 1 + describe ".observe(keyPath)", -> [observeHandler, observeSubscription] = [] diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index d6183a29a..8d9673ceb 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -18,7 +18,7 @@ describe "DisplayBuffer", -> displayBuffer.destroy() buffer.release() - describe ".copy()", -> + describe "::copy()", -> it "creates a new DisplayBuffer with the same initial state", -> marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1) marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], isReversed: true, id: 2) @@ -536,7 +536,7 @@ describe "DisplayBuffer", -> expect(displayBuffer.outermostFoldsInBufferRowRange(3, 18)).toEqual [fold1, fold3, fold5] expect(displayBuffer.outermostFoldsInBufferRowRange(5, 16)).toEqual [fold3] - describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> + describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> beforeEach -> displayBuffer.setSoftWrap(true) displayBuffer.setEditorWidthInChars(50) @@ -598,7 +598,7 @@ describe "DisplayBuffer", -> expect(displayBuffer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, tabLength] expect(displayBuffer.clipScreenPosition([0, tabLength], skipAtomicTokens: true)).toEqual [0, tabLength] - describe ".screenPositionForBufferPosition(bufferPosition, options)", -> + describe "::screenPositionForBufferPosition(bufferPosition, options)", -> it "clips the specified buffer position", -> expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 2] expect(displayBuffer.screenPositionForBufferPosition([0, 100000])).toEqual [0, 29] @@ -618,13 +618,13 @@ describe "DisplayBuffer", -> expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 0] expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 10] - describe ".getMaxLineLength()", -> + describe "::getMaxLineLength()", -> it "returns the length of the longest screen line", -> expect(displayBuffer.getMaxLineLength()).toBe 65 buffer.delete([[6, 0], [6, 65]]) expect(displayBuffer.getMaxLineLength()).toBe 62 - describe ".destroy()", -> + describe "::destroy()", -> it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", -> marker = displayBuffer.markBufferPosition([12, 2]) displayBuffer.destroy() @@ -777,17 +777,17 @@ describe "DisplayBuffer", -> isValid: true } - xit "triggers the 'changed' event whenever the marker is invalidated or revalidated", -> + it "triggers the 'changed' event whenever the marker is invalidated or revalidated", -> buffer.deleteRow(8) expect(markerChangedHandler).toHaveBeenCalled() expect(markerChangedHandler.argsForCall[0][0]).toEqual { oldHeadScreenPosition: [5, 10] oldHeadBufferPosition: [8, 10] - newHeadScreenPosition: [5, 10] + newHeadScreenPosition: [5, 0] newHeadBufferPosition: [8, 0] oldTailScreenPosition: [5, 4] oldTailBufferPosition: [8, 4] - newTailScreenPosition: [5, 4] + newTailScreenPosition: [5, 0] newTailBufferPosition: [8, 0] textChanged: true isValid: false @@ -798,12 +798,12 @@ describe "DisplayBuffer", -> expect(markerChangedHandler).toHaveBeenCalled() expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 10] - oldHeadBufferPosition: [8, 10] + oldHeadScreenPosition: [5, 0] + oldHeadBufferPosition: [8, 0] newHeadScreenPosition: [5, 10] newHeadBufferPosition: [8, 10] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] + oldTailScreenPosition: [5, 0] + oldTailBufferPosition: [8, 0] newTailScreenPosition: [5, 4] newTailBufferPosition: [8, 4] textChanged: true @@ -814,7 +814,7 @@ describe "DisplayBuffer", -> displayBuffer.createFold(10, 11) expect(markerChangedHandler).not.toHaveBeenCalled() - xit "updates markers before emitting buffer change events, but does not notify their observers until the change event", -> + it "updates markers before emitting buffer change events, but does not notify their observers until the change event", -> marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]]) marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler") displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange() @@ -902,7 +902,7 @@ describe "DisplayBuffer", -> expect(changeHandler).toHaveBeenCalled() expect(markerChangedHandler).toHaveBeenCalled() - describe ".findMarkers(attributes)", -> + describe "::findMarkers(attributes)", -> it "allows the startBufferRow and endBufferRow to be specified", -> marker1 = displayBuffer.markBufferRange([[0, 0], [3, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[0, 0], [5, 0]], class: 'a') @@ -932,7 +932,7 @@ describe "DisplayBuffer", -> buffer.getMarker(marker2.id).destroy() expect(destroyedHandler).toHaveBeenCalled() - describe "DisplayBufferMarker.copy(attributes)", -> + describe "DisplayBufferMarker::copy(attributes)", -> it "creates a copy of the marker with the given attributes merged in", -> initialMarkerCount = displayBuffer.getMarkerCount() marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]], a: 1, b: 2) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index de8d55bfb..247f83e0f 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -1424,6 +1424,26 @@ describe "Editor", -> editor.undo() expect(editor.getCursorBufferPosition()).toEqual [3,4] + it "indents the new line to the same indent level as the current line when editor.autoIndent is true", -> + atom.config.set('editor.autoIndent', true) + + editor.setText(' var test') + editor.setCursorBufferPosition([0,2]) + editor.insertNewlineAbove() + + expect(editor.getCursorBufferPosition()).toEqual [0,2] + expect(editor.lineForBufferRow(0)).toBe ' ' + expect(editor.lineForBufferRow(1)).toBe ' var test' + + editor.setText('\n var test') + editor.setCursorBufferPosition([1,2]) + editor.insertNewlineAbove() + + expect(editor.getCursorBufferPosition()).toEqual [1,2] + expect(editor.lineForBufferRow(0)).toBe '' + expect(editor.lineForBufferRow(1)).toBe ' ' + expect(editor.lineForBufferRow(2)).toBe ' var test' + describe ".backspace()", -> describe "when there is a single cursor", -> changeScreenRangeHandler = null @@ -1596,6 +1616,11 @@ describe "Editor", -> expect(cursor1.getBufferPosition()).toEqual [1, 13] expect(cursor2.getBufferPosition()).toEqual [2, 34] + editor.setText(' var sort') + editor.setCursorBufferPosition([0, 2]) + editor.backspaceToBeginningOfWord() + expect(buffer.lineForRow(0)).toBe 'var sort' + describe "when text is selected", -> it "deletes only selected text", -> editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]]) diff --git a/spec/editor-view-spec.coffee b/spec/editor-view-spec.coffee index ab3b1a99d..af37fa535 100644 --- a/spec/editor-view-spec.coffee +++ b/spec/editor-view-spec.coffee @@ -2802,7 +2802,7 @@ describe "EditorView", -> describe "when the escape key is pressed on the editor view", -> it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", -> - atom.keymap.bindKeys 'name', '.editor', {'escape': 'test-event'} + atom.keymaps.bindKeys 'name', '.editor', {'escape': 'test-event'} testEventHandler = jasmine.createSpy("testEventHandler") editorView.on 'test-event', testEventHandler diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 51cb7c36c..4899663b8 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -46,6 +46,16 @@ describe "LanguageMode", -> languageMode.toggleLineCommentsForBufferRows(0, 0) expect(buffer.lineForRow(0)).toBe " // var i;" + buffer.setText(' ') + languageMode.toggleLineCommentsForBufferRows(0, 0) + expect(buffer.lineForRow(0)).toBe " // " + + buffer.setText (' a\n \n b') + languageMode.toggleLineCommentsForBufferRows(0, 2) + expect(buffer.lineForRow(0)).toBe " // a" + expect(buffer.lineForRow(1)).toBe " // " + expect(buffer.lineForRow(2)).toBe " // b" + describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", -> it "returns the start/end rows of the foldable region starting at the given row", -> expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12] diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 3f68f9062..5574abae5 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -6,8 +6,8 @@ require '../vendor/jasmine-jquery' path = require 'path' _ = require 'underscore-plus' fs = require 'fs-plus' -Keymap = require '../src/keymap-extensions' -{$, WorkspaceView} = require 'atom' +KeymapManager = require '../src/keymap-extensions' +{$, WorkspaceView, Workspace} = require 'atom' Config = require '../src/config' {Point} = require 'text-buffer' Project = require '../src/project' @@ -22,8 +22,8 @@ atom.themes.requireStylesheet '../static/jasmine' fixturePackagesPath = path.resolve(__dirname, './fixtures/packages') atom.packages.packageDirPaths.unshift(fixturePackagesPath) -atom.keymap.loadBundledKeymaps() -keyBindingsToRestore = atom.keymap.getKeyBindings() +atom.keymaps.loadBundledKeymaps() +keyBindingsToRestore = atom.keymaps.getKeyBindings() $(window).on 'core:close', -> window.close() $(window).on 'unload', -> @@ -50,7 +50,8 @@ beforeEach -> $.fx.off = true projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures') atom.project = new Project(path: projectPath) - atom.keymap.keyBindings = _.clone(keyBindingsToRestore) + atom.workspace = new Workspace() + atom.keymaps.keyBindings = _.clone(keyBindingsToRestore) window.resetTimeouts() atom.packages.packageStates = {} @@ -112,7 +113,7 @@ afterEach -> atom.workspaceView = null delete atom.state.workspace - atom.project?.destroy?() + atom.project?.destroy() atom.project = null delete atom.state.packageStates @@ -187,7 +188,7 @@ window.keydownEvent = (key, properties={}) -> originalEventProperties.cmd = properties.metaKey originalEventProperties.target = properties.target?[0] ? properties.target originalEventProperties.which = properties.which - originalEvent = Keymap.keydownEvent(key, originalEventProperties) + originalEvent = KeymapManager.keydownEvent(key, originalEventProperties) properties = $.extend({originalEvent}, properties) $.Event("keydown", properties) @@ -278,7 +279,7 @@ $.fn.resultOfTrigger = (type) -> event.result $.fn.enableKeymap = -> - @on 'keydown', (e) => atom.keymap.handleKeyEvent(e) + @on 'keydown', (e) => atom.keymaps.handleKeyEvent(e) $.fn.attachToDom = -> @appendTo($('#jasmine-content')) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 41e9940d3..b9bc06233 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -111,7 +111,7 @@ describe "WorkspaceView", -> commandHandler = jasmine.createSpy('commandHandler') atom.workspaceView.on('foo-command', commandHandler) - atom.keymap.bindKeys('name', '*', 'x': 'foo-command') + atom.keymaps.bindKeys('name', '*', 'x': 'foo-command') describe "when a keydown event is triggered in the WorkspaceView", -> it "triggers matching keybindings for that event", -> diff --git a/src/atom.coffee b/src/atom.coffee index d37a91c7d..6ec8424c7 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -25,7 +25,7 @@ WindowEventHandler = require './window-event-handler' # * `atom.config` - A {Config} instance # * `atom.contextMenu` - A {ContextMenuManager} instance # * `atom.deserializers` - A {DeserializerManager} instance -# * `atom.keymap` - A {Keymap} instance +# * `atom.keymaps` - A {Keymap} instance # * `atom.menu` - A {MenuManager} instance # * `atom.packages` - A {PackageManager} instance # * `atom.project` - A {Project} instance @@ -141,7 +141,7 @@ class Atom extends Model @loadTime = null Config = require './config' - Keymap = require './keymap-extensions' + KeymapManager = require './keymap-extensions' PackageManager = require './package-manager' Clipboard = require './clipboard' Syntax = require './syntax' @@ -158,7 +158,8 @@ class Atom extends Model process.env.NODE_PATH = exportsPath @config = new Config({configDirPath, resourcePath}) - @keymap = new Keymap({configDirPath, resourcePath}) + @keymaps = new KeymapManager({configDirPath, resourcePath}) + @keymap = @keymaps # Deprecated @packages = new PackageManager({devMode, configDirPath, resourcePath}) @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath}) @contextMenu = new ContextMenuManager(devMode) @@ -247,7 +248,7 @@ class Atom extends Model WorkspaceView = require './workspace-view' @workspace = Workspace.deserialize(@state.workspace) ? new Workspace @workspaceView = new WorkspaceView(@workspace) - @keymap.defaultTarget = @workspaceView[0] + @keymaps.defaultTarget = @workspaceView[0] $(@workspaceViewParentSelector).append(@workspaceView) deserializePackageStates: -> @@ -272,12 +273,12 @@ class Atom extends Model @config.load() @config.setDefaults('core', require('./workspace-view').configDefaults) @config.setDefaults('editor', require('./editor-view').configDefaults) - @keymap.loadBundledKeymaps() + @keymaps.loadBundledKeymaps() @themes.loadBaseStylesheets() @packages.loadPackages() @deserializeEditorWindow() @packages.activate() - @keymap.loadUserKeymap() + @keymaps.loadUserKeymap() @requireUserInitScript() @menu.update() @@ -301,7 +302,7 @@ class Atom extends Model @workspaceView = null @project.destroy() @windowEventHandler?.unsubscribe() - @keymap.destroy() + @keymaps.destroy() @windowState = null loadThemes: -> diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index b310659b9..5aa611666 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -147,7 +147,6 @@ class ApplicationMenu modifiers = firstKeystroke.split('-') key = modifiers.pop() - modifiers.push("Shift") if key != key.toLowerCase() modifiers = modifiers.map (modifier) -> modifier.replace(/shift/ig, "Shift") .replace(/cmd/ig, "Command") diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index d209ffd41..2c4dc7c23 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -125,7 +125,7 @@ class AtomApplication # Configures required javascript environment flags. setupJavaScriptArguments: -> - app.commandLine.appendSwitch 'js-flags', '--harmony_collections --harmony-proxies' + app.commandLine.appendSwitch 'js-flags', '--harmony' # Enable updates unless running from a local build of Atom. setupAutoUpdater: -> @@ -169,25 +169,30 @@ class AtomApplication # Registers basic application commands, non-idempotent. handleEvents: -> - @on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:') @on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath) @on 'application:run-benchmarks', -> @runBenchmarks() @on 'application:quit', -> app.quit() - @on 'application:hide', -> Menu.sendActionToFirstResponder('hide:') - @on 'application:hide-other-applications', -> Menu.sendActionToFirstResponder('hideOtherApplications:') - @on 'application:unhide-all-applications', -> Menu.sendActionToFirstResponder('unhideAllApplications:') @on 'application:new-window', -> @openPath(initialSize: @getFocusedWindowSize()) @on 'application:new-file', -> (@focusedWindow() ? this).openPath() @on 'application:open', -> @promptForPath() @on 'application:open-dev', -> @promptForPath(devMode: true) - @on 'application:minimize', -> Menu.sendActionToFirstResponder('performMiniaturize:') - @on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:') - @on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:') @on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y) @on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app') @on 'application:install-update', -> autoUpdater.quitAndInstall() @on 'application:check-for-update', => @checkForUpdate() + if process.platform is 'darwin' + @on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:') + @on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:') + @on 'application:hide', -> Menu.sendActionToFirstResponder('hide:') + @on 'application:hide-other-applications', -> Menu.sendActionToFirstResponder('hideOtherApplications:') + @on 'application:minimize', -> Menu.sendActionToFirstResponder('performMiniaturize:') + @on 'application:unhide-all-applications', -> Menu.sendActionToFirstResponder('unhideAllApplications:') + @on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:') + else + @on 'application:minimize', -> @focusedWindow()?.minimize() + @on 'application:zoom', -> @focusedWindow()?.maximize() + @openPathOnEvent('application:show-settings', 'atom://config') @openPathOnEvent('application:open-your-config', 'atom://.atom/config') @openPathOnEvent('application:open-your-init-script', 'atom://.atom/init-script') diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 33389403c..fed11e4d6 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -138,6 +138,10 @@ class AtomWindow getSize: -> @browserWindow.getSize() + minimize: -> @browserWindow.minimize() + + maximize: -> @browserWindow.maximize() + handlesAtomCommands: -> not @isSpecWindow() and @isWebViewFocused() diff --git a/src/config.coffee b/src/config.coffee index 775d051ee..0c0e558aa 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -86,7 +86,7 @@ class Config hash = hash[key] _.extend hash, defaults - @update() + @emit 'updated' # Public: Get the path to the config file being used. getUserConfigPath: -> diff --git a/src/cursor.coffee b/src/cursor.coffee index e946a3523..23209e854 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -315,12 +315,14 @@ class Cursor # :includeNonWordCharacters - A {Boolean} indicating whether to include # non-word characters in the default word regex. # Has no effect if wordRegex is set. + # :allowPrevious - A {Boolean} indicating whether the beginning of the + # previous word can be returned. # # Returns a {Range}. getBeginningOfCurrentWordBufferPosition: (options = {}) -> allowPrevious = options.allowPrevious ? true currentBufferPosition = @getBufferPosition() - previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row) + previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row) ? 0 scanRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = null @@ -330,7 +332,12 @@ class Cursor if not beginningOfWordPosition?.isEqual(currentBufferPosition) stop() - beginningOfWordPosition or currentBufferPosition + if beginningOfWordPosition? + beginningOfWordPosition + else if allowPrevious + new Point(0, 0) + else + currentBufferPosition # Public: Retrieves buffer position of previous word boundary. It might be on # the current word, or the previous word. diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 2be2aca2a..b07eb3424 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -343,7 +343,6 @@ class EditorView extends View @subscribe atom.config.observe 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily) @subscribe atom.config.observe 'editor.lineHeight', (lineHeight) => @setLineHeight(lineHeight) - handleEvents: -> @on 'focus', => @hiddenInput.focus() diff --git a/src/editor.coffee b/src/editor.coffee index 5e4b78ef0..825c9c400 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -623,11 +623,18 @@ class Editor extends Model # Public: For each cursor, insert a newline at the end of the preceding line. insertNewlineAbove: -> @transact => - onFirstLine = @getCursorBufferPosition().row is 0 + bufferRow = @getCursorBufferPosition().row + indentLevel = @indentationForBufferRow(bufferRow) + onFirstLine = bufferRow is 0 + @moveCursorToBeginningOfLine() @moveCursorLeft() @insertNewline() - @moveCursorUp() if onFirstLine + @setIndentationForBufferRow(bufferRow, indentLevel) if @shouldAutoIndent() + + if onFirstLine + @moveCursorUp() + @moveCursorToEndOfLine() # Indent all lines intersecting selections. See {Selection::indent} for more # information. diff --git a/src/git.coffee b/src/git.coffee index aa3acab92..54148af3c 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -71,6 +71,9 @@ class Git @statuses = {} @upstream = {ahead: 0, behind: 0} + for submodulePath, submoduleRepo of @repo.submodules + submoduleRepo.upstream = {ahead: 0, behind: 0} + {@project, refreshOnWindowFocus} = options refreshOnWindowFocus ?= true diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index c86e82b11..a4153de92 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -1,27 +1,27 @@ fs = require 'fs-plus' path = require 'path' -Keymap = require 'atom-keymap' +KeymapManager = require 'atom-keymap' CSON = require 'season' {jQuery} = require 'space-pen' -Keymap::loadBundledKeymaps = -> - @loadKeyBindings(path.join(@resourcePath, 'keymaps')) +KeymapManager::loadBundledKeymaps = -> + @loadKeymap(path.join(@resourcePath, 'keymaps')) @emit('bundled-keymaps-loaded') -Keymap::getUserKeymapPath = -> +KeymapManager::getUserKeymapPath = -> if userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap')) userKeymapPath else path.join(@configDirPath, 'keymap.cson') -Keymap::loadUserKeymap = -> +KeymapManager::loadUserKeymap = -> userKeymapPath = @getUserKeymapPath() if fs.isFileSync(userKeymapPath) - @loadKeyBindings(userKeymapPath, watch: true, suppressErrors: true) + @loadKeymap(userKeymapPath, watch: true, suppressErrors: true) # This enables command handlers registered via jQuery to call # `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler. jQuery.Event::abortKeyBinding = -> @originalEvent?.abortKeyBinding?() -module.exports = Keymap +module.exports = KeymapManager diff --git a/src/language-mode.coffee b/src/language-mode.coffee index bc075a662..31b66f7bc 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -74,7 +74,10 @@ class LanguageMode columnEnd = columnStart + match[2].length buffer.change([[row, columnStart], [row, columnEnd]], "") else - indent = @minIndentLevelForRowRange(start, end) + if start is end + indent = @editor.indentationForBufferRow(start) + else + indent = @minIndentLevelForRowRange(start, end) indentString = @editor.buildIndentString(indent) tabLength = @editor.getTabLength() indentRegex = new RegExp("(\t|[ ]{#{tabLength}}){#{Math.floor(indent)}}") diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index b47dbeaad..a9edb867f 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -14,7 +14,7 @@ class MenuManager constructor: ({@resourcePath}) -> @pendingUpdateOperation = null @template = [] - atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems() + atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems() # Public: Adds the given item definition to the existing template. # @@ -59,7 +59,8 @@ class MenuManager testBody.classList.add(@classesForElement(document.body)...) testWorkspace = document.createElement('body') - workspaceClasses = @classesForElement(document.body.querySelector('.workspace')) ? ['.workspace'] + workspaceClasses = @classesForElement(document.body.querySelector('.workspace')) + workspaceClasses = ['workspace'] if workspaceClasses.length is 0 testWorkspace.classList.add(workspaceClasses...) testBody.appendChild(testWorkspace) @@ -75,7 +76,7 @@ class MenuManager clearImmediate(@pendingUpdateOperation) if @pendingUpdateOperation? @pendingUpdateOperation = setImmediate => keystrokesByCommand = {} - for binding in atom.keymap.getKeyBindings() when @includeSelector(binding.selector) + for binding in atom.keymaps.getKeyBindings() when @includeSelector(binding.selector) keystrokesByCommand[binding.command] ?= [] keystrokesByCommand[binding.command].unshift binding.keystroke @sendToBrowserProcess(@template, keystrokesByCommand) @@ -116,10 +117,10 @@ class MenuManager normalizeLabel: (label) -> return undefined unless label? - if process.platform is 'win32' - label.replace(/\&/g, '') - else + if process.platform is 'darwin' label + else + label.replace(/\&/g, '') # Get an {Array} of {String} classes for the given element. classesForElement: (element) -> diff --git a/src/package.coffee b/src/package.coffee index 05d74eefb..f51d9aea4 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -124,7 +124,7 @@ class Package @stylesheetsActivated = true activateResources: -> - atom.keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps + atom.keymaps.add(keymapPath, map) for [keymapPath, map] in @keymaps atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu @@ -232,7 +232,7 @@ class Package deactivateResources: -> grammar.deactivate() for grammar in @grammars scopedProperties.deactivate() for scopedProperties in @scopedProperties - atom.keymap.remove(keymapPath) for [keymapPath] in @keymaps + atom.keymaps.remove(keymapPath) for [keymapPath] in @keymaps atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets @stylesheetsActivated = false @grammarsActivated = false diff --git a/src/space-pen-extensions.coffee b/src/space-pen-extensions.coffee index a6b197231..0ce3e1d4f 100644 --- a/src/space-pen-extensions.coffee +++ b/src/space-pen-extensions.coffee @@ -40,9 +40,9 @@ jQuery.fn.setTooltip = (tooltipOptions, {command, commandElement}={}) -> tooltipOptions = {title: tooltipOptions} if _.isString(tooltipOptions) if commandElement - bindings = atom.keymap.keyBindingsForCommandMatchingElement(command, commandElement) + bindings = atom.keymaps.keyBindingsForCommandMatchingElement(command, commandElement) else if command - bindings = atom.keymap.keyBindingsForCommand(command) + bindings = atom.keymaps.keyBindingsForCommand(command) tooltipOptions.title = "#{tooltipOptions.title} #{getKeystroke(bindings)}" diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 8cf637cc6..21d1940fb 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -55,7 +55,7 @@ class WindowEventHandler @subscribeToCommand $(document), 'core:focus-previous', @focusPrevious @subscribe $(document), 'keydown', (event) -> - atom.keymap.handleKeyEvent(event) + atom.keymaps.handleKeyEvent(event) @subscribe $(document), 'drop', (e) -> e.preventDefault() diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 72f98cf55..2c316c845 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -79,7 +79,7 @@ class WorkspaceView extends View @div class: 'panes', outlet: 'panes' initialize: (@model) -> - @model ?= new Workspace + @model = atom.workspace ? new Workspace unless @model? panes = new PaneContainerView(@model.paneContainer) @panes.replaceWith(panes) diff --git a/src/workspace.coffee b/src/workspace.coffee index 1f301a94e..18b253e1b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -34,7 +34,7 @@ class Workspace extends Model when 'atom://.atom/stylesheet' @open(atom.themes.getUserStylesheetPath()) when 'atom://.atom/keymap' - @open(atom.keymap.getUserKeymapPath()) + @open(atom.keymaps.getUserKeymapPath()) when 'atom://.atom/config' @open(atom.config.getUserConfigPath()) when 'atom://.atom/init-script'