mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 18:24:09 +03:00
Merge branch 'master' into ns-workspace-specs-cleanup
This commit is contained in:
commit
29f480661c
@ -1,4 +1,4 @@
|
||||
# Atom — The hackable, ~~collaborative~~ editor
|
||||
# Atom — The hackable editor
|
||||
|
||||
![Atom](http://i.imgur.com/OrTvUAD.png)
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"biscotto": "0.0.17",
|
||||
"biscotto": "git://github.com/atom/biscotto.git#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be",
|
||||
"first-mate": "~0.13.0",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "1.x",
|
||||
@ -19,7 +19,7 @@
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.5.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.6.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
@ -30,6 +30,7 @@
|
||||
"rcedit": "~0.1.2",
|
||||
"request": "~2.27.0",
|
||||
"rimraf": "~2.2.2",
|
||||
"runas": "~0.3.0",
|
||||
"unzip": "~0.1.9",
|
||||
"vm-compatibility-layer": "~0.1.0",
|
||||
"walkdir": "0.0.7"
|
||||
|
@ -5,12 +5,14 @@ fs = require 'fs-plus'
|
||||
request = require 'request'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{rm} = require('./task-helpers')(grunt)
|
||||
|
||||
cmd = path.join('node_modules', '.bin', 'coffee')
|
||||
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
|
||||
opts =
|
||||
stdio: 'inherit'
|
||||
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src/app', ->
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
|
||||
done = @async()
|
||||
|
||||
downloadFileFromRepo = ({repo, file}, callback) ->
|
||||
@ -34,6 +36,7 @@ module.exports = (grunt) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
rm('docs/output/api')
|
||||
args = [
|
||||
commonArgs...
|
||||
'--title', 'Atom API Documentation'
|
||||
|
@ -1,11 +1,25 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
{cp, mkdir, rm, spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'install', 'Install the built application', ->
|
||||
installDir = grunt.config.get('atom.installDir')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
if process.platform is 'win32'
|
||||
done = @async()
|
||||
|
||||
runas = require 'runas'
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
# cmd /c ""script" "source" "destination""
|
||||
arg = "/c \"\"#{copyFolder}\" \"#{shellAppDir}\" \"#{installDir}\"\""
|
||||
if runas('cmd', [arg], hide: true) isnt 0
|
||||
done("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
|
||||
createShortcut = path.resolve 'script', 'create-shortcut.cmd'
|
||||
args = ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom']
|
||||
spawn {cmd: 'cmd', args}, done
|
||||
else
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
cp shellAppDir, installDir
|
||||
|
@ -41,7 +41,7 @@ module.exports = (grunt) ->
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'The hackable, collaborative editor'
|
||||
FileDescription: 'The hackable editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
||||
|
@ -14,12 +14,26 @@ overview of the main editor API.
|
||||
Check out the [Atom][Atom] class docs to see what globals are available and
|
||||
what they provide.
|
||||
|
||||
You can also require many of these classes in your packages via:
|
||||
You can also require many of these classes in your package via:
|
||||
|
||||
```coffee
|
||||
{EditorView} = require 'atom'
|
||||
```
|
||||
|
||||
The classes available from `require 'atom'` are:
|
||||
* [BufferedProcess][BufferedProcess]
|
||||
* [BufferedNodeProcess][BufferedNodeProcess]
|
||||
* [Directory][Directory]
|
||||
* [EditorView][EditorView]
|
||||
* [File][File]
|
||||
* [Git][Git]
|
||||
* [Point][Point]
|
||||
* [Range][Range]
|
||||
* [ScrollView][ScrollView]
|
||||
* [SelectList][SelectList]
|
||||
* [View][View]
|
||||
* [WorkspaceView][WorkspaceView]
|
||||
|
||||
### How do I create a package?
|
||||
|
||||
You probably want to read the [creating a package][creating-a-package]
|
||||
@ -31,7 +45,18 @@ Atom ships with node 0.11.10 and the comprehensive node API docs are available
|
||||
[here][node-docs].
|
||||
|
||||
[Atom]: ../classes/Atom.html
|
||||
[BufferedProcess]: ../classes/BufferedProcess.html
|
||||
[BufferedNodeProcess]: ../classes/BufferedNodeProcess.html
|
||||
[Directory]: ../classes/Directory.html
|
||||
[Editor]: ../classes/Editor.html
|
||||
[EditorView]: ../classes/EditorView.html
|
||||
[File]: ../classes/File.html
|
||||
[Git]: ../classes/Git.html
|
||||
[Point]: ../classes/Point.html
|
||||
[Range]: ../classes/Range.html
|
||||
[ScrollView]: ../classes/ScrollView.html
|
||||
[SelectList]: ../classes/SelectList.html
|
||||
[View]: ../classes/View.html
|
||||
[WorkspaceView]: ../classes/WorkspaceView.html
|
||||
[creating-a-package]: https://www.atom.io/docs/latest/creating-a-package
|
||||
[node-docs]: http://nodejs.org/docs/v0.11.10/api
|
||||
|
76
docs/advanced/configuration.md
Normal file
76
docs/advanced/configuration.md
Normal 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
34
docs/advanced/globals.md
Normal 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
123
docs/advanced/keymaps.md
Normal 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.
|
@ -154,7 +154,7 @@ loaded in alphabetical order. An optional `keymaps` array in your _package.json_
|
||||
can specify which keymaps to load and in what order.
|
||||
|
||||
|
||||
Keybindings are executed by determining which element the keypress occured on. In
|
||||
Keybindings are executed by determining which element the keypress occurred on. In
|
||||
the example above, `changer:magic` command is executed when pressing `ctrl-V` on
|
||||
the `.tree-view-scroller` element.
|
||||
|
||||
@ -381,7 +381,7 @@ Additional libraries can be found by browsing Atom's *node_modules* folder.
|
||||
[apm]: https://github.com/atom/apm
|
||||
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
|
||||
[wrap-guide]: https://github.com/atom/wrap-guide/
|
||||
[keymaps]: internals/keymaps.md
|
||||
[keymaps]: advanced/keymaps.md
|
||||
[theme-variables]: theme-variables.md
|
||||
[tm-tokens]: http://manual.macromates.com/en/language_grammars.html
|
||||
[spacepen]: https://github.com/nathansobo/space-pen
|
||||
|
@ -19,13 +19,11 @@ bindings][key-bindings] section.
|
||||
|
||||
### Working With Files
|
||||
|
||||
Atom windows are scoped to the directory they're opened from. If you launch Atom
|
||||
from the command line everything will be relative to the current directory. This
|
||||
means that the tree view on the left will only show files contained within that
|
||||
directory.
|
||||
|
||||
This can be a useful way to organize multiple projects, as each project will be
|
||||
contained within its own window.
|
||||
Atom windows are scoped to a single directory on disk. If you launch Atom from
|
||||
the command line via the `atom` command and don't specify a path, Atom opens a
|
||||
window for the current working directory. The current window's directory will be
|
||||
visible as the root of the tree view at the left, and also serve as the context
|
||||
for all file-related operations.
|
||||
|
||||
#### Finding Files
|
||||
|
||||
@ -34,20 +32,17 @@ begin typing the name of the file you're looking for. If you are looking for a
|
||||
file that is already open press `cmd-b` to bring up a searchable list of open
|
||||
files.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open or move focus to
|
||||
the tree view, press `cmd-\`. You can then navigate to a file using the arrow
|
||||
keys and select it with `return`.
|
||||
You can also use the tree view to navigate to a file. To open and focus the
|
||||
the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
|
||||
`cmd-\`.
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
|
||||
Currently, all file modification is performed via the tree view. To add a file,
|
||||
select a directory in the tree view and press `a`. Then type the name of the
|
||||
file. Any intermediate directories you type will be created automatically if
|
||||
needed.
|
||||
|
||||
To move or rename a file or directory, select it in the tree view and press `m`.
|
||||
|
||||
To delete a file, select it in the tree view and press `delete`.
|
||||
Currently, all file modification is performed via the tree view. Add, move, or
|
||||
delete a file by right-clicking in the tree view and selecting the desired
|
||||
operation from the context menu. You can also perform these operations from the
|
||||
keyboard by selecting a file or directory and using `a` to add, `m` to move, and
|
||||
`delete` to delete.
|
||||
|
||||
### Searching
|
||||
|
||||
@ -58,35 +53,43 @@ To search within a buffer use `cmd-f`. To search the entire project use
|
||||
|
||||
#### Navigating By Symbols
|
||||
|
||||
If you want to jump to a method press `cmd-r`. It opens a list of all symbols
|
||||
in the current file.
|
||||
To jump to a symbol such as a method definition, press `cmd-r`. This opens a
|
||||
list of all symbols in the current file, which you can fuzzy filter similarly to
|
||||
`cmd-t`.
|
||||
|
||||
To search for symbols across your project use `cmd-shift-r`, but you'll need to
|
||||
make sure you have a ctags installed and a tags file generated for your project.
|
||||
Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags`
|
||||
file to understand the language. Here is [a good example][ctags].
|
||||
To search for symbols across your project, use `cmd-shift-r`. First you'll need
|
||||
to make sure you have ctags installed and a tags file generated for your
|
||||
project. Also, if you're editing CoffeeScript, it's a good idea to update your
|
||||
`~/.ctags` file to understand the language. Here is [a good example][ctags].
|
||||
|
||||
### Split Panes
|
||||
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right` or
|
||||
`cmd-k down`. Once you have a split pane, you can move focus between them with
|
||||
`cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all tabs inside it.
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right`
|
||||
or `cmd-k down`. Once you have a split pane, you can move focus between them
|
||||
with `cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all its
|
||||
editors with `meta-w`, then press `meta-w` one more time to close the pane. You
|
||||
can configure panes to auto-close with empty in the preferences.
|
||||
|
||||
### Folding
|
||||
|
||||
You can fold everything with `alt-cmd-{` and unfold everything with
|
||||
`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and
|
||||
`alt-cmd-]`.
|
||||
You can fold blocks of code by clicking the arrows that appear when you hover
|
||||
your mouse cursor over the gutter. You can also fold and unfold from the
|
||||
keyboard with `alt-cmd-[` and `alt-cmd-]`. To fold everything, use
|
||||
`alt-cmd-shift-{` and to unfold everything use `alt-cmd-shift-}`. You can also
|
||||
fold at a specific indentation level with `cmd-k cmd-N` where N is the
|
||||
indentation depth.
|
||||
|
||||
### Soft-Wrap
|
||||
|
||||
If you want to toggle soft wrap, trigger the command from the command palette.
|
||||
Press `cmd-shift-P` to open the palette, then type "wrap" to find the correct
|
||||
command.
|
||||
command. By default, lines will wrap based on the size of the editor. If you
|
||||
prefer to wrap at a specific line length, toggle "Wrap at preferred line length"
|
||||
in preferences.
|
||||
|
||||
## Configuration
|
||||
|
||||
Press `cmd-,` to display the a settings pane. This serves as the primary
|
||||
Press `cmd-,` to display the preferences pane. This serves as the primary
|
||||
interface for adjusting config settings, installing packages and changing
|
||||
themes.
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
* [Configuration](internals/configuration.md)
|
||||
* [Keymaps](internals/keymaps.md)
|
||||
* [Serialization](internals/serialization.md)
|
||||
* [View System](internals/view-system.md)
|
||||
* [Configuration](advanced/configuration.md)
|
||||
* [Keymaps](advanced/keymaps.md)
|
||||
* [Serialization](advanced/serialization.md)
|
||||
* [View System](advanced/view-system.md)
|
||||
|
@ -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)
|
||||
```
|
@ -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.
|
@ -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?
|
@ -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
|
@ -1,335 +1,145 @@
|
||||
# Creating Your First Package
|
||||
# Create Your First Package
|
||||
|
||||
Let's take a look at creating your first package.
|
||||
This tutorial will guide you though creating a simple command that replaces the
|
||||
selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you
|
||||
run our new command with the word "cool" selected, it will be replaced with:
|
||||
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Package" to generate
|
||||
a package. Once you select the "Generate Package" command, it'll ask you for a
|
||||
name for your new package. Let's call ours _changer_.
|
||||
|
||||
Atom will pop open a new window, showing the _changer_ package with a default set of
|
||||
folders and files created for us. Hit `cmd-shift-P` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far,
|
||||
so good!
|
||||
|
||||
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
|
||||
is going to do two things:
|
||||
|
||||
1. It'll show only modified files in the file tree
|
||||
2. It'll append a new pane to the editor with some information about the modified
|
||||
files
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Changing Keybindings and Commands
|
||||
|
||||
Since Changer is primarily concerned with the file tree, let's write a
|
||||
key binding that works only when the tree is focused. Instead of using the
|
||||
default `toggle`, our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ should change to look like this:
|
||||
|
||||
```coffeescript
|
||||
'.tree-view':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
/\_ \
|
||||
___ ___ ___\//\ \
|
||||
/'___\ / __`\ / __`\\ \ \
|
||||
/\ \__//\ \L\ \/\ \L\ \\_\ \_
|
||||
\ \____\ \____/\ \____//\____\
|
||||
\/____/\/___/ \/___/ \/____/
|
||||
```
|
||||
|
||||
Notice that the keybinding is called `ctrl-V` — that's actually `ctrl-shift-v`.
|
||||
You can use capital letters to denote using `shift` for your binding.
|
||||
The final package can be viewed at
|
||||
[https://github.com/atom/ascii-art](https://github.com/atom/ascii-art).
|
||||
|
||||
`.tree-view` represents the parent container for the tree view.
|
||||
Keybindings only work within the context of where they're entered. In this case,
|
||||
hitting `ctrl-V` anywhere other than tree won't do anything. Obviously, you can
|
||||
bind to any part of the editor using element, id, or class names. For example,
|
||||
you can map to `body` if you want to scope to anywhere in Atom, or just `.editor`
|
||||
for the editor portion.
|
||||
To begin, press `cmd-shift-P` to bring up the [Command
|
||||
Palette](https://github.com/atom/command-palette). Type "generate package" and
|
||||
select the "Package Generator: Generate Package" command. Now we need to name
|
||||
the package. Let's call it _ascii-art_.
|
||||
|
||||
To bind keybindings to a command, we'll need to do a bit of association in our
|
||||
CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command
|
||||
name and executes a callback function. Open up _lib/changer-view.coffee_, and
|
||||
change `atom.workspaceView.command "changer:toggle"` to look like this:
|
||||
Atom will open a new window with the contents of our new _ascii-art_ package
|
||||
displayed in the Tree View. Because this window is opened **after** the package
|
||||
is created, the ASCII Art package will be loaded and available in our new
|
||||
window. To verify this, toggle the Command Palette (`cmd-shift-P`) and type
|
||||
"ASCII Art" you'll see a new `ASCII Art: Toggle` command. When triggered, this
|
||||
command displays a default message.
|
||||
|
||||
Now let's edit the package files to make our ascii art package do something
|
||||
interesting. Since this package doesn't need any UI, we can remove all
|
||||
view-related code. Start by opening up _lib/ascii-art.coffee_. Remove all view
|
||||
code, so the file looks like this:
|
||||
|
||||
```coffeescript
|
||||
atom.workspaceView.command "changer:magic", => @magic()
|
||||
module.exports =
|
||||
activate: ->
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, separated
|
||||
with a colon (`:`). Make sure to rename your `toggle` method to `magic` as well.
|
||||
## Create a Command
|
||||
|
||||
Every time you reload the Atom editor, changes to your package code will be reevaluated,
|
||||
just as if you were writing a script for the browser. Reload the editor, click on
|
||||
the tree, hit your keybinding, and...nothing happens! What the heck?!
|
||||
Now let's add a command. We recommend that you namespace your commands with the
|
||||
package name followed by a `:`, so we'll call our command `ascii-art:convert`.
|
||||
Register the command in _lib/ascii-art.coffee_:
|
||||
|
||||
Open up the _package.json_ file, and find the property called `activationEvents`.
|
||||
Basically, this key tells Atom to not load a package until it hears a certain event.
|
||||
Change the event to `changer:magic` and reload the editor:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: ->
|
||||
atom.workspaceView.command "ascii-art:convert", => @convert()
|
||||
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
editor.insertText('Hello, World!')
|
||||
```
|
||||
|
||||
The `atom.workspaceView.command` method takes a command name and a callback. The
|
||||
callback executes when the command is triggered. In this case, when the command
|
||||
is triggered the callback will call the `convert` method and insert 'Hello,
|
||||
World!'.
|
||||
|
||||
## Reload the Package
|
||||
|
||||
Before we can trigger `ascii-art:convert`, we need to load the latest code for
|
||||
our package by reloading the window. Run the command `window:reload` from the
|
||||
command palette or by pressing `ctrl-alt-cmd-l`.
|
||||
|
||||
## Trigger the Command
|
||||
|
||||
Now open the command panel and search for the `ascii-art:convert` command. But
|
||||
its not there! To fix this open _package.json_ and find the property called
|
||||
`activationEvents`. Activation Events speed up load time by allowing an Atom to
|
||||
delay a package's activation until it's needed. So add the `ascii-art:convert`
|
||||
to the activationEvents array:
|
||||
|
||||
```json
|
||||
"activationEvents": ["changer:magic"]
|
||||
"activationEvents": ["ascii-art:convert"],
|
||||
```
|
||||
|
||||
Hitting the key binding on the tree now works!
|
||||
First, run reload the window by running the command `window:reload`. Now when
|
||||
you run the `ascii-art:convert` command it will output 'Hello, World!'
|
||||
|
||||
## Working with Styles
|
||||
## Add A Key Binding
|
||||
|
||||
The next step is to hide elements in the tree that aren't modified. To do that,
|
||||
we'll first try and get a list of files that have not changed.
|
||||
|
||||
All packages are able to use jQuery in their code. In fact, there's [a list of
|
||||
the bundled libraries Atom provides by default][bundled-libs].
|
||||
|
||||
We bring in jQuery by requiring the `atom` package and binding it to the `$` variable:
|
||||
Now let's add a key binding to trigger the `ascii-art:convert` command. Open
|
||||
_keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the
|
||||
`ascii-art:convert` command. When finished, the file will look like this:
|
||||
|
||||
```coffeescript
|
||||
{$, View} = require 'atom'
|
||||
'.editor':
|
||||
'cmd-alt-a': 'ascii-art:convert'
|
||||
```
|
||||
|
||||
Now, we can define the `magic` method to query the tree to get us a list of every
|
||||
file that _wasn't_ modified:
|
||||
Notice `.editor` on the first line. Just like CSS, keymap selectors *scope* key
|
||||
bindings so they only apply to specific elements. In this case, our binding is
|
||||
only active for elements matching the `.editor` selector. If the Tree View has
|
||||
focus, pressing `cmd-alt-a` won't trigger the `ascii-art:convert` command. But
|
||||
if the editor has focus, the `ascii-art:convert` method *will* be triggered.
|
||||
More information on key bindings can be found in the
|
||||
[keymaps](advanced/keymaps.html) documentation.
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
console.log el
|
||||
```
|
||||
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.
|
||||
|
||||
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`:
|
||||
## Add the ASCII Art
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
$(el).addClass("hide-me")
|
||||
```
|
||||
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:
|
||||
|
||||
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;
|
||||
```json
|
||||
"dependencies": {
|
||||
"figlet": "1.0.8"
|
||||
}
|
||||
```
|
||||
|
||||
Refresh Atom, and run the `changer` command. You'll see all the non-changed
|
||||
files disappear from the tree. Success!
|
||||
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.
|
||||
|
||||
![Changer_File_View]
|
||||
|
||||
There are a number of ways you can get the list back; let's just naively iterate
|
||||
over the same elements and remove the class:
|
||||
Now require the figlet node module in _lib/ascii-art.coffee_ and instead of
|
||||
inserting 'Hello, World!' convert the selected text to ascii art!
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
if !$(el).hasClass("hide-me")
|
||||
$(el).addClass("hide-me")
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
selection = editor.getSelection()
|
||||
|
||||
figlet = require 'figlet'
|
||||
figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) ->
|
||||
if error
|
||||
console.error(error)
|
||||
else
|
||||
$(el).removeClass("hide-me")
|
||||
selection.insertText("\n#{asciiArt}\n")
|
||||
```
|
||||
|
||||
## Creating a New Panel
|
||||
|
||||
The next goal of this package is to append a panel to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first open up [the style guide][styleguide]. The Style
|
||||
Guide lists every type of UI element that can be created by an Atom package. Aside
|
||||
from helping you avoid writing fresh code from scratch, it ensures that packages
|
||||
have the same look and feel no matter how they're built.
|
||||
|
||||
Every package that extends from the `View` class can provide an optional class
|
||||
method called `content`. The `content` method constructs the DOM that your
|
||||
package uses as its UI. The principals of `content` are built entirely on
|
||||
[SpacePen][space-pen], which we'll touch upon only briefly here.
|
||||
|
||||
Our display will simply be an unordered list of the file names, and their
|
||||
modified times. We'll append this list to a panel on the bottom of the editor. A
|
||||
basic `panel` element inside a `tool-panel` will work well for us. Let's start by carving out a
|
||||
`div` to hold the filenames:
|
||||
|
||||
```coffeescript
|
||||
@content: ->
|
||||
@div class: "changer tool-panel panel-bottom", =>
|
||||
@div class: "panel", =>
|
||||
@div class: "panel-heading", "Modified Files"
|
||||
@div class: "panel-body padded", outlet: 'modifiedFilesContainer', =>
|
||||
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
|
||||
@li 'Modified File Test'
|
||||
@li 'Modified File Test'
|
||||
```
|
||||
|
||||
You can add any HTML attribute you like. `outlet` names the variable your
|
||||
package can use to manipulate the element directly. The fat arrow (`=>`)
|
||||
indicates that the next DOM set are nested children.
|
||||
|
||||
Once again, you can style `li` elements using your stylesheets. Let's test that
|
||||
out by adding these lines to the _changer.css_ file:
|
||||
|
||||
```css
|
||||
ul.modified-files-list {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
We'll add one more line to the end of the `magic` method to make this pane
|
||||
appear:
|
||||
|
||||
```coffeescript
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
If you refresh Atom and hit the key command, you'll see a box appear right
|
||||
underneath the editor:
|
||||
|
||||
![Changer_Panel_Append]
|
||||
|
||||
As you might have guessed, `atom.workspaceView.prependToBottom` tells Atom to
|
||||
prepend `this` item (_i.e._, whatever is defined by`@content`). If we had called
|
||||
`atom.workspaceView.appendToBottom`, the pane would be attached below the status
|
||||
bar.
|
||||
|
||||
Before we populate this panel for real, let's apply some logic to toggle the
|
||||
pane off and on, just like we did with the tree view. Replace the
|
||||
`atom.workspaceView.prependToBottom` call with this code:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@remove()
|
||||
else
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
might not be the most efficient one. If you know your package needs to be
|
||||
toggled on and off more freely, it might be better to draw the interface during the
|
||||
initialization, then immediately call `hide()` on the element to remove it from
|
||||
the view. You can then swap between `show()` and `hide()`, and instead of
|
||||
forcing Atom to add and remove the element as we're doing here, it'll just set a
|
||||
CSS property to control your package's visibility.
|
||||
|
||||
Refresh Atom, hit the key combo, and watch your test list appear and disappear.
|
||||
|
||||
## Calling Node.js Code
|
||||
|
||||
Since Atom is built on top of [Node.js][node], you can call any of its libraries,
|
||||
including other modules that your package requires.
|
||||
|
||||
We'll iterate through our resulting tree, and construct the path to our modified
|
||||
file based on its depth in the tree. We'll use Node to handle path joining for
|
||||
directories.
|
||||
|
||||
Add the following Node module to the top of your file:
|
||||
|
||||
```coffeescript
|
||||
path = require 'path'
|
||||
```
|
||||
|
||||
Then, add these lines to your `magic` method, _before_ your pane drawing code:
|
||||
|
||||
```coffeescript
|
||||
modifiedFiles = []
|
||||
# for each single entry...
|
||||
$('ol.entries li.file.status-modified span.name').each (i, el) ->
|
||||
filePath = []
|
||||
# ...grab its name...
|
||||
filePath.unshift($(el).text())
|
||||
|
||||
# ... then find its parent directories, and grab their names
|
||||
parents = $(el).parents('.directory.status-modified')
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(atom.project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
`modifiedFiles` is an array containing a list of our modified files. We're also
|
||||
using the node.js [`path` library][path] to get the proper directory separator
|
||||
for our system.
|
||||
|
||||
Remove the two `@li` elements we added in `@content`, so that we can
|
||||
populate our `modifiedFilesList` with real information. We'll do that by
|
||||
iterating over `modifiedFiles`, accessing a file's last modified time, and
|
||||
appending it to `modifiedFilesList`:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<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
|
||||
|
||||
For more information on the mechanics of packages, check out
|
||||
[Creating a Package][creating-a-package].
|
||||
|
||||
[bundled-libs]: creating-a-package.html#included-libraries
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
[space-pen]: https://github.com/atom/space-pen
|
||||
[node]: http://nodejs.org/
|
||||
[path]: http://nodejs.org/docs/latest/api/path.html
|
||||
[changer_file_view]: https://f.cloud.github.com/assets/69169/1441187/d7a7cb46-41a7-11e3-8128-d93f70a5d5c1.png
|
||||
[changer_panel_append]: https://f.cloud.github.com/assets/69169/1441189/db0c74da-41a7-11e3-8286-b82dd9190c34.png
|
||||
[changer_panel_timestamps]: https://f.cloud.github.com/assets/69169/1441190/dcc8eeb6-41a7-11e3-830f-1f1b33072fcd.png
|
||||
[theme-vars]: theme-variables.html
|
||||
[creating-a-package]: creating-a-package.html
|
||||
For more information on the mechanics of packages, check out [Creating a
|
||||
Package](creating-a-package.html)
|
||||
|
15
dot-atom/snippets.cson
Normal file
15
dot-atom/snippets.cson
Normal 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'
|
||||
#
|
@ -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'
|
@ -8,6 +8,7 @@ module.exports =
|
||||
File: require '../src/file'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
ConfigObserver: require '../src/config-observer'
|
||||
Point: Point
|
||||
Range: Range
|
||||
|
||||
|
@ -24,9 +24,6 @@
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'.tool-panel':
|
||||
'escape': 'core:close'
|
||||
|
||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
'.platform-darwin':
|
||||
'body':
|
||||
# Apple specific
|
||||
'cmd-q': 'application:quit'
|
||||
'cmd-h': 'application:hide'
|
||||
@ -87,7 +87,7 @@
|
||||
'cmd-8': 'pane:show-item-8'
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.platform-darwin .editor':
|
||||
'.editor':
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
@ -113,7 +113,7 @@
|
||||
'cmd-k cmd-l': 'editor:lower-case'
|
||||
'cmd-l': 'editor:select-line'
|
||||
|
||||
'body.platform-darwin .editor:not(.mini)':
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-cmd-z': 'editor:checkout-head-revision'
|
||||
'cmd-<': 'editor:scroll-to-cursor'
|
||||
@ -148,7 +148,7 @@
|
||||
'cmd-k cmd-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'body.platform-darwin .native-key-bindings':
|
||||
'body .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
|
@ -1,4 +1,4 @@
|
||||
'.platform-win32':
|
||||
'body':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
@ -50,7 +50,7 @@
|
||||
'ctrl-k ctrl-left': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-right': 'window:focus-next-pane'
|
||||
|
||||
'.platform-win32 .editor':
|
||||
'.workspace .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
'.platform-win32 .editor:not(.mini)':
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
@ -94,7 +94,7 @@
|
||||
'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'.platform-win32 input:not(.hidden-input), .platform-win32 .native-key-bindings':
|
||||
'body .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
|
@ -9,8 +9,11 @@
|
||||
{ label: 'Preferences...', command: 'application:show-settings' }
|
||||
{ label: 'Open Your Config', command: 'application:open-your-config' }
|
||||
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
|
||||
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
|
||||
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
{ label: 'Show All', command: 'application:unhide-all-applications' }
|
||||
|
23
package.json
23
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.48.0",
|
||||
"version": "0.49.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -16,7 +16,7 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.8.5",
|
||||
"atomShellVersion": "0.8.7",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
@ -28,15 +28,15 @@
|
||||
"first-mate": "1.x",
|
||||
"fs-plus": "1.x",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "0.6.0",
|
||||
"git-utils": "0.33.1",
|
||||
"fuzzaldrin": "0.7.0",
|
||||
"git-utils": "0.34.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "0.3.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"keytar": "0.15.1",
|
||||
"less-cache": "0.11.0",
|
||||
"mixto": "1.x",
|
||||
"nslog": "0.3.0",
|
||||
"nslog": "0.4.0",
|
||||
"oniguruma": "1.x",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.14.2",
|
||||
@ -75,7 +75,7 @@
|
||||
"editor-stats": "0.12.0",
|
||||
"exception-reporting": "0.12.0",
|
||||
"feedback": "0.22.0",
|
||||
"find-and-replace": "0.79.0",
|
||||
"find-and-replace": "0.80.0",
|
||||
"fuzzy-finder": "0.31.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.23.0",
|
||||
@ -86,27 +86,28 @@
|
||||
"keybinding-resolver": "0.9.0",
|
||||
"link": "0.15.0",
|
||||
"markdown-preview": "0.25.1",
|
||||
"metrics": "0.21.0",
|
||||
"metrics": "0.24.0",
|
||||
"package-generator": "0.24.0",
|
||||
"release-notes": "0.17.0",
|
||||
"settings-view": "0.57.0",
|
||||
"snippets": "0.20.0",
|
||||
"snippets": "0.22.0",
|
||||
"spell-check": "0.20.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.21.0",
|
||||
"symbols-view": "0.29.0",
|
||||
"tabs": "0.18.0",
|
||||
"terminal": "0.26.0",
|
||||
"terminal": "0.27.0",
|
||||
"timecop": "0.13.0",
|
||||
"to-the-hubs": "0.17.0",
|
||||
"to-the-hubs": "0.18.0",
|
||||
"tree-view": "0.65.0",
|
||||
"update-package-dependencies": "0.2.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.4.0",
|
||||
"language-coffee-script": "0.6.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.12.0",
|
||||
"language-git": "0.3.0",
|
||||
|
18
script/copy-folder.cmd
Normal file
18
script/copy-folder.cmd
Normal 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
|
23
script/create-shortcut.cmd
Normal file
23
script/create-shortcut.cmd
Normal 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%
|
@ -2,8 +2,8 @@
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
// node build/node_modules/grunt-cli/bin/grunt "$@"
|
||||
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', 'grunt-cli', 'bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = [gruntPath, '--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
// node build/node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(process.execPath, args, process.exit);
|
||||
cp.safeSpawn(gruntPath, args, process.exit);
|
||||
|
@ -3,9 +3,8 @@
|
||||
path = require 'path'
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
callback = (error, sourcePath, destinationPath) ->
|
||||
unless error?
|
||||
console.log "#{sourcePath} intalled to #{destinationPath}"
|
||||
callback = (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback)
|
||||
CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback)
|
||||
|
@ -24,7 +24,7 @@ fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
octiconUtfDest = path.join atomDir, 'static', 'octicon-utf-codes.less'
|
||||
octiconUtfDest = path.join atomDir, 'static', 'variables', 'octicon-utf-codes.less'
|
||||
output = []
|
||||
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
@ -4,33 +4,31 @@ temp = require 'temp'
|
||||
installer = require '../src/command-installer'
|
||||
|
||||
describe "install(commandPath, callback)", ->
|
||||
directory = path.join(temp.dir, 'install-atom-command', 'atom')
|
||||
commandPath = path.join(directory, 'source')
|
||||
destinationPath = path.join(directory, 'bin', 'source')
|
||||
commandFilePath = temp.openSync("atom-command").path
|
||||
commandName = path.basename(commandFilePath)
|
||||
installationPath = temp.mkdirSync("atom-bin")
|
||||
installationFilePath = path.join(installationPath, commandName)
|
||||
|
||||
beforeEach ->
|
||||
spyOn(installer, 'findInstallDirectory').andCallFake (callback) ->
|
||||
callback(directory)
|
||||
|
||||
fs.removeSync(directory) if fs.existsSync(directory)
|
||||
spyOn(installer, 'getInstallDirectory').andReturn installationPath
|
||||
|
||||
describe "on #darwin", ->
|
||||
it "symlinks the command and makes it executable", ->
|
||||
fs.writeFileSync(commandPath, 'test')
|
||||
expect(fs.isFileSync(commandPath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(destinationPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(commandFilePath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandFilePath)).toBeFalsy()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeFalsy()
|
||||
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.install commandPath, (error) ->
|
||||
installer.install commandFilePath, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
|
||||
waitsFor -> installDone
|
||||
waitsFor ->
|
||||
installDone
|
||||
|
||||
runs ->
|
||||
expect(installError).toBeNull()
|
||||
expect(fs.isFileSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath)
|
||||
expect(fs.isExecutableSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeTruthy()
|
||||
expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath)
|
||||
expect(fs.isExecutableSync(installationFilePath)).toBeTruthy()
|
||||
|
@ -218,7 +218,7 @@ describe "Config", ->
|
||||
runs ->
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
|
||||
describe ".loadUserConfig()", ->
|
||||
|
@ -63,7 +63,7 @@ describe 'File', ->
|
||||
afterEach ->
|
||||
if fs.existsSync(newPath)
|
||||
fs.removeSync(newPath)
|
||||
waitsFor "remove event", (done) -> file.on 'removed', done
|
||||
waitsFor "remove event", 30000, (done) -> file.on 'removed', done
|
||||
|
||||
it "it updates its path", ->
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
|
@ -354,6 +354,27 @@ describe "Keymap", ->
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
describe "loading platform specific keybindings", ->
|
||||
customKeymap = null
|
||||
|
||||
beforeEach ->
|
||||
resourcePath = temp.mkdirSync('atom')
|
||||
customKeymap = new Keymap({configDirPath, resourcePath})
|
||||
|
||||
afterEach ->
|
||||
customKeymap.destroy()
|
||||
|
||||
it "doesn't load keybindings from other platforms", ->
|
||||
win32FilePath = path.join(resourcePath, "keymaps", "win32.cson")
|
||||
darwinFilePath = path.join(resourcePath, "keymaps", "darwin.cson")
|
||||
fs.writeFileSync(win32FilePath, '"body": "ctrl-l": "core:win32-move-left"')
|
||||
fs.writeFileSync(darwinFilePath, '"body": "ctrl-l": "core:darwin-move-left"')
|
||||
|
||||
customKeymap.loadBundledKeymaps()
|
||||
keyBindings = customKeymap.keyBindingsForKeystroke('ctrl-l')
|
||||
expect(keyBindings).toHaveLength 1
|
||||
expect(keyBindings[0].command).toBe "core:#{process.platform}-move-left"
|
||||
|
||||
describe "when the user keymap file is changed", ->
|
||||
it "is reloaded", ->
|
||||
keymapFilePath = path.join(configDirPath, "keymap.cson")
|
||||
|
@ -34,7 +34,7 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
module.exports =
|
||||
class Atom extends Model
|
||||
# Public: Load or create the Atom environment in the given mode
|
||||
# Public: Load or create the Atom environment in the given mode.
|
||||
#
|
||||
# - mode: Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
@ -249,10 +249,12 @@ class Atom extends Model
|
||||
|
||||
# Private: Call this method when establishing a real application window.
|
||||
startEditorWindow: ->
|
||||
if process.platform is 'darwin'
|
||||
CommandInstaller = require './command-installer'
|
||||
CommandInstaller.installAtomCommand()
|
||||
CommandInstaller.installApmCommand()
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
CommandInstaller.installAtomCommand resourcePath, (error) ->
|
||||
console.warn error.message if error?
|
||||
CommandInstaller.installApmCommand resourcePath, (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
@restoreWindowDimensions()
|
||||
@config.load()
|
||||
|
@ -15,7 +15,11 @@ url = require 'url'
|
||||
{EventEmitter} = require 'events'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
socketPath = path.join(os.tmpdir(), 'atom.sock')
|
||||
socketPath =
|
||||
if process.platform is 'win32'
|
||||
'\\\\.\\pipe\\atom-sock'
|
||||
else
|
||||
path.join(os.tmpdir(), 'atom.sock')
|
||||
|
||||
# Private: The application's singleton class.
|
||||
#
|
||||
@ -35,14 +39,10 @@ class AtomApplication
|
||||
# take a few seconds to trigger 'error' event, it could be a bug of node
|
||||
# or atom-shell, before it's fixed we check the existence of socketPath to
|
||||
# speedup startup.
|
||||
if (not fs.existsSync socketPath) or options.test
|
||||
if (process.platform isnt 'win32' and not fs.existsSync socketPath) or options.test
|
||||
createAtomApplication()
|
||||
return
|
||||
|
||||
# The net.connect is slow in atom-shell for now, use this workaround until
|
||||
# atom/atom-shell#159 is fixed.
|
||||
process.activateUvLoop()
|
||||
|
||||
client = net.connect {path: socketPath}, ->
|
||||
client.write JSON.stringify(options), ->
|
||||
client.end()
|
||||
@ -103,7 +103,8 @@ class AtomApplication
|
||||
# the other launches will just pass their information to this server and then
|
||||
# close immediately.
|
||||
listenForArgumentsFromNewProcess: ->
|
||||
fs.unlinkSync socketPath if fs.existsSync(socketPath)
|
||||
if process.platform isnt 'win32' and fs.existsSync(socketPath)
|
||||
fs.unlinkSync socketPath
|
||||
server = net.createServer (connection) =>
|
||||
connection.on 'data', (data) =>
|
||||
@openWithOptions(JSON.parse(data))
|
||||
@ -148,15 +149,18 @@ class AtomApplication
|
||||
@on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new')
|
||||
|
||||
@openPathOnEvent('application:show-settings', 'atom://config')
|
||||
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
|
||||
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
|
||||
@openPathOnEvent('application:open-your-config', 'atom://.atom/config')
|
||||
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
|
||||
@openPathOnEvent('application:open-your-snippets', 'atom://.atom/snippets')
|
||||
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
|
||||
|
||||
app.on 'window-all-closed', ->
|
||||
app.quit() if process.platform is 'win32'
|
||||
|
||||
app.on 'will-quit', =>
|
||||
fs.unlinkSync socketPath if fs.existsSync(socketPath) # Clean the socket file when quit normally.
|
||||
# Clean the socket file when quit normally.
|
||||
if process.platform isnt 'win32' and fs.existsSync(socketPath)
|
||||
fs.unlinkSync socketPath
|
||||
|
||||
app.on 'open-file', (event, pathToOpen) =>
|
||||
event.preventDefault()
|
||||
|
@ -74,6 +74,8 @@ class AtomWindow
|
||||
global.atomApplication.removeWindow(this)
|
||||
|
||||
@browserWindow.on 'unresponsive', =>
|
||||
return if @isSpec
|
||||
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
|
@ -7,16 +7,14 @@ fs = require 'fs'
|
||||
module = require 'module'
|
||||
path = require 'path'
|
||||
optimist = require 'optimist'
|
||||
# TODO: NSLog is missing .lib on windows
|
||||
nslog = require 'nslog' unless process.platform is 'win32'
|
||||
nslog = require 'nslog'
|
||||
dialog = require 'dialog'
|
||||
|
||||
console.log = (args...) ->
|
||||
# TODO: Make NSLog work as expected
|
||||
output = args.map((arg) -> JSON.stringify(arg)).join(" ")
|
||||
if process.platform == 'darwin'
|
||||
nslog(output)
|
||||
else
|
||||
if process.platform isnt 'darwin'
|
||||
fs.writeFileSync('debug.log', output, flag: 'a')
|
||||
|
||||
process.on 'uncaughtException', (error={}) ->
|
||||
|
@ -6,6 +6,12 @@ path = require 'path'
|
||||
#
|
||||
# This may seem unnecessary but on Windows we have to have separate executables
|
||||
# for each script without this since Windows doesn't support shebang strings.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedNodeProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedNodeProcess extends BufferedProcess
|
||||
# Executes the given Node script.
|
||||
|
@ -1,6 +1,12 @@
|
||||
ChildProcess = require 'child_process'
|
||||
|
||||
# Public: A wrapper which provides line buffering for Node's ChildProcess.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedProcess
|
||||
process: null
|
||||
|
@ -22,7 +22,10 @@ getCachedJavaScript = (cachePath) ->
|
||||
fs.readFileSync(cachePath, 'utf8') if stat.isFile()
|
||||
|
||||
compileCoffeeScript = (coffee, filePath, cachePath) ->
|
||||
js = CoffeeScript.compile(coffee, filename: filePath)
|
||||
{js,v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true)
|
||||
# Include source map in the web page environment.
|
||||
if btoa? and JSON? and unescape? and encodeURIComponent?
|
||||
js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{filePath}"
|
||||
try
|
||||
mkdir(path.dirname(cachePath))
|
||||
fs.writeFileSync(cachePath, js)
|
||||
|
@ -23,46 +23,32 @@ unlinkCommand = (destinationPath, callback) ->
|
||||
callback()
|
||||
|
||||
module.exports =
|
||||
findInstallDirectory: (callback) ->
|
||||
directories = ['/opt/boxen', '/opt/github', '/usr/local']
|
||||
async.detect(directories, fs.isDirectory, callback)
|
||||
getInstallDirectory: ->
|
||||
"/usr/local/bin"
|
||||
|
||||
install: (commandPath, callback) ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
install: (commandPath, commandName, callback) ->
|
||||
if not commandName? or _.isFunction(commandName)
|
||||
callback = commandName
|
||||
commandName = path.basename(commandPath, path.extname(commandPath))
|
||||
|
||||
installCallback = (error, sourcePath, destinationPath) ->
|
||||
directory = @getInstallDirectory()
|
||||
if fs.existsSync(directory)
|
||||
destinationPath = path.join(directory, commandName)
|
||||
unlinkCommand destinationPath, (error) =>
|
||||
if error?
|
||||
console.warn "Failed to install `#{commandName}` binary", error
|
||||
callback?(error, sourcePath, destinationPath)
|
||||
|
||||
@findInstallDirectory (directory) ->
|
||||
if directory?
|
||||
destinationPath = path.join(directory, 'bin', commandName)
|
||||
unlinkCommand destinationPath, (error) ->
|
||||
if error?
|
||||
installCallback(error)
|
||||
error = new Error "Could not remove file at #{destinationPath}." if error
|
||||
callback?(error)
|
||||
else
|
||||
symlinkCommand commandPath, destinationPath, (error) ->
|
||||
installCallback(error, commandPath, destinationPath)
|
||||
symlinkCommand commandPath, destinationPath, (error) =>
|
||||
error = new Error "Failed to symlink #{commandPath} to #{destinationPath}." if error
|
||||
callback?(error)
|
||||
else
|
||||
installCallback(new Error("No destination directory exists to install"))
|
||||
error = new Error "Directory '#{directory} doesn't exist."
|
||||
callback?(error)
|
||||
|
||||
installAtomCommand: (resourcePath, callback) ->
|
||||
if _.isFunction(resourcePath)
|
||||
callback = resourcePath
|
||||
resourcePath = null
|
||||
|
||||
resourcePath ?= atom.getLoadSettings().resourcePath
|
||||
commandPath = path.join(resourcePath, 'atom.sh')
|
||||
@install(commandPath, callback)
|
||||
@install commandPath, callback
|
||||
|
||||
installApmCommand: (resourcePath, callback) ->
|
||||
if _.isFunction(resourcePath)
|
||||
callback = resourcePath
|
||||
resourcePath = null
|
||||
|
||||
resourcePath ?= atom.getLoadSettings().resourcePath
|
||||
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
|
||||
@install(commandPath, callback)
|
||||
@install commandPath, callback
|
||||
|
@ -1,4 +1,7 @@
|
||||
Mixin = require 'mixto'
|
||||
|
||||
module.exports =
|
||||
class ConfigObserver extends Mixin
|
||||
observeConfig: (keyPath, args...) ->
|
||||
@configSubscriptions ?= {}
|
||||
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)
|
||||
|
@ -7,7 +7,13 @@ pathWatcher = require 'pathwatcher'
|
||||
|
||||
File = require './file'
|
||||
|
||||
# Public: Represents a directory using {File}s
|
||||
# Public: Represents a directory using {File}s.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {Directory} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class Directory
|
||||
Emitter.includeInto(this)
|
||||
|
@ -15,7 +15,7 @@ ConfigObserver = require './config-observer'
|
||||
module.exports =
|
||||
class DisplayBuffer extends Model
|
||||
Serializable.includeInto(this)
|
||||
_.extend @prototype, ConfigObserver
|
||||
ConfigObserver.includeInto(this)
|
||||
|
||||
@properties
|
||||
softWrap: null
|
||||
@ -297,8 +297,12 @@ class DisplayBuffer extends Model
|
||||
[startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row)
|
||||
for screenRow in [startScreenRow...endScreenRow]
|
||||
unless screenLine = @screenLines[screenRow]
|
||||
throw new Error("No screen line exists for screen row #{screenRow}, converted from buffer position (#{row}, #{column})")
|
||||
|
||||
throw new Error """
|
||||
No screen line exists for screen row #{screenRow}, converted from buffer position (#{row}, #{column})
|
||||
Soft wrap enabled: #{@getSoftWrap()}
|
||||
Fold count: #{@findFoldMarkers().length}
|
||||
Last buffer row: #{@getLastRow()}
|
||||
"""
|
||||
maxBufferColumn = screenLine.getMaxBufferColumn()
|
||||
if screenLine.isSoftWrapped() and column > maxBufferColumn
|
||||
continue
|
||||
|
@ -1,6 +1,6 @@
|
||||
{View, $, $$$} = require './space-pen-extensions'
|
||||
TextBuffer = require './text-buffer'
|
||||
Gutter = require './gutter'
|
||||
GutterView = require './gutter-view'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
Editor = require './editor'
|
||||
CursorView = require './cursor-view'
|
||||
@ -16,6 +16,12 @@ LongLineLength = 1000
|
||||
# Public: Represents the entire visual pane in Atom.
|
||||
#
|
||||
# The EditorView manages the {Editor}, which manages the file buffers.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {EditorView} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class EditorView extends View
|
||||
@characterWidthCache: {}
|
||||
@ -41,7 +47,7 @@ class EditorView extends View
|
||||
attributes = { class: @classes(params), tabindex: -1 }
|
||||
_.extend(attributes, params.attributes) if params.attributes
|
||||
@div attributes, =>
|
||||
@subview 'gutter', new Gutter
|
||||
@subview 'gutter', new GutterView
|
||||
@div class: 'scroll-view', outlet: 'scrollView', =>
|
||||
@div class: 'overlayer', outlet: 'overlayer'
|
||||
@div class: 'lines', outlet: 'renderedLines'
|
||||
@ -405,7 +411,7 @@ class EditorView extends View
|
||||
|
||||
selectedText = null
|
||||
@hiddenInput.on 'compositionstart', =>
|
||||
selectedText = @getSelectedText()
|
||||
selectedText = @editor.getSelectedText()
|
||||
@hiddenInput.css('width', '100%')
|
||||
@hiddenInput.on 'compositionupdate', (e) =>
|
||||
@editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'})
|
||||
|
@ -202,8 +202,8 @@ class Editor extends Model
|
||||
# Deprecated: Use the ::scrollLeft property directly
|
||||
getScrollLeft: -> @scrollLeft
|
||||
|
||||
# Set the number of characters that can be displayed horizontally in the
|
||||
# editor that contains this edit session.
|
||||
# Public: Set the number of characters that can be displayed horizontally in
|
||||
# the editor.
|
||||
#
|
||||
# editorWidthInChars - A {Number} of characters
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
@ -765,27 +765,27 @@ class Editor extends Model
|
||||
findMarkers: (attributes) ->
|
||||
@displayBuffer.findMarkers(attributes)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markScreenRange}
|
||||
# Public: {Delegates to: DisplayBuffer.markScreenRange}
|
||||
markScreenRange: (args...) ->
|
||||
@displayBuffer.markScreenRange(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markBufferRange}
|
||||
# Public: {Delegates to: DisplayBuffer.markBufferRange}
|
||||
markBufferRange: (args...) ->
|
||||
@displayBuffer.markBufferRange(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markScreenPosition}
|
||||
# Public: {Delegates to: DisplayBuffer.markScreenPosition}
|
||||
markScreenPosition: (args...) ->
|
||||
@displayBuffer.markScreenPosition(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markBufferPosition}
|
||||
# Public: {Delegates to: DisplayBuffer.markBufferPosition}
|
||||
markBufferPosition: (args...) ->
|
||||
@displayBuffer.markBufferPosition(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.destroyMarker}
|
||||
# Public: {Delegates to: DisplayBuffer.destroyMarker}
|
||||
destroyMarker: (args...) ->
|
||||
@displayBuffer.destroyMarker(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.getMarkerCount}
|
||||
# Public: {Delegates to: DisplayBuffer.getMarkerCount}
|
||||
getMarkerCount: ->
|
||||
@buffer.getMarkerCount()
|
||||
|
||||
|
@ -10,6 +10,12 @@ fs = require 'fs-plus'
|
||||
#
|
||||
# You should probably create a {Directory} and access the {File} objects that
|
||||
# it creates, rather than instantiating the {File} class directly.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {File} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class File
|
||||
Emitter.includeInto(this)
|
||||
|
@ -7,7 +7,8 @@ GitUtils = require 'git-utils'
|
||||
# Public: Represents the underlying git operations performed by Atom.
|
||||
#
|
||||
# This class shouldn't be instantiated directly but instead by accessing the
|
||||
# `atom.project` global and calling `getRepo()`.
|
||||
# `atom.project` global and calling `getRepo()`. Note that this will only be
|
||||
# available when the project is backed by a Git repository.
|
||||
#
|
||||
# ## Example
|
||||
#
|
||||
@ -15,6 +16,12 @@ GitUtils = require 'git-utils'
|
||||
# git = atom.project.getRepo()
|
||||
# console.log git.getOriginUrl()
|
||||
# ```
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {Git} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class Git
|
||||
Emitter.includeInto(this)
|
||||
@ -250,12 +257,7 @@ class Git
|
||||
# Public: Returns the upstream branch for the current HEAD, or null if there
|
||||
# is no upstream branch for the current HEAD.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# getUpstreamBranch()
|
||||
# # => "refs/remotes/origin/master"
|
||||
#
|
||||
# Returns a String.
|
||||
# Returns a String branch name such as `refs/remotes/origin/master`
|
||||
getUpstreamBranch: -> @getRepo().getUpstreamBranch()
|
||||
|
||||
# Public: Returns the current SHA for the given reference.
|
||||
|
@ -6,7 +6,7 @@ _ = require 'underscore-plus'
|
||||
#
|
||||
# The gutter also indicates if rows are folded.
|
||||
module.exports =
|
||||
class Gutter extends View
|
||||
class GutterView extends View
|
||||
|
||||
### Internal ###
|
||||
|
||||
@ -234,7 +234,7 @@ class Gutter extends View
|
||||
lastBufferRow = null
|
||||
for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow
|
||||
lastBufferRow = bufferRow
|
||||
lineNumberElement = @getLineNumberElement(bufferRow)[0]
|
||||
if lineNumberElement = @getLineNumberElement(bufferRow)[0]
|
||||
if editor.isFoldableAtBufferRow(bufferRow)
|
||||
lineNumberElement.classList.add('foldable')
|
||||
else
|
@ -142,7 +142,12 @@ class Keymap
|
||||
@userKeymapFile.on 'contents-changed moved removed', => @loadUserKeymap()
|
||||
|
||||
loadDirectory: (directoryPath) ->
|
||||
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
||||
platforms = ['darwin', 'freebsd', 'linux', 'sunos', 'win32']
|
||||
otherPlatforms = platforms.filter (name) -> name != process.platform
|
||||
|
||||
for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
||||
continue if path.basename(filePath, path.extname(filePath)) in otherPlatforms
|
||||
@load(filePath)
|
||||
|
||||
load: (path) ->
|
||||
@add(path, CSON.readFileSync(path))
|
||||
|
@ -1,7 +1,9 @@
|
||||
clipboard = require 'clipboard'
|
||||
crypto = require 'crypto'
|
||||
|
||||
# Internal: Represents the clipboard used for copying and pasting in Atom.
|
||||
# Public: Represents the clipboard used for copying and pasting in Atom.
|
||||
#
|
||||
# A pasteboard instance is always available under the `atom.pasteboard` global.
|
||||
module.exports =
|
||||
class Pasteboard
|
||||
signatureForMetadata: null
|
||||
@ -14,18 +16,19 @@ class Pasteboard
|
||||
md5: (text) ->
|
||||
crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
|
||||
# Saves from the clipboard.
|
||||
# Public: Write the given text to the clipboard.
|
||||
#
|
||||
# text - A {String} to store
|
||||
# metadata - An object of additional info to associate with the text
|
||||
# text - A {String} to store.
|
||||
# metadata - An {Object} of additional info to associate with the text.
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
clipboard.writeText(text)
|
||||
|
||||
# Loads from the clipboard.
|
||||
# Public: Read the text from the clipboard.
|
||||
#
|
||||
# Returns an {Array}. The first index is the saved text, and the second is any metadata associated with the text.
|
||||
# Returns an {Array}. The first element is the saved text and the second is
|
||||
# any metadata associated with the text.
|
||||
read: ->
|
||||
text = clipboard.readText()
|
||||
value = [text]
|
||||
|
@ -16,8 +16,7 @@ Git = require './git'
|
||||
|
||||
# Public: Represents a project that's opened in Atom.
|
||||
#
|
||||
# Ultimately, a project is a git directory that's been opened. It's a collection
|
||||
# of directories and files that you can operate on.
|
||||
# There is always a project available under the `atom.project` global.
|
||||
module.exports =
|
||||
class Project extends Model
|
||||
atom.deserializers.add(this)
|
||||
|
@ -5,9 +5,11 @@
|
||||
# This `View` subclass listens to events such as `page-up`, `page-down`,
|
||||
# `move-to-top`, and `move-to-bottom`.
|
||||
#
|
||||
# FIXME: I don't actually understand if this is useful or not. I think it is
|
||||
# a base of package widgets but I don't really understand how the core events
|
||||
# work.
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {ScrollView} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class ScrollView extends View
|
||||
|
||||
|
@ -4,6 +4,12 @@ fuzzyFilter = require('fuzzaldrin').filter
|
||||
|
||||
# Public: Provides a widget for users to make a selection from a list of
|
||||
# choices.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {SelectList} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class SelectList extends View
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
_ = require 'underscore-plus'
|
||||
spacePen = require 'space-pen'
|
||||
ConfigObserver = require './config-observer'
|
||||
{Subscriber} = require 'emissary'
|
||||
ConfigObserver = require './config-observer'
|
||||
|
||||
_.extend spacePen.View.prototype, ConfigObserver
|
||||
ConfigObserver.includeInto(spacePen.View)
|
||||
Subscriber.includeInto(spacePen.View)
|
||||
|
||||
jQuery = spacePen.jQuery
|
||||
|
@ -6,7 +6,12 @@ _ = require 'underscore-plus'
|
||||
{$, $$} = require './space-pen-extensions'
|
||||
Token = require './token'
|
||||
|
||||
### Public ###
|
||||
# Public: Syntax class holding the grammars used for tokenizing.
|
||||
#
|
||||
# The Syntax class also contains properties for things such as the
|
||||
# language-specific comment regexes.
|
||||
#
|
||||
# There is always a syntax object available under the `atom.syntax` global.
|
||||
module.exports =
|
||||
class Syntax extends GrammarRegistry
|
||||
Subscriber.includeInto(this)
|
||||
@ -49,6 +54,18 @@ class Syntax extends GrammarRegistry
|
||||
@scopedProperties = []
|
||||
@scopedPropertiesIndex = 0
|
||||
|
||||
# Public: Get a property for the given scope and key path.
|
||||
#
|
||||
# ## Example
|
||||
# ```coffee
|
||||
# comment = atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')
|
||||
# console.log(comment) # '# '
|
||||
# ```
|
||||
#
|
||||
# * scope: An {Array} of {String} scopes.
|
||||
# * keyPath: A {String} key path.
|
||||
#
|
||||
# Returns a {String} property value or undefined.
|
||||
getProperty: (scope, keyPath) ->
|
||||
for object in @propertiesForScope(scope, keyPath)
|
||||
value = _.valueForKeyPath(object, keyPath)
|
||||
|
@ -12,6 +12,12 @@ child_process = require 'child_process'
|
||||
# * task:warn - Emitted when console.warn is called within the task.
|
||||
# * task:error - Emitted when console.error is called within the task.
|
||||
# * task:completed - Emitted when the task has succeeded or failed.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {Task} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class Task
|
||||
Emitter.includeInto(this)
|
||||
|
@ -1,13 +1,9 @@
|
||||
### Internal ###
|
||||
|
||||
isHighSurrogate = (string, index) ->
|
||||
0xD800 <= string.charCodeAt(index) <= 0xDBFF
|
||||
|
||||
isLowSurrogate = (string, index) ->
|
||||
0xDC00 <= string.charCodeAt(index) <= 0xDFFF
|
||||
|
||||
### Public ###
|
||||
|
||||
# Is the character at the given index the start of a high/low surrogate pair?
|
||||
#
|
||||
# string - The {String} to check for a surrogate pair.
|
||||
|
@ -8,9 +8,9 @@ fs = require 'fs-plus'
|
||||
AtomPackage = require './atom-package'
|
||||
File = require './file'
|
||||
|
||||
# Private: Handles discovering and loading available themes.
|
||||
# Public: Handles loading and activating available themes.
|
||||
#
|
||||
# Themes are a subset of packages
|
||||
# A ThemeManager instance is always available under the `atom.themes` global.
|
||||
module.exports =
|
||||
class ThemeManager
|
||||
Emitter.includeInto(this)
|
||||
@ -19,27 +19,26 @@ class ThemeManager
|
||||
@lessCache = null
|
||||
@packageManager.registerPackageActivator(this, ['theme'])
|
||||
|
||||
# Internal-only:
|
||||
getAvailableNames: ->
|
||||
# TODO: Maybe should change to list all the available themes out there?
|
||||
@getLoadedNames()
|
||||
|
||||
# Public: Get an array of all the loaded theme names.
|
||||
getLoadedNames: ->
|
||||
theme.name for theme in @getLoadedThemes()
|
||||
|
||||
# Internal-only:
|
||||
# Public: Get an array of all the active theme names.
|
||||
getActiveNames: ->
|
||||
theme.name for theme in @getActiveThemes()
|
||||
|
||||
# Internal-only:
|
||||
# Public: Get an array of all the active themes.
|
||||
getActiveThemes: ->
|
||||
pack for pack in @packageManager.getActivePackages() when pack.isTheme()
|
||||
|
||||
# Internal-only:
|
||||
# Public: Get an array of all the loaded themes.
|
||||
getLoadedThemes: ->
|
||||
pack for pack in @packageManager.getLoadedPackages() when pack.isTheme()
|
||||
|
||||
# Internal-only: adhere to the PackageActivator interface
|
||||
activatePackages: (themePackages) -> @activateThemes()
|
||||
|
||||
# Private: Get the enabled theme names from the config.
|
||||
@ -53,7 +52,6 @@ class ThemeManager
|
||||
# the first/top theme to override later themes in the stack.
|
||||
themeNames.reverse()
|
||||
|
||||
# Internal-only:
|
||||
activateThemes: ->
|
||||
# atom.config.observe runs the callback once, then on subsequent changes.
|
||||
atom.config.observe 'core.themes', =>
|
||||
@ -69,13 +67,11 @@ class ThemeManager
|
||||
|
||||
@emit('reloaded')
|
||||
|
||||
# Internal-only:
|
||||
deactivateThemes: ->
|
||||
@unwatchUserStylesheet()
|
||||
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
|
||||
null
|
||||
|
||||
# Internal-only:
|
||||
refreshLessCache: ->
|
||||
@lessCache?.setImportPaths(@getImportPaths())
|
||||
|
||||
@ -85,7 +81,6 @@ class ThemeManager
|
||||
setEnabledThemes: (enabledThemeNames) ->
|
||||
atom.config.set('core.themes', enabledThemeNames)
|
||||
|
||||
# Public:
|
||||
getImportPaths: ->
|
||||
activeThemes = @getActiveThemes()
|
||||
if activeThemes.length > 0
|
||||
@ -98,7 +93,7 @@ class ThemeManager
|
||||
|
||||
themePath for themePath in themePaths when fs.isDirectorySync(themePath)
|
||||
|
||||
# Public:
|
||||
# Public: Returns the {String} path to the user's stylesheet under ~/.atom
|
||||
getUserStylesheetPath: ->
|
||||
stylesheetPath = fs.resolve(path.join(@configDirPath, 'user'), ['css', 'less'])
|
||||
if fs.isFileSync(stylesheetPath)
|
||||
@ -106,13 +101,11 @@ class ThemeManager
|
||||
else
|
||||
path.join(@configDirPath, 'user.less')
|
||||
|
||||
#Private:
|
||||
unwatchUserStylesheet: ->
|
||||
@userStylesheetFile?.off()
|
||||
@userStylesheetFile = null
|
||||
@removeStylesheet(@userStylesheetPath) if @userStylesheetPath?
|
||||
|
||||
# Private:
|
||||
loadUserStylesheet: ->
|
||||
@unwatchUserStylesheet()
|
||||
userStylesheetPath = @getUserStylesheetPath()
|
||||
@ -125,34 +118,32 @@ class ThemeManager
|
||||
userStylesheetContents = @loadStylesheet(userStylesheetPath)
|
||||
@applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme')
|
||||
|
||||
# Internal-only:
|
||||
loadBaseStylesheets: ->
|
||||
@requireStylesheet('bootstrap/less/bootstrap')
|
||||
@reloadBaseStylesheets()
|
||||
|
||||
# Internal-only:
|
||||
reloadBaseStylesheets: ->
|
||||
@requireStylesheet('../static/atom')
|
||||
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
|
||||
@requireStylesheet(nativeStylesheetPath)
|
||||
|
||||
# Internal-only:
|
||||
stylesheetElementForId: (id, htmlElement=$('html')) ->
|
||||
htmlElement.find("""head style[id="#{id}"]""")
|
||||
|
||||
# Internal-only:
|
||||
resolveStylesheet: (stylesheetPath) ->
|
||||
if path.extname(stylesheetPath).length > 0
|
||||
fs.resolveOnLoadPath(stylesheetPath)
|
||||
else
|
||||
fs.resolveOnLoadPath(stylesheetPath, ['css', 'less'])
|
||||
|
||||
# Public: resolves and applies the stylesheet specified by the path.
|
||||
# Public: Resolve and apply the stylesheet specified by the path.
|
||||
#
|
||||
# * stylesheetPath: String. Can be an absolute path or the name of a CSS or
|
||||
# LESS file in the stylesheets path.
|
||||
# This supports both CSS and LESS stylsheets.
|
||||
#
|
||||
# Returns the absolute path to the stylesheet
|
||||
# * stylesheetPath: A {String} path to the stylesheet that can be an absolute
|
||||
# path or a relative path that will be resolved against the load path.
|
||||
#
|
||||
# Returns the absolute path to the required stylesheet.
|
||||
requireStylesheet: (stylesheetPath, ttype = 'bundled', htmlElement) ->
|
||||
if fullPath = @resolveStylesheet(stylesheetPath)
|
||||
content = @loadStylesheet(fullPath)
|
||||
@ -162,14 +153,12 @@ class ThemeManager
|
||||
|
||||
fullPath
|
||||
|
||||
# Internal-only:
|
||||
loadStylesheet: (stylesheetPath) ->
|
||||
if path.extname(stylesheetPath) is '.less'
|
||||
@loadLessStylesheet(stylesheetPath)
|
||||
else
|
||||
fs.readFileSync(stylesheetPath, 'utf8')
|
||||
|
||||
# Internal-only:
|
||||
loadLessStylesheet: (lessStylesheetPath) ->
|
||||
unless @lessCache?
|
||||
LessCompileCache = require './less-compile-cache'
|
||||
@ -184,16 +173,13 @@ class ThemeManager
|
||||
#{e.message}
|
||||
"""
|
||||
|
||||
# Internal-only:
|
||||
stringToId: (string) ->
|
||||
string.replace(/\\/g, '/')
|
||||
|
||||
# Internal-only:
|
||||
removeStylesheet: (stylesheetPath) ->
|
||||
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
|
||||
@stylesheetElementForId(@stringToId(fullPath)).remove()
|
||||
|
||||
# Internal-only:
|
||||
applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) ->
|
||||
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
|
||||
if styleElement.length
|
||||
|
@ -1,9 +1,9 @@
|
||||
# Public: Measure how long a function takes to run.
|
||||
#
|
||||
# * description:
|
||||
# A String description that will be logged to the console.
|
||||
# A {String} description that will be logged to the console.
|
||||
# * fn:
|
||||
# A Function to measure the duration of.
|
||||
# A {Function} to measure the duration of.
|
||||
#
|
||||
# Returns the value returned by the given function.
|
||||
window.measure = (description, fn) ->
|
||||
@ -16,10 +16,10 @@ window.measure = (description, fn) ->
|
||||
# Public: Create a dev tools profile for a function.
|
||||
#
|
||||
# * description:
|
||||
# A String descrption that will be available in the Profiles tab of the dev
|
||||
# A {String} descrption that will be available in the Profiles tab of the dev
|
||||
# tools.
|
||||
# * fn:
|
||||
# A Function to profile.
|
||||
# A {Function} to profile.
|
||||
#
|
||||
# Return the value returned by the given function.
|
||||
window.profile = (description, fn) ->
|
||||
|
@ -6,6 +6,7 @@ Delegator = require 'delegato'
|
||||
{$, $$, View} = require './space-pen-extensions'
|
||||
fs = require 'fs-plus'
|
||||
Workspace = require './workspace'
|
||||
CommandInstaller = require './command-installer'
|
||||
EditorView = require './editor-view'
|
||||
PaneView = require './pane-view'
|
||||
PaneColumnView = require './pane-column-view'
|
||||
@ -37,6 +38,11 @@ Editor = require './editor'
|
||||
# * `application:bring-all-windows-to-front` - Brings all {AtomWindow}s to the
|
||||
# the front.
|
||||
#
|
||||
# ## Requiring in package specs
|
||||
#
|
||||
# ```coffee
|
||||
# {WorkspaceView} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class WorkspaceView extends View
|
||||
Delegator.includeInto(this)
|
||||
@ -101,8 +107,11 @@ class WorkspaceView extends View
|
||||
@command 'application:bring-all-windows-to-front', -> ipc.sendChannel('command', 'application:bring-all-windows-to-front')
|
||||
@command 'application:open-your-config', -> ipc.sendChannel('command', 'application:open-your-config')
|
||||
@command 'application:open-your-keymap', -> ipc.sendChannel('command', 'application:open-your-keymap')
|
||||
@command 'application:open-your-snippets', -> ipc.sendChannel('command', 'application:open-your-snippets')
|
||||
@command 'application:open-your-stylesheet', -> ipc.sendChannel('command', 'application:open-your-stylesheet')
|
||||
|
||||
@command 'window:install-shell-commands', => @installShellCommands()
|
||||
|
||||
@command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec'))
|
||||
@command 'window:increase-font-size', => @increaseFontSize()
|
||||
@command 'window:decrease-font-size', => @decreaseFontSize()
|
||||
@ -122,6 +131,26 @@ class WorkspaceView extends View
|
||||
@command 'core:save', => @saveActivePaneItem()
|
||||
@command 'core:save-as', => @saveActivePaneItemAs()
|
||||
|
||||
installShellCommands: ->
|
||||
showErrorDialog = (error) ->
|
||||
installDirectory = CommandInstaller.getInstallDirectory()
|
||||
atom.confirm
|
||||
message: error.message
|
||||
detailedMessage: "Make sure #{installDirectory} exists and is writable. Run 'sudo mkdir -p #{installDirectory} && sudo chown $USER #{installDirectory}' to fix this problem."
|
||||
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
CommandInstaller.installAtomCommand resourcePath, (error) =>
|
||||
if error?
|
||||
showDialog(error)
|
||||
else
|
||||
CommandInstaller.installApmCommand resourcePath, (error) =>
|
||||
if error?
|
||||
showDialog(error)
|
||||
else
|
||||
atom.confirm
|
||||
message: "Commands installed."
|
||||
detailedMessage: "The shell commands `atom` and `apm` are installed."
|
||||
|
||||
# Private:
|
||||
handleFocus: (e) ->
|
||||
if @getActivePane()
|
||||
|
@ -27,6 +27,7 @@
|
||||
.make-icon(book);
|
||||
.make-icon(bookmark);
|
||||
.make-icon(broadcast);
|
||||
.make-icon(browser);
|
||||
.make-icon(bug);
|
||||
.make-icon(calendar);
|
||||
.make-icon(check);
|
||||
@ -46,6 +47,7 @@
|
||||
.make-icon(comment-add);
|
||||
.make-icon(comment-discussion);
|
||||
.make-icon(credit-card);
|
||||
.make-icon(dash);
|
||||
.make-icon(dashboard);
|
||||
.make-icon(database);
|
||||
.make-icon(device-camera);
|
||||
@ -74,6 +76,7 @@
|
||||
.make-icon(file-symlink-file);
|
||||
.make-icon(file-text);
|
||||
.make-icon(file-zip);
|
||||
.make-icon(fold);
|
||||
.make-icon(gear);
|
||||
.make-icon(gift);
|
||||
.make-icon(gist);
|
||||
@ -92,6 +95,7 @@
|
||||
.make-icon(git-pull-request-abandoned);
|
||||
.make-icon(globe);
|
||||
.make-icon(graph);
|
||||
.make-icon(heart);
|
||||
.make-icon(history);
|
||||
.make-icon(home);
|
||||
.make-icon(horizontal-rule);
|
||||
@ -127,10 +131,14 @@
|
||||
.make-icon(mail);
|
||||
.make-icon(mail-read);
|
||||
.make-icon(mail-reply);
|
||||
.make-icon(mark-facebook);
|
||||
.make-icon(mark-github);
|
||||
.make-icon(mark-github-detail);
|
||||
.make-icon(mark-google);
|
||||
.make-icon(mark-twitter);
|
||||
.make-icon(markdown);
|
||||
.make-icon(megaphone);
|
||||
.make-icon(mention);
|
||||
.make-icon(microscope);
|
||||
.make-icon(milestone);
|
||||
.make-icon(mirror-private);
|
||||
@ -142,6 +150,7 @@
|
||||
.make-icon(mute);
|
||||
.make-icon(mute-video);
|
||||
.make-icon(no-newline);
|
||||
.make-icon(node-js);
|
||||
.make-icon(octoface);
|
||||
.make-icon(organization);
|
||||
.make-icon(pencil);
|
||||
@ -150,11 +159,16 @@
|
||||
.make-icon(person-follow);
|
||||
.make-icon(person-remove);
|
||||
.make-icon(pin);
|
||||
.make-icon(playback-fast-forward);
|
||||
.make-icon(playback-pause);
|
||||
.make-icon(playback-play);
|
||||
.make-icon(playback-rewind);
|
||||
.make-icon(plus);
|
||||
.make-icon(podium);
|
||||
.make-icon(primitive-dot);
|
||||
.make-icon(primitive-square);
|
||||
.make-icon(pulse);
|
||||
.make-icon(puzzle);
|
||||
.make-icon(question);
|
||||
.make-icon(quote);
|
||||
.make-icon(radio-tower);
|
||||
@ -175,18 +189,22 @@
|
||||
.make-icon(screen-full);
|
||||
.make-icon(screen-normal);
|
||||
.make-icon(search);
|
||||
.make-icon(search-save);
|
||||
.make-icon(server);
|
||||
.make-icon(settings);
|
||||
.make-icon(split);
|
||||
.make-icon(squirrel);
|
||||
.make-icon(star);
|
||||
.make-icon(star-add);
|
||||
.make-icon(star-delete);
|
||||
.make-icon(steps);
|
||||
.make-icon(stop);
|
||||
.make-icon(sync);
|
||||
.make-icon(tag);
|
||||
.make-icon(tag-add);
|
||||
.make-icon(tag-remove);
|
||||
.make-icon(telescope);
|
||||
.make-icon(terminal);
|
||||
.make-icon(three-bars);
|
||||
.make-icon(tools);
|
||||
.make-icon(triangle-down);
|
||||
|
Binary file not shown.
@ -14,6 +14,7 @@
|
||||
@book: "\f007";
|
||||
@bookmark: "\f07b";
|
||||
@broadcast: "\f048";
|
||||
@browser: "\f0c5";
|
||||
@bug: "\f091";
|
||||
@calendar: "\f068";
|
||||
@check: "\f03a";
|
||||
@ -33,6 +34,7 @@
|
||||
@comment-add: "\f06f";
|
||||
@comment-discussion: "\f04f";
|
||||
@credit-card: "\f045";
|
||||
@dash: "\f0ca";
|
||||
@dashboard: "\f07d";
|
||||
@database: "\f096";
|
||||
@device-camera: "\f056";
|
||||
@ -61,6 +63,7 @@
|
||||
@file-symlink-file: "\f0b0";
|
||||
@file-text: "\f011";
|
||||
@file-zip: "\f013";
|
||||
@fold: "\f0cc";
|
||||
@gear: "\f02f";
|
||||
@gift: "\f042";
|
||||
@gist: "\f00e";
|
||||
@ -79,6 +82,7 @@
|
||||
@git-pull-request-abandoned: "\f090";
|
||||
@globe: "\f0b6";
|
||||
@graph: "\f043";
|
||||
@heart: "\2665";
|
||||
@history: "\f07e";
|
||||
@home: "\f08d";
|
||||
@horizontal-rule: "\f070";
|
||||
@ -114,10 +118,14 @@
|
||||
@mail: "\f03b";
|
||||
@mail-read: "\f03c";
|
||||
@mail-reply: "\f051";
|
||||
@mark-facebook: "\f0ce";
|
||||
@mark-github: "\f00a";
|
||||
@mark-github-detail: "\f093";
|
||||
@mark-google: "\f0cd";
|
||||
@mark-twitter: "\f0ae";
|
||||
@markdown: "\f0c9";
|
||||
@megaphone: "\f077";
|
||||
@mention: "\f0be";
|
||||
@microscope: "\f089";
|
||||
@milestone: "\f075";
|
||||
@mirror-private: "\f025";
|
||||
@ -129,19 +137,26 @@
|
||||
@mute: "\f080";
|
||||
@mute-video: "\f0b8";
|
||||
@no-newline: "\f09c";
|
||||
@node-js: "\f0c3";
|
||||
@octoface: "\f008";
|
||||
@organization: "\f037";
|
||||
@package: "\f0c4";
|
||||
@pencil: "\f058";
|
||||
@person: "\f018";
|
||||
@person-add: "\f01a";
|
||||
@person-follow: "\f01c";
|
||||
@person-remove: "\f01b";
|
||||
@pin: "\f041";
|
||||
@playback-fast-forward: "\f0bd";
|
||||
@playback-pause: "\f0bb";
|
||||
@playback-play: "\f0bf";
|
||||
@playback-rewind: "\f0bc";
|
||||
@plus: "\f05d";
|
||||
@podium: "\f0af";
|
||||
@primitive-dot: "\f052";
|
||||
@primitive-square: "\f053";
|
||||
@pulse: "\f085";
|
||||
@puzzle: "\f0c0";
|
||||
@question: "\f02c";
|
||||
@quote: "\f063";
|
||||
@radio-tower: "\f030";
|
||||
@ -162,18 +177,22 @@
|
||||
@screen-full: "\f066";
|
||||
@screen-normal: "\f067";
|
||||
@search: "\f02e";
|
||||
@search-save: "\f0cb";
|
||||
@server: "\f097";
|
||||
@settings: "\f07c";
|
||||
@split: "\f0c6";
|
||||
@squirrel: "\f0b2";
|
||||
@star: "\f02a";
|
||||
@star-add: "\f082";
|
||||
@star-delete: "\f083";
|
||||
@steps: "\f0c7";
|
||||
@stop: "\f08f";
|
||||
@sync: "\f087";
|
||||
@tag: "\f015";
|
||||
@tag-add: "\f054";
|
||||
@tag-remove: "\f055";
|
||||
@telescope: "\f088";
|
||||
@terminal: "\f0c8";
|
||||
@three-bars: "\f05e";
|
||||
@tools: "\f031";
|
||||
@triangle-down: "\f05b";
|
||||
|
2
vendor/apm
vendored
2
vendor/apm
vendored
@ -1 +1 @@
|
||||
Subproject commit b80ef23ce8d2a1e8b4f40eb0f89c87f32dcc3415
|
||||
Subproject commit 3f8701bfe624de844641863391c04def9cca5c86
|
Loading…
Reference in New Issue
Block a user