From 1fa04cb1616d39361413f2293620fdd9d67f4c78 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 15:54:28 -0800 Subject: [PATCH 001/111] Use biscotto fork --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 29855c350..f9a49a083 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "async": "~0.2.9", - "biscotto": "0.0.17", + "biscotto": "git://github.com/atom/biscotto.git#9b5b6ceb6cc28e90e22e39c5524bd5a0be23a47f", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", From 2e4e178091c49d6a75bea4b1c6d0978e7dd9bf30 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 16:03:34 -0800 Subject: [PATCH 002/111] Don't load keymap files with other platforms in the suffix Example: On osx `keymap.cson` and `keymap-darwin.cson` would load. But `keymap-win32.cson` would not load. --- spec/keymap-spec.coffee | 21 +++++++++++++++++++++ src/keymap.coffee | 8 +++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/spec/keymap-spec.coffee b/spec/keymap-spec.coffee index d976144ed..5757018bc 100644 --- a/spec/keymap-spec.coffee +++ b/spec/keymap-spec.coffee @@ -354,6 +354,27 @@ describe "Keymap", -> bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el) expect(bindings).toHaveLength 0 + describe "loading platform specific keybindings", -> + customKeymap = null + + beforeEach -> + resourcePath = temp.mkdirSync('atom') + customKeymap = new Keymap({configDirPath, resourcePath}) + + afterEach -> + customKeymap.destroy() + + it "doesn't load keybindings from other platforms", -> + win32FilePath = path.join(resourcePath, "keymaps", "test-win32.cson") + darwinFilePath = path.join(resourcePath, "keymaps", "test-darwin.cson") + fs.writeFileSync(win32FilePath, '"body": "ctrl-l": "core:win32-move-left"') + fs.writeFileSync(darwinFilePath, '"body": "ctrl-l": "core:darwin-move-left"') + + customKeymap.loadBundledKeymaps() + keyBindings = customKeymap.keyBindingsForKeystroke('ctrl-l') + expect(keyBindings).toHaveLength 1 + expect(keyBindings[0].command).toBe "core:#{process.platform}-move-left" + describe "when the user keymap file is changed", -> it "is reloaded", -> keymapFilePath = path.join(configDirPath, "keymap.cson") diff --git a/src/keymap.coffee b/src/keymap.coffee index e75faf17d..daf9d8f3b 100644 --- a/src/keymap.coffee +++ b/src/keymap.coffee @@ -142,7 +142,13 @@ class Keymap @userKeymapFile.on 'contents-changed moved removed', => @loadUserKeymap() loadDirectory: (directoryPath) -> - @load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json']) + platforms = ['darwin', 'freebsd', 'linux', 'sunos', 'win32'] + otherPlatforms = platforms.filter (name) -> name != process.platform + + for filePath in fs.listSync(directoryPath, ['.cson', '.json']) + platform = filePath.match(/-(\w+)\.\w+$/)?[1] + continue if platform in otherPlatforms + @load(filePath) load: (path) -> @add(path, CSON.readFileSync(path)) From 051f1b4777ace20872b718ab100a4065ce4302e7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 16:12:01 -0800 Subject: [PATCH 003/111] :memo: Mark setEditorWidthInChars as public --- src/editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor.coffee b/src/editor.coffee index a5aede5ed..a7cbd1891 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -202,8 +202,8 @@ class Editor extends Model # Deprecated: Use the ::scrollLeft property directly getScrollLeft: -> @scrollLeft - # Set the number of characters that can be displayed horizontally in the - # editor that contains this edit session. + # Public: Set the number of characters that can be displayed horizontally in + # the editor. # # editorWidthInChars - A {Number} of characters setEditorWidthInChars: (editorWidthInChars) -> From ec558f9a9b24b77e82ee30c04ab7807a189c99ce Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 16:13:45 -0800 Subject: [PATCH 004/111] Update keymap files --- keymaps/{darwin.cson => base-darwin.cson} | 8 ++++---- keymaps/{win32.cson => base-win32.cson} | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) rename keymaps/{darwin.cson => base-darwin.cson} (97%) rename keymaps/{win32.cson => base-win32.cson} (95%) diff --git a/keymaps/darwin.cson b/keymaps/base-darwin.cson similarity index 97% rename from keymaps/darwin.cson rename to keymaps/base-darwin.cson index facd6cc8e..64e287b06 100644 --- a/keymaps/darwin.cson +++ b/keymaps/base-darwin.cson @@ -1,4 +1,4 @@ -'.platform-darwin': +'.workspace': # Apple specific 'cmd-q': 'application:quit' 'cmd-h': 'application:hide' @@ -87,7 +87,7 @@ 'cmd-8': 'pane:show-item-8' 'cmd-9': 'pane:show-item-9' -'.platform-darwin .editor': +'.editor': # Apple Specific 'cmd-backspace': 'editor:backspace-to-beginning-of-line' 'cmd-delete': 'editor:backspace-to-beginning-of-line' @@ -113,7 +113,7 @@ 'cmd-k cmd-l': 'editor:lower-case' 'cmd-l': 'editor:select-line' -'body.platform-darwin .editor:not(.mini)': +'.workspace .editor:not(.mini)': # Atom specific 'alt-cmd-z': 'editor:checkout-head-revision' 'cmd-<': 'editor:scroll-to-cursor' @@ -148,7 +148,7 @@ 'cmd-k cmd-9': 'editor:fold-at-indent-level-9' # allow standard input fields to work correctly -'body.platform-darwin .native-key-bindings': +'.workspace .native-key-bindings': 'cmd-z': 'native!' 'cmd-Z': 'native!' 'cmd-x': 'native!' diff --git a/keymaps/win32.cson b/keymaps/base-win32.cson similarity index 95% rename from keymaps/win32.cson rename to keymaps/base-win32.cson index 60825bf16..f80fd310d 100644 --- a/keymaps/win32.cson +++ b/keymaps/base-win32.cson @@ -1,4 +1,4 @@ -'.platform-win32': +'.workspace': # Atom Specific 'enter': 'core:confirm' 'escape': 'core:cancel' @@ -50,7 +50,7 @@ 'ctrl-k ctrl-left': 'window:focus-previous-pane' 'ctrl-k ctrl-right': 'window:focus-next-pane' -'.platform-win32 .editor': +'.workspace .editor': # Windows specific 'ctrl-delete': 'editor:backspace-to-beginning-of-word' @@ -60,7 +60,7 @@ 'ctrl-k ctrl-u': 'editor:upper-case' 'ctrl-k ctrl-l': 'editor:lower-case' -'.platform-win32 .editor:not(.mini)': +'.workspace .editor:not(.mini)': # Atom specific 'alt-ctrl-z': 'editor:checkout-head-revision' 'ctrl-<': 'editor:scroll-to-cursor' @@ -94,7 +94,7 @@ 'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9' # allow standard input fields to work correctly -'.platform-win32 input:not(.hidden-input), .platform-win32 .native-key-bindings': +'.workspace .native-key-bindings': 'ctrl-z': 'native!' 'ctrl-Z': 'native!' 'ctrl-x': 'native!' From 4b3b0145a77e9244d74ed1242472fcff4de58ebd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 16:17:47 -0800 Subject: [PATCH 005/111] :memo: Mark DisplayBuffer delegates as public --- src/editor.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/editor.coffee b/src/editor.coffee index a7cbd1891..753754be2 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -765,27 +765,27 @@ class Editor extends Model findMarkers: (attributes) -> @displayBuffer.findMarkers(attributes) - # {Delegates to: DisplayBuffer.markScreenRange} + # Public: {Delegates to: DisplayBuffer.markScreenRange} markScreenRange: (args...) -> @displayBuffer.markScreenRange(args...) - # {Delegates to: DisplayBuffer.markBufferRange} + # Public: {Delegates to: DisplayBuffer.markBufferRange} markBufferRange: (args...) -> @displayBuffer.markBufferRange(args...) - # {Delegates to: DisplayBuffer.markScreenPosition} + # Public: {Delegates to: DisplayBuffer.markScreenPosition} markScreenPosition: (args...) -> @displayBuffer.markScreenPosition(args...) - # {Delegates to: DisplayBuffer.markBufferPosition} + # Public: {Delegates to: DisplayBuffer.markBufferPosition} markBufferPosition: (args...) -> @displayBuffer.markBufferPosition(args...) - # {Delegates to: DisplayBuffer.destroyMarker} + # Public: {Delegates to: DisplayBuffer.destroyMarker} destroyMarker: (args...) -> @displayBuffer.destroyMarker(args...) - # {Delegates to: DisplayBuffer.getMarkerCount} + # Public: {Delegates to: DisplayBuffer.getMarkerCount} getMarkerCount: -> @buffer.getMarkerCount() From 3fb54b657dd4cdefdcbdda45d86ea56c6c13d5b2 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 16:27:25 -0800 Subject: [PATCH 006/111] Update apm with new keybinding docs --- vendor/apm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/apm b/vendor/apm index b80ef23ce..50d23f5f4 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit b80ef23ce8d2a1e8b4f40eb0f89c87f32dcc3415 +Subproject commit 50d23f5f419230cf1833e7bb8ee7bf0d7289ab1d From e53ed101691a81a40e01b17cc8835fd3c486acca Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 16:34:52 -0800 Subject: [PATCH 007/111] Only allow one platform specific keybinding file. --- keymaps/{base-darwin.cson => darwin.cson} | 0 keymaps/{base-win32.cson => win32.cson} | 0 spec/keymap-spec.coffee | 4 ++-- src/keymap.coffee | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename keymaps/{base-darwin.cson => darwin.cson} (100%) rename keymaps/{base-win32.cson => win32.cson} (100%) diff --git a/keymaps/base-darwin.cson b/keymaps/darwin.cson similarity index 100% rename from keymaps/base-darwin.cson rename to keymaps/darwin.cson diff --git a/keymaps/base-win32.cson b/keymaps/win32.cson similarity index 100% rename from keymaps/base-win32.cson rename to keymaps/win32.cson diff --git a/spec/keymap-spec.coffee b/spec/keymap-spec.coffee index 5757018bc..064c5b1c7 100644 --- a/spec/keymap-spec.coffee +++ b/spec/keymap-spec.coffee @@ -365,8 +365,8 @@ describe "Keymap", -> customKeymap.destroy() it "doesn't load keybindings from other platforms", -> - win32FilePath = path.join(resourcePath, "keymaps", "test-win32.cson") - darwinFilePath = path.join(resourcePath, "keymaps", "test-darwin.cson") + win32FilePath = path.join(resourcePath, "keymaps", "win32.cson") + darwinFilePath = path.join(resourcePath, "keymaps", "darwin.cson") fs.writeFileSync(win32FilePath, '"body": "ctrl-l": "core:win32-move-left"') fs.writeFileSync(darwinFilePath, '"body": "ctrl-l": "core:darwin-move-left"') diff --git a/src/keymap.coffee b/src/keymap.coffee index daf9d8f3b..fb3bf9597 100644 --- a/src/keymap.coffee +++ b/src/keymap.coffee @@ -146,7 +146,7 @@ class Keymap otherPlatforms = platforms.filter (name) -> name != process.platform for filePath in fs.listSync(directoryPath, ['.cson', '.json']) - platform = filePath.match(/-(\w+)\.\w+$/)?[1] + platform = path.basename(filePath).match(/^(\w+)\.\w+$/)?[1] continue if platform in otherPlatforms @load(filePath) From 1142da1848c64d4e17c498ea761b123652dc6ea6 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 16:48:56 -0800 Subject: [PATCH 008/111] Use body instead of workspace so keymaps work inside spec window --- keymaps/darwin.cson | 4 ++-- keymaps/win32.cson | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 64e287b06..a112bfbf7 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -1,4 +1,4 @@ -'.workspace': +'body': # Apple specific 'cmd-q': 'application:quit' 'cmd-h': 'application:hide' @@ -148,7 +148,7 @@ 'cmd-k cmd-9': 'editor:fold-at-indent-level-9' # allow standard input fields to work correctly -'.workspace .native-key-bindings': +'body .native-key-bindings': 'cmd-z': 'native!' 'cmd-Z': 'native!' 'cmd-x': 'native!' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index f80fd310d..3b6a230a5 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -1,4 +1,4 @@ -'.workspace': +'body': # Atom Specific 'enter': 'core:confirm' 'escape': 'core:cancel' @@ -94,7 +94,7 @@ 'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9' # allow standard input fields to work correctly -'.workspace .native-key-bindings': +'body .native-key-bindings': 'ctrl-z': 'native!' 'ctrl-Z': 'native!' 'ctrl-x': 'native!' From 03250f79c6060baebcba25c33298123768619f61 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:05:55 -0800 Subject: [PATCH 009/111] :memo: Add period to first line of loadOrCreate doc --- src/atom.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom.coffee b/src/atom.coffee index 848be9b3b..a35523c17 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -34,7 +34,7 @@ WindowEventHandler = require './window-event-handler' # * `atom.themes` - A {ThemeManager} instance module.exports = class Atom extends Model - # Public: Load or create the Atom environment in the given mode + # Public: Load or create the Atom environment in the given mode. # # - mode: Pass 'editor' or 'spec' depending on the kind of environment you # want to build. From 002c4dcb80adf436ec01c6540bf6343515af734e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:16:50 -0800 Subject: [PATCH 010/111] Upgrade to atom/biscotto@7ba14a419 --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index f9a49a083..e5f4008e5 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "async": "~0.2.9", - "biscotto": "git://github.com/atom/biscotto.git#9b5b6ceb6cc28e90e22e39c5524bd5a0be23a47f", + "biscotto": "git://github.com/atom/biscotto.git#7ba14a41980f6e2a9afa342d3e5597acc323be9f", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", From 352eab47df4da60bd28dc02f1a2a064a0a83f45c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:21:42 -0800 Subject: [PATCH 011/111] :memo: Merge example with return doc --- src/git.coffee | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/git.coffee b/src/git.coffee index dd795310a..f445a0caf 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -250,12 +250,7 @@ class Git # Public: Returns the upstream branch for the current HEAD, or null if there # is no upstream branch for the current HEAD. # - # Examples - # - # getUpstreamBranch() - # # => "refs/remotes/origin/master" - # - # Returns a String. + # Returns a String branch name such as `refs/remotes/origin/master` getUpstreamBranch: -> @getRepo().getUpstreamBranch() # Public: Returns the current SHA for the given reference. From e9c296968d5d6dbc9dd8825cba5063c45fb5af07 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:43:36 -0800 Subject: [PATCH 012/111] Upgrade to atom/biscotto@408b1862da --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index e5f4008e5..4f789b4be 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "async": "~0.2.9", - "biscotto": "git://github.com/atom/biscotto.git#7ba14a41980f6e2a9afa342d3e5597acc323be9f", + "biscotto": "git://github.com/atom/biscotto.git#408b1862dab6d11d2c4e7cecff0a59701b7b2d5e", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", From 1d9bca12e0e15e4348eb2d79538da800f76a933e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:45:32 -0800 Subject: [PATCH 013/111] :memo: Mention that atom.project.getRepo() might be unavailable --- src/git.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/git.coffee b/src/git.coffee index f445a0caf..db5a62ce2 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -7,7 +7,8 @@ GitUtils = require 'git-utils' # Public: Represents the underlying git operations performed by Atom. # # This class shouldn't be instantiated directly but instead by accessing the -# `atom.project` global and calling `getRepo()`. +# `atom.project` global and calling `getRepo()`. Note that this will only be +# available when the project is backed by a Git repository. # # ## Example # From 41de6bdb707f9cda0646a41020047e79216c1ef1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 17:51:54 -0800 Subject: [PATCH 014/111] Just hackable for now --- README.md | 2 +- build/tasks/set-version-task.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7f0b4807d..4a9e91317 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Atom — The hackable, ~~collaborative~~ editor +# Atom — The hackable editor ![Atom](http://i.imgur.com/OrTvUAD.png) diff --git a/build/tasks/set-version-task.coffee b/build/tasks/set-version-task.coffee index be41866a8..bdf6e0c4d 100644 --- a/build/tasks/set-version-task.coffee +++ b/build/tasks/set-version-task.coffee @@ -41,7 +41,7 @@ module.exports = (grunt) -> strings = CompanyName: 'GitHub, Inc.' - FileDescription: 'The hackable, collaborative editor' + FileDescription: 'The hackable editor' LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved' ProductName: 'Atom' ProductVersion: version From 7d1be155fae2c7195cc98dffe65aa93d6a8415dc Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 17:55:05 -0800 Subject: [PATCH 015/111] :lipstick: --- src/keymap.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/keymap.coffee b/src/keymap.coffee index fb3bf9597..3c3a8ec44 100644 --- a/src/keymap.coffee +++ b/src/keymap.coffee @@ -146,8 +146,7 @@ class Keymap otherPlatforms = platforms.filter (name) -> name != process.platform for filePath in fs.listSync(directoryPath, ['.cson', '.json']) - platform = path.basename(filePath).match(/^(\w+)\.\w+$/)?[1] - continue if platform in otherPlatforms + continue if path.basename(filePath, path.extname(filePath)) in otherPlatforms @load(filePath) load: (path) -> From 1bd4d518797cfd33db47eb191f445fda97006be9 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 18:00:59 -0800 Subject: [PATCH 016/111] Install atom and apm cli commands to /usr/local/bin --- src/command-installer.coffee | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/command-installer.coffee b/src/command-installer.coffee index aa6a9e803..622e3a249 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -23,10 +23,6 @@ unlinkCommand = (destinationPath, callback) -> callback() module.exports = - findInstallDirectory: (callback) -> - directories = ['/opt/boxen', '/opt/github', '/usr/local'] - async.detect(directories, fs.isDirectory, callback) - install: (commandPath, commandName, callback) -> if not commandName? or _.isFunction(commandName) callback = commandName @@ -37,17 +33,17 @@ module.exports = console.warn "Failed to install `#{commandName}` binary", error callback?(error, sourcePath, destinationPath) - @findInstallDirectory (directory) -> - if directory? - destinationPath = path.join(directory, 'bin', commandName) - unlinkCommand destinationPath, (error) -> - if error? - installCallback(error) - else - symlinkCommand commandPath, destinationPath, (error) -> - installCallback(error, commandPath, destinationPath) - else - installCallback(new Error("No destination directory exists to install")) + directory = "/usr/local" + if fs.existsSync(directory) + destinationPath = path.join(directory, 'bin', commandName) + unlinkCommand destinationPath, (error) -> + if error? + installCallback(error) + else + symlinkCommand commandPath, destinationPath, (error) -> + installCallback(error, commandPath, destinationPath) + else + installCallback(new Error("No destination directory exists to install")) installAtomCommand: (resourcePath, callback) -> if _.isFunction(resourcePath) From 5a70276201ccc3c713f6c5529acede3070bb980d Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 21 Jan 2014 18:09:53 -0800 Subject: [PATCH 017/111] Fix specs --- spec/command-installer-spec.coffee | 4 +--- src/command-installer.coffee | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee index c6b8ee321..679a9b5df 100644 --- a/spec/command-installer-spec.coffee +++ b/spec/command-installer-spec.coffee @@ -9,9 +9,7 @@ describe "install(commandPath, callback)", -> destinationPath = path.join(directory, 'bin', 'source') beforeEach -> - spyOn(installer, 'findInstallDirectory').andCallFake (callback) -> - callback(directory) - + spyOn(installer, 'getInstallDirectory').andReturn directory fs.removeSync(directory) if fs.existsSync(directory) describe "on #darwin", -> diff --git a/src/command-installer.coffee b/src/command-installer.coffee index 622e3a249..a41b8cc15 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -23,6 +23,9 @@ unlinkCommand = (destinationPath, callback) -> callback() module.exports = + getInstallDirectory: -> + "/usr/local" + install: (commandPath, commandName, callback) -> if not commandName? or _.isFunction(commandName) callback = commandName @@ -33,7 +36,7 @@ module.exports = console.warn "Failed to install `#{commandName}` binary", error callback?(error, sourcePath, destinationPath) - directory = "/usr/local" + directory = @getInstallDirectory() if fs.existsSync(directory) destinationPath = path.join(directory, 'bin', commandName) unlinkCommand destinationPath, (error) -> From 292ff0de52d7f0b979703c522b0c17b6c1ff3d14 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 18:27:49 -0800 Subject: [PATCH 018/111] :memo: Use singular package --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 9010e60f1..280d1a3b5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,7 +14,7 @@ overview of the main editor API. Check out the [Atom][Atom] class docs to see what globals are available and what they provide. -You can also require many of these classes in your packages via: +You can also require many of these classes in your package via: ```coffee {EditorView} = require 'atom' From e4bcb525733ac8773b0e0a97b4b59ebb1e45545e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 18:31:40 -0800 Subject: [PATCH 019/111] :memo: Add requiring in packages example to exported classes --- src/buffered-node-process.coffee | 6 ++++++ src/buffered-process.coffee | 6 ++++++ src/directory.coffee | 8 +++++++- src/editor-view.coffee | 6 ++++++ src/file.coffee | 6 ++++++ src/git.coffee | 6 ++++++ src/scroll-view.coffee | 8 +++++--- src/select-list.coffee | 6 ++++++ src/task.coffee | 6 ++++++ 9 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/buffered-node-process.coffee b/src/buffered-node-process.coffee index a98734506..397c7bb98 100644 --- a/src/buffered-node-process.coffee +++ b/src/buffered-node-process.coffee @@ -6,6 +6,12 @@ path = require 'path' # # This may seem unnecessary but on Windows we have to have separate executables # for each script without this since Windows doesn't support shebang strings. +# +# ## Requiring in packages +# +# ```coffee +# {BufferedNodeProcess} = require 'atom' +# ``` module.exports = class BufferedNodeProcess extends BufferedProcess # Executes the given Node script. diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index 3a555c3b6..b27c315be 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -1,6 +1,12 @@ ChildProcess = require 'child_process' # Public: A wrapper which provides line buffering for Node's ChildProcess. +# +# ## Requiring in packages +# +# ```coffee +# {BufferedProcess} = require 'atom' +# ``` module.exports = class BufferedProcess process: null diff --git a/src/directory.coffee b/src/directory.coffee index a90dc290d..6404dc5ba 100644 --- a/src/directory.coffee +++ b/src/directory.coffee @@ -7,7 +7,13 @@ pathWatcher = require 'pathwatcher' File = require './file' -# Public: Represents a directory using {File}s +# Public: Represents a directory using {File}s. +# +# ## Requiring in packages +# +# ```coffee +# {Directory} = require 'atom' +# ``` module.exports = class Directory Emitter.includeInto(this) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 35acb9b33..7cd699046 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -16,6 +16,12 @@ LongLineLength = 1000 # Public: Represents the entire visual pane in Atom. # # The EditorView manages the {Editor}, which manages the file buffers. +# +# ## Requiring in packages +# +# ```coffee +# {EditorView} = require 'atom' +# ``` module.exports = class EditorView extends View @characterWidthCache: {} diff --git a/src/file.coffee b/src/file.coffee index bc8152d8a..bb09cc4f2 100644 --- a/src/file.coffee +++ b/src/file.coffee @@ -10,6 +10,12 @@ fs = require 'fs-plus' # # You should probably create a {Directory} and access the {File} objects that # it creates, rather than instantiating the {File} class directly. +# +# ## Requiring in packages +# +# ```coffee +# {File} = require 'atom' +# ``` module.exports = class File Emitter.includeInto(this) diff --git a/src/git.coffee b/src/git.coffee index db5a62ce2..b22466461 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -16,6 +16,12 @@ GitUtils = require 'git-utils' # git = atom.project.getRepo() # console.log git.getOriginUrl() # ``` +# +# ## Requiring in packages +# +# ```coffee +# {Git} = require 'atom' +# ``` module.exports = class Git Emitter.includeInto(this) diff --git a/src/scroll-view.coffee b/src/scroll-view.coffee index 380669cfa..7f09cf73d 100644 --- a/src/scroll-view.coffee +++ b/src/scroll-view.coffee @@ -5,9 +5,11 @@ # This `View` subclass listens to events such as `page-up`, `page-down`, # `move-to-top`, and `move-to-bottom`. # -# FIXME: I don't actually understand if this is useful or not. I think it is -# a base of package widgets but I don't really understand how the core events -# work. +# ## Requiring in packages +# +# ```coffee +# {ScrollView} = require 'atom' +# ``` module.exports = class ScrollView extends View diff --git a/src/select-list.coffee b/src/select-list.coffee index 415560aed..925d6b56e 100644 --- a/src/select-list.coffee +++ b/src/select-list.coffee @@ -4,6 +4,12 @@ fuzzyFilter = require('fuzzaldrin').filter # Public: Provides a widget for users to make a selection from a list of # choices. +# +# ## Requiring in packages +# +# ```coffee +# {SelectList} = require 'atom' +# ``` module.exports = class SelectList extends View diff --git a/src/task.coffee b/src/task.coffee index 80eaa81b8..03f80cc56 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -12,6 +12,12 @@ child_process = require 'child_process' # * task:warn - Emitted when console.warn is called within the task. # * task:error - Emitted when console.error is called within the task. # * task:completed - Emitted when the task has succeeded or failed. +# +# ## Requiring in packages +# +# ```coffee +# {Task} = require 'atom' +# ``` module.exports = class Task Emitter.includeInto(this) From 1ae10e59ae2e3d100e03695d0124489a09edb8a7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 18:42:14 -0800 Subject: [PATCH 020/111] Upgrade to atom/biscotto@4a0c9cb9d7e --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 4f789b4be..320798152 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "async": "~0.2.9", - "biscotto": "git://github.com/atom/biscotto.git#408b1862dab6d11d2c4e7cecff0a59701b7b2d5e", + "biscotto": "git://github.com/atom/biscotto.git#4a0c9cb9d7eb818508da357b37f947d47f1c7892", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", From f0339936eec915e27ee4b921cc2df11822ad38d6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jan 2014 18:56:06 -0800 Subject: [PATCH 021/111] Remove docs/output/api before building --- build/tasks/docs-task.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/tasks/docs-task.coffee b/build/tasks/docs-task.coffee index 07367b31d..6f9b3c8b4 100644 --- a/build/tasks/docs-task.coffee +++ b/build/tasks/docs-task.coffee @@ -5,12 +5,14 @@ fs = require 'fs-plus' request = require 'request' module.exports = (grunt) -> + {rm} = require('./task-helpers')(grunt) + cmd = path.join('node_modules', '.bin', 'coffee') commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--'] opts = stdio: 'inherit' - grunt.registerTask 'build-docs', 'Builds the API docs in src/app', -> + grunt.registerTask 'build-docs', 'Builds the API docs in src', -> done = @async() downloadFileFromRepo = ({repo, file}, callback) -> @@ -34,6 +36,7 @@ module.exports = (grunt) -> if error? done(error) else + rm('docs/output/api') args = [ commonArgs... '--title', 'Atom API Documentation' From 7dcb34064355d9a27fbe95c34031c57fda76c034 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 20:33:39 -0700 Subject: [PATCH 022/111] Streamline the getting started docs --- docs/getting-started.md | 67 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 8d3c5414a..fe6fe86fe 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -19,13 +19,11 @@ bindings][key-bindings] section. ### Working With Files -Atom windows are scoped to the directory they're opened from. If you launch Atom -from the command line everything will be relative to the current directory. This -means that the tree view on the left will only show files contained within that -directory. - -This can be a useful way to organize multiple projects, as each project will be -contained within its own window. +Atom windows are scoped to a single directory on disk. If you launch Atom from +the command line via the `atom` command and don't specify a path, Atom opens a +window for the current working directory. The current window's directory will be +visible as the root of the tree view at the left, and also serve as the context +for all file-related operations. #### Finding Files @@ -34,20 +32,17 @@ begin typing the name of the file you're looking for. If you are looking for a file that is already open press `cmd-b` to bring up a searchable list of open files. -You can also use the tree view to navigate to a file. To open or move focus to -the tree view, press `cmd-\`. You can then navigate to a file using the arrow -keys and select it with `return`. +You can also use the tree view to navigate to a file. To open and focus the +the tree view, press `ctrl-0`. The tree view can be toggled open and closed with +`cmd-\`. #### Adding, Moving, Deleting Files -Currently, all file modification is performed via the tree view. To add a file, -select a directory in the tree view and press `a`. Then type the name of the -file. Any intermediate directories you type will be created automatically if -needed. - -To move or rename a file or directory, select it in the tree view and press `m`. - -To delete a file, select it in the tree view and press `delete`. +Currently, all file modification is performed via the tree view. Add, move, or +delete a file by right-clicking in the tree view and selecting the desired +operation from the context menu. You can also perform these operations from the +keyboard by selecting a file or directory and using `a` to add, `m` to move, and +`delete` to delete. ### Searching @@ -58,35 +53,43 @@ To search within a buffer use `cmd-f`. To search the entire project use #### Navigating By Symbols -If you want to jump to a method press `cmd-r`. It opens a list of all symbols -in the current file. +To jump to a symbol such as a method definition, press `cmd-r`. This opens a +list of all symbols in the current file, which you can fuzzy filter similarly to +`cmd-t`. -To search for symbols across your project use `cmd-shift-r`, but you'll need to -make sure you have a ctags installed and a tags file generated for your project. -Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags` -file to understand the language. Here is [a good example][ctags]. +To search for symbols across your project, use `cmd-shift-r`. First you'll need +to make sure you have ctags installed and a tags file generated for your +project. Also, if you're editing CoffeeScript, it's a good idea to update your +`~/.ctags` file to understand the language. Here is [a good example][ctags]. ### Split Panes -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 tabs inside it. +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 +can configure panes to auto-close with empty in the preferences. ### Folding -You can fold everything with `alt-cmd-{` and unfold everything with -`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and -`alt-cmd-]`. +You can fold blocks of code by clicking the arrows that appear when you hover +your mouse cursor over the gutter. You can also fold and unfold from the +keyboard with `alt-cmd-[` and `alt-cmd-]`. To fold everything, use +`alt-cmd-shift-{` and to unfold everything use `alt-cmd-shift-}`. You can also +fold at a specific indentation level with `cmd-k cmd-N` where N is the +indentation depth. ### Soft-Wrap If you want to toggle soft wrap, trigger the command from the command palette. Press `cmd-shift-P` to open the palette, then type "wrap" to find the correct -command. +command. By default, lines will wrap based on the size of the editor. If you +prefer to wrap at a specific line length, toggle "Wrap at preferred line length" +in preferences. ## Configuration -Press `cmd-,` to display the a settings pane. This serves as the primary +Press `cmd-,` to display the preferences pane. This serves as the primary interface for adjusting config settings, installing packages and changing themes. From b380551e5e7bb738dc211c7cd292a0122d7bc4a1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 20:34:57 -0700 Subject: [PATCH 023/111] Nuke private beta docs --- docs/proposals/private-beta-tasks.md | 70 ------------------------- docs/proposals/private-beta-timeline.md | 16 ------ 2 files changed, 86 deletions(-) delete mode 100644 docs/proposals/private-beta-tasks.md delete mode 100644 docs/proposals/private-beta-timeline.md diff --git a/docs/proposals/private-beta-tasks.md b/docs/proposals/private-beta-tasks.md deleted file mode 100644 index 98db56041..000000000 --- a/docs/proposals/private-beta-tasks.md +++ /dev/null @@ -1,70 +0,0 @@ -**Polish the user experience** - -First and foremost, Atom is a **product**. Atom needs to feel familiar and -inviting. This includes a solid introductory experience and parity with the most -important features of Sublime Text. - - * First launch UI and flow (actions below should be easily discoverable) - * Create a new file - * Open a project and edit an existing file - * Install a package - * Change settings (adjust theme, change key bindings, set config options) - * How to use command P - * Use collaboration internally - * How and where to edit keyBinding should be obvious to new users - * Finish find and replace in buffer/project - * Atom should start < 300ms - * Match Sublime's multiple selection functionality (#523) - * Fix softwrap bugs - * Menus & Context menus - * Track usage/engagement of our users (make this opted in?) - * Windows support - * Reliably and securely auto-update and list what's new - * Secure access to the keychain (don't give every package access to the keychain) - * Secure access to GitHub (each package can ask to have it's own oauth token) - * Don't crash when opening/editing large (> 10Mb) files - * Send js and native crash reports to a remote server - -**Lay solid groundwork for a package and theme ecosystem** - -Extensibility is one of Atom's key value propositions, so a smooth experience -for creating and maintaining packages is just as important as the user -experience. The package development, dependency and publishing workflow needs to -be solid. We also want to have a mechanism for clearly communicating with -package authors about breaking API changes. - - * Finish APM backend (integrate with GitHub Releases) - * Streamline Dev workflow - * `apm create` - create package scaffolding - * `apm test` - so users can run focused package tests - * `apm publish` - should integrate release best practices (ie npm version) - * Determine which classes and methods should be included in the public API - * Users can find/install/update/fork existing packages and themes - -**Tighten up the view layer** -Our current approach to the view layer need some improvement. We want to -actively promote the use of the M-V-VM design pattern, provide some declarative -event binding mechanisms in the view layer, and improve the performance of the -typical package specs. We don't want the current approach to be used as an -example in a bunch of new packages, so it's important to improve it now. - - * Add marker view API - -**Get atom.io online with some exciting articles and documentation** -We'd love to send our private alpha candidates to a nice site with information -about what Atom is, the philosophies and technologies behind it, and guidance -for how to get started. - - * Design and create www.atom.io - * Guides - * Theme & Package creation guide - * Full API per release tag - * Changelog per release - * Explanation of features - * Explain Semver and general plans for the future (reassure developers we care about them) - * General Values/Goals - * Make docs accessible from Atom - * Community/contribution guidelines - * Is all communication to be done through issues? - * When should you publish a plugin? - * Do we need to vet plugins from a security perspective? diff --git a/docs/proposals/private-beta-timeline.md b/docs/proposals/private-beta-timeline.md deleted file mode 100644 index dfcc93c6f..000000000 --- a/docs/proposals/private-beta-timeline.md +++ /dev/null @@ -1,16 +0,0 @@ -## Proposed Timeline - -1. **October 30st** - Internal launch - persuade as many githubbers to switch as -possible. - -1. Triage bugs and identify what needs to be fixed before private alpha. Maybe -talk to @chrissiebrodigan about doing a UX study. - -1. **November 22st** - Private alpha launch - -1. Trickle out invites as people ask/we need more testers. - -1. If our usage metrics/engagement metrics decrease, stop, identify the issue -and fix it before continuing. - -1. Launch From f6c7dd0b2f0c6c950bb341c967ed1efa768633eb Mon Sep 17 00:00:00 2001 From: Kyle Robinson Young Date: Tue, 21 Jan 2014 19:41:53 -0800 Subject: [PATCH 024/111] docs: occured -> occurred. --- docs/creating-a-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/creating-a-package.md b/docs/creating-a-package.md index 24072d822..92fb8a371 100644 --- a/docs/creating-a-package.md +++ b/docs/creating-a-package.md @@ -154,7 +154,7 @@ loaded in alphabetical order. An optional `keymaps` array in your _package.json_ can specify which keymaps to load and in what order. -Keybindings are executed by determining which element the keypress occured on. In +Keybindings are executed by determining which element the keypress occurred on. In the example above, `changer:magic` command is executed when pressing `ctrl-V` on the `.tree-view-scroller` element. From 8ca8ac5efcc1451ff6e951b71291476f594a6810 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 20:56:23 -0700 Subject: [PATCH 025/111] Make ConfigObserver a proper mixin and export it --- exports/atom.coffee | 1 + src/config-observer.coffee | 3 +++ src/display-buffer.coffee | 2 +- src/space-pen-extensions.coffee | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/exports/atom.coffee b/exports/atom.coffee index c5a7ffb63..2eb3c6e36 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -8,6 +8,7 @@ module.exports = File: require '../src/file' fs: require 'fs-plus' Git: require '../src/git' + ConfigObserver: require '../src/config-observer' Point: Point Range: Range diff --git a/src/config-observer.coffee b/src/config-observer.coffee index ca52945c5..46d3c44dd 100644 --- a/src/config-observer.coffee +++ b/src/config-observer.coffee @@ -1,4 +1,7 @@ +Mixin = require 'mixto' + module.exports = +class ConfigObserver extends Mixin observeConfig: (keyPath, args...) -> @configSubscriptions ?= {} @configSubscriptions[keyPath] = atom.config.observe(keyPath, args...) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 6974ad9ba..2479101d7 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -15,7 +15,7 @@ ConfigObserver = require './config-observer' module.exports = class DisplayBuffer extends Model Serializable.includeInto(this) - _.extend @prototype, ConfigObserver + ConfigObserver.includeInto(this) @properties softWrap: null diff --git a/src/space-pen-extensions.coffee b/src/space-pen-extensions.coffee index c048d803a..11bcc3c1a 100644 --- a/src/space-pen-extensions.coffee +++ b/src/space-pen-extensions.coffee @@ -1,9 +1,9 @@ _ = require 'underscore-plus' spacePen = require 'space-pen' -ConfigObserver = require './config-observer' {Subscriber} = require 'emissary' +ConfigObserver = require './config-observer' -_.extend spacePen.View.prototype, ConfigObserver +ConfigObserver.includeInto(spacePen.View) Subscriber.includeInto(spacePen.View) jQuery = spacePen.jQuery From 8d193d542bf923489855b9d026eef0f2e82b2fee Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 20:56:30 -0700 Subject: [PATCH 026/111] Update configuration docs --- docs/internals/configuration.md | 39 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/docs/internals/configuration.md b/docs/internals/configuration.md index b70493dc1..37048f3d0 100644 --- a/docs/internals/configuration.md +++ b/docs/internals/configuration.md @@ -3,14 +3,15 @@ ### Reading Config Settings If you are writing a package that you want to make configurable, you'll need to -read config settings. You can read a value from `config` with `config.get`: +read config settings via the `atom.config` global. You can read the current +value of a namespaced config key with `atom.config.get`: ```coffeescript # read a value with `config.get` @showInvisibles() if config.get "edtior.showInvisibles" ``` -Or you can use `observeConfig` to track changes from a view object. +Or you can use the `::observeConfig` to track changes from any view object. ```coffeescript class MyView extends View @@ -19,7 +20,7 @@ class MyView extends View @adjustFontSize() ``` -The `observeConfig` method will call the given callback immediately with the +The `::observeConfig` method will call the given callback immediately with the current value for the specified key path, and it will also call it in the future whenever the value of that key path changes. @@ -35,26 +36,40 @@ You can add the ability to observe config values to non-view classes by extending their prototype with the `ConfigObserver` mixin: ```coffeescript -ConfigObserver = require 'config-observer' -_.extend MyClass.prototype, ConfigObserver +{ConfigObserver} = require 'atom' + +class MyClass + ConfigObserver.includeInto(this) + + constructor: -> + @observeConfig 'editor.showInvisibles', -> # ... + + destroy: -> + @unobserveConfig() ``` ### Writing Config Settings -As discussed above, the config database is automatically populated from -`config.cson` when Atom is started, but you can programmatically write to it in -the following way: +The `atom.config` database is populated on startup from `~/.atom/config.cson`, +but you can programmatically write to it with `atom.config.set`: ```coffeescript # basic key update -config.set("core.showInvisibles", true) +atom.config.set("core.showInvisibles", true) +``` -config.pushAtKeyPath("core.disabledPackages", "wrap-guide") +You should never mutate the value of a config key, because that would circumvent +the notification of observers. You can however use methods like `pushAtKeyPath`, +`unshiftAtKeyPath`, and `removeAtKeyPath` to manipulate mutable config values. + +```coffeescript +atom.config.pushAtKeyPath("core.disabledPackages", "wrap-guide") +atom.config.removeAtKeyPath("core.disabledPackages", "terminal") ``` You can also use `setDefaults`, which will assign default values for keys that -are always overridden by values assigned with `set`. Defaults are not written out -to the the `config.json` file to prevent it from becoming cluttered. +are always overridden by values assigned with `set`. Defaults are not written +out to the the `config.json` file to prevent it from becoming cluttered. ```coffeescript config.setDefaults("editor", fontSize: 18, showInvisibles: true) From 81987efed758e6bd40b6140fe80f3a9aaf28d94e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 21:00:00 -0700 Subject: [PATCH 027/111] Fix typo --- docs/internals/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/configuration.md b/docs/internals/configuration.md index 37048f3d0..81766b8e4 100644 --- a/docs/internals/configuration.md +++ b/docs/internals/configuration.md @@ -8,7 +8,7 @@ value of a namespaced config key with `atom.config.get`: ```coffeescript # read a value with `config.get` -@showInvisibles() if config.get "edtior.showInvisibles" +@showInvisibles() if config.get "editor.showInvisibles" ``` Or you can use the `::observeConfig` to track changes from any view object. From 042684212ef19fb230c8b0f4815a794a68e35d61 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 21:00:54 -0700 Subject: [PATCH 028/111] Use `atom.config` instead of config --- docs/internals/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/internals/configuration.md b/docs/internals/configuration.md index 81766b8e4..606432804 100644 --- a/docs/internals/configuration.md +++ b/docs/internals/configuration.md @@ -8,7 +8,7 @@ value of a namespaced config key with `atom.config.get`: ```coffeescript # read a value with `config.get` -@showInvisibles() if config.get "editor.showInvisibles" +@showInvisibles() if atom.config.get "editor.showInvisibles" ``` Or you can use the `::observeConfig` to track changes from any view object. @@ -72,5 +72,5 @@ are always overridden by values assigned with `set`. Defaults are not written out to the the `config.json` file to prevent it from becoming cluttered. ```coffeescript -config.setDefaults("editor", fontSize: 18, showInvisibles: true) +atom.config.setDefaults("editor", fontSize: 18, showInvisibles: true) ``` From c3d5f713ca83f20ef6ef6b3e0702f9916616df50 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 21:02:25 -0700 Subject: [PATCH 029/111] Rename internals directory to advanced --- docs/{internals => advanced}/configuration.md | 0 docs/{internals => advanced}/keymaps.md | 0 docs/{internals => advanced}/serialization.md | 0 docs/{internals => advanced}/view-system.md | 0 docs/creating-a-package.md | 2 +- docs/index.md | 8 ++++---- vendor/apm | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename docs/{internals => advanced}/configuration.md (100%) rename docs/{internals => advanced}/keymaps.md (100%) rename docs/{internals => advanced}/serialization.md (100%) rename docs/{internals => advanced}/view-system.md (100%) diff --git a/docs/internals/configuration.md b/docs/advanced/configuration.md similarity index 100% rename from docs/internals/configuration.md rename to docs/advanced/configuration.md diff --git a/docs/internals/keymaps.md b/docs/advanced/keymaps.md similarity index 100% rename from docs/internals/keymaps.md rename to docs/advanced/keymaps.md diff --git a/docs/internals/serialization.md b/docs/advanced/serialization.md similarity index 100% rename from docs/internals/serialization.md rename to docs/advanced/serialization.md diff --git a/docs/internals/view-system.md b/docs/advanced/view-system.md similarity index 100% rename from docs/internals/view-system.md rename to docs/advanced/view-system.md diff --git a/docs/creating-a-package.md b/docs/creating-a-package.md index 92fb8a371..183a5d28a 100644 --- a/docs/creating-a-package.md +++ b/docs/creating-a-package.md @@ -381,7 +381,7 @@ Additional libraries can be found by browsing Atom's *node_modules* folder. [apm]: https://github.com/atom/apm [git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging [wrap-guide]: https://github.com/atom/wrap-guide/ -[keymaps]: internals/keymaps.md +[keymaps]: advanced/keymaps.md [theme-variables]: theme-variables.md [tm-tokens]: http://manual.macromates.com/en/language_grammars.html [spacepen]: https://github.com/nathansobo/space-pen diff --git a/docs/index.md b/docs/index.md index a27dc1d9d..001836228 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ ### Advanced Topics -* [Configuration](internals/configuration.md) -* [Keymaps](internals/keymaps.md) -* [Serialization](internals/serialization.md) -* [View System](internals/view-system.md) +* [Configuration](advanced/configuration.md) +* [Keymaps](advanced/keymaps.md) +* [Serialization](advanced/serialization.md) +* [View System](advanced/view-system.md) diff --git a/vendor/apm b/vendor/apm index 50d23f5f4..3f8701bfe 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 50d23f5f419230cf1833e7bb8ee7bf0d7289ab1d +Subproject commit 3f8701bfe624de844641863391c04def9cca5c86 From d13d2d3112ac443929ac84be41bb5a0eaba2bca0 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jan 2014 14:36:34 +0800 Subject: [PATCH 030/111] Generate source map in coffee-cache. --- src/coffee-cache.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coffee-cache.coffee b/src/coffee-cache.coffee index 263cd924c..9653b735d 100644 --- a/src/coffee-cache.coffee +++ b/src/coffee-cache.coffee @@ -22,7 +22,10 @@ getCachedJavaScript = (cachePath) -> fs.readFileSync(cachePath, 'utf8') if stat.isFile() compileCoffeeScript = (coffee, filePath, cachePath) -> - js = CoffeeScript.compile(coffee, filename: filePath) + {js,v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true) + # Include source map in the web page environment. + if btoa? and JSON? and unescape? and encodeURIComponent? + js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{filePath}" try mkdir(path.dirname(cachePath)) fs.writeFileSync(cachePath, js) From ced02faf0fd83391ada9b7765f36d37f18823727 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 18 Jan 2014 18:45:21 +0800 Subject: [PATCH 031/111] Updater to terminal@0.27.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89324d063..fff674aec 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "styleguide": "0.21.0", "symbols-view": "0.29.0", "tabs": "0.18.0", - "terminal": "0.26.0", + "terminal": "0.27.0", "timecop": "0.13.0", "to-the-hubs": "0.17.0", "tree-view": "0.65.0", From cf4e08cdc08e772049acc4c82a98a55e2505688b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 18 Jan 2014 20:56:10 +0800 Subject: [PATCH 032/111] Fix executing grunt under Windows. --- script/grunt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/grunt b/script/grunt index 3f8516512..1523b2fed 100755 --- a/script/grunt +++ b/script/grunt @@ -2,8 +2,8 @@ var cp = require('./utils/child-process-wrapper.js'); var path = require('path'); -// node build/node_modules/grunt-cli/bin/grunt "$@" -var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', 'grunt-cli', 'bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : ''); -var args = [gruntPath, '--gruntfile', path.resolve('build', 'Gruntfile.coffee')]; +// node build/node_modules/.bin/grunt "$@" +var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : ''); +var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')]; args = args.concat(process.argv.slice(2)); -cp.safeSpawn(process.execPath, args, process.exit); +cp.safeSpawn(gruntPath, args, process.exit); From 325482353ea9b97b1158d620118d71d8dab1461e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 20 Jan 2014 21:17:38 +0800 Subject: [PATCH 033/111] Update grunt-download-atom-shell to v0.6.0. --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 320798152..3d6c6c81c 100644 --- a/build/package.json +++ b/build/package.json @@ -19,7 +19,7 @@ "grunt-contrib-coffee": "~0.7.0", "grunt-contrib-less": "~0.8.0", "grunt-cson": "0.5.0", - "grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.5.0", + "grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.6.0", "grunt-lesslint": "0.13.0", "grunt-markdown": "~0.4.0", "grunt-peg": "~1.1.0", From 3aa67bff8be0158279e593d6b5908c6b21e6e3b4 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 20 Jan 2014 21:50:16 +0800 Subject: [PATCH 034/111] Update to nslog@0.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fff674aec..d1929b8e9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "keytar": "0.15.1", "less-cache": "0.11.0", "mixto": "1.x", - "nslog": "0.3.0", + "nslog": "0.4.0", "oniguruma": "1.x", "optimist": "0.4.0", "pathwatcher": "0.14.2", From 4d74a69277c9e4dfcae8fcf0f2ea981ed1fc57aa Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 20 Jan 2014 21:50:37 +0800 Subject: [PATCH 035/111] Use nslog on Windows. --- src/browser/main.coffee | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index cd3c144f5..bb24a5efa 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -7,16 +7,14 @@ fs = require 'fs' module = require 'module' path = require 'path' optimist = require 'optimist' -# TODO: NSLog is missing .lib on windows -nslog = require 'nslog' unless process.platform is 'win32' +nslog = require 'nslog' dialog = require 'dialog' console.log = (args...) -> # TODO: Make NSLog work as expected output = args.map((arg) -> JSON.stringify(arg)).join(" ") - if process.platform == 'darwin' - nslog(output) - else + nslog(output) + if process.platform isnt 'darwin' fs.writeFileSync('debug.log', output, flag: 'a') process.on 'uncaughtException', (error={}) -> From 1fa2099eba03583dde069b988e9bdd01500b3399 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:05:53 -0800 Subject: [PATCH 036/111] Don't mark text-utils methods as public --- src/text-utils.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index 4577ae321..a043d7c73 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -1,13 +1,9 @@ -### Internal ### - isHighSurrogate = (string, index) -> 0xD800 <= string.charCodeAt(index) <= 0xDBFF isLowSurrogate = (string, index) -> 0xDC00 <= string.charCodeAt(index) <= 0xDFFF -### Public ### - # Is the character at the given index the start of a high/low surrogate pair? # # string - The {String} to check for a surrogate pair. From b9d45680c38641a9535b736a31b5028f1bd064d6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:09:08 -0800 Subject: [PATCH 037/111] :memo: Wrap types in curlies --- src/window.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/window.coffee b/src/window.coffee index d9767fe3d..891515ed9 100644 --- a/src/window.coffee +++ b/src/window.coffee @@ -1,9 +1,9 @@ # Public: Measure how long a function takes to run. # # * description: -# A String description that will be logged to the console. +# A {String} description that will be logged to the console. # * fn: -# A Function to measure the duration of. +# A {Function} to measure the duration of. # # Returns the value returned by the given function. window.measure = (description, fn) -> @@ -16,10 +16,10 @@ window.measure = (description, fn) -> # Public: Create a dev tools profile for a function. # # * description: -# A String descrption that will be available in the Profiles tab of the dev +# A {String} descrption that will be available in the Profiles tab of the dev # tools. # * fn: -# A Function to profile. +# A {Function} to profile. # # Return the value returned by the given function. window.profile = (description, fn) -> From 0875e00f9c085652ebdea550bc08cb767603b27b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:17:04 -0800 Subject: [PATCH 038/111] Upgrade to atom/biscotto@12188bfbe5 --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 320798152..2f0e1db64 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "async": "~0.2.9", - "biscotto": "git://github.com/atom/biscotto.git#4a0c9cb9d7eb818508da357b37f947d47f1c7892", + "biscotto": "git://github.com/atom/biscotto.git#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", From 4d4ff84047655f4a10a0438b6a7c60a4b99df1b6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:19:54 -0800 Subject: [PATCH 039/111] :memo: Doc how to require WorkspaceView in package specs --- src/workspace-view.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 3bf9cb2ba..40201b1a2 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -37,6 +37,11 @@ Editor = require './editor' # * `application:bring-all-windows-to-front` - Brings all {AtomWindow}s to the # the front. # +# ## Requiring in package specs +# +# ```coffee +# {WorkspaceView} = require 'atom' +# ``` module.exports = class WorkspaceView extends View Delegator.includeInto(this) From b31089cbb99250e830b8f936b1e93571f700d93a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:21:37 -0800 Subject: [PATCH 040/111] :memo: Mention atom.project global in project docs --- src/project.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index 30c3711ba..62f5f0dbe 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -16,8 +16,7 @@ Git = require './git' # Public: Represents a project that's opened in Atom. # -# Ultimately, a project is a git directory that's been opened. It's a collection -# of directories and files that you can operate on. +# There is always a project available under the `atom.project` global. module.exports = class Project extends Model atom.deserializers.add(this) From cc53b6fbef856803c35f7dbdbe6bcdba98d265e9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 10:30:25 -0800 Subject: [PATCH 041/111] :memo: Doc Syntax class --- src/syntax.coffee | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/syntax.coffee b/src/syntax.coffee index ac5edb3e0..af1a6278a 100644 --- a/src/syntax.coffee +++ b/src/syntax.coffee @@ -6,7 +6,12 @@ _ = require 'underscore-plus' {$, $$} = require './space-pen-extensions' Token = require './token' -### Public ### +# Public: Syntax class holding the grammars used for tokenizing. +# +# The Syntax class also contains properties for things such as the +# language-specific comment regexes. +# +# There is always a syntax object available under the `atom.syntax` global. module.exports = class Syntax extends GrammarRegistry Subscriber.includeInto(this) @@ -49,6 +54,18 @@ class Syntax extends GrammarRegistry @scopedProperties = [] @scopedPropertiesIndex = 0 + # Public: Get a property for the given scope and key path. + # + # ## Example + # ```coffee + # comment = atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart') + # console.log(comment) # '# ' + # ``` + # + # * scope: An {Array} of {String} scopes. + # * keyPath: A {String} key path. + # + # Returns a {String} property value or undefined. getProperty: (scope, keyPath) -> for object in @propertiesForScope(scope, keyPath) value = _.valueForKeyPath(object, keyPath) From a42a4dd3529a3e3101a896016afb7b53d2c034c3 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 10:13:56 -0800 Subject: [PATCH 042/111] Escape from a tool panel does not emit core:close --- keymaps/base.cson | 3 --- 1 file changed, 3 deletions(-) diff --git a/keymaps/base.cson b/keymaps/base.cson index 980a42969..4fe2e868c 100644 --- a/keymaps/base.cson +++ b/keymaps/base.cson @@ -24,9 +24,6 @@ 'ctrl-shift-up': 'editor:add-selection-above' 'ctrl-shift-down': 'editor:add-selection-below' -'.tool-panel': - 'escape': 'core:close' - '.tool-panel.panel-left, .tool-panel.panel-right': 'escape': 'tool-panel:unfocus' From eb28c15f69c2ddcece1c03f70d66926076a28193 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 13:35:07 -0800 Subject: [PATCH 043/111] Simplify installation callbacks --- spec/command-installer-spec.coffee | 28 +++++++++--------- src/command-installer.coffee | 47 ++++++++++-------------------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee index 679a9b5df..bf40996b5 100644 --- a/spec/command-installer-spec.coffee +++ b/spec/command-installer-spec.coffee @@ -4,31 +4,31 @@ temp = require 'temp' installer = require '../src/command-installer' describe "install(commandPath, callback)", -> - directory = path.join(temp.dir, 'install-atom-command', 'atom') - commandPath = path.join(directory, 'source') - destinationPath = path.join(directory, 'bin', 'source') + commandFilePath = temp.openSync("atom-command").path + commandName = path.basename(commandFilePath) + instalationPath = temp.mkdirSync("atom-bin") + instalationFilePath = path.join(instalationPath, commandName) beforeEach -> - spyOn(installer, 'getInstallDirectory').andReturn directory - fs.removeSync(directory) if fs.existsSync(directory) + spyOn(installer, 'getInstallDirectory').andReturn instalationPath describe "on #darwin", -> it "symlinks the command and makes it executable", -> - fs.writeFileSync(commandPath, 'test') - expect(fs.isFileSync(commandPath)).toBeTruthy() - expect(fs.isExecutableSync(commandPath)).toBeFalsy() - expect(fs.isFileSync(destinationPath)).toBeFalsy() + expect(fs.isFileSync(commandFilePath)).toBeTruthy() + expect(fs.isExecutableSync(commandFilePath)).toBeFalsy() + expect(fs.isFileSync(instalationFilePath)).toBeFalsy() installDone = false installError = null - installer.install commandPath, (error) -> + installer.install commandFilePath, (error) -> installDone = true installError = error - waitsFor -> installDone + waitsFor -> + installDone runs -> expect(installError).toBeNull() - expect(fs.isFileSync(destinationPath)).toBeTruthy() - expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath) - expect(fs.isExecutableSync(destinationPath)).toBeTruthy() + expect(fs.isFileSync(instalationFilePath)).toBeTruthy() + expect(fs.realpathSync(instalationFilePath)).toBe fs.realpathSync(commandFilePath) + expect(fs.isExecutableSync(instalationFilePath)).toBeTruthy() diff --git a/src/command-installer.coffee b/src/command-installer.coffee index a41b8cc15..e085dd2f2 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -24,44 +24,29 @@ unlinkCommand = (destinationPath, callback) -> module.exports = getInstallDirectory: -> - "/usr/local" + "/usr/local/bin" - install: (commandPath, commandName, callback) -> - if not commandName? or _.isFunction(commandName) - callback = commandName - commandName = path.basename(commandPath, path.extname(commandPath)) - - installCallback = (error, sourcePath, destinationPath) -> - if error? - console.warn "Failed to install `#{commandName}` binary", error - callback?(error, sourcePath, destinationPath) + install: (commandPath, callback) -> + commandName = path.basename(commandPath, path.extname(commandPath)) directory = @getInstallDirectory() if fs.existsSync(directory) - destinationPath = path.join(directory, 'bin', commandName) + destinationPath = path.join(directory, commandName) unlinkCommand destinationPath, (error) -> - if error? - installCallback(error) - else - symlinkCommand commandPath, destinationPath, (error) -> - installCallback(error, commandPath, destinationPath) + return callback(error) if error? + symlinkCommand commandPath, destinationPath, (error) -> callback(error) else - installCallback(new Error("No destination directory exists to install")) + error = new Error("No destination directory exists to install") + callback(error) - installAtomCommand: (resourcePath, callback) -> - if _.isFunction(resourcePath) - callback = resourcePath - resourcePath = null - - resourcePath ?= atom.getLoadSettings().resourcePath + installAtomCommand: -> + resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'atom.sh') - @install(commandPath, callback) + @install commandPath, (error) -> + console.warn "Failed to install `#{commandPath}` binary", error if error? - installApmCommand: (resourcePath, callback) -> - if _.isFunction(resourcePath) - callback = resourcePath - resourcePath = null - - resourcePath ?= atom.getLoadSettings().resourcePath + installApmCommand: -> + resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') - @install(commandPath, callback) + @install commandPath, (error) -> + console.warn "Failed to install `#{commandPath}` binary", error if error? From 8973a6d777d368b54dfaf6a0bf46ad0189d3bcce Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 13:51:51 -0800 Subject: [PATCH 044/111] Display dialog error when commands can't be installed --- src/command-installer.coffee | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/command-installer.coffee b/src/command-installer.coffee index e085dd2f2..4617c6d3c 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -26,27 +26,33 @@ module.exports = getInstallDirectory: -> "/usr/local/bin" - install: (commandPath, callback) -> + install: (commandPath) -> commandName = path.basename(commandPath, path.extname(commandPath)) - directory = @getInstallDirectory() if fs.existsSync(directory) destinationPath = path.join(directory, commandName) - unlinkCommand destinationPath, (error) -> - return callback(error) if error? - symlinkCommand commandPath, destinationPath, (error) -> callback(error) + unlinkCommand destinationPath, (error) => + if error? + @displayError(error, commandPath) + else + symlinkCommand commandPath, destinationPath, (error) => + @displayError(error, commandPath) if error else error = new Error("No destination directory exists to install") - callback(error) + @displayError(error, commandPath) if error + + displayError: (error, commandPath) -> + atom.confirm + type: 'warning' + message: "Failed to install `#{commandPath}` binary" + detailedMessage: "#{error.message}\n\nRun 'sudo ln -s #{commandPath} #{path.join(@getInstallDirectory(), path.basename(commandPath))}' to install." installAtomCommand: -> resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'atom.sh') - @install commandPath, (error) -> - console.warn "Failed to install `#{commandPath}` binary", error if error? + @install commandPath installApmCommand: -> resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') - @install commandPath, (error) -> - console.warn "Failed to install `#{commandPath}` binary", error if error? + @install commandPath From cd41637df09cb290b8fe8fe43aef2ce566cecc1f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 14:15:19 -0800 Subject: [PATCH 045/111] Upgrade to git-utils@0.34.0 for symlink fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89324d063..29c61b018 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "fs-plus": "1.x", "fstream": "0.1.24", "fuzzaldrin": "0.6.0", - "git-utils": "0.33.1", + "git-utils": "0.34.0", "guid": "0.0.10", "jasmine-tagged": "0.3.0", "mkdirp": "0.3.5", From 19e0854036813210d00b0e55e261f34f875e0eb9 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 14:46:39 -0800 Subject: [PATCH 046/111] Log warning if commands fail to install --- src/atom.coffee | 6 +++--- src/command-installer.coffee | 26 +++++++++++--------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index a35523c17..e4e2e149f 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -250,9 +250,9 @@ class Atom extends Model # Private: Call this method when establishing a real application window. startEditorWindow: -> if process.platform is 'darwin' - CommandInstaller = require './command-installer' - CommandInstaller.installAtomCommand() - CommandInstaller.installApmCommand() + CommandInstaller = require './command-installer' + CommandInstaller.installAtomCommand (error) -> console.warn error.message if error? + CommandInstaller.installApmCommand (error) -> console.warn error.message if error? @restoreWindowDimensions() @config.load() diff --git a/src/command-installer.coffee b/src/command-installer.coffee index 4617c6d3c..2f97c7ef2 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -26,33 +26,29 @@ module.exports = getInstallDirectory: -> "/usr/local/bin" - install: (commandPath) -> + install: (commandPath, callback) -> commandName = path.basename(commandPath, path.extname(commandPath)) directory = @getInstallDirectory() if fs.existsSync(directory) destinationPath = path.join(directory, commandName) unlinkCommand destinationPath, (error) => if error? - @displayError(error, commandPath) + error = new Error "Could not remove file at #{destinationPath}." if error + callback?(error) else symlinkCommand commandPath, destinationPath, (error) => - @displayError(error, commandPath) if error + error = new Error "Failed to symlink #{commandPath} to #{destinationPath}." if error + callback?(error) else - error = new Error("No destination directory exists to install") - @displayError(error, commandPath) if error + error = new Error "Directory '#{directory} doesn't exist." + callback?(error) - displayError: (error, commandPath) -> - atom.confirm - type: 'warning' - message: "Failed to install `#{commandPath}` binary" - detailedMessage: "#{error.message}\n\nRun 'sudo ln -s #{commandPath} #{path.join(@getInstallDirectory(), path.basename(commandPath))}' to install." - - installAtomCommand: -> + installAtomCommand: (callback) -> resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'atom.sh') - @install commandPath + @install commandPath, callback - installApmCommand: -> + installApmCommand: (callback) -> resourcePath = atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') - @install commandPath + @install commandPath, callback From 9169e9e22796bb8b7aaeddf1ec5313aaffbec34a Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 14:54:01 -0800 Subject: [PATCH 047/111] Verify platform inside command installer --- src/atom.coffee | 1 - src/command-installer.coffee | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/atom.coffee b/src/atom.coffee index e4e2e149f..b802ba7b0 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -249,7 +249,6 @@ class Atom extends Model # Private: Call this method when establishing a real application window. startEditorWindow: -> - if process.platform is 'darwin' CommandInstaller = require './command-installer' CommandInstaller.installAtomCommand (error) -> console.warn error.message if error? CommandInstaller.installApmCommand (error) -> console.warn error.message if error? diff --git a/src/command-installer.coffee b/src/command-installer.coffee index 2f97c7ef2..f60a606d2 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -27,6 +27,8 @@ module.exports = "/usr/local/bin" install: (commandPath, callback) -> + return unless process.platform is 'darwin' + commandName = path.basename(commandPath, path.extname(commandPath)) directory = @getInstallDirectory() if fs.existsSync(directory) From 52dcf6a721e03d6cdb501664e5922a1af6972758 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 22 Jan 2014 16:06:46 -0700 Subject: [PATCH 048/111] Guard against null line number elements when updating fold indicators Fixes #1461 but doesn't really address the root cause. --- src/gutter.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gutter.coffee b/src/gutter.coffee index ff7926954..c66191cc0 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -234,11 +234,11 @@ class Gutter extends View lastBufferRow = null for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow lastBufferRow = bufferRow - lineNumberElement = @getLineNumberElement(bufferRow)[0] - if editor.isFoldableAtBufferRow(bufferRow) - lineNumberElement.classList.add('foldable') - else - lineNumberElement.classList.remove('foldable') + if lineNumberElement = @getLineNumberElement(bufferRow)[0] + if editor.isFoldableAtBufferRow(bufferRow) + lineNumberElement.classList.add('foldable') + else + lineNumberElement.classList.remove('foldable') removeLineHighlights: -> return unless @highlightedLineNumbers From 4ef6eae02f3b2bc36c371f410ff7ef3c85cb1d58 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:17:23 -0800 Subject: [PATCH 049/111] Add install cli commands for apm and atom --- src/workspace-view.coffee | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 40201b1a2..bc2d5c048 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -6,6 +6,7 @@ Delegator = require 'delegato' {$, $$, View} = require './space-pen-extensions' fs = require 'fs-plus' Workspace = require './workspace' +CommandInstaller = require './command-installer' EditorView = require './editor-view' PaneView = require './pane-view' PaneColumnView = require './pane-column-view' @@ -108,6 +109,12 @@ class WorkspaceView extends View @command 'application:open-your-keymap', -> ipc.sendChannel('command', 'application:open-your-keymap') @command 'application:open-your-stylesheet', -> ipc.sendChannel('command', 'application:open-your-stylesheet') + @command 'window:install-atom-cli', => + CommandInstaller.installApmCommand (error) => @commandInstalationDialog(error, 'atom') + + @command 'window:install-apm-cli', => + CommandInstaller.installApmCommand (error) => @commandInstalationDialog(error, 'apm') + @command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec')) @command 'window:increase-font-size', => atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1) @@ -131,6 +138,17 @@ class WorkspaceView extends View @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() + commandInstalationDialog: (error, commandName) -> + if error? + installDirectory = CommandInstaller.getInstallDirectory() + atom.confirm + message: error.message + detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} is writable. Run 'sudo chmod o+w #{installDirectory}' to fix this problem." + else + atom.confirm + message: "Command installed." + detailedMessage: "You can now use `#{commandName}` from the terminal." + # Private: handleFocus: (e) -> if @getActivePane() From 04c8549c38d5143057e01ca68e8346f51d83b89d Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:30:53 -0800 Subject: [PATCH 050/111] Use a single command to install apm and atom --- src/workspace-view.coffee | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index bc2d5c048..a966a0bf4 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -109,11 +109,7 @@ class WorkspaceView extends View @command 'application:open-your-keymap', -> ipc.sendChannel('command', 'application:open-your-keymap') @command 'application:open-your-stylesheet', -> ipc.sendChannel('command', 'application:open-your-stylesheet') - @command 'window:install-atom-cli', => - CommandInstaller.installApmCommand (error) => @commandInstalationDialog(error, 'atom') - - @command 'window:install-apm-cli', => - CommandInstaller.installApmCommand (error) => @commandInstalationDialog(error, 'apm') + @command 'window:install-shell-commands', => @installShellCommands() @command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec')) @command 'window:increase-font-size', => @@ -138,16 +134,22 @@ class WorkspaceView extends View @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() - commandInstalationDialog: (error, commandName) -> - if error? - installDirectory = CommandInstaller.getInstallDirectory() - atom.confirm - message: error.message - detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} is writable. Run 'sudo chmod o+w #{installDirectory}' to fix this problem." - else - atom.confirm - message: "Command installed." - detailedMessage: "You can now use `#{commandName}` from the terminal." + installShellCommands: -> + showDialog = (error, commandName)-> + if error? + installDirectory = CommandInstaller.getInstallDirectory() + atom.confirm + message: error.message + detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} is writable. Run 'sudo chmod o+w #{installDirectory}' to fix this problem." + else + atom.confirm + message: "Command installed." + detailedMessage: "The shell command `#{commandName}` is installed." + + CommandInstaller.installApmCommand (error) => + showDialog(error, 'atom') + unless error? + CommandInstaller.installApmCommand (error) => showDialog(error, 'apm') # Private: handleFocus: (e) -> From 40048da3149923b8be00bdc3e200b61a6f7d5e43 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:33:32 -0800 Subject: [PATCH 051/111] Add 'Install Shell Commands' menu item. --- menus/darwin.cson | 2 ++ 1 file changed, 2 insertions(+) diff --git a/menus/darwin.cson b/menus/darwin.cson index de22e7673..24490bfa3 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -11,6 +11,8 @@ { label: 'Open Your Keymap', command: 'application:open-your-keymap' } { label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' } { type: 'separator' } + { label: 'Install Shell Commands', command: 'window:install-shell-commands' } + { type: 'separator' } { label: 'Hide Atom', command: 'application:hide' } { label: 'Hide Others', command: 'application:hide-other-applications' } { label: 'Show All', command: 'application:unhide-all-applications' } From 3da7fe73003d2dcca579bd679ccec2d00e2eab7f Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:42:08 -0800 Subject: [PATCH 052/111] Update error message --- src/workspace-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index a966a0bf4..ea6ea3482 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -140,7 +140,7 @@ class WorkspaceView extends View installDirectory = CommandInstaller.getInstallDirectory() atom.confirm message: error.message - detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} is writable. Run 'sudo chmod o+w #{installDirectory}' to fix this problem." + detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} exists and is writable. Run 'sudo mkdir -p #{installDirectory} && sudo chown $USER #{installDirectory}' to fix this problem." else atom.confirm message: "Command installed." From 399c4f9f950bc9d1966dfa2634677f1f31d6fff0 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:54:49 -0800 Subject: [PATCH 053/111] Fix spelling error --- spec/command-installer-spec.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee index bf40996b5..6bd66b4d3 100644 --- a/spec/command-installer-spec.coffee +++ b/spec/command-installer-spec.coffee @@ -6,17 +6,17 @@ installer = require '../src/command-installer' describe "install(commandPath, callback)", -> commandFilePath = temp.openSync("atom-command").path commandName = path.basename(commandFilePath) - instalationPath = temp.mkdirSync("atom-bin") - instalationFilePath = path.join(instalationPath, commandName) + installationPath = temp.mkdirSync("atom-bin") + installationFilePath = path.join(installationPath, commandName) beforeEach -> - spyOn(installer, 'getInstallDirectory').andReturn instalationPath + spyOn(installer, 'getInstallDirectory').andReturn installationPath describe "on #darwin", -> it "symlinks the command and makes it executable", -> expect(fs.isFileSync(commandFilePath)).toBeTruthy() expect(fs.isExecutableSync(commandFilePath)).toBeFalsy() - expect(fs.isFileSync(instalationFilePath)).toBeFalsy() + expect(fs.isFileSync(installationFilePath)).toBeFalsy() installDone = false installError = null @@ -29,6 +29,6 @@ describe "install(commandPath, callback)", -> runs -> expect(installError).toBeNull() - expect(fs.isFileSync(instalationFilePath)).toBeTruthy() - expect(fs.realpathSync(instalationFilePath)).toBe fs.realpathSync(commandFilePath) - expect(fs.isExecutableSync(instalationFilePath)).toBeTruthy() + expect(fs.isFileSync(installationFilePath)).toBeTruthy() + expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath) + expect(fs.isExecutableSync(installationFilePath)).toBeTruthy() From d2e4c61e0b851b0913714898657485307555bc69 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:56:05 -0800 Subject: [PATCH 054/111] Make cli installation work with install-cli script --- script/install-cli | 5 ++--- src/atom.coffee | 7 +++++-- src/command-installer.coffee | 6 ++---- src/workspace-view.coffee | 6 ++++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/script/install-cli b/script/install-cli index b1ade33db..c24835852 100755 --- a/script/install-cli +++ b/script/install-cli @@ -3,9 +3,8 @@ path = require 'path' CommandInstaller = require '../src/command-installer' -callback = (error, sourcePath, destinationPath) -> - unless error? - console.log "#{sourcePath} intalled to #{destinationPath}" +callback = (error) -> + console.warn error.message if error? CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback) CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback) diff --git a/src/atom.coffee b/src/atom.coffee index b802ba7b0..3cc30c31e 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -250,8 +250,11 @@ class Atom extends Model # Private: Call this method when establishing a real application window. startEditorWindow: -> CommandInstaller = require './command-installer' - CommandInstaller.installAtomCommand (error) -> console.warn error.message if error? - CommandInstaller.installApmCommand (error) -> console.warn error.message if error? + resourcePath = atom.getLoadSettings().resourcePath + CommandInstaller.installAtomCommand resourcePath, (error) -> + console.warn error.message if error? + CommandInstaller.installApmCommand resourcePath, (error) -> + console.warn error.message if error? @restoreWindowDimensions() @config.load() diff --git a/src/command-installer.coffee b/src/command-installer.coffee index f60a606d2..a0374bf99 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -45,12 +45,10 @@ module.exports = error = new Error "Directory '#{directory} doesn't exist." callback?(error) - installAtomCommand: (callback) -> - resourcePath = atom.getLoadSettings().resourcePath + installAtomCommand: (resourcePath, callback) -> commandPath = path.join(resourcePath, 'atom.sh') @install commandPath, callback - installApmCommand: (callback) -> - resourcePath = atom.getLoadSettings().resourcePath + installApmCommand: (resourcePath, callback) -> commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') @install commandPath, callback diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index ea6ea3482..e43d8af52 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -146,10 +146,12 @@ class WorkspaceView extends View message: "Command installed." detailedMessage: "The shell command `#{commandName}` is installed." - CommandInstaller.installApmCommand (error) => + resourcePath = atom.getLoadSettings().resourcePath + CommandInstaller.installApmCommand resourcePath, (error) => showDialog(error, 'atom') unless error? - CommandInstaller.installApmCommand (error) => showDialog(error, 'apm') + CommandInstaller.installApmCommand resourcePath, (error) => + showDialog(error, 'apm') # Private: handleFocus: (e) -> From ca8609c925a891814a920b43bf9b70c1aeb3336c Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 15:56:18 -0800 Subject: [PATCH 055/111] Fix :bug: --- src/workspace-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index e43d8af52..fa4babeca 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -147,7 +147,7 @@ class WorkspaceView extends View detailedMessage: "The shell command `#{commandName}` is installed." resourcePath = atom.getLoadSettings().resourcePath - CommandInstaller.installApmCommand resourcePath, (error) => + CommandInstaller.installAtomCommand resourcePath, (error) => showDialog(error, 'atom') unless error? CommandInstaller.installApmCommand resourcePath, (error) => From ac2985af36340636d92390987f9f5988c262a69e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 15:56:12 -0800 Subject: [PATCH 056/111] Call getSelectedText() on Editor --- src/editor-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 7cd699046..59b095a1e 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -411,7 +411,7 @@ class EditorView extends View selectedText = null @hiddenInput.on 'compositionstart', => - selectedText = @getSelectedText() + selectedText = @editor.getSelectedText() @hiddenInput.css('width', '100%') @hiddenInput.on 'compositionupdate', (e) => @editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'}) From 020326ffad6bf8dffe52e76591ab2d58d62f5e02 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 22 Jan 2014 16:01:52 -0800 Subject: [PATCH 057/111] Only display one dialog on successful installation. --- src/workspace-view.coffee | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index fa4babeca..fffebd826 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -135,23 +135,24 @@ class WorkspaceView extends View @command 'core:save-as', => @saveActivePaneItemAs() installShellCommands: -> - showDialog = (error, commandName)-> - if error? - installDirectory = CommandInstaller.getInstallDirectory() - atom.confirm - message: error.message - detailedMessage: "Make sure #{CommandInstaller.getInstallDirectory()} exists and is writable. Run 'sudo mkdir -p #{installDirectory} && sudo chown $USER #{installDirectory}' to fix this problem." - else - atom.confirm - message: "Command installed." - detailedMessage: "The shell command `#{commandName}` is installed." + showErrorDialog = (error) -> + installDirectory = CommandInstaller.getInstallDirectory() + atom.confirm + message: error.message + detailedMessage: "Make sure #{installDirectory} exists and is writable. Run 'sudo mkdir -p #{installDirectory} && sudo chown $USER #{installDirectory}' to fix this problem." resourcePath = atom.getLoadSettings().resourcePath CommandInstaller.installAtomCommand resourcePath, (error) => - showDialog(error, 'atom') - unless error? + if error? + showDialog(error) + else CommandInstaller.installApmCommand resourcePath, (error) => - showDialog(error, 'apm') + if error? + showDialog(error) + else + atom.confirm + message: "Commands installed." + detailedMessage: "The shell commands `atom` and `apm` are installed." # Private: handleFocus: (e) -> From f3f6ec424f8b30befe68f62d44f5c76f82f34f77 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 16:11:46 -0800 Subject: [PATCH 058/111] :memo: Mark Pasteboard class as public --- src/pasteboard.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pasteboard.coffee b/src/pasteboard.coffee index 92d808395..2f97333d8 100644 --- a/src/pasteboard.coffee +++ b/src/pasteboard.coffee @@ -1,7 +1,9 @@ clipboard = require 'clipboard' crypto = require 'crypto' -# Internal: Represents the clipboard used for copying and pasting in Atom. +# Public: Represents the clipboard used for copying and pasting in Atom. +# +# A pasteboard instance is always available under the `atom.pasteboard` global. module.exports = class Pasteboard signatureForMetadata: null @@ -14,18 +16,19 @@ class Pasteboard md5: (text) -> crypto.createHash('md5').update(text, 'utf8').digest('hex') - # Saves from the clipboard. + # Public: Write the given text to the clipboard. # - # text - A {String} to store - # metadata - An object of additional info to associate with the text + # text - A {String} to store. + # metadata - An {Object} of additional info to associate with the text. write: (text, metadata) -> @signatureForMetadata = @md5(text) @metadata = metadata clipboard.writeText(text) - # Loads from the clipboard. + # Public: Read the text from the clipboard. # - # Returns an {Array}. The first index is the saved text, and the second is any metadata associated with the text. + # Returns an {Array}. The first element is the saved text and the second is + # any metadata associated with the text. read: -> text = clipboard.readText() value = [text] From a4754b2bd568b5130fdbbd2088ea4c7a01321371 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 16:25:55 -0800 Subject: [PATCH 059/111] :memo: Mark ThemeManager class public --- src/theme-manager.coffee | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index b073a9b45..a8cef87f4 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -8,9 +8,9 @@ fs = require 'fs-plus' AtomPackage = require './atom-package' File = require './file' -# Private: Handles discovering and loading available themes. +# Public: Handles loading and activating available themes. # -# Themes are a subset of packages +# A ThemeManager instance is always available under the `atom.themes` global. module.exports = class ThemeManager Emitter.includeInto(this) @@ -19,7 +19,6 @@ class ThemeManager @lessCache = null @packageManager.registerPackageActivator(this, ['theme']) - # Internal-only: getAvailableNames: -> # TODO: Maybe should change to list all the available themes out there? @getLoadedNames() @@ -27,19 +26,15 @@ class ThemeManager getLoadedNames: -> theme.name for theme in @getLoadedThemes() - # Internal-only: getActiveNames: -> theme.name for theme in @getActiveThemes() - # Internal-only: getActiveThemes: -> pack for pack in @packageManager.getActivePackages() when pack.isTheme() - # Internal-only: getLoadedThemes: -> pack for pack in @packageManager.getLoadedPackages() when pack.isTheme() - # Internal-only: adhere to the PackageActivator interface activatePackages: (themePackages) -> @activateThemes() # Private: Get the enabled theme names from the config. @@ -53,7 +48,6 @@ class ThemeManager # the first/top theme to override later themes in the stack. themeNames.reverse() - # Internal-only: activateThemes: -> # atom.config.observe runs the callback once, then on subsequent changes. atom.config.observe 'core.themes', => @@ -69,13 +63,11 @@ class ThemeManager @emit('reloaded') - # Internal-only: deactivateThemes: -> @unwatchUserStylesheet() @packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes() null - # Internal-only: refreshLessCache: -> @lessCache?.setImportPaths(@getImportPaths()) @@ -85,7 +77,6 @@ class ThemeManager setEnabledThemes: (enabledThemeNames) -> atom.config.set('core.themes', enabledThemeNames) - # Public: getImportPaths: -> activeThemes = @getActiveThemes() if activeThemes.length > 0 @@ -98,7 +89,7 @@ class ThemeManager themePath for themePath in themePaths when fs.isDirectorySync(themePath) - # Public: + # Public: Returns the {String} path to the user's stylesheet under ~/.atom getUserStylesheetPath: -> stylesheetPath = fs.resolve(path.join(@configDirPath, 'user'), ['css', 'less']) if fs.isFileSync(stylesheetPath) @@ -106,13 +97,11 @@ class ThemeManager else path.join(@configDirPath, 'user.less') - #Private: unwatchUserStylesheet: -> @userStylesheetFile?.off() @userStylesheetFile = null @removeStylesheet(@userStylesheetPath) if @userStylesheetPath? - # Private: loadUserStylesheet: -> @unwatchUserStylesheet() userStylesheetPath = @getUserStylesheetPath() @@ -125,34 +114,32 @@ class ThemeManager userStylesheetContents = @loadStylesheet(userStylesheetPath) @applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme') - # Internal-only: loadBaseStylesheets: -> @requireStylesheet('bootstrap/less/bootstrap') @reloadBaseStylesheets() - # Internal-only: reloadBaseStylesheets: -> @requireStylesheet('../static/atom') if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less']) @requireStylesheet(nativeStylesheetPath) - # Internal-only: stylesheetElementForId: (id, htmlElement=$('html')) -> htmlElement.find("""head style[id="#{id}"]""") - # Internal-only: resolveStylesheet: (stylesheetPath) -> if path.extname(stylesheetPath).length > 0 fs.resolveOnLoadPath(stylesheetPath) else fs.resolveOnLoadPath(stylesheetPath, ['css', 'less']) - # Public: resolves and applies the stylesheet specified by the path. + # Public: Resolve and apply the stylesheet specified by the path. # - # * stylesheetPath: String. Can be an absolute path or the name of a CSS or - # LESS file in the stylesheets path. + # This supports both CSS and LESS stylsheets. # - # Returns the absolute path to the stylesheet + # * stylesheetPath: A {String} path to the stylesheet that can be an absolute + # path or a relative path that will be resolved against the load path. + # + # Returns the absolute path to the required stylesheet. requireStylesheet: (stylesheetPath, ttype = 'bundled', htmlElement) -> if fullPath = @resolveStylesheet(stylesheetPath) content = @loadStylesheet(fullPath) @@ -162,14 +149,12 @@ class ThemeManager fullPath - # Internal-only: loadStylesheet: (stylesheetPath) -> if path.extname(stylesheetPath) is '.less' @loadLessStylesheet(stylesheetPath) else fs.readFileSync(stylesheetPath, 'utf8') - # Internal-only: loadLessStylesheet: (lessStylesheetPath) -> unless @lessCache? LessCompileCache = require './less-compile-cache' @@ -184,16 +169,13 @@ class ThemeManager #{e.message} """ - # Internal-only: stringToId: (string) -> string.replace(/\\/g, '/') - # Internal-only: removeStylesheet: (stylesheetPath) -> fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath @stylesheetElementForId(@stringToId(fullPath)).remove() - # Internal-only: applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) -> styleElement = @stylesheetElementForId(@stringToId(path), htmlElement) if styleElement.length From d8c158753407e9b55a2ca753e9b3caf00e8058ac Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 16:31:10 -0800 Subject: [PATCH 060/111] :memo: Mark getters a public --- src/theme-manager.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index a8cef87f4..a1d9caa89 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -23,15 +23,19 @@ class ThemeManager # TODO: Maybe should change to list all the available themes out there? @getLoadedNames() + # Public: Get an array of all the loaded theme names. getLoadedNames: -> theme.name for theme in @getLoadedThemes() + # Public: Get an array of all the active theme names. getActiveNames: -> theme.name for theme in @getActiveThemes() + # Public: Get an array of all the active themes. getActiveThemes: -> pack for pack in @packageManager.getActivePackages() when pack.isTheme() + # Public: Get an array of all the loaded themes. getLoadedThemes: -> pack for pack in @packageManager.getLoadedPackages() when pack.isTheme() From d18a45f37c3e64321ff922452de3c5de0b927081 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 16:38:47 -0800 Subject: [PATCH 061/111] Bump remove timeout to 30 seconds --- spec/file-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/file-spec.coffee b/spec/file-spec.coffee index c2a93ffad..05ab6fb54 100644 --- a/spec/file-spec.coffee +++ b/spec/file-spec.coffee @@ -63,7 +63,7 @@ describe 'File', -> afterEach -> if fs.existsSync(newPath) fs.removeSync(newPath) - waitsFor "remove event", (done) -> file.on 'removed', done + waitsFor "remove event", 30000, (done) -> file.on 'removed', done it "it updates its path", -> jasmine.unspy(window, "setTimeout") From 3dc51365b362d7f6173fe00b4f35e64a9ba24aed Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 17:25:33 -0800 Subject: [PATCH 062/111] Prepare 0.49.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29c61b018..7f8208983 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.48.0", + "version": "0.49.0", "main": "./src/browser/main.js", "repository": { "type": "git", From 00ba7d92d035fb9f226cfecbf6027bb008430e37 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jan 2014 17:47:32 -0800 Subject: [PATCH 063/111] Upgrade to language-coffee-script@0.5.0 for regex fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f8208983..8f53f05db 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "wrap-guide": "0.12.0", "language-c": "0.2.0", "language-clojure": "0.1.0", - "language-coffee-script": "0.4.0", + "language-coffee-script": "0.5.0", "language-css": "0.2.0", "language-gfm": "0.12.0", "language-git": "0.3.0", From c424a39538b30ab17e0191434d637ac7ea8b5468 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jan 2014 21:02:41 +0800 Subject: [PATCH 064/111] Upgrade to atom-shell@v0.8.6. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84210998d..df706934d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "url": "http://github.com/atom/atom/raw/master/LICENSE.md" } ], - "atomShellVersion": "0.8.5", + "atomShellVersion": "0.8.6", "dependencies": { "async": "0.2.6", "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", From 5f0a3061acaa756dbfda0770ebbb46d4d718f646 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jan 2014 21:03:17 +0800 Subject: [PATCH 065/111] Remove the workaround for net.connect delay. --- src/browser/atom-application.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 149e87241..5daea762c 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -39,10 +39,6 @@ class AtomApplication createAtomApplication() return - # The net.connect is slow in atom-shell for now, use this workaround until - # atom/atom-shell#159 is fixed. - process.activateUvLoop() - client = net.connect {path: socketPath}, -> client.write JSON.stringify(options), -> client.end() From 9e381db2656061b7c502b4d0ce73feb55c938746 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jan 2014 15:05:52 -0800 Subject: [PATCH 066/111] Upgrade to to-the-hubs@0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index df706934d..53fa6657a 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "tabs": "0.18.0", "terminal": "0.27.0", "timecop": "0.13.0", - "to-the-hubs": "0.17.0", + "to-the-hubs": "0.18.0", "tree-view": "0.65.0", "visual-bell": "0.6.0", "welcome": "0.4.0", From 8e6aa1ee9a7ee1e8c00968396f0eeb5b043b6933 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jan 2014 15:13:12 -0800 Subject: [PATCH 067/111] Doc available classes from --- docs/README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/README.md b/docs/README.md index 280d1a3b5..41be7406f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,6 +20,20 @@ You can also require many of these classes in your package via: {EditorView} = require 'atom' ``` +The classes available from `require 'atom'` are: + * [BufferedProcess][BufferedProcess] + * [BufferedNodeProcess][BufferedNodeProcess] + * [Directory][Directory] + * [EditorView][EditorView] + * [File][File] + * [Git][Git] + * [Point][Point] + * [Range][Range] + * [ScrollView][ScrollView] + * [SelectList][SelectList] + * [View][View] + * [WorkspaceView][WorkspaceView] + ### How do I create a package? You probably want to read the [creating a package][creating-a-package] @@ -31,7 +45,18 @@ Atom ships with node 0.11.10 and the comprehensive node API docs are available [here][node-docs]. [Atom]: ../classes/Atom.html +[BufferedProcess]: ../classes/BufferedProcess.html +[BufferedNodeProcess]: ../classes/BufferedNodeProcess.html +[Directory]: ../classes/Directory.html [Editor]: ../classes/Editor.html [EditorView]: ../classes/EditorView.html +[File]: ../classes/File.html +[Git]: ../classes/Git.html +[Point]: ../classes/Point.html +[Range]: ../classes/Range.html +[ScrollView]: ../classes/ScrollView.html +[SelectList]: ../classes/SelectList.html +[View]: ../classes/View.html +[WorkspaceView]: ../classes/WorkspaceView.html [creating-a-package]: https://www.atom.io/docs/latest/creating-a-package [node-docs]: http://nodejs.org/docs/v0.11.10/api From ee24b373c0062b4b0276f8d0ab96e285f7e66e36 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 23 Jan 2014 15:32:44 -0800 Subject: [PATCH 068/111] Update metrics package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53fa6657a..392859559 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "keybinding-resolver": "0.9.0", "link": "0.15.0", "markdown-preview": "0.25.1", - "metrics": "0.21.0", + "metrics": "0.23.0", "package-generator": "0.24.0", "release-notes": "0.17.0", "settings-view": "0.57.0", From 0d34df2238b06d4084c5f36dc0488699ec9769a7 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Thu, 23 Jan 2014 15:51:42 -0800 Subject: [PATCH 069/111] Update metrics package to 0.24.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 392859559..f67be14fe 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "keybinding-resolver": "0.9.0", "link": "0.15.0", "markdown-preview": "0.25.1", - "metrics": "0.23.0", + "metrics": "0.24.0", "package-generator": "0.24.0", "release-notes": "0.17.0", "settings-view": "0.57.0", From c5d7614d8017e3d9ee02061cb19cd8372c29c59c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jan 2014 19:22:13 +0800 Subject: [PATCH 070/111] Use named pipe instead of socket file on Windows. --- src/browser/atom-application.coffee | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 5daea762c..bca05312e 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -15,7 +15,11 @@ url = require 'url' {EventEmitter} = require 'events' _ = require 'underscore-plus' -socketPath = path.join(os.tmpdir(), 'atom.sock') +socketPath = + if process.platform is 'win32' + '\\\\.\\pipe\\atom-sock' + else + path.join(os.tmpdir(), 'atom.sock') # Private: The application's singleton class. # @@ -35,7 +39,7 @@ class AtomApplication # take a few seconds to trigger 'error' event, it could be a bug of node # or atom-shell, before it's fixed we check the existence of socketPath to # speedup startup. - if (not fs.existsSync socketPath) or options.test + if (process.platform isnt 'win32' and not fs.existsSync socketPath) or options.test createAtomApplication() return @@ -99,7 +103,8 @@ class AtomApplication # the other launches will just pass their information to this server and then # close immediately. listenForArgumentsFromNewProcess: -> - fs.unlinkSync socketPath if fs.existsSync(socketPath) + if process.platform isnt 'win32' and fs.existsSync(socketPath) + fs.unlinkSync socketPath server = net.createServer (connection) => connection.on 'data', (data) => @openWithOptions(JSON.parse(data)) @@ -152,7 +157,9 @@ class AtomApplication app.quit() if process.platform is 'win32' app.on 'will-quit', => - fs.unlinkSync socketPath if fs.existsSync(socketPath) # Clean the socket file when quit normally. + # Clean the socket file when quit normally. + if process.platform isnt 'win32' and fs.existsSync(socketPath) + fs.unlinkSync socketPath app.on 'open-file', (event, pathToOpen) => event.preventDefault() From b1778aa7dfc26a24451bad3fc17592b461228a61 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jan 2014 21:07:32 +0800 Subject: [PATCH 071/111] Use administrator power to install Atom on Windows. --- build/package.json | 1 + build/tasks/install-task.coffee | 14 +++++++++++--- script/copy-folder.cmd | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 script/copy-folder.cmd diff --git a/build/package.json b/build/package.json index 836679d8c..14b1437e4 100644 --- a/build/package.json +++ b/build/package.json @@ -30,6 +30,7 @@ "rcedit": "~0.1.2", "request": "~2.27.0", "rimraf": "~2.2.2", + "runas": "~0.3.0", "unzip": "~0.1.9", "vm-compatibility-layer": "~0.1.0", "walkdir": "0.0.7" diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 23cae1bb9..ee7e7156a 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -6,6 +6,14 @@ module.exports = (grunt) -> grunt.registerTask 'install', 'Install the built application', -> installDir = grunt.config.get('atom.installDir') shellAppDir = grunt.config.get('atom.shellAppDir') - rm installDir - mkdir path.dirname(installDir) - cp shellAppDir, installDir + if process.platform is 'win32' + runas = require 'runas' + copyFolder = path.resolve 'script', 'copy-folder.cmd' + # cmd /c ""script" "source" "destination"" + arg = "/c \"\"#{copyFolder}\" \"#{shellAppDir}\" \"#{installDir}\"\"" + if runas('cmd', [arg], hide: true) isnt 0 + throw new Error("Failed to copy #{shellAppDir} to #{installDir}") + else + rm installDir + mkdir path.dirname(installDir) + cp shellAppDir, installDir diff --git a/script/copy-folder.cmd b/script/copy-folder.cmd new file mode 100644 index 000000000..532675ca2 --- /dev/null +++ b/script/copy-folder.cmd @@ -0,0 +1,18 @@ +@echo off + +set USAGE="Usage: %0 source destination" + +if [%1] == [] ( + echo %USAGE% + exit 1 +) +if [%2] == [] ( + echo %USAGE% + exit 2 +) + +:: rm -rf %2 +if exist %2 rmdir %2 /s /q + +:: cp -rf %1 %2 +xcopy %1 %2 /e /h /c /i /y /r From d931299b9f9ffcaaa3a9538d318eaf7d13696582 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jan 2014 21:55:49 +0800 Subject: [PATCH 072/111] Create shortcut on Desktop. --- build/tasks/install-task.coffee | 10 ++++++++-- script/copy-folder.cmd | 2 +- script/create-shortcut.cmd | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 script/create-shortcut.cmd diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index ee7e7156a..a2b591734 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -1,18 +1,24 @@ path = require 'path' module.exports = (grunt) -> - {cp, mkdir, rm} = require('./task-helpers')(grunt) + {cp, mkdir, rm, spawn} = require('./task-helpers')(grunt) grunt.registerTask 'install', 'Install the built application', -> installDir = grunt.config.get('atom.installDir') shellAppDir = grunt.config.get('atom.shellAppDir') if process.platform is 'win32' + done = @async() + runas = require 'runas' copyFolder = path.resolve 'script', 'copy-folder.cmd' # cmd /c ""script" "source" "destination"" arg = "/c \"\"#{copyFolder}\" \"#{shellAppDir}\" \"#{installDir}\"\"" if runas('cmd', [arg], hide: true) isnt 0 - throw new Error("Failed to copy #{shellAppDir} to #{installDir}") + done("Failed to copy #{shellAppDir} to #{installDir}") + + createShortcut = path.resolve 'script', 'create-shortcut.cmd' + args = ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom'] + spawn {cmd: 'cmd', args}, done else rm installDir mkdir path.dirname(installDir) diff --git a/script/copy-folder.cmd b/script/copy-folder.cmd index 532675ca2..b17473992 100644 --- a/script/copy-folder.cmd +++ b/script/copy-folder.cmd @@ -1,6 +1,6 @@ @echo off -set USAGE="Usage: %0 source destination" +set USAGE=Usage: %0 source destination if [%1] == [] ( echo %USAGE% diff --git a/script/create-shortcut.cmd b/script/create-shortcut.cmd new file mode 100644 index 000000000..ca295d434 --- /dev/null +++ b/script/create-shortcut.cmd @@ -0,0 +1,23 @@ +@echo off + +set USAGE=Usage: %0 source name-on-desktop + +if [%1] == [] ( + echo %USAGE% + exit 1 +) +if [%2] == [] ( + echo %USAGE% + exit 2 +) + +set SCRIPT="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs" + +echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT% +echo sLinkFile = "%USERPROFILE%\Desktop\%2.lnk" >> %SCRIPT% +echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT% +echo oLink.TargetPath = %1 >> %SCRIPT% +echo oLink.Save >> %SCRIPT% + +cscript /nologo %SCRIPT% +del %SCRIPT% From f92f130606a44c87ec5a94f102a0dfa66aeca586 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Fri, 24 Jan 2014 15:10:51 -0800 Subject: [PATCH 073/111] Remove sparkle keys from plist. --- resources/mac/atom-Info.plist | 4 ---- 1 file changed, 4 deletions(-) diff --git a/resources/mac/atom-Info.plist b/resources/mac/atom-Info.plist index d3865ab50..1c30e6eaa 100644 --- a/resources/mac/atom-Info.plist +++ b/resources/mac/atom-Info.plist @@ -32,10 +32,6 @@ MainMenu NSPrincipalClass AtomApplication - SUPublicDSAKeyFile - speakeasy.pem - SUScheduledCheckInterval - 3600 CFBundleURLTypes From be0e36b663b25e08c876e32b17a41adbe126793c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jan 2014 17:30:29 -0800 Subject: [PATCH 074/111] Rename Gutter to GutterView Refs #1468 --- src/editor-view.coffee | 4 ++-- src/{gutter.coffee => gutter-view.coffee} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/{gutter.coffee => gutter-view.coffee} (99%) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 59b095a1e..ea82127d8 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -1,6 +1,6 @@ {View, $, $$$} = require './space-pen-extensions' TextBuffer = require './text-buffer' -Gutter = require './gutter' +GutterView = require './gutter-view' {Point, Range} = require 'text-buffer' Editor = require './editor' CursorView = require './cursor-view' @@ -47,7 +47,7 @@ class EditorView extends View attributes = { class: @classes(params), tabindex: -1 } _.extend(attributes, params.attributes) if params.attributes @div attributes, => - @subview 'gutter', new Gutter + @subview 'gutter', new GutterView @div class: 'scroll-view', outlet: 'scrollView', => @div class: 'overlayer', outlet: 'overlayer' @div class: 'lines', outlet: 'renderedLines' diff --git a/src/gutter.coffee b/src/gutter-view.coffee similarity index 99% rename from src/gutter.coffee rename to src/gutter-view.coffee index c66191cc0..cae0d51c0 100644 --- a/src/gutter.coffee +++ b/src/gutter-view.coffee @@ -6,7 +6,7 @@ _ = require 'underscore-plus' # # The gutter also indicates if rows are folded. module.exports = -class Gutter extends View +class GutterView extends View ### Internal ### From b5449dec6cb585b5e84391334cded6984a0c8557 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jan 2014 17:56:01 -0800 Subject: [PATCH 075/111] Upgrade to fuzzaldrin@0.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f67be14fe..f943faaec 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "first-mate": "1.x", "fs-plus": "1.x", "fstream": "0.1.24", - "fuzzaldrin": "0.6.0", + "fuzzaldrin": "0.7.0", "git-utils": "0.34.0", "guid": "0.0.10", "jasmine-tagged": "0.3.0", From 5be6cfa67884b43ea7cefff3acbc4f2d0f1f1f1b Mon Sep 17 00:00:00 2001 From: probablycorey Date: Fri, 24 Jan 2014 18:07:37 -0800 Subject: [PATCH 076/111] Update first package documentation --- docs/your-first-package.md | 385 ++++++++++--------------------------- 1 file changed, 101 insertions(+), 284 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 9f9901229..b09268a7e 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,328 +1,145 @@ -# Creating Your First Package +# Create Your First Package -Let's take a look at creating your first package. +We are going to create a simple package that replaces selected text with ascii +art. So if "cool" was selected the output would be: -To get started, hit `cmd-shift-P`, and start typing "Generate Package" to generate -a package. Once you select the "Generate Package" command, it'll ask you for a -name for your new package. Let's call ours _changer_. +``` + /\_ \ + ___ ___ ___\//\ \ + /'___\ / __`\ / __`\\ \ \ +/\ \__//\ \L\ \/\ \L\ \\_\ \_ +\ \____\ \____/\ \____//\____\ + \/____/\/___/ \/___/ \/____/ +``` -Atom will pop open a new window, showing the _changer_ package with a default set of -folders and files created for us. Hit `cmd-shift-P` and start typing "Changer." You'll -see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far, -so good! +The final package can be viewed at +[https://github.com/atom/ascii-art]([https://github.com/atom/ascii-art]). -In order to demonstrate the capabilities of Atom and its API, our Changer plugin -is going to do two things: +To begin, press `cmd-shift-P` to bring up the Command Palette. Type "generate +package" and select the "Package Generator: Generate Package" command. You will +be prompt for package name, let's call ours _ascii-art_. -1. It'll show only modified files in the file tree -2. It'll append a new pane to the editor with some information about the modified -files +Atom will open a new window with the _ascii-art_ package contents displayed in +the Tree View. This window will already has the Ascii Art package installed. If +you toggle the Command Palette `cmd-shift-P` and type "Ascii Art" you'll see a +new `Ascii Art: Toggle` command. If triggered, this command displays a message +created by the package generator. -Let's get started! - -## Changing Keybindings and Commands - -Since Changer is primarily concerned with the file tree, let's write a -key binding that works only when the tree is focused. Instead of using the -default `toggle`, our keybinding executes a new command called `magic`. - -_keymaps/changer.cson_ should change to look like this: +Now let's edit the package files to make our ascii art package work! Since this +package doesn't need any UI we can remove all view related code. Start by +opening up _lib/ascii-art.coffee_. Remove all view code until the file looks +like this: ```coffeescript -'.tree-view': - 'ctrl-V': 'changer:magic' + module.exports = + activate: -> ``` -Notice that the keybinding is called `ctrl-V` — that's actually `ctrl-shift-v`. -You can use capital letters to denote using `shift` for your binding. +## Create Command -`.tree-view` represents the parent container for the tree view. -Keybindings only work within the context of where they're entered. In this case, -hitting `ctrl-V` anywhere other than tree won't do anything. Obviously, you can -bind to any part of the editor using element, id, or class names. For example, -you can map to `body` if you want to scope to anywhere in Atom, or just `.editor` -for the editor portion. - -To bind keybindings to a command, we'll need to do a bit of association in our -CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command -name and executes a callback function. Open up _lib/changer-view.coffee_, and -change `atom.workspaceView.command "changer:toggle"` to look like this: +Now let's add a command. It's recommended to namespace your commands with your +package name, separated with a colon (`:`). So we'll call this command +`ascii-art:convert`. Register the command in _lib/ascii-art.coffee_: ```coffeescript -atom.workspaceView.command "changer:magic", => @magic() +module.exports = + activate: -> + atom.workspaceView.command "ascii-art:convert", => @convert() + + convert: -> + # This assumes the active pane item is an editor + editor = atom.workspace.activePaneItem. + selection = editor.getSelection() + upperCaseSelectedText = selection.getText().toUpperCase() + selection.insertText(upperCaseSelectedText) ``` -It's common practice to namespace your commands with your package name, separated -with a colon (`:`). Make sure to rename your `toggle` method to `magic` as well. +`atom.workspaceView.command` method takes a command name and a callback. The +callback executes when the command is triggered. In this case, when the command +is triggered the callback will uppercase the selected text. -Every time you reload the Atom editor, changes to your package code will be reevaluated, -just as if you were writing a script for the browser. Reload the editor, click on -the tree, hit your keybinding, and...nothing happens! What the heck?! +## Reload The Package -Open up the _package.json_ file, and find the property called `activationEvents`. -Basically, this key tells Atom to not load a package until it hears a certain event. -Change the event to `changer:magic` and reload the editor: +Before we trigger the `ascii-art:convert` we'll need to reload the window. This +will reevaluate our package code, just as if we were writing a script for the +browser. Trigger `window:reload` command using the command palette or press +`ctrl-alt-cmd-l`. -```json -"activationEvents": ["changer:magic"] -``` +## Trigger the command -Hitting the key binding on the tree now works! - -## Working with Styles - -The next step is to hide elements in the tree that aren't modified. To do that, -we'll first try and get a list of files that have not changed. - -All packages are able to use jQuery in their code. In fact, there's [a list of -the bundled libraries Atom provides by default][bundled-libs]. - -We bring in jQuery by requiring the `atom` package and binding it to the `$` variable: +Open the command panel and search for the `ascii-art:convert` command. Its not +there, what the heck?! To fix this open _package.json_ and find the property +called `activationEvents`. Activation Events speed up load time by allowing an +Atom to delay it's activation until needed. So add the `ascii-art:convert` to +the activationEvents array: ```coffeescript -{$, View} = require 'atom' +"activationEvents": ["ascii-art:convert"], ``` -Now, we can define the `magic` method to query the tree to get us a list of every -file that _wasn't_ modified: +Now reload window `ctrl-alt-cmd-l` and use the command panel to trigger the +`ascii-art:convert` command. It will uppercase any text you have selected. + +## Add Key Binding + +Now let's add a key binding to trigger the `ascii-art:convert` command. Open +_keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the +`ascii-art:convert` command. + +When finished, the file will look like this: ```coffeescript -magic: -> - $('ol.entries li').each (i, el) -> - if !$(el).hasClass("status-modified") - console.log el +'.editor': + 'cmd-alt-a': 'ascii-art:convert' ``` -You can access the dev console by hitting `alt-cmd-i`. Here, you'll see all the -statements from `console` calls. When we execute the `changer:magic` command, the -browser console lists items that are not being modified (_i.e._, those without the -`status-modified` class). Let's add a class to each of these elements called `hide-me`: +Notice the `.editor` line. This limits the key binding to only work when the +focused element matches the selector `.editor`, much like CSS. For example, if +the Tree View has focus, pressing `cmd-alt-a` won't trigger the +`ascii-art:convert` command. But if the editor has focus, the +`ascii-art:convert` method will be triggered. More information on key bindings +can be found in the [keymaps][keymaps] documentation. + +Now reload the window and verify that pressing the key binding works! You can +also verify that it **doesn't** work when the Tree View is focused. + +## Add The Ascii Art + +Now the fun part, we need to convert the selected text to ascii art. To do this +we will use the [figlet node module](https://npmjs.org/package/figlet). Open +_package.json_ add the latest version of figlet to the dependencies: ```coffeescript -magic: -> - $('ol.entries li').each (i, el) -> - if !$(el).hasClass("status-modified") - $(el).addClass("hide-me") + "dependencies": { + "figlet": "1.0.8" + } ``` -With our newly added class, we can manipulate the visibility of the elements -with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory, -and add a single entry: +NOW GO TO THE COMMAND LINE AND RUN APM UPDATE BUT REALLY THIS STEP SEEMS LIKE +IT COULD BE AN ATOM COMMAND. -```css -ol.entries .hide-me { - display: none; -} -``` - -Refresh Atom, and run the `changer` command. You'll see all the non-changed -files disappear from the tree. Success! - -![Changer_File_View] - -There are a number of ways you can get the list back; let's just naively iterate -over the same elements and remove the class: +Now you can require the figlet node module in _lib/ascii-art.coffee_ and +instead of uppercasing the text, you can convert it to ascii art! ```coffeescript -magic: -> - $('ol.entries li').each (i, el) -> - if !$(el).hasClass("status-modified") - if !$(el).hasClass("hide-me") - $(el).addClass("hide-me") - else - $(el).removeClass("hide-me") +convert: -> + # This assumes the active pane item is an editor + selection = atom.workspace.activePaneItem.getSelection() + + figlet = require 'figlet' + figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) -> + if error + console.error(error) + else + selection.insertText("\n" + asciiArt + "\n") ``` -## Creating a New Panel - -The next goal of this package is to append a panel to the Atom editor that lists -some information about the modified files. - -To do that, we're going to first open up [the style guide][styleguide]. The Style -Guide lists every type of UI element that can be created by an Atom package. Aside -from helping you avoid writing fresh code from scratch, it ensures that packages -have the same look and feel no matter how they're built. - -Every package that extends from the `View` class can provide an optional class -method called `content`. The `content` method constructs the DOM that your -package uses as its UI. The principals of `content` are built entirely on -[SpacePen][space-pen], which we'll touch upon only briefly here. - -Our display will simply be an unordered list of the file names, and their -modified times. We'll append this list to a panel on the bottom of the editor. A -basic `panel` element inside a `tool-panel` will work well for us. Let's start by carving out a -`div` to hold the filenames: - -```coffeescript -@content: -> - @div class: "changer tool-panel panel-bottom", => - @div class: "panel", => - @div class: "panel-heading", "Modified Files" - @div class: "panel-body padded", outlet: 'modifiedFilesContainer', => - @ul class: 'modified-files-list', outlet: 'modifiedFilesList', => - @li 'Modified File Test' - @li 'Modified File Test' -``` - -You can add any HTML attribute you like. `outlet` names the variable your -package can use to manipulate the element directly. The fat arrow (`=>`) -indicates that the next DOM set are nested children. - -Once again, you can style `li` elements using your stylesheets. Let's test that -out by adding these lines to the _changer.css_ file: - -```css -ul.modified-files-list { - color: white; -} -``` - -We'll add one more line to the end of the `magic` method to make this pane -appear: - -```coffeescript -atom.workspaceView.prependToBottom(this) -``` - -If you refresh Atom and hit the key command, you'll see a box appear right -underneath the editor: - -![Changer_Panel_Append] - -As you might have guessed, `atom.workspaceView.prependToBottom` tells Atom to -prepend `this` item (_i.e._, whatever is defined by`@content`). If we had called -`atom.workspaceView.appendToBottom`, the pane would be attached below the status -bar. - -Before we populate this panel for real, let's apply some logic to toggle the -pane off and on, just like we did with the tree view. Replace the -`atom.workspaceView.prependToBottom` call with this code: - -```coffeescript -# toggles the pane -if @hasParent() - @remove() -else - atom.workspaceView.prependToBottom(this) -``` - -There are about a hundred different ways to toggle a pane on and off, and this -might not be the most efficient one. If you know your package needs to be -toggled on and off more freely, it might be better to draw the interface during the -initialization, then immediately call `hide()` on the element to remove it from -the view. You can then swap between `show()` and `hide()`, and instead of -forcing Atom to add and remove the element as we're doing here, it'll just set a -CSS property to control your package's visibility. - -Refresh Atom, hit the key combo, and watch your test list appear and disappear. - -## Calling Node.js Code - -Since Atom is built on top of [Node.js][node], you can call any of its libraries, -including other modules that your package requires. - -We'll iterate through our resulting tree, and construct the path to our modified -file based on its depth in the tree. We'll use Node to handle path joining for -directories. - -Add the following Node module to the top of your file: - -```coffeescript -path = require 'path' -``` - -Then, add these lines to your `magic` method, _before_ your pane drawing code: - -```coffeescript -modifiedFiles = [] -# for each single entry... -$('ol.entries li.file.status-modified span.name').each (i, el) -> - filePath = [] - # ...grab its name... - filePath.unshift($(el).text()) - - # ... then find its parent directories, and grab their names - parents = $(el).parents('.directory.status-modified') - parents.each (i, el) -> - filePath.unshift($(el).find('div.header span.name').eq(0).text()) - - modifiedFilePath = path.join(atom.project.rootDirectory.path, filePath.join(path.sep)) - modifiedFiles.push modifiedFilePath -``` - -`modifiedFiles` is an array containing a list of our modified files. We're also -using the node.js [`path` library][path] to get the proper directory separator -for our system. - -Remove the two `@li` elements we added in `@content`, so that we can -populate our `modifiedFilesList` with real information. We'll do that by -iterating over `modifiedFiles`, accessing a file's last modified time, and -appending it to `modifiedFilesList`: - -```coffeescript -# toggles the pane -if @hasParent() - @remove() -else - for file in modifiedFiles - stat = fs.lstatSync(file) - mtime = stat.mtime - @modifiedFilesList.append("
  • #{file} - Modified at #{mtime}") - atom.workspaceView.prependToBottom(this) -``` - -When you toggle the modified files list, your pane is now populated with the -filenames and modified times of files in your project: - -![Changer_Panel_Timestamps] - -You might notice that subsequent calls to this command reduplicate information. -We could provide an elegant way of rechecking files already in the list, but for -this demonstration, we'll just clear the `modifiedFilesList` each time it's closed: - -```coffeescript -# toggles the pane -if @hasParent() - @modifiedFilesList.empty() # added this to clear the list on close - @remove() -else - for file in modifiedFiles - stat = fs.lstatSync(file) - mtime = stat.mtime - @modifiedFilesList.append("
  • #{file} - Modified at #{mtime}") - atom.workspaceView.prependToBottom(this) -``` - -## Coloring UI Elements - -For packages that create new UI elements, adhering to the style guide is just one -part to keeping visual consistency. Packages dealing with color, fonts, padding, -margins, and other visual cues should rely on [Theme Variables][theme-vars], instead -of developing individual styles. Theme variables are variables defined by Atom -for use in packages and themes. They're only available in [`LESS`](http://lesscss.org/) -stylesheets. - -For our package, let's remove the style defined by `ul.modified-files-list` in -_changer.css_. Create a new file under the _stylesheets_ directory called _text-colors.less_. -Here, we'll import the _ui-variables.less_ file, and define some Atom-specific -styles: - -```less -@import "ui-variables"; - -ul.modified-files-list { - color: @text-color; - background-color: @background-color-info; -} -``` - -Using theme variables ensures that packages look great alongside any theme. - ## Further reading For more information on the mechanics of packages, check out [Creating a Package][creating-a-package]. +[keymaps]: advanced/keymaps.html [bundled-libs]: creating-a-package.html#included-libraries [styleguide]: https://github.com/atom/styleguide [space-pen]: https://github.com/atom/space-pen From 2b34b2b9ba10323fed126ec71b5351c261d9c81a Mon Sep 17 00:00:00 2001 From: probablycorey Date: Fri, 24 Jan 2014 18:25:05 -0800 Subject: [PATCH 077/111] Revert "Remove sparkle keys from plist." This reverts commit f92f130606a44c87ec5a94f102a0dfa66aeca586. --- resources/mac/atom-Info.plist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/mac/atom-Info.plist b/resources/mac/atom-Info.plist index 1c30e6eaa..d3865ab50 100644 --- a/resources/mac/atom-Info.plist +++ b/resources/mac/atom-Info.plist @@ -32,6 +32,10 @@ MainMenu NSPrincipalClass AtomApplication + SUPublicDSAKeyFile + speakeasy.pem + SUScheduledCheckInterval + 3600 CFBundleURLTypes From f631660b32ed1967d8c2cf34e6cab41d7ec1756c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 21 Jan 2014 21:29:51 -0700 Subject: [PATCH 078/111] Start on overview of atom's global variables --- docs/advanced/globals.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/advanced/globals.md diff --git a/docs/advanced/globals.md b/docs/advanced/globals.md new file mode 100644 index 000000000..ba8285975 --- /dev/null +++ b/docs/advanced/globals.md @@ -0,0 +1,34 @@ +# Globals + +Atom exposes several services through singleton objects accessible via the +`atom` global: + +* atom + * workspace: + Manipulate and query the state of the user interface for the current + window. Open editors, manipulate panes. + * workspaceView: + Similar to workspace, but provides access to the root of all views in the + current window. + * project: + Access the directory associated with the current window. Load editors, + perform project-wide searches, register custom openers for special file + types. + * config: + Read, write, and observe user configuration settings. + * keymap: + Add and query the currently active keybindings. + * deserializers: + Deserialize instances from their state objects and register deserializers. + * packages: + Activate, deactivate, and query user packages. + * themes: + Activate, deactivate, and query user themes. + * contextMenu: + Register context menus. + * menu: + Register application menus. + * pasteboard: + Read from and write to the system pasteboard. + * syntax: + Assign and query syntactically-scoped properties. From 17d30cc526718331e5829539258ed716ca05076f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 25 Jan 2014 12:41:56 -0800 Subject: [PATCH 079/111] Revise in-depth keymap overview --- docs/advanced/keymaps.md | 144 +++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 45 deletions(-) diff --git a/docs/advanced/keymaps.md b/docs/advanced/keymaps.md index c346ff7dd..aaa5d398b 100644 --- a/docs/advanced/keymaps.md +++ b/docs/advanced/keymaps.md @@ -1,58 +1,99 @@ -## Keymaps In-Depth +# Keymaps In-Depth -### Structure of a Keymap File +## Structure of a Keymap File -Keymap files are encoded as JSON or CSON files containing nested hashes. The -top-level keys of a keymap are **CSS 3 selectors**, which specify a particular -context in Atom's interface. Common selectors are `.editor`, which scopes -bindings to just work when an editor is focused, and `body`, which scopes -bindings globally. +Keymap files are encoded as JSON or CSON files containing nested hashes. They +work much like stylesheets, but instead of applying style properties to elements +matching the selector, they specify the meaning of keystrokes on elements +matching the selector. Here is an example of some bindings that apply when +keystrokes pass through elements with the class `.editor`: -Beneath the selectors are hashes mapping **keystroke patterns** to -**semantic events**. A keystroke pattern looks like the following examples. -Note that the last example describes multiple keystrokes in succession: +```coffee +'.editor': + 'cmd-delete': 'editor:backspace-to-beginning-of-line' + 'alt-backspace': 'editor:backspace-to-beginning-of-word' + 'ctrl-A': 'editor:select-to-first-character-of-line' + 'ctrl-shift-e': 'editor:select-to-end-of-line' + 'cmd-left': 'editor:move-to-first-character-of-line' -- `p` -- `2` -- `ctrl-p` -- `ctrl-alt-cmd-p` -- `tab` -- `escape` -- `enter` -- `ctrl-w w` +'.editor:not(.mini)' + 'cmd-alt-[': 'editor:fold-current-row' + 'cmd-alt-]': 'editor:unfold-current-row' +``` -A semantic event is the name of the custom event that will be triggered on the -target of the keydown event when a key binding matches. You can use the command -palette (bound to `cmd-shift-P`), to get a list of relevant events and their bindings -in any focused context in Atom. +Beneath the first selector are several bindings, mapping specific *keystroke +patterns* to *commands*. When an element with the `.editor` class is focused and +`cmd-delete` is pressed, an custom DOM event called +`editor:backspace-to-beginning-of-line` is emitted on the `.editor` element. -### Rules for Mapping A Keydown Event to A Semantic Event +The second selector group also targets editors, but only if they don't have the +`.mini` class. In this example, the commands for code folding don't really make +sense on mini-editors, so the selector restricts them to regular editors. -A keymap's job is to translate a physical keystroke event (like `cmd-D`) into a -semantic event (like `editor:duplicate-line`). Whenever a keydown event occurs -on a focused element, it bubbles up the DOM as usual. As soon as an element on -the bubble path matches a key binding for the keystroke, the binding's semantic -event is triggered on the original target of the keydown event. Just as with -CSS, if multiple selectors match an element, the most specific selector is -favored. If two selectors have the same specificity, the selector that occurs -latest in the cascade is favored. +### Keystroke Patterns + +Keystroke patterns express one or more keystrokes combined with optional +modifier keys. For example: `ctrl-w v`, or `cmd-shift-up`. A keystroke is +composed of the following symbols, separated by a `-`. A multi-keystroke pattern +can be expressed as keystroke patterns separated by spaces. + + +| Type | Examples +| --------------------|---------------------------- +| Character literals | `a` `4` `$` +| Modifier keys | `cmd` `ctrl` `alt` `shift` +| Special keys | `enter` `escape` `backspace` `delete` `tab` `home` `end` `pageup` `pagedown` `left` `right` `up` `down` + +### Commands + +Commands are custom DOM events that are triggered when a keystroke matches a +binding. This allows user interface code to listen for named commands without +specifying the specific keybinding that triggers it. For example, the following +code sets up {EditorView} to listen for commands to move the cursor to the first +character of the current line: + +```coffee +class EditorView + listenForEvents: -> + @command 'editor:move-to-first-character-of-line', => + @editor.moveCursorToFirstCharacterOfLine() +``` + +The `::command` method is basically an enhanced version of jQuery's `::on` +method that listens for a custom DOM event and adds some metadata to the DOM, +which is read by the command palette. + +When you are looking to bind new keys, it is often useful to use the command +palette (`ctrl-shift-p`) to discover what commands are being listened for in a +given focus context. Commands are "humanized" following a simple algorithm, so a +command like `editor:fold-current-row` would appear as "Editor: Fold Current +Row". + +### Specificity and Cascade Order + +As is the case with CSS applying styles, when multiple bindings match for a +single element, the conflict is resolved by choosing the most *specific* +selector. If two matching selectors have the same specificity, the binding +for the selector appearing later in the cascade takes precedence. Currently, there's no way to specify selector ordering within a single keymap, -because JSON hashes do not preserve order. Rather than making the format more -awkward in order to preserve order, we've opted to handle cases where order is -critical by breaking the keymap into two separate files, such as -`snippets-1.cson` and `snippets-2.cson`. +because JSON objects do not preserve order. We eventually plan to introduce a +custom CSS-like file format for keymaps that allows for ordering within a single +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`. -### Overloading Key Bindings +## Overloading Key Bindings Occasionally, it makes sense to layer multiple actions on top of the same key -binding. An example of this is the snippets package. You expand a snippet by -pressing `tab` immediately following a snippet's prefix. But if the cursor is -not following a valid snippet prefix, then we want tab to perform its normal -action (probably inserting a tab character or the appropriate number of spaces). +binding. An example of this is the snippets package. Snippets are inserted by +typing a snippet prefix such as `for` and then pressing `tab`. Every time `tab` +is pressed, we want to execute code attempting to expand a snippet if one exists +for the text preceding the cursor. If a snippet *doesn't* exist, we want `tab` +to actually insert whitespace. -To achieve this, the snippets package makes use of the `abortKeyBinding` method -on the event object that's triggered by the binding for `tab`. +To achieve this, the snippets package makes use of the `.abortKeyBinding()` +method on the event object representing the `snippets:expand` command. ```coffee-script # pseudo-code @@ -64,6 +105,19 @@ editor.command 'snippets:expand', (e) => ``` When the event handler observes that the cursor does not follow a valid prefix, -it calls `e.abortKeyBinding()`, which tells the keymap system to continue -searching up the cascade for another matching binding. In this case, the default -implementation of `tab` ends up getting triggered. +it calls `e.abortKeyBinding()`, telling the keymap system to continue searching +for another matching binding. + +## Step-by-Step: How Keydown Events are Mapped to Commands + +* A keydown event occurs on a *focused* element. +* Starting at the focused element, the keymap walks upward towards the root of + the document, searching for the most specific CSS selector that matches the + current DOM element and also contains a keystroke pattern matching the keydown + event. +* When a matching keystroke pattern is found, the search is terminated and the + pattern's corresponding command is triggered on the current element. +* If `.abortKeyBinding()` is called on the triggered event object, the search + is resumed, triggering a binding on the next-most-specific CSS selector for + the same element or continuing upward to parent elements. +* If no bindings are found, the event is handled by Chromium normally. From b05e20245faf492c07ff2cb6077aa90b444ae603 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 26 Jan 2014 14:26:14 -0700 Subject: [PATCH 080/111] Upgrade find and replace to 0.80.0 to fix invalid regex exceptions And also to remove live project search --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f943faaec..4029690aa 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "editor-stats": "0.12.0", "exception-reporting": "0.12.0", "feedback": "0.22.0", - "find-and-replace": "0.79.0", + "find-and-replace": "0.80.0", "fuzzy-finder": "0.31.0", "gists": "0.15.0", "git-diff": "0.23.0", From aec9e75ecb98fee850cf0f39363cdaadce4a957c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 26 Jan 2014 15:00:58 -0700 Subject: [PATCH 081/111] Send more info on errors converting buffer positions to screen positions We've gotten one rogue error but I have no idea how to reproduce it. This will tell us if soft wrap is enabled and if any folds are present so hopefully we can start narrowing these down. --- src/display-buffer.coffee | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 2479101d7..3c835863b 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -297,8 +297,12 @@ class DisplayBuffer extends Model [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) for screenRow in [startScreenRow...endScreenRow] unless screenLine = @screenLines[screenRow] - throw new Error("No screen line exists for screen row #{screenRow}, converted from buffer position (#{row}, #{column})") - + throw new Error """ + No screen line exists for screen row #{screenRow}, converted from buffer position (#{row}, #{column}) + Soft wrap enabled: #{@getSoftWrap()} + Fold count: #{@findFoldMarkers().length} + Last buffer row: #{@getLastRow()} + """ maxBufferColumn = screenLine.getMaxBufferColumn() if screenLine.isSoftWrapped() and column > maxBufferColumn continue From b6cf097abdc313948094ca7db31d23980b276ef0 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 27 Jan 2014 18:12:21 +0800 Subject: [PATCH 082/111] Update atom-shell to v0.8.7. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4029690aa..7fff783d6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "url": "http://github.com/atom/atom/raw/master/LICENSE.md" } ], - "atomShellVersion": "0.8.6", + "atomShellVersion": "0.8.7", "dependencies": { "async": "0.2.6", "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", From 7c4b453e645ae3d2c5a2d5f7e14a31c2c1cdbc99 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 27 Jan 2014 18:56:13 +0800 Subject: [PATCH 083/111] Do not show unresponsive dialog when running specs. --- src/browser/atom-window.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 0f09bfaa4..4ca0ab3ad 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -74,6 +74,8 @@ class AtomWindow global.atomApplication.removeWindow(this) @browserWindow.on 'unresponsive', => + return if @isSpec + chosen = dialog.showMessageBox @browserWindow, type: 'warning' buttons: ['Close', 'Keep Waiting'] From 0b0ad42610e76d61177b5bae6d663476090e2fe1 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 08:46:27 -0800 Subject: [PATCH 084/111] :lipstick: --- docs/your-first-package.md | 67 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index b09268a7e..c984203b9 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,7 +1,8 @@ # Create Your First Package -We are going to create a simple package that replaces selected text with ascii -art. So if "cool" was selected the output would be: +The tutorial will lead you though creating a simple package that replaces +selected text with ascii art. For example, if "cool" was selected the output +would be: ``` /\_ \ @@ -13,17 +14,18 @@ art. So if "cool" was selected the output would be: ``` The final package can be viewed at -[https://github.com/atom/ascii-art]([https://github.com/atom/ascii-art]). +[https://github.com/atom/ascii-art](https://github.com/atom/ascii-art). -To begin, press `cmd-shift-P` to bring up the Command Palette. Type "generate -package" and select the "Package Generator: Generate Package" command. You will -be prompt for package name, let's call ours _ascii-art_. +To begin, press `cmd-shift-P` to bring up the [Command +Palette](https://github.com/atom/command-palette). Type "generate package" and +select the "Package Generator: Generate Package" command. Now we need to name +the package, let's call ours _ascii-art_. Atom will open a new window with the _ascii-art_ package contents displayed in -the Tree View. This window will already has the Ascii Art package installed. If -you toggle the Command Palette `cmd-shift-P` and type "Ascii Art" you'll see a -new `Ascii Art: Toggle` command. If triggered, this command displays a message -created by the package generator. +the Tree View. Because the window was opened **after** the Ascii Art package was +created, the Ascii Art package will be loaded. To verify this toggle the Command +Palette (`cmd-shift-P`) and type "Ascii Art" you'll see a new `Ascii Art: +Toggle` command. If triggered, this command displays a default message. Now let's edit the package files to make our ascii art package work! Since this package doesn't need any UI we can remove all view related code. Start by @@ -35,10 +37,10 @@ like this: activate: -> ``` -## Create Command +## Create A Command -Now let's add a command. It's recommended to namespace your commands with your -package name, separated with a colon (`:`). So we'll call this command +Now let's add a command. It's recommended to start your commands with the +package name followed by a colon (`:`). We'll call this command `ascii-art:convert`. Register the command in _lib/ascii-art.coffee_: ```coffeescript @@ -54,24 +56,25 @@ module.exports = selection.insertText(upperCaseSelectedText) ``` -`atom.workspaceView.command` method takes a command name and a callback. The +The `atom.workspaceView.command` method takes a command name and a callback. The callback executes when the command is triggered. In this case, when the command -is triggered the callback will uppercase the selected text. +is triggered the callback will call the `convert` method and uppercase the +selected text. ## Reload The Package -Before we trigger the `ascii-art:convert` we'll need to reload the window. This -will reevaluate our package code, just as if we were writing a script for the -browser. Trigger `window:reload` command using the command palette or press -`ctrl-alt-cmd-l`. +Before we can trigger `ascii-art:convert` the window needs to reevaluate the +Ascii Art package. To do this we reload the window, just like when writing a +script in browser. Trigger `window:reload` command using the command palette or +by pressing `ctrl-alt-cmd-l`. -## Trigger the command +## Trigger The command -Open the command panel and search for the `ascii-art:convert` command. Its not -there, what the heck?! To fix this open _package.json_ and find the property -called `activationEvents`. Activation Events speed up load time by allowing an -Atom to delay it's activation until needed. So add the `ascii-art:convert` to -the activationEvents array: +Now open the command panel and search for the `ascii-art:convert` command. But +its not there! To fix this open _package.json_ and find the property called +`activationEvents`. Activation Events speed up load time by allowing an Atom to +delay a package's activation until it's needed. So add the `ascii-art:convert` to the +activationEvents array: ```coffeescript "activationEvents": ["ascii-art:convert"], @@ -80,20 +83,18 @@ the activationEvents array: Now reload window `ctrl-alt-cmd-l` and use the command panel to trigger the `ascii-art:convert` command. It will uppercase any text you have selected. -## Add Key Binding +## Add A Key Binding Now let's add a key binding to trigger the `ascii-art:convert` command. Open _keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the -`ascii-art:convert` command. - -When finished, the file will look like this: +`ascii-art:convert` command. When finished, the file will look like this: ```coffeescript '.editor': 'cmd-alt-a': 'ascii-art:convert' ``` -Notice the `.editor` line. This limits the key binding to only work when the +Notice `.editor` on the first line. This limits the key binding to work when the focused element matches the selector `.editor`, much like CSS. For example, if the Tree View has focus, pressing `cmd-alt-a` won't trigger the `ascii-art:convert` command. But if the editor has focus, the @@ -105,8 +106,8 @@ also verify that it **doesn't** work when the Tree View is focused. ## Add The Ascii Art -Now the fun part, we need to convert the selected text to ascii art. To do this -we will use the [figlet node module](https://npmjs.org/package/figlet). Open +Now we need to convert the selected text to ascii art. To do this we will use +the [figlet node module](https://npmjs.org/package/figlet) from NPM. Open _package.json_ add the latest version of figlet to the dependencies: ```coffeescript @@ -118,7 +119,7 @@ _package.json_ add the latest version of figlet to the dependencies: NOW GO TO THE COMMAND LINE AND RUN APM UPDATE BUT REALLY THIS STEP SEEMS LIKE IT COULD BE AN ATOM COMMAND. -Now you can require the figlet node module in _lib/ascii-art.coffee_ and +Require the figlet node module in _lib/ascii-art.coffee_ and instead of uppercasing the text, you can convert it to ascii art! ```coffeescript From 5bda9b8a8ee46ba38fd006d8addad5ce918e1890 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 08:53:47 -0800 Subject: [PATCH 085/111] Remove trailing dot --- docs/your-first-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index c984203b9..e5072232c 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -50,7 +50,7 @@ module.exports = convert: -> # This assumes the active pane item is an editor - editor = atom.workspace.activePaneItem. + editor = atom.workspace.activePaneItem selection = editor.getSelection() upperCaseSelectedText = selection.getText().toUpperCase() selection.insertText(upperCaseSelectedText) From def2001eb626515c39b09d0c48ba9827a2e58ace Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 08:54:47 -0800 Subject: [PATCH 086/111] Use json instead of coffeescript in code block --- docs/your-first-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index e5072232c..85c799130 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -110,7 +110,7 @@ Now we need to convert the selected text to ascii art. To do this we will use the [figlet node module](https://npmjs.org/package/figlet) from NPM. Open _package.json_ add the latest version of figlet to the dependencies: -```coffeescript +```json "dependencies": { "figlet": "1.0.8" } From 72bb83ccd0b4ec2d5af930f5c88d20d05a4eab51 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 08:55:35 -0800 Subject: [PATCH 087/111] Link to ascii art --- docs/your-first-package.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 85c799130..2bccede9b 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,8 +1,8 @@ # Create Your First Package The tutorial will lead you though creating a simple package that replaces -selected text with ascii art. For example, if "cool" was selected the output -would be: +selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). For +example, if "cool" was selected the output would be: ``` /\_ \ From bf1a2a532e0a0fb07cee0dc0c9154fca8580b731 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 27 Jan 2014 11:00:32 -0700 Subject: [PATCH 088/111] A little wordsmithing. Feel free to push back on whatever. --- docs/your-first-package.md | 81 ++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 2bccede9b..4f42ddf82 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,8 +1,8 @@ # Create Your First Package -The tutorial will lead you though creating a simple package that replaces -selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). For -example, if "cool" was selected the output would be: +This tutorial will guide you though creating a simple replaces the selected text +with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you run our new +command with the word "cool" selected, it will be replaced with: ``` /\_ \ @@ -19,29 +19,30 @@ The final package can be viewed at To begin, press `cmd-shift-P` to bring up the [Command Palette](https://github.com/atom/command-palette). Type "generate package" and select the "Package Generator: Generate Package" command. Now we need to name -the package, let's call ours _ascii-art_. +the package. Let's call it _ascii-art_. -Atom will open a new window with the _ascii-art_ package contents displayed in -the Tree View. Because the window was opened **after** the Ascii Art package was -created, the Ascii Art package will be loaded. To verify this toggle the Command -Palette (`cmd-shift-P`) and type "Ascii Art" you'll see a new `Ascii Art: -Toggle` command. If triggered, this command displays a default message. +Atom will open a new window with the contents of our new _ascii-art_ package +displayed in the Tree View. Because this window is opened **after** the package +is created, the ASCII Art package will be loaded and available in our new +window. To verify this, toggle the Command Palette (`cmd-shift-P`) and type +"ASCII Art" you'll see a new `ASCII Art: Toggle` command. When triggered, this +command displays a default message. -Now let's edit the package files to make our ascii art package work! Since this -package doesn't need any UI we can remove all view related code. Start by -opening up _lib/ascii-art.coffee_. Remove all view code until the file looks -like this: +Now let's edit the package files to make our ascii art package do something +interesting. Since this package doesn't need any UI, we can remove all +view-related code. Start by opening up _lib/ascii-art.coffee_. Remove all view +code, so the file looks like this: ```coffeescript module.exports = activate: -> ``` -## Create A Command +## Create a Command -Now let's add a command. It's recommended to start your commands with the -package name followed by a colon (`:`). We'll call this command -`ascii-art:convert`. Register the command in _lib/ascii-art.coffee_: +Now let's add a command. We recommend that you namespace your commands with the +package name followed by a `:`, so we'll call our command `ascii-art:convert`. +Register the command in _lib/ascii-art.coffee_: ```coffeescript module.exports = @@ -61,20 +62,21 @@ callback executes when the command is triggered. In this case, when the command is triggered the callback will call the `convert` method and uppercase the selected text. -## Reload The Package +## Reload the Package -Before we can trigger `ascii-art:convert` the window needs to reevaluate the -Ascii Art package. To do this we reload the window, just like when writing a -script in browser. Trigger `window:reload` command using the command palette or -by pressing `ctrl-alt-cmd-l`. +Before we can trigger `ascii-art:convert`, we need to load the latest code for +our package by reloading the window. Run the `window:reload` command from the +command palette or by pressing `ctrl-alt-cmd-l`. -## Trigger The command +## Trigger the Command Now open the command panel and search for the `ascii-art:convert` command. But its not there! To fix this open _package.json_ and find the property called `activationEvents`. Activation Events speed up load time by allowing an Atom to -delay a package's activation until it's needed. So add the `ascii-art:convert` to the -activationEvents array: +delay a package's activation until it's needed. So add the `ascii-art:convert` +to the activationEvents array: + +IT SEEMS LIKE ACTIVATION EVENTS SHOULDN'T BE REQUIRED HERE. WHAT AM I MISSING? ```coffeescript "activationEvents": ["ascii-art:convert"], @@ -94,20 +96,21 @@ _keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the 'cmd-alt-a': 'ascii-art:convert' ``` -Notice `.editor` on the first line. This limits the key binding to work when the -focused element matches the selector `.editor`, much like CSS. For example, if -the Tree View has focus, pressing `cmd-alt-a` won't trigger the -`ascii-art:convert` command. But if the editor has focus, the -`ascii-art:convert` method will be triggered. More information on key bindings -can be found in the [keymaps][keymaps] documentation. +Notice `.editor` on the first line. Just like CSS, keymap selectors *scope* key +bindings so they only apply to specific elements. In this case, our binding is +only active for elements matching the `.editor` selector. If the Tree View has +focus, pressing `cmd-alt-a` won't trigger the `ascii-art:convert` command. But +if the editor has focus, the `ascii-art:convert` method *will* be triggered. +More information on key bindings can be found in the [keymaps][keymaps] +documentation. -Now reload the window and verify that pressing the key binding works! You can -also verify that it **doesn't** work when the Tree View is focused. +Now reload the window and verify that the key binding works! You can also verify +that it **doesn't** work when the Tree View is focused. -## Add The Ascii Art +## Add the ASCII Art Now we need to convert the selected text to ascii art. To do this we will use -the [figlet node module](https://npmjs.org/package/figlet) from NPM. Open +the [figlet](https://npmjs.org/package/figlet) node module from npm. Open _package.json_ add the latest version of figlet to the dependencies: ```json @@ -119,8 +122,8 @@ _package.json_ add the latest version of figlet to the dependencies: NOW GO TO THE COMMAND LINE AND RUN APM UPDATE BUT REALLY THIS STEP SEEMS LIKE IT COULD BE AN ATOM COMMAND. -Require the figlet node module in _lib/ascii-art.coffee_ and -instead of uppercasing the text, you can convert it to ascii art! +Require the figlet node module in _lib/ascii-art.coffee_ and instead of +uppercasing the text, you can convert it to ascii art! ```coffeescript convert: -> @@ -137,8 +140,8 @@ convert: -> ## Further reading -For more information on the mechanics of packages, check out -[Creating a Package][creating-a-package]. +For more information on the mechanics of packages, check out [Creating a +Package][creating-a-package]. [keymaps]: advanced/keymaps.html [bundled-libs]: creating-a-package.html#included-libraries From 632ba0217a9f68a014dcd55e3edac83fc9d3bfa4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 27 Jan 2014 11:49:09 -0700 Subject: [PATCH 089/111] :lipstick: --- docs/your-first-package.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 4f42ddf82..bafdcfecd 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,8 +1,8 @@ # Create Your First Package -This tutorial will guide you though creating a simple replaces the selected text -with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you run our new -command with the word "cool" selected, it will be replaced with: +This tutorial will guide you though creating a simple command that replaces the +selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you +run our new command with the word "cool" selected, it will be replaced with: ``` /\_ \ From 257d0d3a2514ac0ceaeaab095aee0249eed23c72 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 11:02:52 -0800 Subject: [PATCH 090/111] Remove template snippets folder --- dot-atom/snippets/coffee.cson | 44 ----------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 dot-atom/snippets/coffee.cson diff --git a/dot-atom/snippets/coffee.cson b/dot-atom/snippets/coffee.cson deleted file mode 100644 index 5421f12f4..000000000 --- a/dot-atom/snippets/coffee.cson +++ /dev/null @@ -1,44 +0,0 @@ -".source.coffee": - "Describe block": - prefix: "de" - body: """ - describe "${1:description}", -> - ${2:body} - """ - "It block": - prefix: "i" - body: """ - it "$1", -> - $2 - """ - "Before each": - prefix: "be" - body: """ - beforeEach -> - $1 - """ - "After each": - prefix: "af" - body: """ - afterEach -> - $1 - """ - "Expectation": - prefix: "ex" - body: "expect($1).to$2" - "Console log": - prefix: "log" - body: "console.log $1" - "Range array": - prefix: "ra" - body: "[[$1, $2], [$3, $4]]" - "Point array": - prefix: "pt" - body: "[$1, $2]" - - "Key-value pair": - prefix: ":" - body: '${1:"${2:key}"}: ${3:value}' - "Create Jasmine spy": - prefix: "spy" - body: 'jasmine.createSpy("${1:description}")$2' From e6e7106ac5bc961cd0e209c2a8cfc1c10fc183d9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 11:16:24 -0800 Subject: [PATCH 091/111] Add sample snippets.cson to dot-atom template --- dot-atom/snippets.cson | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 dot-atom/snippets.cson diff --git a/dot-atom/snippets.cson b/dot-atom/snippets.cson new file mode 100644 index 000000000..e9d644de1 --- /dev/null +++ b/dot-atom/snippets.cson @@ -0,0 +1,15 @@ +# Your snippets +# +# Atom snippets allow you to enter a simple prefix in the editor and hit tab to +# expand the prefix into a larger code block with templated values. +# +# You can create a new snippet in this file by typing `snip` and then hitting +# tab. +# +# An example CoffeeScript snippet to expand log to console.log: +# +# '.source.coffee': +# 'Console log': +# 'prefix': 'log' +# 'body': 'console.log $1' +# From 5eb8875ad2ea89140e0f2886c2b2a057736077ef Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 11:17:20 -0800 Subject: [PATCH 092/111] Add Open Your Snippets menu item --- menus/darwin.cson | 1 + src/browser/atom-application.coffee | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/menus/darwin.cson b/menus/darwin.cson index 24490bfa3..44e88dead 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -9,6 +9,7 @@ { label: 'Preferences...', command: 'application:show-settings' } { label: 'Open Your Config', command: 'application:open-your-config' } { label: 'Open Your Keymap', command: 'application:open-your-keymap' } + { label: 'Open Your Snippets', command: 'application:open-your-snippets' } { label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' } { type: 'separator' } { label: 'Install Shell Commands', command: 'window:install-shell-commands' } diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index bca05312e..affabc0ae 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -149,9 +149,10 @@ class AtomApplication @on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new') @openPathOnEvent('application:show-settings', 'atom://config') - @openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet') - @openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap') @openPathOnEvent('application:open-your-config', 'atom://.atom/config') + @openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap') + @openPathOnEvent('application:open-your-snippets', 'atom://.atom/snippets') + @openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet') app.on 'window-all-closed', -> app.quit() if process.platform is 'win32' From a67d6362c25f815de8dfc653141374cee2e68f36 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 12:50:57 -0800 Subject: [PATCH 093/111] Assert snippet.cson is copied over --- spec/config-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 13785568f..77455c5c2 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -218,7 +218,7 @@ describe "Config", -> runs -> expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy() expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy() - expect(fs.existsSync(path.join(atom.config.configDirPath, 'snippets'))).toBeTruthy() + expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy() expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy() describe ".loadUserConfig()", -> From cb0e0751d83713f1a54ee7a17fbda4d99f9c18a8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 12:58:46 -0800 Subject: [PATCH 094/111] Upgrade to snippets@0.21.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fff783d6..9a95d8d13 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "package-generator": "0.24.0", "release-notes": "0.17.0", "settings-view": "0.57.0", - "snippets": "0.20.0", + "snippets": "0.21.0", "spell-check": "0.20.0", "status-bar": "0.32.0", "styleguide": "0.21.0", From 5b39fc2e11e834deab64112020705e74da8d4b91 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 13:06:27 -0800 Subject: [PATCH 095/111] Upgrade to language-coffee-script@0.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a95d8d13..2bbb39cfa 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "wrap-guide": "0.12.0", "language-c": "0.2.0", "language-clojure": "0.1.0", - "language-coffee-script": "0.5.0", + "language-coffee-script": "0.6.0", "language-css": "0.2.0", "language-gfm": "0.12.0", "language-git": "0.3.0", From 0940668263402138b05bf06a7b3a3ad9635460f5 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 15:42:04 -0800 Subject: [PATCH 096/111] Add update-package-dependencies package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f67be14fe..e818d9f12 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "timecop": "0.13.0", "to-the-hubs": "0.18.0", "tree-view": "0.65.0", + "update-package-dependencies": "0.1.0", "visual-bell": "0.6.0", "welcome": "0.4.0", "whitespace": "0.10.0", From aceeb10a0db7b073e7d7b3ce478f9299d5f5b317 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 17:26:24 -0800 Subject: [PATCH 097/111] Upgrade to update-package-dependencies@0.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e818d9f12..88e233e03 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "timecop": "0.13.0", "to-the-hubs": "0.18.0", "tree-view": "0.65.0", - "update-package-dependencies": "0.1.0", + "update-package-dependencies": "0.2.0", "visual-bell": "0.6.0", "welcome": "0.4.0", "whitespace": "0.10.0", From 5708140f103f40f978f259294c12e0e5e9644a02 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 17:30:14 -0800 Subject: [PATCH 098/111] Use string interpolation --- docs/your-first-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index bafdcfecd..de03f483d 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -135,7 +135,7 @@ convert: -> if error console.error(error) else - selection.insertText("\n" + asciiArt + "\n") + selection.insertText("\n#{asciiArt}\n") ``` ## Further reading From 064a3843189dccfd3c66dd8c495fdad98b02300d Mon Sep 17 00:00:00 2001 From: probablycorey Date: Mon, 27 Jan 2014 17:40:19 -0800 Subject: [PATCH 099/111] Insert 'Hello, World!' text instead of uppercasing selection. --- docs/your-first-package.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index de03f483d..a1a20faa4 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -52,15 +52,13 @@ module.exports = convert: -> # This assumes the active pane item is an editor editor = atom.workspace.activePaneItem - selection = editor.getSelection() - upperCaseSelectedText = selection.getText().toUpperCase() - selection.insertText(upperCaseSelectedText) + editor.insertText('Hello, World!') ``` The `atom.workspaceView.command` method takes a command name and a callback. The callback executes when the command is triggered. In this case, when the command -is triggered the callback will call the `convert` method and uppercase the -selected text. +is triggered the callback will call the `convert` method and insert 'Hello, +World!'. ## Reload the Package @@ -128,7 +126,8 @@ uppercasing the text, you can convert it to ascii art! ```coffeescript convert: -> # This assumes the active pane item is an editor - selection = atom.workspace.activePaneItem.getSelection() + editor = atom.workspace.activePaneItem + selection = editor.getSelection() figlet = require 'figlet' figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) -> From 0f2931d0a29e88ed6323a7db625f537f5a1480ce Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 18:58:07 -0800 Subject: [PATCH 100/111] Add open-your-snippets command --- src/workspace-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index fffebd826..3e94d730b 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -107,6 +107,7 @@ class WorkspaceView extends View @command 'application:bring-all-windows-to-front', -> ipc.sendChannel('command', 'application:bring-all-windows-to-front') @command 'application:open-your-config', -> ipc.sendChannel('command', 'application:open-your-config') @command 'application:open-your-keymap', -> ipc.sendChannel('command', 'application:open-your-keymap') + @command 'application:open-your-snippets', -> ipc.sendChannel('command', 'application:open-your-snippets') @command 'application:open-your-stylesheet', -> ipc.sendChannel('command', 'application:open-your-stylesheet') @command 'window:install-shell-commands', => @installShellCommands() From 44999b0b37bef27e4cd690fb53135b54c5a25e6b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 27 Jan 2014 18:58:26 -0800 Subject: [PATCH 101/111] Upgrade to snippets@0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2bbb39cfa..3c8def908 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "package-generator": "0.24.0", "release-notes": "0.17.0", "settings-view": "0.57.0", - "snippets": "0.21.0", + "snippets": "0.22.0", "spell-check": "0.20.0", "status-bar": "0.32.0", "styleguide": "0.21.0", From 1550511e54b00844895f61cacf5dd1e3b77711cd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jan 2014 10:06:40 -0800 Subject: [PATCH 102/111] Write octicon codes to variables folder --- script/utils/update-octicons | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/utils/update-octicons b/script/utils/update-octicons index 30121e19a..b66402d88 100755 --- a/script/utils/update-octicons +++ b/script/utils/update-octicons @@ -24,7 +24,7 @@ fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest)) # Update Octicon UTF codes glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml') -octiconUtfDest = path.join atomDir, 'static', 'octicon-utf-codes.less' +octiconUtfDest = path.join atomDir, 'static', 'variables', 'octicon-utf-codes.less' output = [] for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString()) output.push "@#{css}: \"\\#{code}\";" From c1a9c3b5fb11db480072d843f4953fcfee053d2c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jan 2014 10:06:54 -0800 Subject: [PATCH 103/111] Update octicons --- static/octicons.less | 18 ++++++++++++++++++ static/octicons.woff | Bin 29768 -> 18292 bytes static/variables/octicon-utf-codes.less | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/static/octicons.less b/static/octicons.less index 267d8a675..32147c0fd 100644 --- a/static/octicons.less +++ b/static/octicons.less @@ -27,6 +27,7 @@ .make-icon(book); .make-icon(bookmark); .make-icon(broadcast); +.make-icon(browser); .make-icon(bug); .make-icon(calendar); .make-icon(check); @@ -46,6 +47,7 @@ .make-icon(comment-add); .make-icon(comment-discussion); .make-icon(credit-card); +.make-icon(dash); .make-icon(dashboard); .make-icon(database); .make-icon(device-camera); @@ -74,6 +76,7 @@ .make-icon(file-symlink-file); .make-icon(file-text); .make-icon(file-zip); +.make-icon(fold); .make-icon(gear); .make-icon(gift); .make-icon(gist); @@ -92,6 +95,7 @@ .make-icon(git-pull-request-abandoned); .make-icon(globe); .make-icon(graph); +.make-icon(heart); .make-icon(history); .make-icon(home); .make-icon(horizontal-rule); @@ -127,10 +131,14 @@ .make-icon(mail); .make-icon(mail-read); .make-icon(mail-reply); +.make-icon(mark-facebook); .make-icon(mark-github); .make-icon(mark-github-detail); +.make-icon(mark-google); .make-icon(mark-twitter); +.make-icon(markdown); .make-icon(megaphone); +.make-icon(mention); .make-icon(microscope); .make-icon(milestone); .make-icon(mirror-private); @@ -142,6 +150,7 @@ .make-icon(mute); .make-icon(mute-video); .make-icon(no-newline); +.make-icon(node-js); .make-icon(octoface); .make-icon(organization); .make-icon(pencil); @@ -150,11 +159,16 @@ .make-icon(person-follow); .make-icon(person-remove); .make-icon(pin); +.make-icon(playback-fast-forward); +.make-icon(playback-pause); +.make-icon(playback-play); +.make-icon(playback-rewind); .make-icon(plus); .make-icon(podium); .make-icon(primitive-dot); .make-icon(primitive-square); .make-icon(pulse); +.make-icon(puzzle); .make-icon(question); .make-icon(quote); .make-icon(radio-tower); @@ -175,18 +189,22 @@ .make-icon(screen-full); .make-icon(screen-normal); .make-icon(search); +.make-icon(search-save); .make-icon(server); .make-icon(settings); +.make-icon(split); .make-icon(squirrel); .make-icon(star); .make-icon(star-add); .make-icon(star-delete); +.make-icon(steps); .make-icon(stop); .make-icon(sync); .make-icon(tag); .make-icon(tag-add); .make-icon(tag-remove); .make-icon(telescope); +.make-icon(terminal); .make-icon(three-bars); .make-icon(tools); .make-icon(triangle-down); diff --git a/static/octicons.woff b/static/octicons.woff index 34a7f90f597fc800197a2f7cd28f557681116e42..bf0b57bdd3490d6b9ae1cbaf4c957297864f75ed 100644 GIT binary patch literal 18292 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 literal 29768 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 diff --git a/static/variables/octicon-utf-codes.less b/static/variables/octicon-utf-codes.less index 0f9dc7202..fc90117be 100644 --- a/static/variables/octicon-utf-codes.less +++ b/static/variables/octicon-utf-codes.less @@ -14,6 +14,7 @@ @book: "\f007"; @bookmark: "\f07b"; @broadcast: "\f048"; +@browser: "\f0c5"; @bug: "\f091"; @calendar: "\f068"; @check: "\f03a"; @@ -33,6 +34,7 @@ @comment-add: "\f06f"; @comment-discussion: "\f04f"; @credit-card: "\f045"; +@dash: "\f0ca"; @dashboard: "\f07d"; @database: "\f096"; @device-camera: "\f056"; @@ -61,6 +63,7 @@ @file-symlink-file: "\f0b0"; @file-text: "\f011"; @file-zip: "\f013"; +@fold: "\f0cc"; @gear: "\f02f"; @gift: "\f042"; @gist: "\f00e"; @@ -79,6 +82,7 @@ @git-pull-request-abandoned: "\f090"; @globe: "\f0b6"; @graph: "\f043"; +@heart: "\2665"; @history: "\f07e"; @home: "\f08d"; @horizontal-rule: "\f070"; @@ -114,10 +118,14 @@ @mail: "\f03b"; @mail-read: "\f03c"; @mail-reply: "\f051"; +@mark-facebook: "\f0ce"; @mark-github: "\f00a"; @mark-github-detail: "\f093"; +@mark-google: "\f0cd"; @mark-twitter: "\f0ae"; +@markdown: "\f0c9"; @megaphone: "\f077"; +@mention: "\f0be"; @microscope: "\f089"; @milestone: "\f075"; @mirror-private: "\f025"; @@ -129,19 +137,26 @@ @mute: "\f080"; @mute-video: "\f0b8"; @no-newline: "\f09c"; +@node-js: "\f0c3"; @octoface: "\f008"; @organization: "\f037"; +@package: "\f0c4"; @pencil: "\f058"; @person: "\f018"; @person-add: "\f01a"; @person-follow: "\f01c"; @person-remove: "\f01b"; @pin: "\f041"; +@playback-fast-forward: "\f0bd"; +@playback-pause: "\f0bb"; +@playback-play: "\f0bf"; +@playback-rewind: "\f0bc"; @plus: "\f05d"; @podium: "\f0af"; @primitive-dot: "\f052"; @primitive-square: "\f053"; @pulse: "\f085"; +@puzzle: "\f0c0"; @question: "\f02c"; @quote: "\f063"; @radio-tower: "\f030"; @@ -162,18 +177,22 @@ @screen-full: "\f066"; @screen-normal: "\f067"; @search: "\f02e"; +@search-save: "\f0cb"; @server: "\f097"; @settings: "\f07c"; +@split: "\f0c6"; @squirrel: "\f0b2"; @star: "\f02a"; @star-add: "\f082"; @star-delete: "\f083"; +@steps: "\f0c7"; @stop: "\f08f"; @sync: "\f087"; @tag: "\f015"; @tag-add: "\f054"; @tag-remove: "\f055"; @telescope: "\f088"; +@terminal: "\f0c8"; @three-bars: "\f05e"; @tools: "\f031"; @triangle-down: "\f05b"; From 0a2b1732703d49e749258ca208c14e5b37bf1dfe Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:11:11 -0800 Subject: [PATCH 104/111] Inline links --- docs/your-first-package.md | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index a1a20faa4..7ce228dfd 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -99,8 +99,8 @@ bindings so they only apply to specific elements. In this case, our binding is only active for elements matching the `.editor` selector. If the Tree View has focus, pressing `cmd-alt-a` won't trigger the `ascii-art:convert` command. But if the editor has focus, the `ascii-art:convert` method *will* be triggered. -More information on key bindings can be found in the [keymaps][keymaps] -documentation. +More information on key bindings can be found in the +[keymaps](advanced/keymaps.html) documentation. Now reload the window and verify that the key binding works! You can also verify that it **doesn't** work when the Tree View is focused. @@ -108,8 +108,9 @@ that it **doesn't** work when the Tree View is focused. ## Add the ASCII Art Now we need to convert the selected text to ascii art. To do this we will use -the [figlet](https://npmjs.org/package/figlet) node module from npm. Open -_package.json_ add the latest version of figlet to the dependencies: +the [figlet](https://npmjs.org/package/figlet) [node](http://nodejs.org/) module +from npm. Open _package.json_ add the latest version of figlet to the +dependencies: ```json "dependencies": { @@ -140,16 +141,4 @@ convert: -> ## Further reading For more information on the mechanics of packages, check out [Creating a -Package][creating-a-package]. - -[keymaps]: advanced/keymaps.html -[bundled-libs]: creating-a-package.html#included-libraries -[styleguide]: https://github.com/atom/styleguide -[space-pen]: https://github.com/atom/space-pen -[node]: http://nodejs.org/ -[path]: http://nodejs.org/docs/latest/api/path.html -[changer_file_view]: https://f.cloud.github.com/assets/69169/1441187/d7a7cb46-41a7-11e3-8128-d93f70a5d5c1.png -[changer_panel_append]: https://f.cloud.github.com/assets/69169/1441189/db0c74da-41a7-11e3-8286-b82dd9190c34.png -[changer_panel_timestamps]: https://f.cloud.github.com/assets/69169/1441190/dcc8eeb6-41a7-11e3-830f-1f1b33072fcd.png -[theme-vars]: theme-variables.html -[creating-a-package]: creating-a-package.html +Package](creating-a-package.html) From e31fab7fe4fdce2bd0f79e92b06182108b149997 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:21:58 -0800 Subject: [PATCH 105/111] Link to npm --- docs/your-first-package.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 7ce228dfd..3b8ccfc3e 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -109,8 +109,8 @@ that it **doesn't** work when the Tree View is focused. Now we need to convert the selected text to ascii art. To do this we will use the [figlet](https://npmjs.org/package/figlet) [node](http://nodejs.org/) module -from npm. Open _package.json_ add the latest version of figlet to the -dependencies: +from [npm](https://npmjs.org/). Open _package.json_ add the latest version of +figlet to the dependencies: ```json "dependencies": { From 885a4e2b2a72fc9e56eb54b1beea2f88d03de354 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:22:17 -0800 Subject: [PATCH 106/111] Document the `update-package-dependencies:update` command. --- docs/your-first-package.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 3b8ccfc3e..69a7618e1 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -118,8 +118,10 @@ figlet to the dependencies: } ``` -NOW GO TO THE COMMAND LINE AND RUN APM UPDATE BUT REALLY THIS STEP SEEMS LIKE -IT COULD BE AN ATOM COMMAND. +Save the file and then run the command 'update-package-dependencies:update' from +the Command Palette. This will install the all the node module a package needs +(only figlet in this case). You will need to run it whenever you update the +dependencies field in your _package.json_ file. Require the figlet node module in _lib/ascii-art.coffee_ and instead of uppercasing the text, you can convert it to ascii art! From e2bb4747fa0859f05cb0f641ebf4a568974fa320 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:23:50 -0800 Subject: [PATCH 107/111] :lipstick: --- docs/your-first-package.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 69a7618e1..51bd86672 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -118,13 +118,14 @@ figlet to the dependencies: } ``` -Save the file and then run the command 'update-package-dependencies:update' from -the Command Palette. This will install the all the node module a package needs -(only figlet in this case). You will need to run it whenever you update the -dependencies field in your _package.json_ file. +After saving the file run the command 'update-package-dependencies:update' from +the Command Palette. This will install the packages node module dependencies, +only figlet in this case. You will need to run +'update-package-dependencies:update' whenever you update the dependencies field +in your _package.json_ file. -Require the figlet node module in _lib/ascii-art.coffee_ and instead of -uppercasing the text, you can convert it to ascii art! +Now require the figlet node module in _lib/ascii-art.coffee_ and instead of +inserting 'Hello, World!' convert the selected text to ascii art! ```coffeescript convert: -> From 386767868eaffec73986d5da4db11965259733ac Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:24:53 -0800 Subject: [PATCH 108/111] Remove activation events comment I think we all agree that it would be better if we didn't have to deal with activationEvents. But for now I'm leaving this in because it is required. --- docs/your-first-package.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 51bd86672..30ff05428 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -74,8 +74,6 @@ its not there! To fix this open _package.json_ and find the property called delay a package's activation until it's needed. So add the `ascii-art:convert` to the activationEvents array: -IT SEEMS LIKE ACTIVATION EVENTS SHOULDN'T BE REQUIRED HERE. WHAT AM I MISSING? - ```coffeescript "activationEvents": ["ascii-art:convert"], ``` From ec3ca7e429c443f0f6cf25ae99901aacdc05ceda Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:27:06 -0800 Subject: [PATCH 109/111] Add an `and` --- docs/your-first-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 30ff05428..940a78ea0 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -107,7 +107,7 @@ that it **doesn't** work when the Tree View is focused. Now we need to convert the selected text to ascii art. To do this we will use the [figlet](https://npmjs.org/package/figlet) [node](http://nodejs.org/) module -from [npm](https://npmjs.org/). Open _package.json_ add the latest version of +from [npm](https://npmjs.org/). Open _package.json_ and add the latest version of figlet to the dependencies: ```json From 6ff553e41c29f49fe2f7eba706bf1e6c779b5326 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:32:31 -0800 Subject: [PATCH 110/111] Fix reload window text --- docs/your-first-package.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 940a78ea0..565b9204f 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -63,7 +63,7 @@ World!'. ## Reload the Package Before we can trigger `ascii-art:convert`, we need to load the latest code for -our package by reloading the window. Run the `window:reload` command from the +our package by reloading the window. Run the command `window:reload` from the command palette or by pressing `ctrl-alt-cmd-l`. ## Trigger the Command @@ -78,8 +78,8 @@ to the activationEvents array: "activationEvents": ["ascii-art:convert"], ``` -Now reload window `ctrl-alt-cmd-l` and use the command panel to trigger the -`ascii-art:convert` command. It will uppercase any text you have selected. +First, run reload the window by running the command `window:reload`. Now when +you run the `ascii-art:convert` command it will output 'Hello, World!' ## Add A Key Binding From 2590bad75fd6730da7a0800d376bc8ccc519f83d Mon Sep 17 00:00:00 2001 From: probablycorey Date: Tue, 28 Jan 2014 11:33:07 -0800 Subject: [PATCH 111/111] Use json to render code block --- docs/your-first-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 565b9204f..c1e0b991d 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -74,7 +74,7 @@ its not there! To fix this open _package.json_ and find the property called delay a package's activation until it's needed. So add the `ascii-art:convert` to the activationEvents array: -```coffeescript +```json "activationEvents": ["ascii-art:convert"], ```