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/package.json b/build/package.json index 29855c350..14b1437e4 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#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be", "first-mate": "~0.13.0", "formidable": "~1.0.14", "fs-plus": "1.x", @@ -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", @@ -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/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' diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 23cae1bb9..a2b591734 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -1,11 +1,25 @@ 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') - rm installDir - mkdir path.dirname(installDir) - cp shellAppDir, installDir + 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 + 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) + cp shellAppDir, installDir 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 diff --git a/docs/README.md b/docs/README.md index 9010e60f1..41be7406f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,12 +14,26 @@ 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' ``` +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 diff --git a/docs/advanced/configuration.md b/docs/advanced/configuration.md new file mode 100644 index 000000000..606432804 --- /dev/null +++ b/docs/advanced/configuration.md @@ -0,0 +1,76 @@ +## Configuration API + +### Reading Config Settings + +If you are writing a package that you want to make configurable, you'll need to +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 atom.config.get "editor.showInvisibles" +``` + +Or you can use the `::observeConfig` to track changes from any view object. + +```coffeescript +class MyView extends View + initialize: -> + @observeConfig 'editor.fontSize', () => + @adjustFontSize() +``` + +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. + +Subscriptions made with `observeConfig` are automatically canceled when the +view is removed. You can cancel config subscriptions manually via the +`unobserveConfig` method. + +```coffeescript +view1.unobserveConfig() # unobserve all properties +``` + +You can add the ability to observe config values to non-view classes by +extending their prototype with the `ConfigObserver` mixin: + +```coffeescript +{ConfigObserver} = require 'atom' + +class MyClass + ConfigObserver.includeInto(this) + + constructor: -> + @observeConfig 'editor.showInvisibles', -> # ... + + destroy: -> + @unobserveConfig() +``` + +### Writing Config Settings + +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 +atom.config.set("core.showInvisibles", true) +``` + +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. + +```coffeescript +atom.config.setDefaults("editor", fontSize: 18, showInvisibles: true) +``` 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. diff --git a/docs/advanced/keymaps.md b/docs/advanced/keymaps.md new file mode 100644 index 000000000..aaa5d398b --- /dev/null +++ b/docs/advanced/keymaps.md @@ -0,0 +1,123 @@ +# Keymaps In-Depth + +## Structure of a Keymap File + +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`: + +```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' + +'.editor:not(.mini)' + 'cmd-alt-[': 'editor:fold-current-row' + 'cmd-alt-]': 'editor:unfold-current-row' +``` + +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. + +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. + +### 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 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 + +Occasionally, it makes sense to layer multiple actions on top of the same key +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 representing the `snippets:expand` command. + +```coffee-script +# pseudo-code +editor.command 'snippets:expand', (e) => + if @cursorFollowsValidPrefix() + @expandSnippet() + else + e.abortKeyBinding() +``` + +When the event handler observes that the cursor does not follow a valid prefix, +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. 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 24072d822..183a5d28a 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. @@ -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/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. 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/docs/internals/configuration.md b/docs/internals/configuration.md deleted file mode 100644 index b70493dc1..000000000 --- a/docs/internals/configuration.md +++ /dev/null @@ -1,61 +0,0 @@ -## Configuration API - -### 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`: - -```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. - -```coffeescript -class MyView extends View - initialize: -> - @observeConfig 'editor.fontSize', () => - @adjustFontSize() -``` - -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. - -Subscriptions made with `observeConfig` are automatically canceled when the -view is removed. You can cancel config subscriptions manually via the -`unobserveConfig` method. - -```coffeescript -view1.unobserveConfig() # unobserve all properties -``` - -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 -``` - -### 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: - -```coffeescript -# basic key update -config.set("core.showInvisibles", true) - -config.pushAtKeyPath("core.disabledPackages", "wrap-guide") -``` - -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. - -```coffeescript -config.setDefaults("editor", fontSize: 18, showInvisibles: true) -``` diff --git a/docs/internals/keymaps.md b/docs/internals/keymaps.md deleted file mode 100644 index c346ff7dd..000000000 --- a/docs/internals/keymaps.md +++ /dev/null @@ -1,69 +0,0 @@ -## Keymaps In-Depth - -### 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. - -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: - -- `p` -- `2` -- `ctrl-p` -- `ctrl-alt-cmd-p` -- `tab` -- `escape` -- `enter` -- `ctrl-w w` - -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. - -### Rules for Mapping A Keydown Event to A Semantic Event - -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. - -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`. - -### 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). - -To achieve this, the snippets package makes use of the `abortKeyBinding` method -on the event object that's triggered by the binding for `tab`. - -```coffee-script -# pseudo-code -editor.command 'snippets:expand', (e) => - if @cursorFollowsValidPrefix() - @expandSnippet() - else - e.abortKeyBinding() -``` - -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. 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 diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 9f9901229..c1e0b991d 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -1,335 +1,145 @@ -# Creating Your First Package +# Create Your First Package -Let's take a look at creating your first package. +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: -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_. - -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! - -In order to demonstrate the capabilities of Atom and its API, our Changer plugin -is going to do two things: - -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 - -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: - -```coffeescript -'.tree-view': - 'ctrl-V': 'changer:magic' +``` + /\_ \ + ___ ___ ___\//\ \ + /'___\ / __`\ / __`\\ \ \ +/\ \__//\ \L\ \/\ \L\ \\_\ \_ +\ \____\ \____/\ \____//\____\ + \/____/\/___/ \/___/ \/____/ ``` -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. +The final package can be viewed at +[https://github.com/atom/ascii-art](https://github.com/atom/ascii-art). -`.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 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 it _ascii-art_. -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: +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 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 -atom.workspaceView.command "changer:magic", => @magic() + module.exports = + activate: -> ``` -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. +## Create a Command -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?! +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_: -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: +```coffeescript +module.exports = + activate: -> + atom.workspaceView.command "ascii-art:convert", => @convert() + + convert: -> + # This assumes the active pane item is an editor + editor = atom.workspace.activePaneItem + 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 insert 'Hello, +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 command `window:reload` from the +command palette or by pressing `ctrl-alt-cmd-l`. + +## 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: ```json -"activationEvents": ["changer:magic"] +"activationEvents": ["ascii-art:convert"], ``` -Hitting the key binding on the tree now works! +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!' -## Working with Styles +## Add A Key Binding -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: +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 -{$, View} = require 'atom' +'.editor': + 'cmd-alt-a': '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: +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](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. + +## 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](http://nodejs.org/) module +from [npm](https://npmjs.org/). Open _package.json_ and add the latest version of +figlet to the dependencies: + +```json + "dependencies": { + "figlet": "1.0.8" + } +``` + +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. + +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 -magic: -> - $('ol.entries li').each (i, el) -> - if !$(el).hasClass("status-modified") - console.log el +convert: -> + # This assumes the active pane item is an editor + editor = atom.workspace.activePaneItem + selection = editor.getSelection() + + figlet = require 'figlet' + figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) -> + if error + console.error(error) + else + selection.insertText("\n#{asciiArt}\n") ``` -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`: - -```coffeescript -magic: -> - $('ol.entries li').each (i, el) -> - if !$(el).hasClass("status-modified") - $(el).addClass("hide-me") -``` - -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: - -```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: - -```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") -``` - -## 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]. - -[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 +For more information on the mechanics of packages, check out [Creating a +Package](creating-a-package.html) 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' +# 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' 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/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' diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index facd6cc8e..a112bfbf7 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -1,4 +1,4 @@ -'.platform-darwin': +'body': # 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': +'body .native-key-bindings': 'cmd-z': 'native!' 'cmd-Z': 'native!' 'cmd-x': 'native!' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 60825bf16..3b6a230a5 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -1,4 +1,4 @@ -'.platform-win32': +'body': # 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': +'body .native-key-bindings': 'ctrl-z': 'native!' 'ctrl-Z': 'native!' 'ctrl-x': 'native!' diff --git a/menus/darwin.cson b/menus/darwin.cson index de22e7673..44e88dead 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -9,8 +9,11 @@ { 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' } + { type: 'separator' } { label: 'Hide Atom', command: 'application:hide' } { label: 'Hide Others', command: 'application:hide-other-applications' } { label: 'Show All', command: 'application:unhide-all-applications' } diff --git a/package.json b/package.json index 89324d063..9a8b9e760 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", @@ -16,7 +16,7 @@ "url": "http://github.com/atom/atom/raw/master/LICENSE.md" } ], - "atomShellVersion": "0.8.5", + "atomShellVersion": "0.8.7", "dependencies": { "async": "0.2.6", "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", @@ -28,15 +28,15 @@ "first-mate": "1.x", "fs-plus": "1.x", "fstream": "0.1.24", - "fuzzaldrin": "0.6.0", - "git-utils": "0.33.1", + "fuzzaldrin": "0.7.0", + "git-utils": "0.34.0", "guid": "0.0.10", "jasmine-tagged": "0.3.0", "mkdirp": "0.3.5", "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", @@ -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", @@ -86,27 +86,28 @@ "keybinding-resolver": "0.9.0", "link": "0.15.0", "markdown-preview": "0.25.1", - "metrics": "0.21.0", + "metrics": "0.24.0", "package-generator": "0.24.0", "release-notes": "0.17.0", "settings-view": "0.57.0", - "snippets": "0.20.0", + "snippets": "0.22.0", "spell-check": "0.20.0", "status-bar": "0.32.0", "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", + "to-the-hubs": "0.18.0", "tree-view": "0.65.0", + "update-package-dependencies": "0.2.0", "visual-bell": "0.6.0", "welcome": "0.4.0", "whitespace": "0.10.0", "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.6.0", "language-css": "0.2.0", "language-gfm": "0.12.0", "language-git": "0.3.0", diff --git a/script/copy-folder.cmd b/script/copy-folder.cmd new file mode 100644 index 000000000..b17473992 --- /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 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% 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); 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/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}\";" diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee index c6b8ee321..6bd66b4d3 100644 --- a/spec/command-installer-spec.coffee +++ b/spec/command-installer-spec.coffee @@ -4,33 +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) + installationPath = temp.mkdirSync("atom-bin") + installationFilePath = path.join(installationPath, commandName) beforeEach -> - spyOn(installer, 'findInstallDirectory').andCallFake (callback) -> - callback(directory) - - fs.removeSync(directory) if fs.existsSync(directory) + spyOn(installer, 'getInstallDirectory').andReturn installationPath 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(installationFilePath)).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(installationFilePath)).toBeTruthy() + expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath) + expect(fs.isExecutableSync(installationFilePath)).toBeTruthy() 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()", -> 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") diff --git a/spec/keymap-spec.coffee b/spec/keymap-spec.coffee index d976144ed..064c5b1c7 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", "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"') + + 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/atom.coffee b/src/atom.coffee index 848be9b3b..3cc30c31e 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. @@ -249,10 +249,12 @@ 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' + 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/browser/atom-application.coffee b/src/browser/atom-application.coffee index 149e87241..affabc0ae 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,14 +39,10 @@ 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 - # 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() @@ -103,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)) @@ -148,15 +149,18 @@ 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' 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() 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'] 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={}) -> 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/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) diff --git a/src/command-installer.coffee b/src/command-installer.coffee index aa6a9e803..a0374bf99 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -23,46 +23,32 @@ unlinkCommand = (destinationPath, callback) -> callback() module.exports = - findInstallDirectory: (callback) -> - directories = ['/opt/boxen', '/opt/github', '/usr/local'] - async.detect(directories, fs.isDirectory, callback) + getInstallDirectory: -> + "/usr/local/bin" - install: (commandPath, commandName, callback) -> - if not commandName? or _.isFunction(commandName) - callback = commandName - commandName = path.basename(commandPath, path.extname(commandPath)) + install: (commandPath, callback) -> + return unless process.platform is 'darwin' - installCallback = (error, sourcePath, destinationPath) -> - if error? - 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")) + commandName = path.basename(commandPath, path.extname(commandPath)) + directory = @getInstallDirectory() + if fs.existsSync(directory) + destinationPath = path.join(directory, commandName) + unlinkCommand destinationPath, (error) => + if error? + error = new Error "Could not remove file at #{destinationPath}." if error + callback?(error) + else + symlinkCommand commandPath, destinationPath, (error) => + error = new Error "Failed to symlink #{commandPath} to #{destinationPath}." if error + callback?(error) + else + error = new Error "Directory '#{directory} doesn't exist." + callback?(error) installAtomCommand: (resourcePath, callback) -> - if _.isFunction(resourcePath) - callback = resourcePath - resourcePath = null - - resourcePath ?= atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'atom.sh') - @install(commandPath, callback) + @install commandPath, callback installApmCommand: (resourcePath, callback) -> - if _.isFunction(resourcePath) - callback = resourcePath - resourcePath = null - - resourcePath ?= atom.getLoadSettings().resourcePath commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') - @install(commandPath, callback) + @install commandPath, callback 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/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/display-buffer.coffee b/src/display-buffer.coffee index 6974ad9ba..3c835863b 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 @@ -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 diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 35acb9b33..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' @@ -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: {} @@ -41,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' @@ -405,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'}) diff --git a/src/editor.coffee b/src/editor.coffee index a5aede5ed..753754be2 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) -> @@ -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() 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 dd795310a..b22466461 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 # @@ -15,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) @@ -250,12 +257,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. diff --git a/src/gutter.coffee b/src/gutter-view.coffee similarity index 97% rename from src/gutter.coffee rename to src/gutter-view.coffee index ff7926954..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 ### @@ -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 diff --git a/src/keymap.coffee b/src/keymap.coffee index e75faf17d..3c3a8ec44 100644 --- a/src/keymap.coffee +++ b/src/keymap.coffee @@ -142,7 +142,12 @@ 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']) + continue if path.basename(filePath, path.extname(filePath)) in otherPlatforms + @load(filePath) load: (path) -> @add(path, CSON.readFileSync(path)) 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] 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) 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/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 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) 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) 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. diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index b073a9b45..a1d9caa89 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,27 +19,26 @@ class ThemeManager @lessCache = null @packageManager.registerPackageActivator(this, ['theme']) - # Internal-only: getAvailableNames: -> # 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() - # Internal-only: + # Public: Get an array of all the active theme names. getActiveNames: -> theme.name for theme in @getActiveThemes() - # Internal-only: + # Public: Get an array of all the active themes. getActiveThemes: -> pack for pack in @packageManager.getActivePackages() when pack.isTheme() - # Internal-only: + # Public: Get an array of all the loaded themes. 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 +52,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 +67,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 +81,6 @@ class ThemeManager setEnabledThemes: (enabledThemeNames) -> atom.config.set('core.themes', enabledThemeNames) - # Public: getImportPaths: -> activeThemes = @getActiveThemes() if activeThemes.length > 0 @@ -98,7 +93,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 +101,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 +118,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 +153,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 +173,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 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) -> diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index f0a819cf4..a45b86b04 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' @@ -37,6 +38,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) @@ -101,8 +107,11 @@ 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() + @command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec')) @command 'window:increase-font-size', => @increaseFontSize() @command 'window:decrease-font-size', => @decreaseFontSize() @@ -122,6 +131,26 @@ class WorkspaceView extends View @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() + installShellCommands: -> + 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) => + if error? + showDialog(error) + else + CommandInstaller.installApmCommand resourcePath, (error) => + if error? + showDialog(error) + else + atom.confirm + message: "Commands installed." + detailedMessage: "The shell commands `atom` and `apm` are installed." + # Private: handleFocus: (e) -> if @getActivePane() 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 34a7f90f5..bf0b57bdd 100644 Binary files a/static/octicons.woff and b/static/octicons.woff differ 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"; diff --git a/vendor/apm b/vendor/apm index b80ef23ce..3f8701bfe 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit b80ef23ce8d2a1e8b4f40eb0f89c87f32dcc3415 +Subproject commit 3f8701bfe624de844641863391c04def9cca5c86