Merge branch 'master' into ns-workspace-specs-cleanup

This commit is contained in:
Nathan Sobo 2014-01-28 18:25:13 -07:00
commit 29f480661c
68 changed files with 823 additions and 798 deletions

View File

@ -1,4 +1,4 @@
# Atom — The hackable, ~~collaborative~~ editor # Atom — The hackable editor
![Atom](http://i.imgur.com/OrTvUAD.png) ![Atom](http://i.imgur.com/OrTvUAD.png)

View File

@ -7,7 +7,7 @@
}, },
"dependencies": { "dependencies": {
"async": "~0.2.9", "async": "~0.2.9",
"biscotto": "0.0.17", "biscotto": "git://github.com/atom/biscotto.git#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be",
"first-mate": "~0.13.0", "first-mate": "~0.13.0",
"formidable": "~1.0.14", "formidable": "~1.0.14",
"fs-plus": "1.x", "fs-plus": "1.x",
@ -19,7 +19,7 @@
"grunt-contrib-coffee": "~0.7.0", "grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-less": "~0.8.0", "grunt-contrib-less": "~0.8.0",
"grunt-cson": "0.5.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-lesslint": "0.13.0",
"grunt-markdown": "~0.4.0", "grunt-markdown": "~0.4.0",
"grunt-peg": "~1.1.0", "grunt-peg": "~1.1.0",
@ -30,6 +30,7 @@
"rcedit": "~0.1.2", "rcedit": "~0.1.2",
"request": "~2.27.0", "request": "~2.27.0",
"rimraf": "~2.2.2", "rimraf": "~2.2.2",
"runas": "~0.3.0",
"unzip": "~0.1.9", "unzip": "~0.1.9",
"vm-compatibility-layer": "~0.1.0", "vm-compatibility-layer": "~0.1.0",
"walkdir": "0.0.7" "walkdir": "0.0.7"

View File

@ -5,12 +5,14 @@ fs = require 'fs-plus'
request = require 'request' request = require 'request'
module.exports = (grunt) -> module.exports = (grunt) ->
{rm} = require('./task-helpers')(grunt)
cmd = path.join('node_modules', '.bin', 'coffee') cmd = path.join('node_modules', '.bin', 'coffee')
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--'] commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
opts = opts =
stdio: 'inherit' 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() done = @async()
downloadFileFromRepo = ({repo, file}, callback) -> downloadFileFromRepo = ({repo, file}, callback) ->
@ -34,6 +36,7 @@ module.exports = (grunt) ->
if error? if error?
done(error) done(error)
else else
rm('docs/output/api')
args = [ args = [
commonArgs... commonArgs...
'--title', 'Atom API Documentation' '--title', 'Atom API Documentation'

View File

@ -1,11 +1,25 @@
path = require 'path' path = require 'path'
module.exports = (grunt) -> 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', -> grunt.registerTask 'install', 'Install the built application', ->
installDir = grunt.config.get('atom.installDir') installDir = grunt.config.get('atom.installDir')
shellAppDir = grunt.config.get('atom.shellAppDir') shellAppDir = grunt.config.get('atom.shellAppDir')
rm installDir if process.platform is 'win32'
mkdir path.dirname(installDir) done = @async()
cp shellAppDir, installDir
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

View File

@ -41,7 +41,7 @@ module.exports = (grunt) ->
strings = strings =
CompanyName: 'GitHub, Inc.' CompanyName: 'GitHub, Inc.'
FileDescription: 'The hackable, collaborative editor' FileDescription: 'The hackable editor'
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved' LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
ProductName: 'Atom' ProductName: 'Atom'
ProductVersion: version ProductVersion: version

View File

@ -14,12 +14,26 @@ overview of the main editor API.
Check out the [Atom][Atom] class docs to see what globals are available and Check out the [Atom][Atom] class docs to see what globals are available and
what they provide. 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 ```coffee
{EditorView} = require 'atom' {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? ### How do I create a package?
You probably want to read the [creating a package][creating-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]. [here][node-docs].
[Atom]: ../classes/Atom.html [Atom]: ../classes/Atom.html
[BufferedProcess]: ../classes/BufferedProcess.html
[BufferedNodeProcess]: ../classes/BufferedNodeProcess.html
[Directory]: ../classes/Directory.html
[Editor]: ../classes/Editor.html [Editor]: ../classes/Editor.html
[EditorView]: ../classes/EditorView.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 [creating-a-package]: https://www.atom.io/docs/latest/creating-a-package
[node-docs]: http://nodejs.org/docs/v0.11.10/api [node-docs]: http://nodejs.org/docs/v0.11.10/api

View File

@ -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)
```

34
docs/advanced/globals.md Normal file
View File

@ -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.

123
docs/advanced/keymaps.md Normal file
View File

@ -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.

View File

@ -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. 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 example above, `changer:magic` command is executed when pressing `ctrl-V` on
the `.tree-view-scroller` element. 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 [apm]: https://github.com/atom/apm
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging [git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
[wrap-guide]: https://github.com/atom/wrap-guide/ [wrap-guide]: https://github.com/atom/wrap-guide/
[keymaps]: internals/keymaps.md [keymaps]: advanced/keymaps.md
[theme-variables]: theme-variables.md [theme-variables]: theme-variables.md
[tm-tokens]: http://manual.macromates.com/en/language_grammars.html [tm-tokens]: http://manual.macromates.com/en/language_grammars.html
[spacepen]: https://github.com/nathansobo/space-pen [spacepen]: https://github.com/nathansobo/space-pen

View File

@ -19,13 +19,11 @@ bindings][key-bindings] section.
### Working With Files ### Working With Files
Atom windows are scoped to the directory they're opened from. If you launch Atom Atom windows are scoped to a single directory on disk. If you launch Atom from
from the command line everything will be relative to the current directory. This the command line via the `atom` command and don't specify a path, Atom opens a
means that the tree view on the left will only show files contained within that window for the current working directory. The current window's directory will be
directory. visible as the root of the tree view at the left, and also serve as the context
for all file-related operations.
This can be a useful way to organize multiple projects, as each project will be
contained within its own window.
#### Finding Files #### 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 file that is already open press `cmd-b` to bring up a searchable list of open
files. files.
You can also use the tree view to navigate to a file. To open or move focus to You can also use the tree view to navigate to a file. To open and focus the
the tree view, press `cmd-\`. You can then navigate to a file using the arrow the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
keys and select it with `return`. `cmd-\`.
#### Adding, Moving, Deleting Files #### Adding, Moving, Deleting Files
Currently, all file modification is performed via the tree view. To add a file, Currently, all file modification is performed via the tree view. Add, move, or
select a directory in the tree view and press `a`. Then type the name of the delete a file by right-clicking in the tree view and selecting the desired
file. Any intermediate directories you type will be created automatically if operation from the context menu. You can also perform these operations from the
needed. keyboard by selecting a file or directory and using `a` to add, `m` to move, and
`delete` to delete.
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`.
### Searching ### Searching
@ -58,35 +53,43 @@ To search within a buffer use `cmd-f`. To search the entire project use
#### Navigating By Symbols #### Navigating By Symbols
If you want to jump to a method press `cmd-r`. It opens a list of all symbols To jump to a symbol such as a method definition, press `cmd-r`. This opens a
in the current file. 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 To search for symbols across your project, use `cmd-shift-r`. First you'll need
make sure you have a ctags installed and a tags file generated for your project. to make sure you have ctags installed and a tags file generated for your
Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags` project. Also, if you're editing CoffeeScript, it's a good idea to update your
file to understand the language. Here is [a good example][ctags]. `~/.ctags` file to understand the language. Here is [a good example][ctags].
### Split Panes ### Split Panes
You can split any editor pane horizontally or vertically by using `cmd-k right` or You can split any editor pane horizontally or vertically by using `cmd-k right`
`cmd-k down`. Once you have a split pane, you can move focus between them with or `cmd-k down`. Once you have a split pane, you can move focus between them
`cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all tabs inside it. 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 ### Folding
You can fold everything with `alt-cmd-{` and unfold everything with You can fold blocks of code by clicking the arrows that appear when you hover
`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and your mouse cursor over the gutter. You can also fold and unfold from the
`alt-cmd-]`. 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 ### Soft-Wrap
If you want to toggle soft wrap, trigger the command from the command palette. 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 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 ## 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 interface for adjusting config settings, installing packages and changing
themes. themes.

View File

@ -7,7 +7,7 @@
### Advanced Topics ### Advanced Topics
* [Configuration](internals/configuration.md) * [Configuration](advanced/configuration.md)
* [Keymaps](internals/keymaps.md) * [Keymaps](advanced/keymaps.md)
* [Serialization](internals/serialization.md) * [Serialization](advanced/serialization.md)
* [View System](internals/view-system.md) * [View System](advanced/view-system.md)

View File

@ -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)
```

View File

@ -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.

View File

@ -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?

View File

@ -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

View File

@ -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 /\ \__//\ \L\ \/\ \L\ \\_\ \_
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'
``` ```
Notice that the keybinding is called `ctrl-V` &mdash; that's actually `ctrl-shift-v`. The final package can be viewed at
You can use capital letters to denote using `shift` for your binding. [https://github.com/atom/ascii-art](https://github.com/atom/ascii-art).
`.tree-view` represents the parent container for the tree view. To begin, press `cmd-shift-P` to bring up the [Command
Keybindings only work within the context of where they're entered. In this case, Palette](https://github.com/atom/command-palette). Type "generate package" and
hitting `ctrl-V` anywhere other than tree won't do anything. Obviously, you can select the "Package Generator: Generate Package" command. Now we need to name
bind to any part of the editor using element, id, or class names. For example, the package. Let's call it _ascii-art_.
you can map to `body` if you want to scope to anywhere in Atom, or just `.editor`
for the editor portion.
To bind keybindings to a command, we'll need to do a bit of association in our Atom will open a new window with the contents of our new _ascii-art_ package
CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command displayed in the Tree View. Because this window is opened **after** the package
name and executes a callback function. Open up _lib/changer-view.coffee_, and is created, the ASCII Art package will be loaded and available in our new
change `atom.workspaceView.command "changer:toggle"` to look like this: 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 ```coffeescript
atom.workspaceView.command "changer:magic", => @magic() module.exports =
activate: ->
``` ```
It's common practice to namespace your commands with your package name, separated ## Create a Command
with a colon (`:`). Make sure to rename your `toggle` method to `magic` as well.
Every time you reload the Atom editor, changes to your package code will be reevaluated, Now let's add a command. We recommend that you namespace your commands with the
just as if you were writing a script for the browser. Reload the editor, click on package name followed by a `:`, so we'll call our command `ascii-art:convert`.
the tree, hit your keybinding, and...nothing happens! What the heck?! Register the command in _lib/ascii-art.coffee_:
Open up the _package.json_ file, and find the property called `activationEvents`. ```coffeescript
Basically, this key tells Atom to not load a package until it hears a certain event. module.exports =
Change the event to `changer:magic` and reload the editor: 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 ```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, Now let's add a key binding to trigger the `ascii-art:convert` command. Open
we'll first try and get a list of files that have not changed. _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:
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:
```coffeescript ```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 Notice `.editor` on the first line. Just like CSS, keymap selectors *scope* key
file that _wasn't_ modified: 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 ```coffeescript
magic: -> convert: ->
$('ol.entries li').each (i, el) -> # This assumes the active pane item is an editor
if !$(el).hasClass("status-modified") editor = atom.workspace.activePaneItem
console.log el 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("<li>#{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("<li>#{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 ## Further reading
For more information on the mechanics of packages, check out For more information on the mechanics of packages, check out [Creating a
[Creating a Package][creating-a-package]. Package](creating-a-package.html)
[bundled-libs]: creating-a-package.html#included-libraries
[styleguide]: https://github.com/atom/styleguide
[space-pen]: https://github.com/atom/space-pen
[node]: http://nodejs.org/
[path]: http://nodejs.org/docs/latest/api/path.html
[changer_file_view]: https://f.cloud.github.com/assets/69169/1441187/d7a7cb46-41a7-11e3-8128-d93f70a5d5c1.png
[changer_panel_append]: https://f.cloud.github.com/assets/69169/1441189/db0c74da-41a7-11e3-8286-b82dd9190c34.png
[changer_panel_timestamps]: https://f.cloud.github.com/assets/69169/1441190/dcc8eeb6-41a7-11e3-830f-1f1b33072fcd.png
[theme-vars]: theme-variables.html
[creating-a-package]: creating-a-package.html

15
dot-atom/snippets.cson Normal file
View File

@ -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'
#

View File

@ -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'

View File

@ -8,6 +8,7 @@ module.exports =
File: require '../src/file' File: require '../src/file'
fs: require 'fs-plus' fs: require 'fs-plus'
Git: require '../src/git' Git: require '../src/git'
ConfigObserver: require '../src/config-observer'
Point: Point Point: Point
Range: Range Range: Range

View File

@ -24,9 +24,6 @@
'ctrl-shift-up': 'editor:add-selection-above' 'ctrl-shift-up': 'editor:add-selection-above'
'ctrl-shift-down': 'editor:add-selection-below' 'ctrl-shift-down': 'editor:add-selection-below'
'.tool-panel':
'escape': 'core:close'
'.tool-panel.panel-left, .tool-panel.panel-right': '.tool-panel.panel-left, .tool-panel.panel-right':
'escape': 'tool-panel:unfocus' 'escape': 'tool-panel:unfocus'

View File

@ -1,4 +1,4 @@
'.platform-darwin': 'body':
# Apple specific # Apple specific
'cmd-q': 'application:quit' 'cmd-q': 'application:quit'
'cmd-h': 'application:hide' 'cmd-h': 'application:hide'
@ -87,7 +87,7 @@
'cmd-8': 'pane:show-item-8' 'cmd-8': 'pane:show-item-8'
'cmd-9': 'pane:show-item-9' 'cmd-9': 'pane:show-item-9'
'.platform-darwin .editor': '.editor':
# Apple Specific # Apple Specific
'cmd-backspace': 'editor:backspace-to-beginning-of-line' 'cmd-backspace': 'editor:backspace-to-beginning-of-line'
'cmd-delete': '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-k cmd-l': 'editor:lower-case'
'cmd-l': 'editor:select-line' 'cmd-l': 'editor:select-line'
'body.platform-darwin .editor:not(.mini)': '.workspace .editor:not(.mini)':
# Atom specific # Atom specific
'alt-cmd-z': 'editor:checkout-head-revision' 'alt-cmd-z': 'editor:checkout-head-revision'
'cmd-<': 'editor:scroll-to-cursor' 'cmd-<': 'editor:scroll-to-cursor'
@ -148,7 +148,7 @@
'cmd-k cmd-9': 'editor:fold-at-indent-level-9' 'cmd-k cmd-9': 'editor:fold-at-indent-level-9'
# allow standard input fields to work correctly # allow standard input fields to work correctly
'body.platform-darwin .native-key-bindings': 'body .native-key-bindings':
'cmd-z': 'native!' 'cmd-z': 'native!'
'cmd-Z': 'native!' 'cmd-Z': 'native!'
'cmd-x': 'native!' 'cmd-x': 'native!'

View File

@ -1,4 +1,4 @@
'.platform-win32': 'body':
# Atom Specific # Atom Specific
'enter': 'core:confirm' 'enter': 'core:confirm'
'escape': 'core:cancel' 'escape': 'core:cancel'
@ -50,7 +50,7 @@
'ctrl-k ctrl-left': 'window:focus-previous-pane' 'ctrl-k ctrl-left': 'window:focus-previous-pane'
'ctrl-k ctrl-right': 'window:focus-next-pane' 'ctrl-k ctrl-right': 'window:focus-next-pane'
'.platform-win32 .editor': '.workspace .editor':
# Windows specific # Windows specific
'ctrl-delete': 'editor:backspace-to-beginning-of-word' 'ctrl-delete': 'editor:backspace-to-beginning-of-word'
@ -60,7 +60,7 @@
'ctrl-k ctrl-u': 'editor:upper-case' 'ctrl-k ctrl-u': 'editor:upper-case'
'ctrl-k ctrl-l': 'editor:lower-case' 'ctrl-k ctrl-l': 'editor:lower-case'
'.platform-win32 .editor:not(.mini)': '.workspace .editor:not(.mini)':
# Atom specific # Atom specific
'alt-ctrl-z': 'editor:checkout-head-revision' 'alt-ctrl-z': 'editor:checkout-head-revision'
'ctrl-<': 'editor:scroll-to-cursor' 'ctrl-<': 'editor:scroll-to-cursor'
@ -94,7 +94,7 @@
'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9' 'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9'
# allow standard input fields to work correctly # 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-Z': 'native!' 'ctrl-Z': 'native!'
'ctrl-x': 'native!' 'ctrl-x': 'native!'

View File

@ -9,8 +9,11 @@
{ label: 'Preferences...', command: 'application:show-settings' } { label: 'Preferences...', command: 'application:show-settings' }
{ label: 'Open Your Config', command: 'application:open-your-config' } { label: 'Open Your Config', command: 'application:open-your-config' }
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' } { 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' } { label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
{ type: 'separator' } { type: 'separator' }
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
{ type: 'separator' }
{ label: 'Hide Atom', command: 'application:hide' } { label: 'Hide Atom', command: 'application:hide' }
{ label: 'Hide Others', command: 'application:hide-other-applications' } { label: 'Hide Others', command: 'application:hide-other-applications' }
{ label: 'Show All', command: 'application:unhide-all-applications' } { label: 'Show All', command: 'application:unhide-all-applications' }

View File

@ -1,7 +1,7 @@
{ {
"name": "atom", "name": "atom",
"productName": "Atom", "productName": "Atom",
"version": "0.48.0", "version": "0.49.0",
"main": "./src/browser/main.js", "main": "./src/browser/main.js",
"repository": { "repository": {
"type": "git", "type": "git",
@ -16,7 +16,7 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md" "url": "http://github.com/atom/atom/raw/master/LICENSE.md"
} }
], ],
"atomShellVersion": "0.8.5", "atomShellVersion": "0.8.7",
"dependencies": { "dependencies": {
"async": "0.2.6", "async": "0.2.6",
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
@ -28,15 +28,15 @@
"first-mate": "1.x", "first-mate": "1.x",
"fs-plus": "1.x", "fs-plus": "1.x",
"fstream": "0.1.24", "fstream": "0.1.24",
"fuzzaldrin": "0.6.0", "fuzzaldrin": "0.7.0",
"git-utils": "0.33.1", "git-utils": "0.34.0",
"guid": "0.0.10", "guid": "0.0.10",
"jasmine-tagged": "0.3.0", "jasmine-tagged": "0.3.0",
"mkdirp": "0.3.5", "mkdirp": "0.3.5",
"keytar": "0.15.1", "keytar": "0.15.1",
"less-cache": "0.11.0", "less-cache": "0.11.0",
"mixto": "1.x", "mixto": "1.x",
"nslog": "0.3.0", "nslog": "0.4.0",
"oniguruma": "1.x", "oniguruma": "1.x",
"optimist": "0.4.0", "optimist": "0.4.0",
"pathwatcher": "0.14.2", "pathwatcher": "0.14.2",
@ -75,7 +75,7 @@
"editor-stats": "0.12.0", "editor-stats": "0.12.0",
"exception-reporting": "0.12.0", "exception-reporting": "0.12.0",
"feedback": "0.22.0", "feedback": "0.22.0",
"find-and-replace": "0.79.0", "find-and-replace": "0.80.0",
"fuzzy-finder": "0.31.0", "fuzzy-finder": "0.31.0",
"gists": "0.15.0", "gists": "0.15.0",
"git-diff": "0.23.0", "git-diff": "0.23.0",
@ -86,27 +86,28 @@
"keybinding-resolver": "0.9.0", "keybinding-resolver": "0.9.0",
"link": "0.15.0", "link": "0.15.0",
"markdown-preview": "0.25.1", "markdown-preview": "0.25.1",
"metrics": "0.21.0", "metrics": "0.24.0",
"package-generator": "0.24.0", "package-generator": "0.24.0",
"release-notes": "0.17.0", "release-notes": "0.17.0",
"settings-view": "0.57.0", "settings-view": "0.57.0",
"snippets": "0.20.0", "snippets": "0.22.0",
"spell-check": "0.20.0", "spell-check": "0.20.0",
"status-bar": "0.32.0", "status-bar": "0.32.0",
"styleguide": "0.21.0", "styleguide": "0.21.0",
"symbols-view": "0.29.0", "symbols-view": "0.29.0",
"tabs": "0.18.0", "tabs": "0.18.0",
"terminal": "0.26.0", "terminal": "0.27.0",
"timecop": "0.13.0", "timecop": "0.13.0",
"to-the-hubs": "0.17.0", "to-the-hubs": "0.18.0",
"tree-view": "0.65.0", "tree-view": "0.65.0",
"update-package-dependencies": "0.2.0",
"visual-bell": "0.6.0", "visual-bell": "0.6.0",
"welcome": "0.4.0", "welcome": "0.4.0",
"whitespace": "0.10.0", "whitespace": "0.10.0",
"wrap-guide": "0.12.0", "wrap-guide": "0.12.0",
"language-c": "0.2.0", "language-c": "0.2.0",
"language-clojure": "0.1.0", "language-clojure": "0.1.0",
"language-coffee-script": "0.4.0", "language-coffee-script": "0.6.0",
"language-css": "0.2.0", "language-css": "0.2.0",
"language-gfm": "0.12.0", "language-gfm": "0.12.0",
"language-git": "0.3.0", "language-git": "0.3.0",

18
script/copy-folder.cmd Normal file
View File

@ -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

View File

@ -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%

View File

@ -2,8 +2,8 @@
var cp = require('./utils/child-process-wrapper.js'); var cp = require('./utils/child-process-wrapper.js');
var path = require('path'); var path = require('path');
// node build/node_modules/grunt-cli/bin/grunt "$@" // node build/node_modules/.bin/grunt "$@"
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', 'grunt-cli', 'bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : ''); var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
var args = [gruntPath, '--gruntfile', path.resolve('build', 'Gruntfile.coffee')]; var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
args = args.concat(process.argv.slice(2)); args = args.concat(process.argv.slice(2));
cp.safeSpawn(process.execPath, args, process.exit); cp.safeSpawn(gruntPath, args, process.exit);

View File

@ -3,9 +3,8 @@
path = require 'path' path = require 'path'
CommandInstaller = require '../src/command-installer' CommandInstaller = require '../src/command-installer'
callback = (error, sourcePath, destinationPath) -> callback = (error) ->
unless error? console.warn error.message if error?
console.log "#{sourcePath} intalled to #{destinationPath}"
CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback) CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback)
CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback) CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback)

View File

@ -24,7 +24,7 @@ fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
# Update Octicon UTF codes # Update Octicon UTF codes
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml') 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 = [] output = []
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString()) for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
output.push "@#{css}: \"\\#{code}\";" output.push "@#{css}: \"\\#{code}\";"

View File

@ -4,33 +4,31 @@ temp = require 'temp'
installer = require '../src/command-installer' installer = require '../src/command-installer'
describe "install(commandPath, callback)", -> describe "install(commandPath, callback)", ->
directory = path.join(temp.dir, 'install-atom-command', 'atom') commandFilePath = temp.openSync("atom-command").path
commandPath = path.join(directory, 'source') commandName = path.basename(commandFilePath)
destinationPath = path.join(directory, 'bin', 'source') installationPath = temp.mkdirSync("atom-bin")
installationFilePath = path.join(installationPath, commandName)
beforeEach -> beforeEach ->
spyOn(installer, 'findInstallDirectory').andCallFake (callback) -> spyOn(installer, 'getInstallDirectory').andReturn installationPath
callback(directory)
fs.removeSync(directory) if fs.existsSync(directory)
describe "on #darwin", -> describe "on #darwin", ->
it "symlinks the command and makes it executable", -> it "symlinks the command and makes it executable", ->
fs.writeFileSync(commandPath, 'test') expect(fs.isFileSync(commandFilePath)).toBeTruthy()
expect(fs.isFileSync(commandPath)).toBeTruthy() expect(fs.isExecutableSync(commandFilePath)).toBeFalsy()
expect(fs.isExecutableSync(commandPath)).toBeFalsy() expect(fs.isFileSync(installationFilePath)).toBeFalsy()
expect(fs.isFileSync(destinationPath)).toBeFalsy()
installDone = false installDone = false
installError = null installError = null
installer.install commandPath, (error) -> installer.install commandFilePath, (error) ->
installDone = true installDone = true
installError = error installError = error
waitsFor -> installDone waitsFor ->
installDone
runs -> runs ->
expect(installError).toBeNull() expect(installError).toBeNull()
expect(fs.isFileSync(destinationPath)).toBeTruthy() expect(fs.isFileSync(installationFilePath)).toBeTruthy()
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath) expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath)
expect(fs.isExecutableSync(destinationPath)).toBeTruthy() expect(fs.isExecutableSync(installationFilePath)).toBeTruthy()

View File

@ -218,7 +218,7 @@ describe "Config", ->
runs -> runs ->
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy() 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, '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() expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
describe ".loadUserConfig()", -> describe ".loadUserConfig()", ->

View File

@ -63,7 +63,7 @@ describe 'File', ->
afterEach -> afterEach ->
if fs.existsSync(newPath) if fs.existsSync(newPath)
fs.removeSync(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", -> it "it updates its path", ->
jasmine.unspy(window, "setTimeout") jasmine.unspy(window, "setTimeout")

View File

@ -354,6 +354,27 @@ describe "Keymap", ->
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el) bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
expect(bindings).toHaveLength 0 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", -> describe "when the user keymap file is changed", ->
it "is reloaded", -> it "is reloaded", ->
keymapFilePath = path.join(configDirPath, "keymap.cson") keymapFilePath = path.join(configDirPath, "keymap.cson")

View File

@ -34,7 +34,7 @@ WindowEventHandler = require './window-event-handler'
# * `atom.themes` - A {ThemeManager} instance # * `atom.themes` - A {ThemeManager} instance
module.exports = module.exports =
class Atom extends Model 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 # - mode: Pass 'editor' or 'spec' depending on the kind of environment you
# want to build. # want to build.
@ -249,10 +249,12 @@ class Atom extends Model
# Private: Call this method when establishing a real application window. # Private: Call this method when establishing a real application window.
startEditorWindow: -> startEditorWindow: ->
if process.platform is 'darwin' CommandInstaller = require './command-installer'
CommandInstaller = require './command-installer' resourcePath = atom.getLoadSettings().resourcePath
CommandInstaller.installAtomCommand() CommandInstaller.installAtomCommand resourcePath, (error) ->
CommandInstaller.installApmCommand() console.warn error.message if error?
CommandInstaller.installApmCommand resourcePath, (error) ->
console.warn error.message if error?
@restoreWindowDimensions() @restoreWindowDimensions()
@config.load() @config.load()

View File

@ -15,7 +15,11 @@ url = require 'url'
{EventEmitter} = require 'events' {EventEmitter} = require 'events'
_ = require 'underscore-plus' _ = 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. # 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 # 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 # or atom-shell, before it's fixed we check the existence of socketPath to
# speedup startup. # speedup startup.
if (not fs.existsSync socketPath) or options.test if (process.platform isnt 'win32' and not fs.existsSync socketPath) or options.test
createAtomApplication() createAtomApplication()
return 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 = net.connect {path: socketPath}, ->
client.write JSON.stringify(options), -> client.write JSON.stringify(options), ->
client.end() client.end()
@ -103,7 +103,8 @@ class AtomApplication
# the other launches will just pass their information to this server and then # the other launches will just pass their information to this server and then
# close immediately. # close immediately.
listenForArgumentsFromNewProcess: -> listenForArgumentsFromNewProcess: ->
fs.unlinkSync socketPath if fs.existsSync(socketPath) if process.platform isnt 'win32' and fs.existsSync(socketPath)
fs.unlinkSync socketPath
server = net.createServer (connection) => server = net.createServer (connection) =>
connection.on 'data', (data) => connection.on 'data', (data) =>
@openWithOptions(JSON.parse(data)) @openWithOptions(JSON.parse(data))
@ -148,15 +149,18 @@ class AtomApplication
@on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new') @on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new')
@openPathOnEvent('application:show-settings', 'atom://config') @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-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.on 'window-all-closed', ->
app.quit() if process.platform is 'win32' app.quit() if process.platform is 'win32'
app.on 'will-quit', => 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) => app.on 'open-file', (event, pathToOpen) =>
event.preventDefault() event.preventDefault()

View File

@ -74,6 +74,8 @@ class AtomWindow
global.atomApplication.removeWindow(this) global.atomApplication.removeWindow(this)
@browserWindow.on 'unresponsive', => @browserWindow.on 'unresponsive', =>
return if @isSpec
chosen = dialog.showMessageBox @browserWindow, chosen = dialog.showMessageBox @browserWindow,
type: 'warning' type: 'warning'
buttons: ['Close', 'Keep Waiting'] buttons: ['Close', 'Keep Waiting']

View File

@ -7,16 +7,14 @@ fs = require 'fs'
module = require 'module' module = require 'module'
path = require 'path' path = require 'path'
optimist = require 'optimist' optimist = require 'optimist'
# TODO: NSLog is missing .lib on windows nslog = require 'nslog'
nslog = require 'nslog' unless process.platform is 'win32'
dialog = require 'dialog' dialog = require 'dialog'
console.log = (args...) -> console.log = (args...) ->
# TODO: Make NSLog work as expected # TODO: Make NSLog work as expected
output = args.map((arg) -> JSON.stringify(arg)).join(" ") output = args.map((arg) -> JSON.stringify(arg)).join(" ")
if process.platform == 'darwin' nslog(output)
nslog(output) if process.platform isnt 'darwin'
else
fs.writeFileSync('debug.log', output, flag: 'a') fs.writeFileSync('debug.log', output, flag: 'a')
process.on 'uncaughtException', (error={}) -> process.on 'uncaughtException', (error={}) ->

View File

@ -6,6 +6,12 @@ path = require 'path'
# #
# This may seem unnecessary but on Windows we have to have separate executables # 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. # for each script without this since Windows doesn't support shebang strings.
#
# ## Requiring in packages
#
# ```coffee
# {BufferedNodeProcess} = require 'atom'
# ```
module.exports = module.exports =
class BufferedNodeProcess extends BufferedProcess class BufferedNodeProcess extends BufferedProcess
# Executes the given Node script. # Executes the given Node script.

View File

@ -1,6 +1,12 @@
ChildProcess = require 'child_process' ChildProcess = require 'child_process'
# Public: A wrapper which provides line buffering for Node's ChildProcess. # Public: A wrapper which provides line buffering for Node's ChildProcess.
#
# ## Requiring in packages
#
# ```coffee
# {BufferedProcess} = require 'atom'
# ```
module.exports = module.exports =
class BufferedProcess class BufferedProcess
process: null process: null

View File

@ -22,7 +22,10 @@ getCachedJavaScript = (cachePath) ->
fs.readFileSync(cachePath, 'utf8') if stat.isFile() fs.readFileSync(cachePath, 'utf8') if stat.isFile()
compileCoffeeScript = (coffee, filePath, cachePath) -> 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 try
mkdir(path.dirname(cachePath)) mkdir(path.dirname(cachePath))
fs.writeFileSync(cachePath, js) fs.writeFileSync(cachePath, js)

View File

@ -23,46 +23,32 @@ unlinkCommand = (destinationPath, callback) ->
callback() callback()
module.exports = module.exports =
findInstallDirectory: (callback) -> getInstallDirectory: ->
directories = ['/opt/boxen', '/opt/github', '/usr/local'] "/usr/local/bin"
async.detect(directories, fs.isDirectory, callback)
install: (commandPath, commandName, callback) -> install: (commandPath, callback) ->
if not commandName? or _.isFunction(commandName) return unless process.platform is 'darwin'
callback = commandName
commandName = path.basename(commandPath, path.extname(commandPath))
installCallback = (error, sourcePath, destinationPath) -> commandName = path.basename(commandPath, path.extname(commandPath))
if error? directory = @getInstallDirectory()
console.warn "Failed to install `#{commandName}` binary", error if fs.existsSync(directory)
callback?(error, sourcePath, destinationPath) destinationPath = path.join(directory, commandName)
unlinkCommand destinationPath, (error) =>
@findInstallDirectory (directory) -> if error?
if directory? error = new Error "Could not remove file at #{destinationPath}." if error
destinationPath = path.join(directory, 'bin', commandName) callback?(error)
unlinkCommand destinationPath, (error) -> else
if error? symlinkCommand commandPath, destinationPath, (error) =>
installCallback(error) error = new Error "Failed to symlink #{commandPath} to #{destinationPath}." if error
else callback?(error)
symlinkCommand commandPath, destinationPath, (error) -> else
installCallback(error, commandPath, destinationPath) error = new Error "Directory '#{directory} doesn't exist."
else callback?(error)
installCallback(new Error("No destination directory exists to install"))
installAtomCommand: (resourcePath, callback) -> installAtomCommand: (resourcePath, callback) ->
if _.isFunction(resourcePath)
callback = resourcePath
resourcePath = null
resourcePath ?= atom.getLoadSettings().resourcePath
commandPath = path.join(resourcePath, 'atom.sh') commandPath = path.join(resourcePath, 'atom.sh')
@install(commandPath, callback) @install commandPath, callback
installApmCommand: (resourcePath, callback) -> installApmCommand: (resourcePath, callback) ->
if _.isFunction(resourcePath)
callback = resourcePath
resourcePath = null
resourcePath ?= atom.getLoadSettings().resourcePath
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm') commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
@install(commandPath, callback) @install commandPath, callback

View File

@ -1,4 +1,7 @@
Mixin = require 'mixto'
module.exports = module.exports =
class ConfigObserver extends Mixin
observeConfig: (keyPath, args...) -> observeConfig: (keyPath, args...) ->
@configSubscriptions ?= {} @configSubscriptions ?= {}
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...) @configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)

View File

@ -7,7 +7,13 @@ pathWatcher = require 'pathwatcher'
File = require './file' 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 = module.exports =
class Directory class Directory
Emitter.includeInto(this) Emitter.includeInto(this)

View File

@ -15,7 +15,7 @@ ConfigObserver = require './config-observer'
module.exports = module.exports =
class DisplayBuffer extends Model class DisplayBuffer extends Model
Serializable.includeInto(this) Serializable.includeInto(this)
_.extend @prototype, ConfigObserver ConfigObserver.includeInto(this)
@properties @properties
softWrap: null softWrap: null
@ -297,8 +297,12 @@ class DisplayBuffer extends Model
[startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row)
for screenRow in [startScreenRow...endScreenRow] for screenRow in [startScreenRow...endScreenRow]
unless screenLine = @screenLines[screenRow] 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() maxBufferColumn = screenLine.getMaxBufferColumn()
if screenLine.isSoftWrapped() and column > maxBufferColumn if screenLine.isSoftWrapped() and column > maxBufferColumn
continue continue

View File

@ -1,6 +1,6 @@
{View, $, $$$} = require './space-pen-extensions' {View, $, $$$} = require './space-pen-extensions'
TextBuffer = require './text-buffer' TextBuffer = require './text-buffer'
Gutter = require './gutter' GutterView = require './gutter-view'
{Point, Range} = require 'text-buffer' {Point, Range} = require 'text-buffer'
Editor = require './editor' Editor = require './editor'
CursorView = require './cursor-view' CursorView = require './cursor-view'
@ -16,6 +16,12 @@ LongLineLength = 1000
# Public: Represents the entire visual pane in Atom. # Public: Represents the entire visual pane in Atom.
# #
# The EditorView manages the {Editor}, which manages the file buffers. # The EditorView manages the {Editor}, which manages the file buffers.
#
# ## Requiring in packages
#
# ```coffee
# {EditorView} = require 'atom'
# ```
module.exports = module.exports =
class EditorView extends View class EditorView extends View
@characterWidthCache: {} @characterWidthCache: {}
@ -41,7 +47,7 @@ class EditorView extends View
attributes = { class: @classes(params), tabindex: -1 } attributes = { class: @classes(params), tabindex: -1 }
_.extend(attributes, params.attributes) if params.attributes _.extend(attributes, params.attributes) if params.attributes
@div attributes, => @div attributes, =>
@subview 'gutter', new Gutter @subview 'gutter', new GutterView
@div class: 'scroll-view', outlet: 'scrollView', => @div class: 'scroll-view', outlet: 'scrollView', =>
@div class: 'overlayer', outlet: 'overlayer' @div class: 'overlayer', outlet: 'overlayer'
@div class: 'lines', outlet: 'renderedLines' @div class: 'lines', outlet: 'renderedLines'
@ -405,7 +411,7 @@ class EditorView extends View
selectedText = null selectedText = null
@hiddenInput.on 'compositionstart', => @hiddenInput.on 'compositionstart', =>
selectedText = @getSelectedText() selectedText = @editor.getSelectedText()
@hiddenInput.css('width', '100%') @hiddenInput.css('width', '100%')
@hiddenInput.on 'compositionupdate', (e) => @hiddenInput.on 'compositionupdate', (e) =>
@editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'}) @editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'})

View File

@ -202,8 +202,8 @@ class Editor extends Model
# Deprecated: Use the ::scrollLeft property directly # Deprecated: Use the ::scrollLeft property directly
getScrollLeft: -> @scrollLeft getScrollLeft: -> @scrollLeft
# Set the number of characters that can be displayed horizontally in the # Public: Set the number of characters that can be displayed horizontally in
# editor that contains this edit session. # the editor.
# #
# editorWidthInChars - A {Number} of characters # editorWidthInChars - A {Number} of characters
setEditorWidthInChars: (editorWidthInChars) -> setEditorWidthInChars: (editorWidthInChars) ->
@ -765,27 +765,27 @@ class Editor extends Model
findMarkers: (attributes) -> findMarkers: (attributes) ->
@displayBuffer.findMarkers(attributes) @displayBuffer.findMarkers(attributes)
# {Delegates to: DisplayBuffer.markScreenRange} # Public: {Delegates to: DisplayBuffer.markScreenRange}
markScreenRange: (args...) -> markScreenRange: (args...) ->
@displayBuffer.markScreenRange(args...) @displayBuffer.markScreenRange(args...)
# {Delegates to: DisplayBuffer.markBufferRange} # Public: {Delegates to: DisplayBuffer.markBufferRange}
markBufferRange: (args...) -> markBufferRange: (args...) ->
@displayBuffer.markBufferRange(args...) @displayBuffer.markBufferRange(args...)
# {Delegates to: DisplayBuffer.markScreenPosition} # Public: {Delegates to: DisplayBuffer.markScreenPosition}
markScreenPosition: (args...) -> markScreenPosition: (args...) ->
@displayBuffer.markScreenPosition(args...) @displayBuffer.markScreenPosition(args...)
# {Delegates to: DisplayBuffer.markBufferPosition} # Public: {Delegates to: DisplayBuffer.markBufferPosition}
markBufferPosition: (args...) -> markBufferPosition: (args...) ->
@displayBuffer.markBufferPosition(args...) @displayBuffer.markBufferPosition(args...)
# {Delegates to: DisplayBuffer.destroyMarker} # Public: {Delegates to: DisplayBuffer.destroyMarker}
destroyMarker: (args...) -> destroyMarker: (args...) ->
@displayBuffer.destroyMarker(args...) @displayBuffer.destroyMarker(args...)
# {Delegates to: DisplayBuffer.getMarkerCount} # Public: {Delegates to: DisplayBuffer.getMarkerCount}
getMarkerCount: -> getMarkerCount: ->
@buffer.getMarkerCount() @buffer.getMarkerCount()

View File

@ -10,6 +10,12 @@ fs = require 'fs-plus'
# #
# You should probably create a {Directory} and access the {File} objects that # You should probably create a {Directory} and access the {File} objects that
# it creates, rather than instantiating the {File} class directly. # it creates, rather than instantiating the {File} class directly.
#
# ## Requiring in packages
#
# ```coffee
# {File} = require 'atom'
# ```
module.exports = module.exports =
class File class File
Emitter.includeInto(this) Emitter.includeInto(this)

View File

@ -7,7 +7,8 @@ GitUtils = require 'git-utils'
# Public: Represents the underlying git operations performed by Atom. # Public: Represents the underlying git operations performed by Atom.
# #
# This class shouldn't be instantiated directly but instead by accessing the # 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 # ## Example
# #
@ -15,6 +16,12 @@ GitUtils = require 'git-utils'
# git = atom.project.getRepo() # git = atom.project.getRepo()
# console.log git.getOriginUrl() # console.log git.getOriginUrl()
# ``` # ```
#
# ## Requiring in packages
#
# ```coffee
# {Git} = require 'atom'
# ```
module.exports = module.exports =
class Git class Git
Emitter.includeInto(this) Emitter.includeInto(this)
@ -250,12 +257,7 @@ class Git
# Public: Returns the upstream branch for the current HEAD, or null if there # Public: Returns the upstream branch for the current HEAD, or null if there
# is no upstream branch for the current HEAD. # is no upstream branch for the current HEAD.
# #
# Examples # Returns a String branch name such as `refs/remotes/origin/master`
#
# getUpstreamBranch()
# # => "refs/remotes/origin/master"
#
# Returns a String.
getUpstreamBranch: -> @getRepo().getUpstreamBranch() getUpstreamBranch: -> @getRepo().getUpstreamBranch()
# Public: Returns the current SHA for the given reference. # Public: Returns the current SHA for the given reference.

View File

@ -6,7 +6,7 @@ _ = require 'underscore-plus'
# #
# The gutter also indicates if rows are folded. # The gutter also indicates if rows are folded.
module.exports = module.exports =
class Gutter extends View class GutterView extends View
### Internal ### ### Internal ###
@ -234,11 +234,11 @@ class Gutter extends View
lastBufferRow = null lastBufferRow = null
for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow
lastBufferRow = bufferRow lastBufferRow = bufferRow
lineNumberElement = @getLineNumberElement(bufferRow)[0] if lineNumberElement = @getLineNumberElement(bufferRow)[0]
if editor.isFoldableAtBufferRow(bufferRow) if editor.isFoldableAtBufferRow(bufferRow)
lineNumberElement.classList.add('foldable') lineNumberElement.classList.add('foldable')
else else
lineNumberElement.classList.remove('foldable') lineNumberElement.classList.remove('foldable')
removeLineHighlights: -> removeLineHighlights: ->
return unless @highlightedLineNumbers return unless @highlightedLineNumbers

View File

@ -142,7 +142,12 @@ class Keymap
@userKeymapFile.on 'contents-changed moved removed', => @loadUserKeymap() @userKeymapFile.on 'contents-changed moved removed', => @loadUserKeymap()
loadDirectory: (directoryPath) -> 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) -> load: (path) ->
@add(path, CSON.readFileSync(path)) @add(path, CSON.readFileSync(path))

View File

@ -1,7 +1,9 @@
clipboard = require 'clipboard' clipboard = require 'clipboard'
crypto = require 'crypto' 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 = module.exports =
class Pasteboard class Pasteboard
signatureForMetadata: null signatureForMetadata: null
@ -14,18 +16,19 @@ class Pasteboard
md5: (text) -> md5: (text) ->
crypto.createHash('md5').update(text, 'utf8').digest('hex') 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 # text - A {String} to store.
# metadata - An object of additional info to associate with the text # metadata - An {Object} of additional info to associate with the text.
write: (text, metadata) -> write: (text, metadata) ->
@signatureForMetadata = @md5(text) @signatureForMetadata = @md5(text)
@metadata = metadata @metadata = metadata
clipboard.writeText(text) 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: -> read: ->
text = clipboard.readText() text = clipboard.readText()
value = [text] value = [text]

View File

@ -16,8 +16,7 @@ Git = require './git'
# Public: Represents a project that's opened in Atom. # Public: Represents a project that's opened in Atom.
# #
# Ultimately, a project is a git directory that's been opened. It's a collection # There is always a project available under the `atom.project` global.
# of directories and files that you can operate on.
module.exports = module.exports =
class Project extends Model class Project extends Model
atom.deserializers.add(this) atom.deserializers.add(this)

View File

@ -5,9 +5,11 @@
# This `View` subclass listens to events such as `page-up`, `page-down`, # This `View` subclass listens to events such as `page-up`, `page-down`,
# `move-to-top`, and `move-to-bottom`. # `move-to-top`, and `move-to-bottom`.
# #
# FIXME: I don't actually understand if this is useful or not. I think it is # ## Requiring in packages
# a base of package widgets but I don't really understand how the core events #
# work. # ```coffee
# {ScrollView} = require 'atom'
# ```
module.exports = module.exports =
class ScrollView extends View class ScrollView extends View

View File

@ -4,6 +4,12 @@ fuzzyFilter = require('fuzzaldrin').filter
# Public: Provides a widget for users to make a selection from a list of # Public: Provides a widget for users to make a selection from a list of
# choices. # choices.
#
# ## Requiring in packages
#
# ```coffee
# {SelectList} = require 'atom'
# ```
module.exports = module.exports =
class SelectList extends View class SelectList extends View

View File

@ -1,9 +1,9 @@
_ = require 'underscore-plus' _ = require 'underscore-plus'
spacePen = require 'space-pen' spacePen = require 'space-pen'
ConfigObserver = require './config-observer'
{Subscriber} = require 'emissary' {Subscriber} = require 'emissary'
ConfigObserver = require './config-observer'
_.extend spacePen.View.prototype, ConfigObserver ConfigObserver.includeInto(spacePen.View)
Subscriber.includeInto(spacePen.View) Subscriber.includeInto(spacePen.View)
jQuery = spacePen.jQuery jQuery = spacePen.jQuery

View File

@ -6,7 +6,12 @@ _ = require 'underscore-plus'
{$, $$} = require './space-pen-extensions' {$, $$} = require './space-pen-extensions'
Token = require './token' 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 = module.exports =
class Syntax extends GrammarRegistry class Syntax extends GrammarRegistry
Subscriber.includeInto(this) Subscriber.includeInto(this)
@ -49,6 +54,18 @@ class Syntax extends GrammarRegistry
@scopedProperties = [] @scopedProperties = []
@scopedPropertiesIndex = 0 @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) -> getProperty: (scope, keyPath) ->
for object in @propertiesForScope(scope, keyPath) for object in @propertiesForScope(scope, keyPath)
value = _.valueForKeyPath(object, keyPath) value = _.valueForKeyPath(object, keyPath)

View File

@ -12,6 +12,12 @@ child_process = require 'child_process'
# * task:warn - Emitted when console.warn is called within the task. # * task:warn - Emitted when console.warn is called within the task.
# * task:error - Emitted when console.error 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. # * task:completed - Emitted when the task has succeeded or failed.
#
# ## Requiring in packages
#
# ```coffee
# {Task} = require 'atom'
# ```
module.exports = module.exports =
class Task class Task
Emitter.includeInto(this) Emitter.includeInto(this)

View File

@ -1,13 +1,9 @@
### Internal ###
isHighSurrogate = (string, index) -> isHighSurrogate = (string, index) ->
0xD800 <= string.charCodeAt(index) <= 0xDBFF 0xD800 <= string.charCodeAt(index) <= 0xDBFF
isLowSurrogate = (string, index) -> isLowSurrogate = (string, index) ->
0xDC00 <= string.charCodeAt(index) <= 0xDFFF 0xDC00 <= string.charCodeAt(index) <= 0xDFFF
### Public ###
# Is the character at the given index the start of a high/low surrogate pair? # Is the character at the given index the start of a high/low surrogate pair?
# #
# string - The {String} to check for a surrogate pair. # string - The {String} to check for a surrogate pair.

View File

@ -8,9 +8,9 @@ fs = require 'fs-plus'
AtomPackage = require './atom-package' AtomPackage = require './atom-package'
File = require './file' 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 = module.exports =
class ThemeManager class ThemeManager
Emitter.includeInto(this) Emitter.includeInto(this)
@ -19,27 +19,26 @@ class ThemeManager
@lessCache = null @lessCache = null
@packageManager.registerPackageActivator(this, ['theme']) @packageManager.registerPackageActivator(this, ['theme'])
# Internal-only:
getAvailableNames: -> getAvailableNames: ->
# TODO: Maybe should change to list all the available themes out there? # TODO: Maybe should change to list all the available themes out there?
@getLoadedNames() @getLoadedNames()
# Public: Get an array of all the loaded theme names.
getLoadedNames: -> getLoadedNames: ->
theme.name for theme in @getLoadedThemes() theme.name for theme in @getLoadedThemes()
# Internal-only: # Public: Get an array of all the active theme names.
getActiveNames: -> getActiveNames: ->
theme.name for theme in @getActiveThemes() theme.name for theme in @getActiveThemes()
# Internal-only: # Public: Get an array of all the active themes.
getActiveThemes: -> getActiveThemes: ->
pack for pack in @packageManager.getActivePackages() when pack.isTheme() pack for pack in @packageManager.getActivePackages() when pack.isTheme()
# Internal-only: # Public: Get an array of all the loaded themes.
getLoadedThemes: -> getLoadedThemes: ->
pack for pack in @packageManager.getLoadedPackages() when pack.isTheme() pack for pack in @packageManager.getLoadedPackages() when pack.isTheme()
# Internal-only: adhere to the PackageActivator interface
activatePackages: (themePackages) -> @activateThemes() activatePackages: (themePackages) -> @activateThemes()
# Private: Get the enabled theme names from the config. # 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. # the first/top theme to override later themes in the stack.
themeNames.reverse() themeNames.reverse()
# Internal-only:
activateThemes: -> activateThemes: ->
# atom.config.observe runs the callback once, then on subsequent changes. # atom.config.observe runs the callback once, then on subsequent changes.
atom.config.observe 'core.themes', => atom.config.observe 'core.themes', =>
@ -69,13 +67,11 @@ class ThemeManager
@emit('reloaded') @emit('reloaded')
# Internal-only:
deactivateThemes: -> deactivateThemes: ->
@unwatchUserStylesheet() @unwatchUserStylesheet()
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes() @packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
null null
# Internal-only:
refreshLessCache: -> refreshLessCache: ->
@lessCache?.setImportPaths(@getImportPaths()) @lessCache?.setImportPaths(@getImportPaths())
@ -85,7 +81,6 @@ class ThemeManager
setEnabledThemes: (enabledThemeNames) -> setEnabledThemes: (enabledThemeNames) ->
atom.config.set('core.themes', enabledThemeNames) atom.config.set('core.themes', enabledThemeNames)
# Public:
getImportPaths: -> getImportPaths: ->
activeThemes = @getActiveThemes() activeThemes = @getActiveThemes()
if activeThemes.length > 0 if activeThemes.length > 0
@ -98,7 +93,7 @@ class ThemeManager
themePath for themePath in themePaths when fs.isDirectorySync(themePath) themePath for themePath in themePaths when fs.isDirectorySync(themePath)
# Public: # Public: Returns the {String} path to the user's stylesheet under ~/.atom
getUserStylesheetPath: -> getUserStylesheetPath: ->
stylesheetPath = fs.resolve(path.join(@configDirPath, 'user'), ['css', 'less']) stylesheetPath = fs.resolve(path.join(@configDirPath, 'user'), ['css', 'less'])
if fs.isFileSync(stylesheetPath) if fs.isFileSync(stylesheetPath)
@ -106,13 +101,11 @@ class ThemeManager
else else
path.join(@configDirPath, 'user.less') path.join(@configDirPath, 'user.less')
#Private:
unwatchUserStylesheet: -> unwatchUserStylesheet: ->
@userStylesheetFile?.off() @userStylesheetFile?.off()
@userStylesheetFile = null @userStylesheetFile = null
@removeStylesheet(@userStylesheetPath) if @userStylesheetPath? @removeStylesheet(@userStylesheetPath) if @userStylesheetPath?
# Private:
loadUserStylesheet: -> loadUserStylesheet: ->
@unwatchUserStylesheet() @unwatchUserStylesheet()
userStylesheetPath = @getUserStylesheetPath() userStylesheetPath = @getUserStylesheetPath()
@ -125,34 +118,32 @@ class ThemeManager
userStylesheetContents = @loadStylesheet(userStylesheetPath) userStylesheetContents = @loadStylesheet(userStylesheetPath)
@applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme') @applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme')
# Internal-only:
loadBaseStylesheets: -> loadBaseStylesheets: ->
@requireStylesheet('bootstrap/less/bootstrap') @requireStylesheet('bootstrap/less/bootstrap')
@reloadBaseStylesheets() @reloadBaseStylesheets()
# Internal-only:
reloadBaseStylesheets: -> reloadBaseStylesheets: ->
@requireStylesheet('../static/atom') @requireStylesheet('../static/atom')
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less']) if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
@requireStylesheet(nativeStylesheetPath) @requireStylesheet(nativeStylesheetPath)
# Internal-only:
stylesheetElementForId: (id, htmlElement=$('html')) -> stylesheetElementForId: (id, htmlElement=$('html')) ->
htmlElement.find("""head style[id="#{id}"]""") htmlElement.find("""head style[id="#{id}"]""")
# Internal-only:
resolveStylesheet: (stylesheetPath) -> resolveStylesheet: (stylesheetPath) ->
if path.extname(stylesheetPath).length > 0 if path.extname(stylesheetPath).length > 0
fs.resolveOnLoadPath(stylesheetPath) fs.resolveOnLoadPath(stylesheetPath)
else else
fs.resolveOnLoadPath(stylesheetPath, ['css', 'less']) 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 # This supports both CSS and LESS stylsheets.
# LESS file in the stylesheets path.
# #
# 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) -> requireStylesheet: (stylesheetPath, ttype = 'bundled', htmlElement) ->
if fullPath = @resolveStylesheet(stylesheetPath) if fullPath = @resolveStylesheet(stylesheetPath)
content = @loadStylesheet(fullPath) content = @loadStylesheet(fullPath)
@ -162,14 +153,12 @@ class ThemeManager
fullPath fullPath
# Internal-only:
loadStylesheet: (stylesheetPath) -> loadStylesheet: (stylesheetPath) ->
if path.extname(stylesheetPath) is '.less' if path.extname(stylesheetPath) is '.less'
@loadLessStylesheet(stylesheetPath) @loadLessStylesheet(stylesheetPath)
else else
fs.readFileSync(stylesheetPath, 'utf8') fs.readFileSync(stylesheetPath, 'utf8')
# Internal-only:
loadLessStylesheet: (lessStylesheetPath) -> loadLessStylesheet: (lessStylesheetPath) ->
unless @lessCache? unless @lessCache?
LessCompileCache = require './less-compile-cache' LessCompileCache = require './less-compile-cache'
@ -184,16 +173,13 @@ class ThemeManager
#{e.message} #{e.message}
""" """
# Internal-only:
stringToId: (string) -> stringToId: (string) ->
string.replace(/\\/g, '/') string.replace(/\\/g, '/')
# Internal-only:
removeStylesheet: (stylesheetPath) -> removeStylesheet: (stylesheetPath) ->
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
@stylesheetElementForId(@stringToId(fullPath)).remove() @stylesheetElementForId(@stringToId(fullPath)).remove()
# Internal-only:
applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) -> applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) ->
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement) styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
if styleElement.length if styleElement.length

View File

@ -1,9 +1,9 @@
# Public: Measure how long a function takes to run. # Public: Measure how long a function takes to run.
# #
# * description: # * description:
# A String description that will be logged to the console. # A {String} description that will be logged to the console.
# * fn: # * fn:
# A Function to measure the duration of. # A {Function} to measure the duration of.
# #
# Returns the value returned by the given function. # Returns the value returned by the given function.
window.measure = (description, fn) -> window.measure = (description, fn) ->
@ -16,10 +16,10 @@ window.measure = (description, fn) ->
# Public: Create a dev tools profile for a function. # Public: Create a dev tools profile for a function.
# #
# * description: # * 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. # tools.
# * fn: # * fn:
# A Function to profile. # A {Function} to profile.
# #
# Return the value returned by the given function. # Return the value returned by the given function.
window.profile = (description, fn) -> window.profile = (description, fn) ->

View File

@ -6,6 +6,7 @@ Delegator = require 'delegato'
{$, $$, View} = require './space-pen-extensions' {$, $$, View} = require './space-pen-extensions'
fs = require 'fs-plus' fs = require 'fs-plus'
Workspace = require './workspace' Workspace = require './workspace'
CommandInstaller = require './command-installer'
EditorView = require './editor-view' EditorView = require './editor-view'
PaneView = require './pane-view' PaneView = require './pane-view'
PaneColumnView = require './pane-column-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 # * `application:bring-all-windows-to-front` - Brings all {AtomWindow}s to the
# the front. # the front.
# #
# ## Requiring in package specs
#
# ```coffee
# {WorkspaceView} = require 'atom'
# ```
module.exports = module.exports =
class WorkspaceView extends View class WorkspaceView extends View
Delegator.includeInto(this) 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: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-config', -> ipc.sendChannel('command', 'application:open-your-config')
@command 'application:open-your-keymap', -> ipc.sendChannel('command', 'application:open-your-keymap') @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 '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:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec'))
@command 'window:increase-font-size', => @increaseFontSize() @command 'window:increase-font-size', => @increaseFontSize()
@command 'window:decrease-font-size', => @decreaseFontSize() @command 'window:decrease-font-size', => @decreaseFontSize()
@ -122,6 +131,26 @@ class WorkspaceView extends View
@command 'core:save', => @saveActivePaneItem() @command 'core:save', => @saveActivePaneItem()
@command 'core:save-as', => @saveActivePaneItemAs() @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: # Private:
handleFocus: (e) -> handleFocus: (e) ->
if @getActivePane() if @getActivePane()

View File

@ -27,6 +27,7 @@
.make-icon(book); .make-icon(book);
.make-icon(bookmark); .make-icon(bookmark);
.make-icon(broadcast); .make-icon(broadcast);
.make-icon(browser);
.make-icon(bug); .make-icon(bug);
.make-icon(calendar); .make-icon(calendar);
.make-icon(check); .make-icon(check);
@ -46,6 +47,7 @@
.make-icon(comment-add); .make-icon(comment-add);
.make-icon(comment-discussion); .make-icon(comment-discussion);
.make-icon(credit-card); .make-icon(credit-card);
.make-icon(dash);
.make-icon(dashboard); .make-icon(dashboard);
.make-icon(database); .make-icon(database);
.make-icon(device-camera); .make-icon(device-camera);
@ -74,6 +76,7 @@
.make-icon(file-symlink-file); .make-icon(file-symlink-file);
.make-icon(file-text); .make-icon(file-text);
.make-icon(file-zip); .make-icon(file-zip);
.make-icon(fold);
.make-icon(gear); .make-icon(gear);
.make-icon(gift); .make-icon(gift);
.make-icon(gist); .make-icon(gist);
@ -92,6 +95,7 @@
.make-icon(git-pull-request-abandoned); .make-icon(git-pull-request-abandoned);
.make-icon(globe); .make-icon(globe);
.make-icon(graph); .make-icon(graph);
.make-icon(heart);
.make-icon(history); .make-icon(history);
.make-icon(home); .make-icon(home);
.make-icon(horizontal-rule); .make-icon(horizontal-rule);
@ -127,10 +131,14 @@
.make-icon(mail); .make-icon(mail);
.make-icon(mail-read); .make-icon(mail-read);
.make-icon(mail-reply); .make-icon(mail-reply);
.make-icon(mark-facebook);
.make-icon(mark-github); .make-icon(mark-github);
.make-icon(mark-github-detail); .make-icon(mark-github-detail);
.make-icon(mark-google);
.make-icon(mark-twitter); .make-icon(mark-twitter);
.make-icon(markdown);
.make-icon(megaphone); .make-icon(megaphone);
.make-icon(mention);
.make-icon(microscope); .make-icon(microscope);
.make-icon(milestone); .make-icon(milestone);
.make-icon(mirror-private); .make-icon(mirror-private);
@ -142,6 +150,7 @@
.make-icon(mute); .make-icon(mute);
.make-icon(mute-video); .make-icon(mute-video);
.make-icon(no-newline); .make-icon(no-newline);
.make-icon(node-js);
.make-icon(octoface); .make-icon(octoface);
.make-icon(organization); .make-icon(organization);
.make-icon(pencil); .make-icon(pencil);
@ -150,11 +159,16 @@
.make-icon(person-follow); .make-icon(person-follow);
.make-icon(person-remove); .make-icon(person-remove);
.make-icon(pin); .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(plus);
.make-icon(podium); .make-icon(podium);
.make-icon(primitive-dot); .make-icon(primitive-dot);
.make-icon(primitive-square); .make-icon(primitive-square);
.make-icon(pulse); .make-icon(pulse);
.make-icon(puzzle);
.make-icon(question); .make-icon(question);
.make-icon(quote); .make-icon(quote);
.make-icon(radio-tower); .make-icon(radio-tower);
@ -175,18 +189,22 @@
.make-icon(screen-full); .make-icon(screen-full);
.make-icon(screen-normal); .make-icon(screen-normal);
.make-icon(search); .make-icon(search);
.make-icon(search-save);
.make-icon(server); .make-icon(server);
.make-icon(settings); .make-icon(settings);
.make-icon(split);
.make-icon(squirrel); .make-icon(squirrel);
.make-icon(star); .make-icon(star);
.make-icon(star-add); .make-icon(star-add);
.make-icon(star-delete); .make-icon(star-delete);
.make-icon(steps);
.make-icon(stop); .make-icon(stop);
.make-icon(sync); .make-icon(sync);
.make-icon(tag); .make-icon(tag);
.make-icon(tag-add); .make-icon(tag-add);
.make-icon(tag-remove); .make-icon(tag-remove);
.make-icon(telescope); .make-icon(telescope);
.make-icon(terminal);
.make-icon(three-bars); .make-icon(three-bars);
.make-icon(tools); .make-icon(tools);
.make-icon(triangle-down); .make-icon(triangle-down);

Binary file not shown.

View File

@ -14,6 +14,7 @@
@book: "\f007"; @book: "\f007";
@bookmark: "\f07b"; @bookmark: "\f07b";
@broadcast: "\f048"; @broadcast: "\f048";
@browser: "\f0c5";
@bug: "\f091"; @bug: "\f091";
@calendar: "\f068"; @calendar: "\f068";
@check: "\f03a"; @check: "\f03a";
@ -33,6 +34,7 @@
@comment-add: "\f06f"; @comment-add: "\f06f";
@comment-discussion: "\f04f"; @comment-discussion: "\f04f";
@credit-card: "\f045"; @credit-card: "\f045";
@dash: "\f0ca";
@dashboard: "\f07d"; @dashboard: "\f07d";
@database: "\f096"; @database: "\f096";
@device-camera: "\f056"; @device-camera: "\f056";
@ -61,6 +63,7 @@
@file-symlink-file: "\f0b0"; @file-symlink-file: "\f0b0";
@file-text: "\f011"; @file-text: "\f011";
@file-zip: "\f013"; @file-zip: "\f013";
@fold: "\f0cc";
@gear: "\f02f"; @gear: "\f02f";
@gift: "\f042"; @gift: "\f042";
@gist: "\f00e"; @gist: "\f00e";
@ -79,6 +82,7 @@
@git-pull-request-abandoned: "\f090"; @git-pull-request-abandoned: "\f090";
@globe: "\f0b6"; @globe: "\f0b6";
@graph: "\f043"; @graph: "\f043";
@heart: "\2665";
@history: "\f07e"; @history: "\f07e";
@home: "\f08d"; @home: "\f08d";
@horizontal-rule: "\f070"; @horizontal-rule: "\f070";
@ -114,10 +118,14 @@
@mail: "\f03b"; @mail: "\f03b";
@mail-read: "\f03c"; @mail-read: "\f03c";
@mail-reply: "\f051"; @mail-reply: "\f051";
@mark-facebook: "\f0ce";
@mark-github: "\f00a"; @mark-github: "\f00a";
@mark-github-detail: "\f093"; @mark-github-detail: "\f093";
@mark-google: "\f0cd";
@mark-twitter: "\f0ae"; @mark-twitter: "\f0ae";
@markdown: "\f0c9";
@megaphone: "\f077"; @megaphone: "\f077";
@mention: "\f0be";
@microscope: "\f089"; @microscope: "\f089";
@milestone: "\f075"; @milestone: "\f075";
@mirror-private: "\f025"; @mirror-private: "\f025";
@ -129,19 +137,26 @@
@mute: "\f080"; @mute: "\f080";
@mute-video: "\f0b8"; @mute-video: "\f0b8";
@no-newline: "\f09c"; @no-newline: "\f09c";
@node-js: "\f0c3";
@octoface: "\f008"; @octoface: "\f008";
@organization: "\f037"; @organization: "\f037";
@package: "\f0c4";
@pencil: "\f058"; @pencil: "\f058";
@person: "\f018"; @person: "\f018";
@person-add: "\f01a"; @person-add: "\f01a";
@person-follow: "\f01c"; @person-follow: "\f01c";
@person-remove: "\f01b"; @person-remove: "\f01b";
@pin: "\f041"; @pin: "\f041";
@playback-fast-forward: "\f0bd";
@playback-pause: "\f0bb";
@playback-play: "\f0bf";
@playback-rewind: "\f0bc";
@plus: "\f05d"; @plus: "\f05d";
@podium: "\f0af"; @podium: "\f0af";
@primitive-dot: "\f052"; @primitive-dot: "\f052";
@primitive-square: "\f053"; @primitive-square: "\f053";
@pulse: "\f085"; @pulse: "\f085";
@puzzle: "\f0c0";
@question: "\f02c"; @question: "\f02c";
@quote: "\f063"; @quote: "\f063";
@radio-tower: "\f030"; @radio-tower: "\f030";
@ -162,18 +177,22 @@
@screen-full: "\f066"; @screen-full: "\f066";
@screen-normal: "\f067"; @screen-normal: "\f067";
@search: "\f02e"; @search: "\f02e";
@search-save: "\f0cb";
@server: "\f097"; @server: "\f097";
@settings: "\f07c"; @settings: "\f07c";
@split: "\f0c6";
@squirrel: "\f0b2"; @squirrel: "\f0b2";
@star: "\f02a"; @star: "\f02a";
@star-add: "\f082"; @star-add: "\f082";
@star-delete: "\f083"; @star-delete: "\f083";
@steps: "\f0c7";
@stop: "\f08f"; @stop: "\f08f";
@sync: "\f087"; @sync: "\f087";
@tag: "\f015"; @tag: "\f015";
@tag-add: "\f054"; @tag-add: "\f054";
@tag-remove: "\f055"; @tag-remove: "\f055";
@telescope: "\f088"; @telescope: "\f088";
@terminal: "\f0c8";
@three-bars: "\f05e"; @three-bars: "\f05e";
@tools: "\f031"; @tools: "\f031";
@triangle-down: "\f05b"; @triangle-down: "\f05b";

2
vendor/apm vendored

@ -1 +1 @@
Subproject commit b80ef23ce8d2a1e8b4f40eb0f89c87f32dcc3415 Subproject commit 3f8701bfe624de844641863391c04def9cca5c86