Merge branch 'cj-make-package-activation-consistent' into cj-update-markdown-preview

Conflicts:
	src/deserializer-manager.coffee
	src/workspace.coffee
This commit is contained in:
probablycorey 2014-02-10 14:17:46 -08:00
commit ffb7093cf3
85 changed files with 1602 additions and 1387 deletions

View File

@ -30,7 +30,7 @@ in the proper package's repository.
* Follow the [CoffeeScript](#coffeescript-styleguide),
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/)
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine)
specs
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
`package.json` dependency.
@ -61,3 +61,32 @@ in the proper package's repository.
* Set parameter defaults without spaces around the equal sign
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
## Documentation Styleguide
* Use [TomDoc](http://tomdoc.org).
* Use [Markdown](https://daringfireball.net/projects/markdown).
* Reference classes with `{ClassName}` style notation.
* Reference methods with `{ClassName.methodName}` style notation.
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
style notation.
### Example
```coffee
# Public: Disable the package with the given name.
#
# This method emits multiple events:
#
# * `package-will-be-disabled` - before the package is disabled.
# * `package-disabled` - after the package is disabled.
#
# name - The {String} name of the package to disable.
# options - The {Object} with disable options (default: {}):
# :trackTime - `true` to track the amount of time disabling took.
# :ignoreErrors - `true` to catch and ignore errors thrown.
# callback - The {Function} to call after the package has been disabled.
#
# Returns `undefined`.
disablePackage: (name, options, callback) ->
```

View File

@ -7,7 +7,7 @@
},
"dependencies": {
"async": "~0.2.9",
"biscotto": "git://github.com/atom/biscotto.git#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be",
"biscotto": "0.6.0",
"first-mate": "1.x",
"formidable": "~1.0.14",
"fs-plus": "1.x",
@ -32,7 +32,6 @@
"runas": "~0.3.0",
"underscore-plus": "1.x",
"unzip": "~0.1.9",
"vm-compatibility-layer": "~0.1.0",
"walkdir": "0.0.7"
"vm-compatibility-layer": "~0.1.0"
}
}

View File

@ -21,7 +21,6 @@ module.exports = (grunt) ->
cp 'atom.sh', path.join(appDir, 'atom.sh')
cp 'package.json', path.join(appDir, 'package.json')
cp 'apm', path.join(appDir, 'apm')
packageDirectories = []
nonPackageDirectories = [
@ -47,8 +46,12 @@ module.exports = (grunt) ->
path.join('less', 'dist')
path.join('less', 'test')
path.join('bootstrap', 'docs')
path.join('bootstrap', 'examples')
path.join('spellchecker', 'vendor')
path.join('xmldom', 'test')
path.join('jasmine-reporters', 'ext')
path.join('build', 'Release', 'obj.target')
path.join('build', 'Release', '.deps')
path.join('vendor', 'apm')
path.join('resources', 'mac')
path.join('resources', 'win')
@ -64,6 +67,7 @@ module.exports = (grunt) ->
cp 'spec', path.join(appDir, 'spec')
cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/
cp 'static', path.join(appDir, 'static')
cp 'apm', path.join(appDir, 'apm'), filter: nodeModulesFilter
if process.platform is 'darwin'
grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->

View File

@ -15,24 +15,7 @@ module.exports = (grunt) ->
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
done = @async()
downloadFileFromRepo = ({repo, file}, callback) ->
uri = "https://raw2.github.com/atom/#{repo}/master/#{file}"
request uri, (error, response, contents) ->
return callback(error) if error?
downloadPath = path.join('docs', 'includes', repo, file)
fs.writeFile downloadPath, contents, (error) ->
callback(error, downloadPath)
includes = [
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
{repo: 'space-pen', file: 'src/space-pen.coffee'}
{repo: 'text-buffer', file: 'src/marker.coffee'}
{repo: 'text-buffer', file: 'src/point.coffee'}
{repo: 'text-buffer', file: 'src/range.coffee'}
{repo: 'theorist', file: 'src/model.coffee'}
]
async.map includes, downloadFileFromRepo, (error, includePaths) ->
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
@ -49,13 +32,32 @@ module.exports = (grunt) ->
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
done = @async()
args = [commonArgs..., '--noOutput', 'src/']
grunt.util.spawn({cmd, args, opts}, done)
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'missing-docs', 'Generate stats about the doc coverage', ->
done = @async()
args = [commonArgs..., '--noOutput', '--missing', 'src/']
grunt.util.spawn({cmd, args, opts}, done)
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'--missing'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'copy-docs', 'Copies over latest API docs to atom-docs', ->
done = @async()
@ -129,3 +131,24 @@ module.exports = (grunt) ->
grunt.util.spawn({cmd, args, opts}, callback)
grunt.util.async.waterfall [fetchTag, stageDocs, fetchSha, commitChanges, pushOrigin, pushHeroku], done
downloadFileFromRepo = ({repo, file}, callback) ->
uri = "https://raw.github.com/atom/#{repo}/master/#{file}"
request uri, (error, response, contents) ->
return callback(error) if error?
downloadPath = path.join('docs', 'includes', repo, file)
fs.writeFile downloadPath, contents, (error) ->
callback(error, downloadPath)
downloadIncludes = (callback) ->
includes = [
{repo: 'first-mate', file: 'src/grammar.coffee'}
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
{repo: 'space-pen', file: 'src/space-pen.coffee'}
{repo: 'text-buffer', file: 'src/marker.coffee'}
{repo: 'text-buffer', file: 'src/point.coffee'}
{repo: 'text-buffer', file: 'src/range.coffee'}
{repo: 'theorist', file: 'src/model.coffee'}
]
async.map(includes, downloadFileFromRepo, callback)

View File

@ -1,27 +1,41 @@
fs = require 'fs'
fs = require 'fs-plus'
path = require 'path'
walkdir = require 'walkdir'
module.exports = (grunt) ->
cp: (source, destination, {filter}={}) ->
unless grunt.file.exists(source)
grunt.fatal("Cannot copy non-existent #{source.cyan} to #{destination.cyan}")
try
walkdir.sync source, (sourcePath, stats) ->
return if filter?.test(sourcePath)
copyFile = (sourcePath, destinationPath) ->
return if filter?.test(sourcePath)
destinationPath = path.join(destination, path.relative(source, sourcePath))
if stats.isSymbolicLink()
grunt.file.mkdir(path.dirname(destinationPath))
fs.symlinkSync(fs.readlinkSync(sourcePath), destinationPath)
else if stats.isFile()
grunt.file.copy(sourcePath, destinationPath)
stats = fs.lstatSync(sourcePath)
if stats.isSymbolicLink()
grunt.file.mkdir(path.dirname(destinationPath))
fs.symlinkSync(fs.readlinkSync(sourcePath), destinationPath)
else if stats.isFile()
grunt.file.copy(sourcePath, destinationPath)
if grunt.file.exists(destinationPath)
fs.chmodSync(destinationPath, fs.statSync(sourcePath).mode)
catch error
grunt.fatal(error)
if grunt.file.exists(destinationPath)
fs.chmodSync(destinationPath, fs.statSync(sourcePath).mode)
if grunt.file.isFile(source)
copyFile(source, destination)
else
try
onFile = (sourcePath) ->
destinationPath = path.join(destination, path.relative(source, sourcePath))
copyFile(sourcePath, destinationPath)
onDirectory = (sourcePath) ->
if fs.isSymbolicLinkSync(sourcePath)
destinationPath = path.join(destination, path.relative(source, sourcePath))
copyFile(sourcePath, destinationPath)
false
else
true
fs.traverseTreeSync source, onFile, onDirectory
catch error
grunt.fatal(error)
grunt.verbose.writeln("Copied #{source.cyan} to #{destination.cyan}.")

View File

@ -0,0 +1,65 @@
## Converting a TextMate Theme
This guide will show you how to convert a [TextMate][TextMate] theme to an Atom
theme.
### Differences
TextMate themes use [plist][plist] files while Atom themes use [CSS][CSS] or
[LESS][LESS] to style the UI and syntax in the editor.
The utility that converts the theme first parses the theme's plist file and
then creates comparable CSS rules and properties that will style Atom similarly.
### Install apm
The `apm` command line utility that ships with Atom supports converting
a TextMate theme to an Atom theme.
Check that you have `apm` installed by running the following command in your
terminal:
```sh
apm help init
```
You should see a message print out with details about the `apm init` command.
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
to install the `apm` and `atom` commands.
You can now run `apm help init` to see all the options for initializing new
packages and themes.
### Convert the Theme
Download the theme you wish to convert, you can browse existing TextMate themes
[here][TextMateThemes].
Now, let's say you've downloaded the theme to `~/Downloads/MyTheme.tmTheme`,
you can convert the theme with the following command:
```sh
apm init --theme ~/.atom/packages/my-theme --convert ~/Downloads/MyTheme.tmTheme
```
You can browse to `~/.atom/packages/my-theme` to see the converted theme.
### Activate the Theme
Now that your theme is installed to `~/.atom/packages` you can enable it
by launching Atom and selecting the _Atom > Preferences..._ menu.
Select the _Themes_ link on the left side and choose _My Theme_ from the
__Syntax Theme__ dropdown menu to enable your new theme.
:tada: Your theme is now enabled, open an editor to see it in action!
:bulb: Consider using `apm publish` to publish this theme to [atom.io][atomio].
[atomio]: https://atom.io
[CSS]: http://en.wikipedia.org/wiki/Cascading_Style_Sheets
[LESS]: http://lesscss.org
[plist]: http://en.wikipedia.org/wiki/Property_list
[TextMate]: http://macromates.com
[TextMateThemes]: http://wiki.macromates.com/Themes/UserSubmittedThemes

View File

@ -0,0 +1,50 @@
## Converting a TextMate Bundle
This guide will show you how to convert a [TextMate][TextMate] bundle to an
Atom package.
Converting a TextMate bundle will allow you to use its editor preferences,
snippets, and colorization inside Atom.
### Install apm
The `apm` command line utility that ships with Atom supports converting
a TextMate bundle to an Atom package.
Check that you have `apm` installed by running the following command in your
terminal:
```sh
apm help init
```
You should see a message print out with details about the `apm init` command.
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
to install the `apm` and `atom` commands.
### Convert the Package
Let's convert the TextMate bundle for the [R][R] programming language. You can find other existing TextMate bundles [here][TextMateOrg].
You can convert the R bundle with the following command:
```sh
apm init --package ~/.atom/packages/language-r --convert https://github.com/textmate/r.tmbundle
```
You can now browse to `~/.atom/packages/language-r` to see the converted bundle.
:tada: Your new package is now ready to use, launch Atom and open a `.r` file in
the editor to see it in action!
:bulb: Consider using `apm publish` to publish this package to
[atom.io][atomio].
[atomio]: https://atom.io
[CSS]: http://en.wikipedia.org/wiki/Cascading_Style_Sheets
[LESS]: http://lesscss.org
[plist]: http://en.wikipedia.org/wiki/Property_list
[R]: http://en.wikipedia.org/wiki/R_(programming_language)
[TextMate]: http://macromates.com
[TextMateOrg]: https://github.com/textmate/r.tmbundle

View File

@ -112,14 +112,16 @@ namespaces: `core` and `editor`.
### Quick Personal Hacks
### user.coffee
### init.coffee
When Atom finishes loading, it will evaluate _user.coffee_ in your _~/.atom_
When Atom finishes loading, it will evaluate _init.coffee_ in your _~/.atom_
directory, giving you a chance to run arbitrary personal CoffeeScript code to
make customizations. You have full access to Atom's API from code in this file.
If customizations become extensive, consider [creating a
package][create-a-package].
This file can also be named _init.js_ and contain JavaScript code.
### styles.css
If you want to apply quick-and-dirty personal styling changes without creating

View File

@ -1,63 +0,0 @@
## Atom Documentation Format
This document describes our documentation format, which is markdown with
a few rules.
### Philosophy
1. Method and argument names **should** clearly communicate its use.
1. Use documentation to enhance and not correct method/argument names.
#### Basic
In some cases all that's required is a single line. **Do not** feel
obligated to write more because we have a format.
```markdown
# Private: Returns the number of pixels from the top of the screen.
```
* **Each method should declare whether it's public or private by using `Public:`
or `Private:`** prefix.
* Following the colon, there should be a short description (that isn't redundant with the
method name).
* Documentation should be hard wrapped to 80 columns.
### Public vs Private
If a method is public it can be used by other classes (and possibly by
the public API). The appropriate steps should be taken to minimize the impact
when changing public methods. In some cases that might mean adding an
appropriate release note. In other cases it might mean doing the legwork to
ensure all affected packages are updated.
#### Complex
For complex methods it's necessary to explain exactly what arguments
are required and how different inputs effect the operation of the
function.
The idea is to communicate things that the API user might not know about,
so repeating information that can be gleaned from the method or argument names
is not useful.
```markdown
# Private: Determine the accelerator for a given command.
#
# * command:
# The name of the command.
# * keystrokesByCommand:
# An {Object} whose keys are commands and the values are Arrays containing
# the keystrokes.
# * options:
# + accelerators:
# Boolean to determine whether accelerators should be shown.
#
# Returns a String containing the keystroke in a format that can be interpreted
# by atom shell to provide nice icons where available.
#
# Raises an Exception if no window is available.
```
* Use curly brackets `{}` to provide links to other classes.
* Use `+` for the options list.

View File

@ -5,6 +5,7 @@ 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:
```
___
/\_ \
___ ___ ___\//\ \
/'___\ / __`\ / __`\\ \ \
@ -25,13 +26,13 @@ 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
"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
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:
code, so the `module.exports` section looks like this:
```coffeescript
module.exports =
@ -69,23 +70,24 @@ 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:
it's not there! To fix this, open _package.json_ and find the property called
`activationEvents`. Activation Events speed up load time by allowing Atom to
delay a package's activation until it's needed. So remove the existing command
and add `ascii-art:convert` to the `activationEvents` array:
```json
"activationEvents": ["ascii-art:convert"],
```
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!'
First, reload the window by running the command `window:reload`. Now when you
run the `ascii-art:convert` command it will output 'Hello, World!'
## Add A Key Binding
## Add a Key Binding
Now let's add a key binding to trigger the `ascii-art:convert` command. Open
_keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the
`ascii-art:convert` command. When finished, the file will look like this:
`ascii-art:convert` command. You can delete the pre-existing key binding since
you don't need it anymore. When finished, the file will look like this:
```coffeescript
'.editor':
@ -105,7 +107,7 @@ that it **doesn't** work when the Tree View is focused.
## Add the ASCII Art
Now we need to convert the selected text to ascii art. To do this we will use
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:
@ -116,14 +118,14 @@ figlet to the dependencies:
}
```
After saving the file run the command 'update-package-dependencies:update' from
the Command Palette. This will install the packages node module dependencies,
After saving the file, run the command 'update-package-dependencies:update' from
the Command Palette. This will install the package's node module dependencies,
only figlet in this case. You will need to run
'update-package-dependencies:update' whenever you update the dependencies field
in your _package.json_ file.
Now require the figlet node module in _lib/ascii-art.coffee_ and instead of
inserting 'Hello, World!' convert the selected text to ascii art!
inserting 'Hello, World!' convert the selected text to ASCII art.
```coffeescript
convert: ->
@ -139,6 +141,9 @@ convert: ->
selection.insertText("\n#{asciiArt}\n")
```
Select some text in an editor window and hit `cmd-alt-a`. :tada: You're now an
ASCII art professional!
## Further reading
For more information on the mechanics of packages, check out [Creating a

14
dot-atom/init.coffee Normal file
View File

@ -0,0 +1,14 @@
# Your init script
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#
# An example hack to make opened Markdown files always be soft wrapped:
#
# path = require 'path'
#
# atom.workspaceView.eachEditorView (editorView) ->
# editor = editorView.getEditor()
# if path.extname(editor.getPath()) is '.md'
# editor.setSoftWrap(true)

View File

@ -1,9 +1,13 @@
# User keymap
# Your keymap
#
# Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
# to apply styles to elements, Atom keymaps use selectors to associate
# keystrokes with events in specific contexts. Here's a small example, excerpted
# from Atom's built-in keymaps:
# keystrokes with events in specific contexts.
#
# You can create a new keybinding in this file by typing "key" and then hitting
# tab.
#
# Here's an example taken from Atom's built-in keymap:
#
# '.editor':
# 'enter': 'editor:newline'
@ -11,3 +15,4 @@
# 'body':
# 'ctrl-P': 'core:move-up'
# 'ctrl-p': 'core:move-down'
#

View File

@ -3,7 +3,7 @@
# 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
# 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:

View File

@ -1 +0,0 @@
# For more on how to configure atom open `~/github/atom/docs/configuring-and-extending.md`

View File

@ -4,7 +4,6 @@ module.exports =
_: require 'underscore-plus'
BufferedNodeProcess: require '../src/buffered-node-process'
BufferedProcess: require '../src/buffered-process'
ConfigObserver: require '../src/config-observer'
Directory: require '../src/directory'
File: require '../src/file'
fs: require 'fs-plus'

View File

@ -75,8 +75,12 @@
'cmd-k right': 'pane:split-right' # Atom Specific
'cmd-k cmd-w': 'pane:close' # Atom Specific
'cmd-k alt-cmd-w': 'pane:close-other-items' # Atom Specific
'cmd-k cmd-left': 'window:focus-previous-pane'
'cmd-k cmd-right': 'window:focus-next-pane'
'cmd-k cmd-p': 'window:focus-previous-pane'
'cmd-k cmd-n': 'window:focus-next-pane'
'cmd-k cmd-up': 'window:focus-pane-above'
'cmd-k cmd-down': 'window:focus-pane-below'
'cmd-k cmd-left': 'window:focus-pane-on-left'
'cmd-k cmd-right': 'window:focus-pane-on-right'
'cmd-1': 'pane:show-item-1'
'cmd-2': 'pane:show-item-2'
'cmd-3': 'pane:show-item-3'

View File

@ -47,8 +47,12 @@
'ctrl-k right': 'pane:split-right' # Atom Specific
'ctrl-k ctrl-w': 'pane:close' # Atom Specific
'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific
'ctrl-k ctrl-left': 'window:focus-previous-pane'
'ctrl-k ctrl-right': 'window:focus-next-pane'
'ctrl-k ctrl-p': 'window:focus-previous-pane'
'ctrl-k ctrl-n': 'window:focus-next-pane'
'ctrl-k ctrl-up': 'window:focus-pane-above'
'ctrl-k ctrl-down': 'window:focus-pane-below'
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
'.workspace .editor':
# Windows specific

View File

@ -4,10 +4,12 @@
submenu: [
{ label: 'About Atom', command: 'application:about' }
{ label: "VERSION", enabled: false }
{ label: "Install update", command: 'application:install-update', visible: false }
{ label: "Restart and Install Update", command: 'application:install-update', visible: false}
{ label: "Check for Update", command: 'application:check-for-update'}
{ type: 'separator' }
{ label: 'Preferences...', command: 'application:show-settings' }
{ label: 'Open Your Config', command: 'application:open-your-config' }
{ label: 'Open Your Init Script', command: 'application:open-your-init-script' }
{ 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' }

View File

@ -16,7 +16,7 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.8.7",
"atomShellVersion": "0.9.1",
"dependencies": {
"async": "0.2.6",
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
@ -25,11 +25,11 @@
"coffeestack": "0.7.0",
"delegato": "1.x",
"emissary": "1.x",
"first-mate": ">=1.1 <2.0",
"first-mate": ">=1.1.4 <2.0",
"fs-plus": "1.x",
"fstream": "0.1.24",
"fuzzaldrin": "1.x",
"git-utils": "0.34.0",
"git-utils": "1.x",
"guid": "0.0.10",
"jasmine-tagged": "1.x",
"mkdirp": "0.3.5",
@ -42,14 +42,14 @@
"pathwatcher": "0.14.2",
"pegjs": "0.8.0",
"property-accessors": "1.x",
"q": "0.9.7",
"scandal": "0.13.0",
"q": "1.0.x",
"scandal": "0.14.0",
"season": "1.x",
"semver": "1.1.4",
"serializable": "1.x",
"space-pen": "3.1.1",
"temp": "0.5.0",
"text-buffer": "0.16.0",
"text-buffer": "1.x",
"theorist": "1.x",
"underscore-plus": "1.x",
"vm-compatibility-layer": "0.1.0"
@ -63,81 +63,81 @@
"solarized-dark-syntax": "0.9.0",
"solarized-light-syntax": "0.5.0",
"archive-view": "0.21.0",
"autocomplete": "0.21.0",
"autoflow": "0.12.0",
"autocomplete": "0.22.0",
"autoflow": "0.13.0",
"autosave": "0.10.0",
"background-tips": "0.5.0",
"bookmarks": "0.18.0",
"bracket-matcher": "0.19.0",
"command-logger": "0.10.0",
"command-palette": "0.15.0",
"dev-live-reload": "0.23.0",
"editor-stats": "0.12.0",
"bracket-matcher": "0.20.0",
"command-logger": "0.11.0",
"command-palette": "0.16.0",
"dev-live-reload": "0.24.0",
"editor-stats": "0.13.0",
"exception-reporting": "0.13.0",
"feedback": "0.22.0",
"feedback": "0.23.0",
"find-and-replace": "0.81.0",
"fuzzy-finder": "0.32.0",
"gists": "0.16.0",
"git-diff": "0.23.0",
"fuzzy-finder": "0.34.0",
"gists": "0.17.0",
"git-diff": "0.24.0",
"github-sign-in": "0.18.0",
"go-to-line": "0.16.0",
"grammar-selector": "0.18.0",
"image-view": "0.18.0",
"grammar-selector": "0.19.0",
"image-view": "0.19.0",
"keybinding-resolver": "0.9.0",
"link": "0.15.0",
"link": "0.16.0",
"markdown-preview": "0.25.1",
"metrics": "0.24.0",
"package-generator": "0.25.0",
"release-notes": "0.17.0",
"settings-view": "0.65.0",
"snippets": "0.24.0",
"spell-check": "0.21.0",
"metrics": "0.25.0",
"package-generator": "0.26.0",
"release-notes": "0.18.0",
"settings-view": "0.71.0",
"snippets": "0.27.0",
"spell-check": "0.23.0",
"status-bar": "0.32.0",
"styleguide": "0.22.0",
"symbols-view": "0.30.0",
"tabs": "0.18.0",
"styleguide": "0.23.0",
"symbols-view": "0.33.0",
"tabs": "0.19.0",
"terminal": "0.27.0",
"timecop": "0.13.0",
"to-the-hubs": "0.19.0",
"tree-view": "0.67.0",
"tree-view": "0.69.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",
"whitespace": "0.11.0",
"wrap-guide": "0.14.0",
"language-c": "0.4.0",
"language-clojure": "0.1.0",
"language-coffee-script": "0.6.0",
"language-css": "0.2.0",
"language-gfm": "0.12.0",
"language-git": "0.3.0",
"language-go": "0.2.0",
"language-html": "0.2.0",
"language-coffee-script": "0.7.0",
"language-css": "0.3.0",
"language-gfm": "0.13.0",
"language-git": "0.4.0",
"language-go": "0.3.0",
"language-html": "0.3.0",
"language-hyperlink": "0.3.0",
"language-java": "0.2.0",
"language-javascript": "0.5.0",
"language-json": "0.2.0",
"language-less": "0.1.0",
"language-make": "0.1.0",
"language-mustache": "0.1.0",
"language-objective-c": "0.2.0",
"language-pegjs": "0.1.0",
"language-perl": "0.2.0",
"language-php": "0.3.0",
"language-property-list": "0.2.0",
"language-puppet": "0.2.0",
"language-python": "0.2.0",
"language-ruby": "0.8.0",
"language-ruby-on-rails": "0.4.0",
"language-sass": "0.3.0",
"language-shellscript": "0.2.0",
"language-source": "0.2.0",
"language-sql": "0.2.0",
"language-text": "0.2.0",
"language-todo": "0.2.0",
"language-java": "0.3.0",
"language-javascript": "0.6.0",
"language-json": "0.3.0",
"language-less": "0.2.0",
"language-make": "0.2.0",
"language-mustache": "0.2.0",
"language-objective-c": "0.3.0",
"language-pegjs": "0.2.0",
"language-perl": "0.3.0",
"language-php": "0.4.0",
"language-property-list": "0.3.0",
"language-puppet": "0.3.0",
"language-python": "0.3.0",
"language-ruby": "0.9.0",
"language-ruby-on-rails": "0.5.0",
"language-sass": "0.4.0",
"language-shellscript": "0.3.0",
"language-source": "0.3.0",
"language-sql": "0.3.0",
"language-text": "0.3.0",
"language-todo": "0.3.0",
"language-toml": "0.7.0",
"language-xml": "0.2.0",
"language-yaml": "0.1.0"
"language-xml": "0.3.0",
"language-yaml": "0.2.0"
},
"private": true,
"scripts": {

View File

@ -6,8 +6,7 @@ sourceMaps = {}
formatStackTrace = (stackTrace) ->
return stackTrace unless stackTrace
jasminePath = require.resolve('../vendor/jasmine')
jasminePattern = new RegExp("\\(#{_.escapeRegExp(jasminePath)}:\\d+:\\d+\\)\\s*$")
jasminePattern = /^\s*at\s+.*\(?.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
convertedLines = []
for line in stackTrace.split('\n')
convertedLines.push(line) unless jasminePattern.test(line)

View File

@ -1,6 +1,7 @@
{$, $$, fs, WorkspaceView} = require 'atom'
Exec = require('child_process').exec
path = require 'path'
AtomPackage = require '../src/atom-package'
ThemeManager = require '../src/theme-manager'
describe "the `atom` global", ->
@ -21,12 +22,16 @@ describe "the `atom` global", ->
describe ".unloadPackage(name)", ->
describe "when the package is active", ->
it "throws an error", ->
pack = atom.packages.activatePackage('package-with-main')
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
expect( -> atom.packages.unloadPackage(pack.name)).toThrow()
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
pack = null
waitsForPromise ->
atom.packages.activatePackage('package-with-main').then (p) -> pack = p
runs ->
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
expect( -> atom.packages.unloadPackage(pack.name)).toThrow()
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
describe "when the package is not loaded", ->
it "throws an error", ->
@ -43,22 +48,42 @@ describe "the `atom` global", ->
describe ".activatePackage(id)", ->
describe "atom packages", ->
describe "when called multiple times", ->
it "it only calls activate on the package once", ->
spyOn(AtomPackage.prototype, 'activateNow').andCallThrough()
atom.packages.activatePackage('package-with-index')
atom.packages.activatePackage('package-with-index')
waitsForPromise ->
atom.packages.activatePackage('package-with-index')
runs ->
expect(AtomPackage.prototype.activateNow.callCount).toBe 1
describe "when the package has a main module", ->
describe "when the metadata specifies a main module path˜", ->
it "requires the module at the specified path", ->
mainModule = require('./fixtures/packages/package-with-main/main-module')
spyOn(mainModule, 'activate')
pack = atom.packages.activatePackage('package-with-main')
expect(mainModule.activate).toHaveBeenCalled()
expect(pack.mainModule).toBe mainModule
pack = null
waitsForPromise ->
atom.packages.activatePackage('package-with-main').then (p) -> pack = p
runs ->
expect(mainModule.activate).toHaveBeenCalled()
expect(pack.mainModule).toBe mainModule
describe "when the metadata does not specify a main module", ->
it "requires index.coffee", ->
indexModule = require('./fixtures/packages/package-with-index/index')
spyOn(indexModule, 'activate')
pack = atom.packages.activatePackage('package-with-index')
expect(indexModule.activate).toHaveBeenCalled()
expect(pack.mainModule).toBe indexModule
pack = null
waitsForPromise ->
atom.packages.activatePackage('package-with-index').then (p) -> pack = p
runs ->
expect(indexModule.activate).toHaveBeenCalled()
expect(pack.mainModule).toBe indexModule
it "assigns config defaults from the module", ->
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
@ -67,20 +92,22 @@ describe "the `atom` global", ->
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
describe "when the package metadata includes activation events", ->
[mainModule, pack] = []
[mainModule, promise] = []
beforeEach ->
mainModule = require './fixtures/packages/package-with-activation-events/index'
spyOn(mainModule, 'activate').andCallThrough()
AtomPackage = require '../src/atom-package'
spyOn(AtomPackage.prototype, 'requireMainModule').andCallThrough()
pack = atom.packages.activatePackage('package-with-activation-events')
promise = atom.packages.activatePackage('package-with-activation-events')
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
expect(pack.requireMainModule).not.toHaveBeenCalled()
expect(mainModule.activate).not.toHaveBeenCalled()
expect(promise.isFulfilled()).not.toBeTruthy()
atom.workspaceView.trigger 'activation-event'
expect(mainModule.activate).toHaveBeenCalled()
waitsForPromise ->
promise
it "triggers the activation event on all handlers registered during activation", ->
atom.workspaceView.openSync()
@ -105,13 +132,17 @@ describe "the `atom` global", ->
expect(console.warn).not.toHaveBeenCalled()
it "passes the activate method the package's previously serialized state if it exists", ->
pack = atom.packages.activatePackage("package-with-serialization")
expect(pack.mainModule.someNumber).not.toBe 77
pack.mainModule.someNumber = 77
atom.packages.deactivatePackage("package-with-serialization")
spyOn(pack.mainModule, 'activate').andCallThrough()
atom.packages.activatePackage("package-with-serialization")
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
pack = null
waitsForPromise ->
atom.packages.activatePackage("package-with-serialization").then (p) -> pack = p
runs ->
expect(pack.mainModule.someNumber).not.toBe 77
pack.mainModule.someNumber = 77
atom.packages.deactivatePackage("package-with-serialization")
spyOn(pack.mainModule, 'activate').andCallThrough()
atom.packages.activatePackage("package-with-serialization")
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
it "logs warning instead of throwing an exception if the package fails to load", ->
atom.config.set("core.disabledPackages", [])
@ -234,29 +265,38 @@ describe "the `atom` global", ->
describe "scoped-property loading", ->
it "loads the scoped properties", ->
atom.packages.activatePackage("package-with-scoped-properties")
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
waitsForPromise ->
atom.packages.activatePackage("package-with-scoped-properties")
runs ->
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
describe "textmate packages", ->
it "loads the package's grammars", ->
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
atom.packages.activatePackage('language-ruby', sync: true)
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
waitsForPromise ->
atom.packages.activatePackage('language-ruby')
runs ->
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
it "translates the package's scoped properties to Atom terms", ->
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
atom.packages.activatePackage('language-ruby', sync: true)
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
waitsForPromise ->
atom.packages.activatePackage('language-ruby')
runs ->
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
describe "when the package has no grammars but does have preferences", ->
it "loads the package's preferences as scoped properties", ->
jasmine.unspy(window, 'setTimeout')
spyOn(atom.syntax, 'addProperties').andCallThrough()
atom.packages.activatePackage('package-with-preferences-tmbundle')
waitsFor ->
atom.syntax.addProperties.callCount > 0
waitsForPromise ->
atom.packages.activatePackage('package-with-preferences-tmbundle')
runs ->
expect(atom.syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
@ -264,30 +304,43 @@ describe "the `atom` global", ->
describe ".deactivatePackage(id)", ->
describe "atom packages", ->
it "calls `deactivate` on the package's main module if activate was successful", ->
pack = atom.packages.activatePackage("package-with-deactivate")
expect(atom.packages.isPackageActive("package-with-deactivate")).toBeTruthy()
spyOn(pack.mainModule, 'deactivate').andCallThrough()
pack = null
waitsForPromise ->
atom.packages.activatePackage("package-with-deactivate").then (p) -> pack = p
atom.packages.deactivatePackage("package-with-deactivate")
expect(pack.mainModule.deactivate).toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-with-module")).toBeFalsy()
runs ->
expect(atom.packages.isPackageActive("package-with-deactivate")).toBeTruthy()
spyOn(pack.mainModule, 'deactivate').andCallThrough()
spyOn(console, 'warn')
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
atom.packages.deactivatePackage("package-with-deactivate")
expect(pack.mainModule.deactivate).toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-with-module")).toBeFalsy()
atom.packages.deactivatePackage("package-that-throws-on-activate")
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
spyOn(console, 'warn')
badPack = null
waitsForPromise ->
atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p
runs ->
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
atom.packages.deactivatePackage("package-that-throws-on-activate")
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
it "does not serialize packages that have not been activated called on their main module", ->
spyOn(console, 'warn')
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
spyOn(badPack.mainModule, 'serialize').andCallThrough()
badPack = null
waitsForPromise ->
atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p
atom.packages.deactivatePackage("package-that-throws-on-activate")
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
runs ->
spyOn(badPack.mainModule, 'serialize').andCallThrough()
atom.packages.deactivatePackage("package-that-throws-on-activate")
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
spyOn(console, 'error')
@ -299,32 +352,44 @@ describe "the `atom` global", ->
expect(console.error).toHaveBeenCalled()
it "removes the package's grammars", ->
atom.packages.activatePackage('package-with-grammars')
atom.packages.deactivatePackage('package-with-grammars')
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
waitsForPromise ->
atom.packages.activatePackage('package-with-grammars')
runs ->
atom.packages.deactivatePackage('package-with-grammars')
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
it "removes the package's keymaps", ->
atom.packages.activatePackage('package-with-keymaps')
atom.packages.deactivatePackage('package-with-keymaps')
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
waitsForPromise ->
atom.packages.activatePackage('package-with-keymaps')
runs ->
atom.packages.deactivatePackage('package-with-keymaps')
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
it "removes the package's stylesheets", ->
atom.packages.activatePackage('package-with-stylesheets')
atom.packages.deactivatePackage('package-with-stylesheets')
one = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css")
two = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less")
three = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css")
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
waitsForPromise ->
atom.packages.activatePackage('package-with-stylesheets')
runs ->
atom.packages.deactivatePackage('package-with-stylesheets')
one = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css")
two = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less")
three = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css")
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
it "removes the package's scoped-properties", ->
atom.packages.activatePackage("package-with-scoped-properties")
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
atom.packages.deactivatePackage("package-with-scoped-properties")
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
waitsForPromise ->
atom.packages.activatePackage("package-with-scoped-properties")
runs ->
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
atom.packages.deactivatePackage("package-with-scoped-properties")
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
describe "textmate packages", ->
it "removes the package's grammars", ->
@ -371,7 +436,7 @@ describe "the `atom` global", ->
themes = themeActivator.mostRecentCall.args[0]
expect(['theme']).toContain(theme.getType()) for theme in themes
describe ".en/disablePackage()", ->
describe ".enablePackage() and disablePackage()", ->
describe "with packages", ->
it ".enablePackage() enables a disabled package", ->
packageName = 'package-with-main'
@ -380,28 +445,36 @@ describe "the `atom` global", ->
expect(atom.config.get('core.disabledPackages')).toContain packageName
pack = atom.packages.enablePackage(packageName)
loadedPackages = atom.packages.getLoadedPackages()
activatedPackages = atom.packages.getActivePackages()
expect(loadedPackages).toContain(pack)
expect(activatedPackages).toContain(pack)
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
activatedPackages = null
waitsFor ->
activatedPackages = atom.packages.getActivePackages()
activatedPackages.length > 0
runs ->
expect(loadedPackages).toContain(pack)
expect(activatedPackages).toContain(pack)
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
it ".disablePackage() disables an enabled package", ->
packageName = 'package-with-main'
atom.packages.activatePackage(packageName)
atom.packages.observeDisabledPackages()
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
waitsForPromise ->
atom.packages.activatePackage(packageName)
pack = atom.packages.disablePackage(packageName)
runs ->
atom.packages.observeDisabledPackages()
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
activatedPackages = atom.packages.getActivePackages()
expect(activatedPackages).not.toContain(pack)
expect(atom.config.get('core.disabledPackages')).toContain packageName
pack = atom.packages.disablePackage(packageName)
activatedPackages = atom.packages.getActivePackages()
expect(activatedPackages).not.toContain(pack)
expect(atom.config.get('core.disabledPackages')).toContain packageName
describe "with themes", ->
beforeEach ->
atom.themes.activateThemes()
waitsForPromise ->
atom.themes.activateThemes()
afterEach ->
atom.themes.deactivateThemes()
@ -415,18 +488,24 @@ describe "the `atom` global", ->
# enabling of theme
pack = atom.packages.enablePackage(packageName)
activatedPackages = atom.packages.getActivePackages()
expect(activatedPackages).toContain(pack)
expect(atom.config.get('core.themes')).toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
# disabling of theme
pack = atom.packages.disablePackage(packageName)
activatedPackages = atom.packages.getActivePackages()
expect(activatedPackages).not.toContain(pack)
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
activatedPackages = null
waitsFor ->
activatedPackages = atom.packages.getActivePackages()
activatedPackages.length > 0
runs ->
expect(activatedPackages).toContain(pack)
expect(atom.config.get('core.themes')).toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
# disabling of theme
pack = atom.packages.disablePackage(packageName)
activatedPackages = atom.packages.getActivePackages()
expect(activatedPackages).not.toContain(pack)
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
describe ".isReleasedVersion()", ->
it "returns false if the version is a SHA and true otherwise", ->

View File

@ -220,6 +220,8 @@ describe "Config", ->
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'init.coffee'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'styles.css'))).toBeTruthy()
describe ".loadUserConfig()", ->
beforeEach ->

View File

@ -21,10 +21,6 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
print: (str) ->
log(str)
onComplete: (runner) ->
log('\n')
timeReporter.logLongestSuites 10, (line) -> log("#{line}\n")
log('\n')
timeReporter.logLongestSpecs 10, (line) -> log("#{line}\n")
fs.closeSync(logStream) if logStream?
atom.exit(runner.results().failedCount > 0 ? 1 : 0)
else

View File

@ -27,32 +27,6 @@ describe "PaneContainerView", ->
afterEach ->
atom.deserializers.remove(TestView)
describe ".focusNextPane()", ->
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
container.attachToDom()
container.focusNextPane()
expect(pane1.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane2.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane3.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane1.activeItem).toMatchSelector ':focus'
describe ".focusPreviousPane()", ->
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
container.attachToDom()
container.getPanes()[0].focus() # activate first pane
container.focusPreviousPane()
expect(pane3.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane2.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane1.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane3.activeItem).toMatchSelector ':focus'
describe ".getActivePane()", ->
it "returns the most-recently focused pane", ->
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
@ -265,3 +239,115 @@ describe "PaneContainerView", ->
pane1.remove()
pane2.remove()
expect(activeItemChangedHandler).not.toHaveBeenCalled()
describe ".focusNextPane()", ->
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
container.attachToDom()
container.focusNextPane()
expect(pane1.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane2.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane3.activeItem).toMatchSelector ':focus'
container.focusNextPane()
expect(pane1.activeItem).toMatchSelector ':focus'
describe ".focusPreviousPane()", ->
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
container.attachToDom()
container.getPanes()[0].focus() # activate first pane
container.focusPreviousPane()
expect(pane3.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane2.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane1.activeItem).toMatchSelector ':focus'
container.focusPreviousPane()
expect(pane3.activeItem).toMatchSelector ':focus'
describe "changing focus directionally between panes", ->
[pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []
beforeEach ->
# Set up a grid of 9 panes, in the following arrangement, where the
# numbers correspond to the variable names below.
#
# -------
# |1|2|3|
# -------
# |4|5|6|
# -------
# |7|8|9|
# -------
container = new PaneContainerView
pane1 = container.getRoot()
pane1.activateItem(new TestView('1'))
pane4 = pane1.splitDown(new TestView('4'))
pane7 = pane4.splitDown(new TestView('7'))
pane2 = pane1.splitRight(new TestView('2'))
pane3 = pane2.splitRight(new TestView('3'))
pane5 = pane4.splitRight(new TestView('5'))
pane6 = pane5.splitRight(new TestView('6'))
pane8 = pane7.splitRight(new TestView('8'))
pane9 = pane8.splitRight(new TestView('9'))
container.height(400)
container.width(400)
container.attachToDom()
describe ".focusPaneAbove()", ->
describe "when there are multiple rows above the focused pane", ->
it "focuses up to the adjacent row", ->
pane8.focus()
container.focusPaneAbove()
expect(pane5.activeItem).toMatchSelector ':focus'
describe "when there are no rows above the focused pane", ->
it "keeps the current pane focused", ->
pane2.focus()
container.focusPaneAbove()
expect(pane2.activeItem).toMatchSelector ':focus'
describe ".focusPaneBelow()", ->
describe "when there are multiple rows below the focused pane", ->
it "focuses down to the adjacent row", ->
pane2.focus()
container.focusPaneBelow()
expect(pane5.activeItem).toMatchSelector ':focus'
describe "when there are no rows below the focused pane", ->
it "keeps the current pane focused", ->
pane8.focus()
container.focusPaneBelow()
expect(pane8.activeItem).toMatchSelector ':focus'
describe ".focusPaneOnLeft()", ->
describe "when there are multiple columns to the left of the focused pane", ->
it "focuses left to the adjacent column", ->
pane6.focus()
container.focusPaneOnLeft()
expect(pane5.activeItem).toMatchSelector ':focus'
describe "when there are no columns to the left of the focused pane", ->
it "keeps the current pane focused", ->
pane4.focus()
container.focusPaneOnLeft()
expect(pane4.activeItem).toMatchSelector ':focus'
describe ".focusPaneOnRight()", ->
describe "when there are multiple columns to the right of the focused pane", ->
it "focuses right to the adjacent column", ->
pane4.focus()
container.focusPaneOnRight()
expect(pane5.activeItem).toMatchSelector ':focus'
describe "when there are no columns to the right of the focused pane", ->
it "keeps the current pane focused", ->
pane6.focus()
container.focusPaneOnRight()
expect(pane6.activeItem).toMatchSelector ':focus'

View File

@ -11,35 +11,6 @@ describe "SpacePen extensions", ->
parent = $$ -> @div()
parent.append(view)
describe "View.observeConfig(keyPath, callback)", ->
observeHandler = null
beforeEach ->
observeHandler = jasmine.createSpy("observeHandler")
view.observeConfig "foo.bar", observeHandler
expect(view.hasParent()).toBeTruthy()
it "observes the keyPath and cancels the subscription when `.unobserveConfig()` is called", ->
expect(observeHandler).toHaveBeenCalledWith(undefined)
observeHandler.reset()
atom.config.set("foo.bar", "hello")
expect(observeHandler).toHaveBeenCalledWith("hello", previous: undefined)
observeHandler.reset()
view.unobserveConfig()
atom.config.set("foo.bar", "goodbye")
expect(observeHandler).not.toHaveBeenCalled()
it "unobserves when the view is removed", ->
observeHandler.reset()
parent.remove()
atom.config.set("foo.bar", "hello")
expect(observeHandler).not.toHaveBeenCalled()
describe "View.subscribe(eventEmitter, eventName, callback)", ->
[emitter, eventHandler] = []

View File

@ -26,11 +26,14 @@ describe "ThemeManager", ->
expect(themes.length).toBeGreaterThan(2)
it 'getActiveThemes get all the active themes', ->
themeManager.activateThemes()
names = atom.config.get('core.themes')
expect(names.length).toBeGreaterThan(0)
themes = themeManager.getActiveThemes()
expect(themes).toHaveLength(names.length)
waitsForPromise ->
themeManager.activateThemes()
runs ->
names = atom.config.get('core.themes')
expect(names.length).toBeGreaterThan(0)
themes = themeManager.getActiveThemes()
expect(themes).toHaveLength(names.length)
describe "getImportPaths()", ->
it "returns the theme directories before the themes are loaded", ->
@ -51,29 +54,58 @@ describe "ThemeManager", ->
it "add/removes stylesheets to reflect the new config value", ->
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
themeManager.activateThemes()
atom.config.set('core.themes', [])
expect($('style.theme').length).toBe 0
expect(reloadHandler).toHaveBeenCalled()
waitsForPromise ->
themeManager.activateThemes()
atom.config.set('core.themes', ['atom-dark-syntax'])
expect($('style.theme').length).toBe 1
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
runs ->
reloadHandler.reset()
atom.config.set('core.themes', [])
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
expect($('style.theme').length).toBe 2
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/
waitsFor ->
reloadHandler.callCount == 1
atom.config.set('core.themes', [])
expect($('style.theme').length).toBe 0
runs ->
reloadHandler.reset()
expect($('style.theme')).toHaveLength 0
atom.config.set('core.themes', ['atom-dark-syntax'])
# atom-dark-ui has an directory path, the syntax one doesn't
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
importPaths = themeManager.getImportPaths()
expect(importPaths.length).toBe 1
expect(importPaths[0]).toContain 'atom-dark-ui'
waitsFor ->
reloadHandler.callCount == 1
runs ->
reloadHandler.reset()
expect($('style.theme')).toHaveLength 1
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
waitsFor ->
reloadHandler.callCount == 1
runs ->
reloadHandler.reset()
expect($('style.theme')).toHaveLength 2
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/
atom.config.set('core.themes', [])
waitsFor ->
reloadHandler.callCount == 1
runs ->
reloadHandler.reset()
expect($('style.theme')).toHaveLength 0
# atom-dark-ui has an directory path, the syntax one doesn't
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
waitsFor ->
reloadHandler.callCount == 1
runs ->
expect($('style.theme')).toHaveLength 2
importPaths = themeManager.getImportPaths()
expect(importPaths.length).toBe 1
expect(importPaths[0]).toContain 'atom-dark-ui'
describe "when a theme fails to load", ->
it "logs a warning", ->
@ -145,18 +177,25 @@ describe "ThemeManager", ->
atom.workspaceView = new WorkspaceView
atom.workspaceView.append $$ -> @div class: 'editor'
atom.workspaceView.attachToDom()
themeManager.activateThemes()
waitsForPromise ->
themeManager.activateThemes()
it "loads the correct values from the theme's ui-variables file", ->
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
atom.config.set('core.themes', ['theme-with-ui-variables'])
# an override loaded in the base css
expect(atom.workspaceView.css("background-color")).toBe "rgb(0, 0, 255)"
waitsFor ->
reloadHandler.callCount > 0
# from within the theme itself
expect($(".editor").css("padding-top")).toBe "150px"
expect($(".editor").css("padding-right")).toBe "150px"
expect($(".editor").css("padding-bottom")).toBe "150px"
runs ->
# an override loaded in the base css
expect(atom.workspaceView.css("background-color")).toBe "rgb(0, 0, 255)"
# from within the theme itself
expect($(".editor").css("padding-top")).toBe "150px"
expect($(".editor").css("padding-right")).toBe "150px"
expect($(".editor").css("padding-bottom")).toBe "150px"
describe "when the user stylesheet changes", ->
it "reloads it", ->
@ -164,12 +203,14 @@ describe "ThemeManager", ->
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
spyOn(themeManager, 'getUserStylesheetPath').andReturn userStylesheetPath
themeManager.activateThemes()
expect($(document.body).css('border-style')).toBe 'dotted'
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
waitsForPromise ->
themeManager.activateThemes()
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
runs ->
expect($(document.body).css('border-style')).toBe 'dotted'
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
waitsFor ->
themeManager.loadUserStylesheet.callCount is 1

View File

@ -2,12 +2,13 @@ Package = require './package'
fs = require 'fs-plus'
path = require 'path'
_ = require 'underscore-plus'
Q = require 'q'
{$} = require './space-pen-extensions'
CSON = require 'season'
{Emitter} = require 'emissary'
### Internal: Loads and resolves packages. ###
# Loads and activates a package's main module and resources such as
# stylesheets, keymaps, grammar, editor properties, and menus.
module.exports =
class AtomPackage extends Package
Emitter.includeInto(this)
@ -55,14 +56,34 @@ class AtomPackage extends Package
@grammars = []
@scopedProperties = []
activate: ({immediate}={}) ->
activate: ->
return @activationDeferred.promise if @activationDeferred?
@activationDeferred = Q.defer()
@measure 'activateTime', =>
@activateResources()
if @metadata.activationEvents? and not immediate
if @metadata.activationEvents?
@subscribeToActivationEvents()
else
@activateNow()
@activationDeferred.promise
# Deprecated
activateSync: ({immediate}={}) ->
@activateResources()
if @metadata.activationEvents? and not immediate
@subscribeToActivationEvents()
else
try
@activateConfig()
@activateStylesheets()
if @requireMainModule()
@mainModule.activate(atom.packages.getPackageState(@name) ? {})
@mainActivated = true
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
activateNow: ->
try
@activateConfig()
@ -73,6 +94,8 @@ class AtomPackage extends Package
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
@activationDeferred.resolve()
activateConfig: ->
return if @configActivated
@ -158,6 +181,8 @@ class AtomPackage extends Package
console.error "Error serializing package '#{@name}'", e.stack
deactivate: ->
@activationDeferred?.reject()
@activationDeferred = null
@unsubscribeFromActivationEvents()
@deactivateResources()
@deactivateConfig()
@ -216,6 +241,8 @@ class AtomPackage extends Package
@unsubscribeFromActivationEvents()
unsubscribeFromActivationEvents: ->
return unless atom.workspaceView?
if _.isArray(@metadata.activationEvents)
atom.workspaceView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
else if _.isString(@metadata.activationEvents)

View File

@ -20,16 +20,18 @@ WindowEventHandler = require './window-event-handler'
#
# ## Useful properties available:
#
# * `atom.config` - A {Config} instance
# * `atom.contextMenu` - A {ContextMenuManager} instance
# * `atom.keymap` - A {Keymap} instance
# * `atom.menu` - A {MenuManager} instance
# * `atom.workspaceView` - A {WorkspaceView} instance
# * `atom.packages` - A {PackageManager} instance
# * `atom.clipboard` - A {Clipboard} instance
# * `atom.project` - A {Project} instance
# * `atom.syntax` - A {Syntax} instance
# * `atom.themes` - A {ThemeManager} instance
# * `atom.clipboard` - A {Clipboard} instance
# * `atom.config` - A {Config} instance
# * `atom.contextMenu` - A {ContextMenuManager} instance
# * `atom.deserializers` - A {DeserializerManager} instance
# * `atom.keymap` - A {Keymap} instance
# * `atom.menu` - A {MenuManager} instance
# * `atom.packages` - A {PackageManager} instance
# * `atom.project` - A {Project} instance
# * `atom.syntax` - A {Syntax} instance
# * `atom.themes` - A {ThemeManager} instance
# * `atom.workspace` - A {Workspace} instance
# * `atom.workspaceView` - A {WorkspaceView} instance
module.exports =
class Atom extends Model
# Public: Load or create the Atom environment in the given mode.
@ -41,11 +43,11 @@ class Atom extends Model
@loadOrCreate: (mode) ->
@deserialize(@loadState(mode)) ? new this({mode, version: @getVersion()})
# Private: Deserializes the Atom environment from a state object
# Deserializes the Atom environment from a state object
@deserialize: (state) ->
new this(state) if state?.version is @getVersion()
# Private: Loads and returns the serialized state corresponding to this window
# Loads and returns the serialized state corresponding to this window
# if it exists; otherwise returns undefined.
@loadState: (mode) ->
statePath = @getStatePath(mode)
@ -63,7 +65,7 @@ class Atom extends Model
catch error
console.warn "Error parsing window state: #{statePath} #{error.stack}", error
# Private: Returns the path where the state for the current window will be
# Returns the path where the state for the current window will be
# located if it exists.
@getStatePath: (mode) ->
switch mode
@ -80,19 +82,19 @@ class Atom extends Model
else
null
# Private: Get the directory path to Atom's configuration area.
# Get the directory path to Atom's configuration area.
#
# Returns the absolute path to ~/.atom
@getConfigDirPath: ->
@configDirPath ?= fs.absolute('~/.atom')
# Private: Get the path to Atom's storage directory.
# Get the path to Atom's storage directory.
#
# Returns the absolute path to ~/.atom/storage
@getStorageDirPath: ->
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
# Private: Returns the load settings hash associated with the current window.
# Returns the load settings hash associated with the current window.
@getLoadSettings: ->
@loadSettings ?= JSON.parse(decodeURIComponent(location.search.substr(14)))
cloned = _.deepClone(@loadSettings)
@ -103,25 +105,24 @@ class Atom extends Model
@getCurrentWindow().loadSettings.windowState = value
cloned
# Private:
@getCurrentWindow: ->
remote.getCurrentWindow()
# Private: Get the version of the Atom application.
# Get the version of the Atom application.
@getVersion: ->
@version ?= @getLoadSettings().appVersion
# Private: Determine whether the current version is an official release.
# Determine whether the current version is an official release.
@isReleasedVersion: ->
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
workspaceViewParentSelector: 'body'
# Private: Call .loadOrCreate instead
# Call .loadOrCreate instead
constructor: (@state) ->
{@mode} = @state
DeserializerManager = require './deserializer-manager'
@deserializers = new DeserializerManager(this)
@deserializers = new DeserializerManager()
# Public: Sets up the basic services that should be available in all modes
# (both spec and application). Call after this instance has been assigned to
@ -173,7 +174,6 @@ class Atom extends Model
# Deprecated: Callers should be converted to use atom.deserializers
registerRepresentationClasses: ->
# Private:
setBodyPlatformClass: ->
document.body.classList.add("platform-#{process.platform}")
@ -209,7 +209,6 @@ class Atom extends Model
else
@center()
# Private:
restoreWindowDimensions: ->
workAreaSize = screen.getPrimaryDisplay().workAreaSize
windowDimensions = @state.windowDimensions ? {}
@ -218,7 +217,6 @@ class Atom extends Model
windowDimensions.width ?= initialSize?.width ? Math.min(workAreaSize.width, 1024)
@setWindowDimensions(windowDimensions)
# Private:
storeWindowDimensions: ->
@state.windowDimensions = @getWindowDimensions()
@ -228,12 +226,10 @@ class Atom extends Model
getLoadSettings: ->
@constructor.getLoadSettings()
# Private:
deserializeProject: ->
Project = require './project'
@project ?= @deserializers.deserialize(@project) ? new Project(path: @getLoadSettings().initialPath)
# Private:
deserializeWorkspaceView: ->
Workspace = require './workspace'
WorkspaceView = require './workspace-view'
@ -241,18 +237,16 @@ class Atom extends Model
@workspaceView = new WorkspaceView(@workspace)
$(@workspaceViewParentSelector).append(@workspaceView)
# Private:
deserializePackageStates: ->
@packages.packageStates = @state.packageStates ? {}
delete @state.packageStates
# Private:
deserializeEditorWindow: ->
@deserializePackageStates()
@deserializeProject()
@deserializeWorkspaceView()
# Private: Call this method when establishing a real application window.
# Call this method when establishing a real application window.
startEditorWindow: ->
CommandInstaller = require './command-installer'
resourcePath = atom.getLoadSettings().resourcePath
@ -281,7 +275,6 @@ class Atom extends Model
@displayWindow()
# Private:
unloadEditorWindow: ->
return if not @project and not @workspaceView
@ -297,11 +290,9 @@ class Atom extends Model
@keymap.destroy()
@windowState = null
# Private:
loadThemes: ->
@themes.load()
# Private:
watchThemes: ->
@themes.on 'reloaded', =>
# Only reload stylesheets from non-theme packages
@ -314,31 +305,32 @@ class Atom extends Model
# Calling this method without an options parameter will open a prompt to pick
# a file/folder to open in the new window.
#
# * options
# * pathsToOpen: A string array of paths to open
# options - An {Object} with the following keys:
# :pathsToOpen - An {Array} of {String} paths to open.
open: (options) ->
ipc.sendChannel('open', options)
# Public: Open a confirm dialog.
#
# ## Example:
# ```coffeescript
# ## Example
#
# ```coffee
# atom.confirm
# message: 'How you feeling?'
# detailedMessage: 'Be honest.'
# buttons:
# Good: -> window.alert('good to hear')
# Bad: -> window.alert('bummer')
# message: 'How you feeling?'
# detailedMessage: 'Be honest.'
# buttons:
# Good: -> window.alert('good to hear')
# Bad: -> window.alert('bummer')
# ```
#
# * options:
# + message: The string message to display.
# + detailedMessage: The string detailed message to display.
# + buttons: Either an array of strings or an object where the values
# are callbacks to invoke when clicked.
# options - An {Object} with the following keys:
# :message - The {String} message to display.
# :detailedMessage - The {String} detailed message to display.
# :buttons - Either an array of strings or an object where keys are
# button names and the values are callbacks to invoke when
# clicked.
#
# Returns the chosen index if buttons was an array or the return of the
# callback if buttons was an object.
# Returns the chosen button index {Number} if the buttons option was an array.
confirm: ({message, detailedMessage, buttons}={}) ->
buttons ?= {}
if _.isArray(buttons)
@ -359,11 +351,9 @@ class Atom extends Model
callback = buttons[buttonLabels[chosen]]
callback?()
# Private:
showSaveDialog: (callback) ->
callback(showSaveDialogSync())
# Private:
showSaveDialogSync: (defaultPath) ->
defaultPath ?= @project?.getPath()
currentWindow = @getCurrentWindow()
@ -396,10 +386,16 @@ class Atom extends Model
ipc.sendChannel('call-window-method', 'hide')
# Public: Set the size of current window.
#
# width - The {Number} of pixels.
# height - The {Number} of pixels.
setSize: (width, height) ->
ipc.sendChannel('call-window-method', 'setSize', width, height)
# Public: Set the position of current window.
#
# x - The {Number} of pixels.
# y - The {Number} of pixels.
setPosition: (x, y) ->
ipc.sendChannel('call-window-method', 'setPosition', x, y)
@ -407,7 +403,7 @@ class Atom extends Model
center: ->
ipc.sendChannel('call-window-method', 'center')
# Private: Schedule the window to be shown and focused on the next tick.
# Schedule the window to be shown and focused on the next tick.
#
# This is done in a next tick to prevent a white flicker from occurring
# if called synchronously.
@ -421,7 +417,6 @@ class Atom extends Model
close: ->
@getCurrentWindow().close()
# Private:
exit: (status) ->
app = remote.require('app')
app.exit(status)
@ -471,7 +466,6 @@ class Atom extends Model
getConfigDirPath: ->
@constructor.getConfigDirPath()
# Private:
saveSync: ->
stateString = JSON.stringify(@state)
if statePath = @constructor.getStatePath(@mode)
@ -489,11 +483,9 @@ class Atom extends Model
getWindowLoadTime: ->
@loadTime
# Private:
crashMainProcess: ->
remote.process.crash()
# Private:
crashRenderProcess: ->
process.crash()
@ -502,9 +494,12 @@ class Atom extends Model
shell.beep() if @config.get('core.audioBeep')
@workspaceView.trigger 'beep'
# Private:
getUserInitScriptPath: ->
initScriptPath = fs.resolve(@getConfigDirPath(), 'init', ['js', 'coffee'])
initScriptPath ? path.join(@getConfigDirPath(), 'init.coffee')
requireUserInitScript: ->
if userInitScriptPath = fs.resolve(@getConfigDirPath(), 'user', ['js', 'coffee'])
if userInitScriptPath = @getUserInitScriptPath()
try
require userInitScriptPath
catch error
@ -514,6 +509,9 @@ class Atom extends Model
#
# The globals will be set on the `window` object and removed after the
# require completes.
#
# id - The {String} module name or path.
# globals - An {Object} to set as globals during require (default: {})
requireWithGlobals: (id, globals={}) ->
existingGlobals = {}
for key, value of globals

View File

@ -3,7 +3,7 @@ ipc = require 'ipc'
Menu = require 'menu'
_ = require 'underscore-plus'
# Private: Used to manage the global application menu.
# Used to manage the global application menu.
#
# It's created by {AtomApplication} upon instantiation and used to add, remove
# and maintain the state of all menu items.
@ -29,7 +29,7 @@ class ApplicationMenu
@menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(@menu)
# Private: Flattens the given menu and submenu items into an single Array.
# Flattens the given menu and submenu items into an single Array.
#
# * menu:
# A complete menu configuration object for atom-shell's menu API.
@ -42,7 +42,7 @@ class ApplicationMenu
items = items.concat(@flattenMenuItems(item.submenu)) if item.submenu
items
# Private: Flattens the given menu template into an single Array.
# Flattens the given menu template into an single Array.
#
# * template:
# An object describing the menu item.
@ -64,26 +64,22 @@ class ApplicationMenu
for item in @flattenMenuItems(@menu)
item.enabled = enable if item.metadata?['windowSpecific']
# Private: Replaces VERSION with the current version.
# Replaces VERSION with the current version.
substituteVersion: (template) ->
if (item = _.find(@flattenMenuTemplate(template), (i) -> i.label == 'VERSION'))
item.label = "Version #{@version}"
# Public: Makes the download menu item visible if available.
#
# Note: The update menu item's must match 'Install update' exactly otherwise
# this function will fail to work.
#
# * newVersion:
# FIXME: Unused.
# * quitAndUpdateCallback:
# Function to call when the install menu item has been clicked.
showDownloadUpdateItem: (newVersion, quitAndUpdateCallback) ->
if (item = _.find(@flattenMenuItems(@menu), (i) -> i.label == 'Install update'))
item.visible = true
item.click = quitAndUpdateCallback
# Toggles Install Update Item
showInstallUpdateItem: (visible=true) ->
if (item = _.find(@flattenMenuItems(@menu), (i) -> i.label == 'Restart and Install Update'))
item.visible = visible
# Private: Default list of menu items.
# Toggles Check For Update Item
showCheckForUpdateItem: (visible=true) ->
if (item = _.find(@flattenMenuItems(@menu), (i) -> i.label == 'Check for Update'))
item.visible = visible
# Default list of menu items.
#
# Returns an Array of menu item Objects.
getDefaultTemplate: ->
@ -97,7 +93,7 @@ class ApplicationMenu
]
]
# Private: Combines a menu template with the appropriate keystroke.
# Combines a menu template with the appropriate keystroke.
#
# * template:
# An Object conforming to atom-shell's menu api but lacking accelerator and
@ -117,7 +113,7 @@ class ApplicationMenu
@translateTemplate(item.submenu, keystrokesByCommand) if item.submenu
template
# Private: Determine the accelerator for a given command.
# Determine the accelerator for a given command.
#
# * command:
# The name of the command.

View File

@ -22,7 +22,7 @@ socketPath =
else
path.join(os.tmpdir(), 'atom.sock')
# Private: The application's singleton class.
# The application's singleton class.
#
# It's the entry point into the Atom application and maintains the global state
# of the application.
@ -73,11 +73,11 @@ class AtomApplication
@listenForArgumentsFromNewProcess()
@setupJavaScriptArguments()
@handleEvents()
@checkForUpdates()
@setupAutoUpdater()
@openWithOptions(options)
# Private: Opens a new window based on the options provided.
# Opens a new window based on the options provided.
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}) ->
if test
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory, logFile})
@ -98,7 +98,7 @@ class AtomApplication
@windows.push window
@applicationMenu?.enableWindowSpecificItems(true)
# Private: Creates server to listen for additional atom application launches.
# Creates server to listen for additional atom application launches.
#
# You can run the atom command multiple times, but after the first launch
# the other launches will just pass their information to this server and then
@ -113,23 +113,59 @@ class AtomApplication
server.listen socketPath
server.on 'error', (error) -> console.error 'Application server failed', error
# Private: Configures required javascript environment flags.
# Configures required javascript environment flags.
setupJavaScriptArguments: ->
app.commandLine.appendSwitch 'js-flags', '--harmony_collections --harmony-proxies'
# Private: Enable updates unless running from a local build of Atom.
checkForUpdates: ->
versionIsSha = /\w{7}/.test @version
# Enable updates unless running from a local build of Atom.
setupAutoUpdater: ->
autoUpdater.setFeedUrl "https://atom.io/api/updates?version=#{@version}"
if versionIsSha
autoUpdater.setAutomaticallyDownloadsUpdates false
autoUpdater.setAutomaticallyChecksForUpdates false
else
autoUpdater.setAutomaticallyDownloadsUpdates true
autoUpdater.setAutomaticallyChecksForUpdates true
autoUpdater.checkForUpdatesInBackground()
autoUpdater.on 'checking-for-update', =>
@applicationMenu.showInstallUpdateItem(false)
@applicationMenu.showCheckForUpdateItem(false)
# Private: Registers basic application commands, non-idempotent.
autoUpdater.on 'update-not-available', =>
@applicationMenu.showInstallUpdateItem(false)
@applicationMenu.showCheckForUpdateItem(true)
autoUpdater.on 'update-downloaded', (event, releaseNotes, releaseName, releaseDate, releaseURL) =>
atomWindow.sendCommand('window:update-available', releaseName) for atomWindow in @windows
@applicationMenu.showInstallUpdateItem(true)
@applicationMenu.showCheckForUpdateItem(false)
@updateVersion = releaseName
autoUpdater.on 'error', (event, message) =>
@applicationMenu.showInstallUpdateItem(false)
@applicationMenu.showCheckForUpdateItem(true)
autoUpdater.checkForUpdates()
checkForUpdate: ->
autoUpdater.once 'update-available', ->
dialog.showMessageBox
type: 'info'
buttons: ['OK']
message: 'Update available.'
detail: 'A new update is being downloading.'
autoUpdater.once 'update-not-available', =>
dialog.showMessageBox
type: 'info'
buttons: ['OK']
message: 'No update available.'
detail: "Version #{@version} is the latest version."
autoUpdater.once 'error', (event, message)->
dialog.showMessageBox
type: 'warning'
buttons: ['OK']
message: 'There was an error checking for updates.'
detail: message
autoUpdater.checkForUpdates()
# Registers basic application commands, non-idempotent.
handleEvents: ->
@on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:')
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath)
@ -148,9 +184,12 @@ class AtomApplication
@on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y)
@on 'application:open-documentation', -> shell.openExternal('https://www.atom.io/docs/latest/?app')
@on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new')
@on 'application:install-update', -> autoUpdater.quitAndInstall()
@on 'application:check-for-update', => @checkForUpdate()
@openPathOnEvent('application:show-settings', 'atom://config')
@openPathOnEvent('application:open-your-config', 'atom://.atom/config')
@openPathOnEvent('application:open-your-init-script', 'atom://.atom/init-script')
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
@openPathOnEvent('application:open-your-snippets', 'atom://.atom/snippets')
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
@ -171,12 +210,6 @@ class AtomApplication
event.preventDefault()
@openUrl({urlToOpen, @devMode})
autoUpdater.on 'ready-for-update-on-quit', (event, version, quitAndUpdateCallback) =>
event.preventDefault()
@updateVersion = version
@applicationMenu.showDownloadUpdateItem(version, quitAndUpdateCallback)
atomWindow.sendCommand('window:update-available', version) for atomWindow in @windows
# A request from the associated render process to open a new render process.
ipc.on 'open', (processId, routingId, options) =>
if options?
@ -230,7 +263,7 @@ class AtomApplication
else
@openPath({pathToOpen})
# Private: Returns the {AtomWindow} for the given path.
# Returns the {AtomWindow} for the given path.
windowForPath: (pathToOpen) ->
for atomWindow in @windows
return atomWindow if atomWindow.containsPath(pathToOpen)
@ -318,7 +351,7 @@ class AtomApplication
console.log("Killing process #{pid} failed: #{error.code}")
delete @pidsToOpenWindows[pid]
# Private: Open an atom:// url.
# Open an atom:// url.
#
# The host of the URL being opened is assumed to be the package name
# responsible for opening the URL. A new window will be created with
@ -350,7 +383,7 @@ class AtomApplication
else
console.log "Opening unknown url: #{urlToOpen}"
# Private: Opens up a new {AtomWindow} to run specs within.
# Opens up a new {AtomWindow} to run specs within.
#
# * options
# + exitWhenDone:
@ -381,7 +414,7 @@ class AtomApplication
isSpec = true
new AtomWindow({bootstrapScript, @resourcePath, isSpec})
# Private: Opens a native dialog to prompt the user for a path.
# Opens a native dialog to prompt the user for a path.
#
# Once paths are selected, they're opened in a new or existing {AtomWindow}s.
#

View File

@ -3,7 +3,7 @@ fs = require 'fs-plus'
path = require 'path'
protocol = require 'protocol'
# Private: Handles requests with 'atom' protocol.
# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation, and is used to create a
# custom resource loader by adding the 'atom' custom protocol.
@ -18,7 +18,7 @@ class AtomProtocolHandler
@registerAtomProtocol()
# Private: Creates the 'atom' custom protocol handler.
# Creates the 'atom' custom protocol handler.
registerAtomProtocol: ->
protocol.registerProtocol 'atom', (request) =>
relativePath = path.normalize(request.url.substr(7))

View File

@ -9,7 +9,6 @@ fs = require 'fs'
url = require 'url'
_ = require 'underscore-plus'
# Private:
module.exports =
class AtomWindow
@iconPath: path.resolve(__dirname, '..', '..', 'resources', 'atom.png')

View File

@ -1,6 +1,5 @@
Menu = require 'menu'
# Private:
module.exports =
class ContextMenu
constructor: (template, browserWindow) ->
@ -8,7 +7,7 @@ class ContextMenu
menu = Menu.buildFromTemplate(template)
menu.popup(browserWindow)
# Private: It's necessary to build the event handlers in this process, otherwise
# It's necessary to build the event handlers in this process, otherwise
# closures are drug across processes and failed to be garbage collected
# appropriately.
createClickHandlers: (template) ->

View File

@ -1,6 +1,5 @@
global.shellStartTime = Date.now()
autoUpdater = require 'auto-updater'
crashReporter = require 'crash-reporter'
app = require 'app'
fs = require 'fs'
@ -42,7 +41,6 @@ start = ->
app.on 'will-finish-launching', ->
setupCrashReporter()
setupAutoUpdater()
app.on 'finish-launching', ->
app.removeListener 'open-file', addPathToOpen
@ -66,9 +64,6 @@ global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
setupCrashReporter = ->
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
setupAutoUpdater = ->
autoUpdater.setFeedUrl 'https://speakeasy.githubapp.com/apps/27/appcast.xml'
parseCommandLine = ->
version = app.getVersion()
options = optimist(process.argv[1..])

View File

@ -12,30 +12,27 @@ class BufferedProcess
process: null
killed: false
# Executes the given executable.
# Public: Executes the given executable.
#
# * options
# + command:
# The path to the executable to execute.
# + args:
# The array of arguments to pass to the script (optional).
# + options:
# The options Object to pass to Node's `ChildProcess.spawn` (optional).
# + stdout:
# The callback that receives a single argument which contains the
# standard output of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + stderr:
# The callback that receives a single argument which contains the
# standard error of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + exit:
# The callback which receives a single argument containing the exit
# status (optional).
# options - An {Object} with the following keys:
# :command - The {String} command to execute.
# :args - The {String}} of arguments to pass to the script (optional).
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
# (optional).
# :stdout - The callback that receives a single argument which contains the
# standard output of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are
# passed until the source stream closes. After the source stream
# has closed all remaining data is sent in a final call
# (optional).
# :stderr - The callback that receives a single argument which contains the
# standard error of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are
# passed until the source stream closes. After the source stream
# has closed all remaining data is sent in a final call
# (optional).
# :exit - The callback which receives a single argument containing the exit
# status (optional).
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
options ?= {}
@process = ChildProcess.spawn(command, args, options)
@ -68,7 +65,7 @@ class BufferedProcess
processExited = true
triggerExitCallback()
# Private: Helper method to pass data line by line.
# Helper method to pass data line by line.
#
# * stream:
# The Stream to read from.
@ -93,7 +90,7 @@ class BufferedProcess
onLines(buffered) if buffered.length > 0
onDone()
# Public: Terminates the process.
# Public: Terminate the process.
kill: ->
@killed = true
@process.kill()

View File

@ -3,7 +3,7 @@ crypto = require 'crypto'
# Public: Represents the clipboard used for copying and pasting in Atom.
#
# A clipboard instance is always available under the `atom.clipboard` global.
# An instance of this class is always available as the `atom.clipboard` global.
module.exports =
class Clipboard
metadata: null
@ -11,7 +11,7 @@ class Clipboard
# Creates an `md5` hash of some text.
#
# * text: A {String} to hash.
# text - A {String} to hash.
#
# Returns a hashed {String}.
md5: (text) ->
@ -22,8 +22,8 @@ class Clipboard
# The metadata associated with the text is available by calling
# {.readWithMetadata}.
#
# * text: A {String} to store.
# * metadata: An {Object} of additional info to associate with the text.
# text - The {String} to store.
# metadata - The additional info to associate with the text.
write: (text, metadata) ->
@signatureForMetadata = @md5(text)
@metadata = metadata
@ -38,8 +38,9 @@ class Clipboard
# Public: Read the text from the clipboard and return both the text and the
# associated metadata.
#
# Returns an {Object} with a `text` key and a `metadata` key if it has
# associated metadata.
# Returns an {Object} with the following keys:
# :text - The {String} clipboard text.
# :metadata - The metadata stored by an earlier call to {.write}.
readWithMetadata: ->
text = @read()
if @signatureForMetadata is @md5(text)

View File

@ -1,12 +0,0 @@
Mixin = require 'mixto'
module.exports =
class ConfigObserver extends Mixin
observeConfig: (keyPath, args...) ->
@configSubscriptions ?= {}
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)
unobserveConfig: ->
if @configSubscriptions?
subscription.off() for keyPath, subscription of @configSubscriptions
@configSubscriptions = null

View File

@ -8,15 +8,14 @@ pathWatcher = require 'pathwatcher'
# Public: Used to access all of Atom's configuration details.
#
# A global instance of this class is available to all plugins which can be
# referenced using `atom.config`
# An instance of this class is always available as the `atom.config` global.
#
# ### Best practices
# ## Best practices
#
# * Create your own root keypath using your package's name.
# * Don't depend on (or write to) configuration keys outside of your keypath.
#
# ### Example
# ## Example
#
# ```coffeescript
# atom.config.set('myplugin.key', 'value')
@ -27,18 +26,14 @@ module.exports =
class Config
Emitter.includeInto(this)
defaultSettings: null
settings: null
configFileHasErrors: null
# Private: Created during initialization, available as `global.config`
# Created during initialization, available as `atom.config`
constructor: ({@configDirPath, @resourcePath}={}) ->
@defaultSettings = {}
@settings = {}
@configFileHasErrors = false
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
# Private:
initializeConfigDirectory: (done) ->
return if fs.existsSync(@configDirPath)
@ -55,13 +50,11 @@ class Config
queue.push({sourcePath, destinationPath})
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
# Private:
load: ->
@initializeConfigDirectory()
@loadUserConfig()
@observeUserConfig()
# Private:
loadUserConfig: ->
unless fs.existsSync(@configFilePath)
fs.makeTreeSync(path.dirname(@configFilePath))
@ -77,17 +70,14 @@ class Config
console.error "Failed to load user config '#{@configFilePath}'", e.message
console.error e.stack
# Private:
observeUserConfig: ->
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
@loadUserConfig() if eventType is 'change' and @watchSubscription?
# Private:
unobserveUserConfig: ->
@watchSubscription?.close()
@watchSubscription = null
# Private:
setDefaults: (keyPath, defaults) ->
keys = keyPath.split('.')
hash = @defaultSettings
@ -205,6 +195,9 @@ class Config
# options - An optional {Object} containing the `callNow` key.
# callback - The {Function} that fires when the. It is given a single argument, `value`,
# which is the new value of `keyPath`.
#
# Returns an {Object} with the following keys:
# :off - A {Function} that unobserves the `keyPath` with called.
observe: (keyPath, options={}, callback) ->
if _.isFunction(options)
callback = options
@ -230,12 +223,10 @@ class Config
unobserve: (keyPath) ->
@off("updated.#{keyPath.replace(/\./, '-')}")
# Private:
update: ->
return if @configFileHasErrors
@save()
@emit 'updated'
# Private:
save: ->
CSON.writeFileSync(@configFilePath, @settings)

View File

@ -5,10 +5,10 @@ remote = require 'remote'
# Public: Provides a registry for commands that you'd like to appear in the
# context menu.
#
# Should be accessed via `atom.contextMenu`.
# An instance of this class is always available as the `atom.contextMenu`
# global.
module.exports =
class ContextMenuManager
# Private:
constructor: (@devMode=false) ->
@definitions = {}
@devModeDefinitions = {}
@ -24,11 +24,11 @@ class ContextMenuManager
# Public: Creates menu definitions from the object specified by the menu
# cson API.
#
# * name: The path of the file that contains the menu definitions.
# * object: The 'context-menu' object specified in the menu cson API.
# * options:
# + devMode - Determines whether the entries should only be shown when
# the window is in dev mode.
# name - The path of the file that contains the menu definitions.
# object - The 'context-menu' object specified in the menu cson API.
# options - An {Object} with the following keys:
# :devMode - Determines whether the entries should only be shown when
# the window is in dev mode.
#
# Returns nothing.
add: (name, object, {devMode}={}) ->
@ -36,20 +36,20 @@ class ContextMenuManager
for label, command of items
@addBySelector(selector, {label, command}, {devMode})
# Private: Registers a command to be displayed when the relevant item is right
# Registers a command to be displayed when the relevant item is right
# clicked.
#
# * selector: The css selector for the active element which should include
# the given command in its context menu.
# * definition: The object containing keys which match the menu template API.
# * options:
# + devMode: Indicates whether this command should only appear while the
# editor is in dev mode.
# selector - The css selector for the active element which should include
# the given command in its context menu.
# definition - The object containing keys which match the menu template API.
# options - An {Object} with the following keys:
# :devMode - Indicates whether this command should only appear while the
# editor is in dev mode.
addBySelector: (selector, definition, {devMode}={}) ->
definitions = if devMode then @devModeDefinitions else @definitions
(definitions[selector] ?= []).push(definition)
# Private: Returns definitions which match the element and devMode.
# Returns definitions which match the element and devMode.
definitionsForElement: (element, {devMode}={}) ->
definitions = if devMode then @devModeDefinitions else @definitions
matchedDefinitions = []
@ -58,14 +58,14 @@ class ContextMenuManager
matchedDefinitions
# Private: Used to generate the context menu for a specific element and it's
# Used to generate the context menu for a specific element and it's
# parents.
#
# The menu items are sorted such that menu items that match closest to the
# active element are listed first. The further down the list you go, the higher
# up the ancestor hierarchy they match.
#
# * element: The DOM element to generate the menu template for.
# element - The DOM element to generate the menu template for.
menuTemplateForMostSpecificElement: (element, {devMode}={}) ->
menuTemplate = @definitionsForElement(element, {devMode})
if element.parentElement
@ -73,7 +73,7 @@ class ContextMenuManager
else
menuTemplate
# Private: Returns a menu template for both normal entries as well as
# Returns a menu template for both normal entries as well as
# development mode entries.
combinedMenuTemplateForElement: (element) ->
normalItems = @menuTemplateForMostSpecificElement(element)
@ -83,7 +83,7 @@ class ContextMenuManager
menuTemplate.push({ type: 'separator' }) if normalItems.length > 0 and devItems.length > 0
menuTemplate.concat(devItems)
# Private: Executes `executeAtBuild` if defined for each menu item with
# Executes `executeAtBuild` if defined for each menu item with
# the provided event and then removes the `executeAtBuild` property from
# the menu item.
#

View File

@ -2,7 +2,6 @@
{Point, Range} = require 'text-buffer'
_ = require 'underscore-plus'
### Internal ###
module.exports =
class CursorView extends View
@content: ->

View File

@ -17,7 +17,7 @@ class Cursor
visible: true
needsAutoscroll: null
# Private: Instantiated by an {Editor}
# Instantiated by an {Editor}
constructor: ({@editor, @marker}) ->
@updateVisibility()
@marker.on 'changed', (e) =>
@ -45,11 +45,9 @@ class Cursor
@emit 'destroyed'
@needsAutoscroll = true
# Private:
destroy: ->
@marker.destroy()
# Private:
changePosition: (options, fn) ->
@clearSelection()
@needsAutoscroll = options.autoscroll ? @isLastCursor()
@ -58,12 +56,11 @@ class Cursor
# Public: Moves a cursor to a given screen position.
#
# * screenPosition:
# An {Array} of two numbers: the screen row, and the screen column.
# * options:
# + autoscroll:
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
# cursor moves to.
# screenPosition - An {Array} of two numbers: the screen row, and the screen
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setScreenPosition: (screenPosition, options={}) ->
@changePosition options, =>
@marker.setHeadScreenPosition(screenPosition, options)
@ -74,12 +71,11 @@ class Cursor
# Public: Moves a cursor to a given buffer position.
#
# * bufferPosition:
# An {Array} of two numbers: the buffer row, and the buffer column.
# * options:
# + autoscroll:
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
# cursor moves to.
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setBufferPosition: (bufferPosition, options={}) ->
@changePosition options, =>
@marker.setHeadBufferPosition(bufferPosition, options)
@ -104,11 +100,11 @@ class Cursor
# Public: Get the RegExp used by the cursor to determine what a "word" is.
#
# * options:
# + includeNonWordCharacters:
# A Boolean indicating whether to include non-word characters in the regex.
# options: An {Object} with the following keys:
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the regex.
#
# Returns a RegExp.
# Returns a {RegExp}.
wordRegExp: ({includeNonWordCharacters}={})->
includeNonWordCharacters ?= true
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
@ -122,7 +118,7 @@ class Cursor
#
# "Last" is defined as the most recently added cursor.
#
# Returns a Boolean.
# Returns a {Boolean}.
isLastCursor: ->
this == @editor.getCursor()
@ -131,7 +127,7 @@ class Cursor
# "Surrounded" here means that all characters before and after the cursor is
# whitespace.
#
# Returns a Boolean.
# Returns a {Boolean}.
isSurroundedByWhitespace: ->
{row, column} = @getBufferPosition()
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
@ -217,9 +213,9 @@ class Cursor
# Public: Moves the cursor left one screen column.
#
# * options:
# + moveToEndOfSelection:
# if true, move to the left of the selection if a selection exists.
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the left of the selection if a
# selection exists.
moveLeft: ({moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@ -231,9 +227,9 @@ class Cursor
# Public: Moves the cursor right one screen column.
#
# * options:
# + moveToEndOfSelection:
# if true, move to the right of the selection if a selection exists.
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the right of the selection if a
# selection exists.
moveRight: ({moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@ -313,12 +309,12 @@ class Cursor
# Public: Retrieves the buffer position of where the current word starts.
#
# * options:
# + wordRegex:
# A RegExp indicating what constitutes a "word" (default: {.wordRegExp})
# + includeNonWordCharacters:
# A Boolean indicating whether to include non-word characters in the
# default word regex. Has no effect if wordRegex is set.
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {.wordRegExp}).
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
#
# Returns a {Range}.
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
@ -381,12 +377,12 @@ class Cursor
# Public: Retrieves the buffer position of where the current word ends.
#
# * options:
# + wordRegex:
# A RegExp indicating what constitutes a "word" (default: {.wordRegExp})
# + includeNonWordCharacters:
# A Boolean indicating whether to include non-word characters in the
# default word regex. Has no effect if wordRegex is set.
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {.wordRegExp})
# :includeNonWordCharacters - A Boolean indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
#
# Returns a {Range}.
getEndOfCurrentWordBufferPosition: (options = {}) ->
@ -405,9 +401,9 @@ class Cursor
# Public: Retrieves the buffer position of where the next word starts.
#
# * options:
# + wordRegex:
# A RegExp indicating what constitutes a "word" (default: {.wordRegExp})
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {.wordRegExp}).
#
# Returns a {Range}.
getBeginningOfNextWordBufferPosition: (options = {}) ->
@ -424,9 +420,9 @@ class Cursor
# Public: Returns the buffer Range occupied by the word located under the cursor.
#
# * options:
# + wordRegex:
# A RegExp indicating what constitutes a "word" (default: {.wordRegExp})
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {.wordRegExp}).
getCurrentWordBufferRange: (options={}) ->
startOptions = _.extend(_.clone(options), allowPrevious: false)
endOptions = _.extend(_.clone(options), allowNext: false)
@ -434,9 +430,9 @@ class Cursor
# Public: Returns the buffer Range for the current line.
#
# * options:
# + includeNewline:
# A boolean which controls whether the Range should include the newline.
# options -
# :includeNewline: - A {Boolean} which controls whether the Range should
# include the newline.
getCurrentLineBufferRange: (options) ->
@editor.bufferRangeForBufferRow(@getBufferRow(), options)

View File

@ -1,20 +1,39 @@
# Public: Manages the deserializers used for serialized state
#
# Should be accessed via `atom.deserializers`
# An instance of this class is always available as the `atom.deserializers`
# global.
#
# ### Registering a deserializer
#
# ```coffee
# class MyPackageView extends View
# atom.deserializers.add(this)
#
# @deserialize: (state) ->
# new MyPackageView(state)
# ```
module.exports =
class DeserializerManager
constructor: (@environment) ->
constructor: ->
@deserializers = {}
# Public: Register the given class(es) as deserializers.
add: (klasses...) ->
@deserializers[klass.name] = klass for klass in klasses
#
# classes - One or more classes to register.
add: (classes...) ->
@deserializers[klass.name] = klass for klass in classes
# Public: Remove the given class(es) as deserializers.
remove: (klasses...) ->
delete @deserializers[klass.name] for klass in klasses
#
# classes - One or more classes to remove.
remove: (classes...) ->
delete @deserializers[name] for {name} in classes
# Public: Deserialize the state and params.
#
# state - The state {Object} to deserialize.
# params - The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
deserialize: (state, params) ->
return unless state?
@ -25,7 +44,9 @@ class DeserializerManager
else
console.warn "No deserializer found for", state
# Private: Get the deserializer for the state.
# Get the deserializer for the state.
#
# state - The state {Object} being deserialized.
get: (state) ->
return unless state?

View File

@ -7,7 +7,7 @@ pathWatcher = require 'pathwatcher'
File = require './file'
# Public: Represents a directory using {File}s.
# Public: Represents a directory on disk.
#
# ## Requiring in packages
#
@ -18,15 +18,12 @@ module.exports =
class Directory
Emitter.includeInto(this)
path: null
realPath: null
# Public: Configures a new Directory instance, no files are accessed.
#
# * path:
# A String containing the absolute path to the directory.
# + symlink:
# A Boolean indicating if the path is a symlink (defaults to false).
# path - A {String} containing the absolute path to the directory.
# symlink - A {Boolean} indicating if the path is a symlink (default: false).
constructor: (@path, @symlink=false) ->
@on 'first-contents-changed-subscription-will-be-added', =>
# Triggered by emissary, when a new contents-changed listener attaches
@ -36,7 +33,7 @@ class Directory
# Triggered by emissary, when the last contents-changed listener detaches
@unsubscribeFromNativeChangeEvents()
# Public: Returns the basename of the directory.
# Public: Returns the {String} basename of the directory.
getBaseName: ->
path.basename(@path)
@ -108,8 +105,8 @@ class Directory
# Public: Reads file entries in this directory from disk asynchronously.
#
# * callback: A function to call with an Error as the first argument and
# an {Array} of {File} and {Directory} objects as the second argument.
# callback - A {Function} to call with an {Error} as the 1st argument and
# an {Array} of {File} and {Directory} objects as the 2nd argument.
getEntries: (callback) ->
fs.list @path, (error, entries) ->
return callback(error) if error?
@ -134,18 +131,16 @@ class Directory
async.eachLimit entries, 1, statEntry, ->
callback(null, directories.concat(files))
# Private:
subscribeToNativeChangeEvents: ->
unless @watchSubscription?
@watchSubscription = pathWatcher.watch @path, (eventType) =>
@emit "contents-changed" if eventType is "change"
# Private:
unsubscribeFromNativeChangeEvents: ->
if @watchSubscription?
@watchSubscription.close()
@watchSubscription = null
# Private: Does given full path start with the given prefix?
# Does given full path start with the given prefix?
isPathPrefixOf: (prefix, fullPath) ->
fullPath.indexOf(prefix) is 0 and fullPath[prefix.length] is path.sep

View File

@ -2,7 +2,6 @@
_ = require 'underscore-plus'
{Emitter, Subscriber} = require 'emissary'
# Private:
module.exports =
class DisplayBufferMarker
Emitter.includeInto(this)
@ -15,8 +14,6 @@ class DisplayBufferMarker
oldTailScreenPosition: null
wasValid: true
### Internal ###
constructor: ({@bufferMarker, @displayBuffer}) ->
@id = @bufferMarker.id
@oldHeadBufferPosition = @getHeadBufferPosition()
@ -28,8 +25,6 @@ class DisplayBufferMarker
@subscribe @bufferMarker, 'destroyed', => @destroyed()
@subscribe @bufferMarker, 'changed', (event) => @notifyObservers(event)
### Public ###
copy: (attributes) ->
@displayBuffer.getMarker(@bufferMarker.copy(attributes).id)
@ -170,8 +165,6 @@ class DisplayBufferMarker
inspect: ->
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange()})"
### Internal ###
destroyed: ->
delete @displayBuffer.markers[@id]
@emit 'destroyed'

View File

@ -1,5 +1,5 @@
_ = require 'underscore-plus'
{Emitter, Subscriber} = require 'emissary'
{Emitter} = require 'emissary'
guid = require 'guid'
Serializable = require 'serializable'
{Model} = require 'theorist'
@ -9,13 +9,10 @@ RowMap = require './row-map'
Fold = require './fold'
Token = require './token'
DisplayBufferMarker = require './display-buffer-marker'
ConfigObserver = require './config-observer'
# Private:
module.exports =
class DisplayBuffer extends Model
Serializable.includeInto(this)
ConfigObserver.includeInto(this)
@properties
softWrap: null
@ -39,10 +36,10 @@ class DisplayBuffer extends Model
@emit 'soft-wrap-changed', softWrap
@updateWrappedScreenLines()
@observeConfig 'editor.preferredLineLength', callNow: false, =>
@subscribe atom.config.observe 'editor.preferredLineLength', callNow: false, =>
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
@observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
@subscribe atom.config.observe 'editor.softWrapAtPreferredLineLength', callNow: false, =>
@updateWrappedScreenLines() if @softWrap
serializeParams: ->
@ -82,8 +79,6 @@ class DisplayBuffer extends Model
bufferDelta = 0
@emitChanged({ start, end, screenDelta, bufferDelta })
### Public ###
# Sets the visibility of the tokenized buffer.
#
# visible - A {Boolean} indicating of the tokenized buffer is shown
@ -419,8 +414,6 @@ class DisplayBuffer extends Model
column = screenLine.clipScreenColumn(column, options)
new Point(row, column)
### Public ###
# Given a line, finds the point where it would wrap.
#
# line - The {String} to check
@ -470,7 +463,7 @@ class DisplayBuffer extends Model
getMarkerCount: ->
@buffer.getMarkerCount()
# Constructs a new marker at the given screen range.
# Public: Constructs a new marker at the given screen range.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {Marker} constructor
@ -480,7 +473,7 @@ class DisplayBuffer extends Model
bufferRange = @bufferRangeForScreenRange(args.shift())
@markBufferRange(bufferRange, args...)
# Constructs a new marker at the given buffer range.
# Public: Constructs a new marker at the given buffer range.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {Marker} constructor
@ -489,7 +482,7 @@ class DisplayBuffer extends Model
markBufferRange: (args...) ->
@getMarker(@buffer.markRange(args...).id)
# Constructs a new marker at the given screen position.
# Public: Constructs a new marker at the given screen position.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {Marker} constructor
@ -498,7 +491,7 @@ class DisplayBuffer extends Model
markScreenPosition: (screenPosition, options) ->
@markBufferPosition(@bufferPositionForScreenPosition(screenPosition), options)
# Constructs a new marker at the given buffer position.
# Public: Constructs a new marker at the given buffer position.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {Marker} constructor
@ -507,7 +500,7 @@ class DisplayBuffer extends Model
markBufferPosition: (bufferPosition, options) ->
@getMarker(@buffer.markPosition(bufferPosition, options).id)
# Removes the marker with the given id.
# Public: Removes the marker with the given id.
#
# id - The {Number} of the ID to remove
destroyMarker: (id) ->
@ -573,15 +566,12 @@ class DisplayBuffer extends Model
marker.unsubscribe() for marker in @getMarkers()
@tokenizedBuffer.destroy()
@unsubscribe()
@unobserveConfig()
logLines: (start=0, end=@getLastRow())->
for row in [start..end]
line = @lineForRow(row).text
console.log row, line, line.length
### Internal ###
handleTokenizedBufferChange: (tokenizedBufferChange) =>
{start, end, delta, bufferChange} = tokenizedBufferChange
@updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?)

View File

@ -41,8 +41,6 @@ class EditorView extends View
@nextEditorId: 1
### Internal ###
@content: (params) ->
attributes = { class: @classes(params), tabindex: -1 }
_.extend(attributes, params.attributes) if params.attributes
@ -79,14 +77,12 @@ class EditorView extends View
redrawOnReattach: false
bottomPaddingInLines: 10
### Public ###
# The constructor for setting up an `EditorView` instance.
#
# editorOrOptions - Either an {Editor}, or an object with one property, `mini`.
# If `mini` is `true`, a "miniature" `Editor` is constructed.
# Typically, this is ideal for scenarios where you need an Atom editor,
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
# If `mini` is `true`, a "miniature" `Editor` is constructed.
# Typically, this is ideal for scenarios where you need an Atom editor,
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
#
initialize: (editorOrOptions) ->
if editorOrOptions instanceof Editor
@ -120,7 +116,7 @@ class EditorView extends View
else
throw new Error("Must supply an Editor or mini: true")
# Internal: Sets up the core Atom commands.
# Sets up the core Atom commands.
#
# Some commands are excluded from mini-editors.
bindKeys: ->
@ -223,6 +219,9 @@ class EditorView extends View
do (name, method) =>
@command name, (e) -> method(e); false
# Public: Get the underlying editor model for this view.
#
# Returns an {Editor}.
getEditor: ->
@editor
@ -238,7 +237,6 @@ class EditorView extends View
insertText: (text, options) ->
@editor.insertText(text, options)
# Private:
setHeightInLines: (heightInLines)->
heightInLines ?= @calculateHeightInLines()
@heightInLines = heightInLines if heightInLines
@ -248,39 +246,41 @@ class EditorView extends View
widthInChars ?= @calculateWidthInChars()
@editor.setEditorWidthInChars(widthInChars) if widthInChars
# Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first.
# Public: Emulates the "page down" key, where the last row of a buffer scrolls
# to become the first.
pageDown: ->
newScrollTop = @scrollTop() + @scrollView[0].clientHeight
@editor.moveCursorDown(@getPageRows())
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
# Public: Emulates the "page up" key, where the frst row of a buffer scrolls to become the last.
# Public: Emulates the "page up" key, where the frst row of a buffer scrolls
# to become the last.
pageUp: ->
newScrollTop = @scrollTop() - @scrollView[0].clientHeight
@editor.moveCursorUp(@getPageRows())
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
# Gets the number of actual page rows existing in an editor.
# Public: Gets the number of actual page rows existing in an editor.
#
# Returns a {Number}.
getPageRows: ->
Math.max(1, Math.ceil(@scrollView[0].clientHeight / @lineHeight))
# Set whether invisible characters are shown.
# Public: Set whether invisible characters are shown.
#
# showInvisibles - A {Boolean} which, if `true`, show invisible characters
# showInvisibles - A {Boolean} which, if `true`, show invisible characters.
setShowInvisibles: (showInvisibles) ->
return if showInvisibles == @showInvisibles
@showInvisibles = showInvisibles
@resetDisplay()
# Defines which characters are invisible.
# Public: Defines which characters are invisible.
#
# invisibles - A hash defining the invisible characters: The defaults are:
# eol: `\u00ac`
# space: `\u00b7`
# tab: `\u00bb`
# cr: `\u00a4`
# invisibles - An {Object} defining the invisible characters:
# :eol - The end of line invisible {String} (default: `\u00ac`).
# :space - The space invisible {String} (default: `\u00b7`).
# :tab - The tab invisible {String} (default: `\u00bb`).
# :cr - The carriage return invisible {String} (default: `\u00a4`).
setInvisibles: (@invisibles={}) ->
_.defaults @invisibles,
eol: '\u00ac'
@ -289,14 +289,20 @@ class EditorView extends View
cr: '\u00a4'
@resetDisplay()
# Sets whether you want to show the indentation guides.
# Public: Sets whether you want to show the indentation guides.
#
# showIndentGuide - A {Boolean} you can set to `true` if you want to see the indentation guides.
# showIndentGuide - A {Boolean} you can set to `true` if you want to see the
# indentation guides.
setShowIndentGuide: (showIndentGuide) ->
return if showIndentGuide == @showIndentGuide
@showIndentGuide = showIndentGuide
@resetDisplay()
# Public: Set the text to appear in the editor when it is empty.
#
# This only affects mini editors.
#
# placeholderText - A {String} of text to display when empty.
setPlaceholderText: (placeholderText) ->
return unless @mini
@placeholderText = placeholderText
@ -310,15 +316,13 @@ class EditorView extends View
if path = @editor.getPath()
atom.project.getRepo()?.checkoutHead(path)
### Internal ###
configure: ->
@observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers)
@observeConfig 'editor.showInvisibles', (showInvisibles) => @setShowInvisibles(showInvisibles)
@observeConfig 'editor.showIndentGuide', (showIndentGuide) => @setShowIndentGuide(showIndentGuide)
@observeConfig 'editor.invisibles', (invisibles) => @setInvisibles(invisibles)
@observeConfig 'editor.fontSize', (fontSize) => @setFontSize(fontSize)
@observeConfig 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily)
@subscribe atom.config.observe 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers)
@subscribe atom.config.observe 'editor.showInvisibles', (showInvisibles) => @setShowInvisibles(showInvisibles)
@subscribe atom.config.observe 'editor.showIndentGuide', (showIndentGuide) => @setShowIndentGuide(showIndentGuide)
@subscribe atom.config.observe 'editor.invisibles', (invisibles) => @setInvisibles(invisibles)
@subscribe atom.config.observe 'editor.fontSize', (fontSize) => @setFontSize(fontSize)
@subscribe atom.config.observe 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily)
handleEvents: ->
@on 'focus', =>
@ -488,12 +492,11 @@ class EditorView extends View
@trigger 'editor:attached', [this]
# TODO: This should be private and only called from the constructor
edit: (editor) ->
return if editor is @editor
if @editor
@saveScrollPositionForeditor()
@saveScrollPositionForEditor()
@editor.off(".editor")
@editor = editor
@ -590,19 +593,18 @@ class EditorView extends View
else
@scrollView.scrollRight()
### Public ###
# Scrolls the editor to the bottom.
# Public: Scrolls the editor to the bottom.
scrollToBottom: ->
@scrollBottom(@editor.getScreenLineCount() * @lineHeight)
# Scrolls the editor to the position of the most recently added cursor.
# Public: Scrolls the editor to the position of the most recently added
# cursor.
#
# The editor is also centered.
scrollToCursorPosition: ->
@scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true)
# Scrolls the editor to the given buffer position.
# Public: Scrolls the editor to the given buffer position.
#
# bufferPosition - An object that represents a buffer position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
@ -610,7 +612,7 @@ class EditorView extends View
scrollToBufferPosition: (bufferPosition, options) ->
@scrollToPixelPosition(@pixelPositionForBufferPosition(bufferPosition), options)
# Scrolls the editor to the given screen position.
# Public: Scrolls the editor to the given screen position.
#
# screenPosition - An object that represents a buffer position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
@ -618,18 +620,20 @@ class EditorView extends View
scrollToScreenPosition: (screenPosition, options) ->
@scrollToPixelPosition(@pixelPositionForScreenPosition(screenPosition), options)
# Scrolls the editor to the given pixel position.
# Public: Scrolls the editor to the given pixel position.
#
# pixelPosition - An object that represents a pixel position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or
# {Point}.
# options - A hash with the following keys:
# center: if `true`, the position is scrolled such that it's in the center of the editor
# :center - if `true`, the position is scrolled such that it's in
# the center of the editor
scrollToPixelPosition: (pixelPosition, options) ->
return unless @attached
@scrollVertically(pixelPosition, options)
@scrollHorizontally(pixelPosition)
# Highlight all the folds within the given buffer range.
# Public: Highlight all the folds within the given buffer range.
#
# "Highlighting" essentially just adds the `fold-selected` class to the line's
# DOM element.
@ -647,29 +651,27 @@ class EditorView extends View
else
element.removeClass('fold-selected')
saveScrollPositionForeditor: ->
saveScrollPositionForEditor: ->
if @attached
@editor.setScrollTop(@scrollTop())
@editor.setScrollLeft(@scrollLeft())
# Toggle soft tabs on the edit session.
# Public: Toggle soft tabs on the edit session.
toggleSoftTabs: ->
@editor.setSoftTabs(not @editor.getSoftTabs())
# Toggle soft wrap on the edit session.
# Public: Toggle soft wrap on the edit session.
toggleSoftWrap: ->
@setWidthInChars()
@editor.setSoftWrap(not @editor.getSoftWrap())
# Private:
calculateWidthInChars: ->
Math.floor(@scrollView.width() / @charWidth)
# Private:
calculateHeightInLines: ->
Math.ceil($(window).height() / @lineHeight)
# Enables/disables soft wrap on the editor.
# Public: Enables/disables soft wrap on the editor.
#
# softWrap - A {Boolean} which, if `true`, enables soft wrap
setSoftWrap: (softWrap) ->
@ -679,7 +681,7 @@ class EditorView extends View
else
@removeClass 'soft-wrap'
# Sets the font size for the editor.
# Public: Sets the font size for the editor.
#
# fontSize - A {Number} indicating the font size in pixels.
setFontSize: (fontSize) ->
@ -692,13 +694,13 @@ class EditorView extends View
else
@redrawOnReattach = @attached
# Retrieves the font size for the editor.
# Public: Retrieves the font size for the editor.
#
# Returns a {Number} indicating the font size in pixels.
getFontSize: ->
parseInt(@css("font-size"))
# Sets the font family for the editor.
# Public: Sets the font family for the editor.
#
# fontFamily - A {String} identifying the CSS `font-family`,
setFontFamily: (fontFamily='') ->
@ -708,12 +710,12 @@ class EditorView extends View
@redraw()
# Gets the font family for the editor.
# Public: Gets the font family for the editor.
#
# Returns a {String} identifying the CSS `font-family`,
getFontFamily: -> @css("font-family")
# Redraw the editor
# Public: Redraw the editor
redraw: ->
return unless @hasParent()
return unless @attached
@ -723,23 +725,27 @@ class EditorView extends View
@updateLayerDimensions()
@requestDisplayUpdate()
# Public: Split the editor view left.
splitLeft: ->
pane = @getPane()
pane?.splitLeft(pane?.copyActiveItem()).activeView
# Public: Split the editor view right.
splitRight: ->
pane = @getPane()
pane?.splitRight(pane?.copyActiveItem()).activeView
# Public: Split the editor view up.
splitUp: ->
pane = @getPane()
pane?.splitUp(pane?.copyActiveItem()).activeView
# Public: Split the editor view down.
splitDown: ->
pane = @getPane()
pane?.splitDown(pane?.copyActiveItem()).activeView
# Retrieve's the `EditorView`'s pane.
# Public: Get this view's pane.
#
# Returns a {Pane}.
getPane: ->
@ -750,7 +756,6 @@ class EditorView extends View
super
atom.workspaceView?.focus()
# Private:
beforeRemove: ->
@trigger 'editor:will-be-removed'
@removed = true
@ -797,8 +802,6 @@ class EditorView extends View
appendToLinesView: (view) ->
@overlayer.append(view)
### Internal ###
# Scrolls the editor vertically to a given position.
scrollVertically: (pixelPosition, {center}={}) ->
scrollViewHeight = @scrollView.height()
@ -835,7 +838,7 @@ class EditorView extends View
@scrollRight(desiredRight)
else if desiredLeft < @scrollLeft()
@scrollLeft(desiredLeft)
@saveScrollPositionForeditor()
@saveScrollPositionForEditor()
calculateDimensions: ->
fragment = $('<div class="line" style="position: absolute; visibility: hidden;"><span>x</span></div>')
@ -1135,9 +1138,8 @@ class EditorView extends View
@renderedLines.css('padding-bottom', paddingBottom)
@gutter.lineNumbers.css('padding-bottom', paddingBottom)
### Public ###
# Retrieves the number of the row that is visible and currently at the top of the editor.
# Public: Retrieves the number of the row that is visible and currently at the
# top of the editor.
#
# Returns a {Number}.
getFirstVisibleScreenRow: ->
@ -1145,7 +1147,8 @@ class EditorView extends View
screenRow = 0 if isNaN(screenRow)
screenRow
# Retrieves the number of the row that is visible and currently at the bottom of the editor.
# Public: Retrieves the number of the row that is visible and currently at the
# bottom of the editor.
#
# Returns a {Number}.
getLastVisibleScreenRow: ->
@ -1154,7 +1157,7 @@ class EditorView extends View
screenRow = 0 if isNaN(screenRow)
screenRow
# Given a row number, identifies if it is currently visible.
# Public: Given a row number, identifies if it is currently visible.
#
# row - A row {Number} to check
#
@ -1162,8 +1165,6 @@ class EditorView extends View
isScreenRowVisible: (row) ->
@getFirstVisibleScreenRow() <= row <= @getLastVisibleScreenRow()
### Internal ###
handleScreenLinesChange: (change) ->
@pendingChanges.push(change)
@requestDisplayUpdate()
@ -1246,21 +1247,19 @@ class EditorView extends View
toggleLineCommentsInSelection: ->
@editor.toggleLineCommentsInSelection()
### Public ###
# Converts a buffer position to a pixel position.
# Public: Converts a buffer position to a pixel position.
#
# position - An object that represents a buffer position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
#
# Returns an object with two values: `top` and `left`, representing the pixel positions.
pixelPositionForBufferPosition: (position) ->
@pixelPositionForScreenPosition(@editor.screenPositionForBufferPosition(position))
# Converts a screen position to a pixel position.
# Public: Converts a screen position to a pixel position.
#
# position - An object that represents a screen position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
#
# Returns an object with two values: `top` and `left`, representing the pixel positions.
pixelPositionForScreenPosition: (position) ->
@ -1297,7 +1296,6 @@ class EditorView extends View
index++
left
# Private:
measureToColumn: (lineElement, tokenizedLine, screenColumn) ->
left = oldLeft = index = 0
iterator = document.createNodeIterator(lineElement, NodeFilter.SHOW_TEXT, TextNodeFilter)
@ -1343,7 +1341,6 @@ class EditorView extends View
returnLeft ? left
# Private:
getCharacterWidthCache: (scopes, char) ->
scopes ?= NoScope
obj = @constructor.characterWidthCache
@ -1352,7 +1349,6 @@ class EditorView extends View
return null unless obj?
obj[char]
# Private:
setCharacterWidthCache: (scopes, char, val) ->
scopes ?= NoScope
obj = @constructor.characterWidthCache
@ -1361,7 +1357,6 @@ class EditorView extends View
obj = obj[scope]
obj[char] = val
# Private:
clearCharacterWidthCache: ->
@constructor.characterWidthCache = {}
@ -1415,8 +1410,6 @@ class EditorView extends View
path = @editor.getPath()
atom.clipboard.write(path) if path?
### Internal ###
@buildLineHtml: ({tokens, text, lineEnding, fold, isSoftWrapped, invisibles, eolInvisibles, htmlEolInvisibles, attributes, showIndentGuide, indentation, editor, mini}) ->
scopeStack = []
line = []

View File

@ -97,7 +97,6 @@ class Editor extends Model
params.registerEditor = true
params
# Private:
subscribeToBuffer: ->
@buffer.retain()
@subscribe @buffer, "path-changed", =>
@ -111,7 +110,6 @@ class Editor extends Model
@subscribe @buffer, "destroyed", => @destroy()
@preserveCursorPositionOnBufferReload()
# Private:
subscribeToDisplayBuffer: ->
@subscribe @displayBuffer, 'marker-created', @handleMarkerCreated
@subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e
@ -119,11 +117,9 @@ class Editor extends Model
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
# Private:
getViewClass: ->
require './editor-view'
# Private:
destroyed: ->
@unsubscribe()
selection.destroy() for selection in @getSelections()
@ -132,7 +128,7 @@ class Editor extends Model
@languageMode.destroy()
atom.project?.removeEditor(this)
# Private: Creates an {Editor} with the same initial state
# Creates an {Editor} with the same initial state
copy: ->
tabLength = @getTabLength()
displayBuffer = @displayBuffer.copy()
@ -242,8 +238,7 @@ class Editor extends Model
# or if its column goes beyond a line's length, this "sanitizes" the value
# to a real position.
#
# * bufferPosition:
# The {Point} to clip
# bufferPosition - The {Point} to clip.
#
# Returns the new, clipped {Point}. Note that this could be the same as
# `bufferPosition` if no clipping was performed.
@ -255,8 +250,7 @@ class Editor extends Model
# or if its column goes beyond a line's length, this "sanitizes" the value
# to a real range.
#
# * range:
# The {Range} to clip
# range - The {Range} to clip.
#
# Returns the new, clipped {Range}. Note that this could be the same as
# `range` if no clipping was performed.
@ -264,17 +258,14 @@ class Editor extends Model
# Public: Returns the indentation level of the given a buffer row
#
# * bufferRow:
# A Number indicating the buffer row.
# bufferRow - A {Number} indicating the buffer row.
indentationForBufferRow: (bufferRow) ->
@indentLevelForLine(@lineForBufferRow(bufferRow))
# Public: Sets the indentation level for the given buffer row.
#
# * bufferRow:
# A {Number} indicating the buffer row.
# * newLevel:
# A {Number} indicating the new indentation level.
# bufferRow - A {Number} indicating the buffer row.
# newLevel - A {Number} indicating the new indentation level.
setIndentationForBufferRow: (bufferRow, newLevel) ->
currentIndentLength = @lineForBufferRow(bufferRow).match(/^\s*/)[0].length
newIndentString = @buildIndentString(newLevel)
@ -282,8 +273,7 @@ class Editor extends Model
# Public: Returns the indentation level of the given line of text.
#
# * line:
# A {String} in the current buffer.
# line - A {String} in the current buffer.
#
# Returns a {Number} or 0 if the text isn't found within the buffer.
indentLevelForLine: (line) ->
@ -295,7 +285,7 @@ class Editor extends Model
else
0
# Private: Constructs the string used for tabs.
# Constructs the string used for tabs.
buildIndentString: (number) ->
if @getSoftTabs()
_.multiplyString(" ", number * @getTabLength())
@ -326,7 +316,7 @@ class Editor extends Model
# Public: Returns a {Number} representing the number of lines in the editor.
getLineCount: -> @buffer.getLineCount()
# Private: Retrieves the current {TextBuffer}.
# Retrieves the current {TextBuffer}.
getBuffer: -> @buffer
# Public: Retrieves the current buffer's URI.
@ -353,8 +343,8 @@ class Editor extends Model
# Public: Returns the range for the given buffer row.
#
# * row: A row {Number}.
# * options: An options hash with an `includeNewline` key.
# row - A row {Number}.
# options - An options hash with an `includeNewline` key.
#
# Returns a {Range}.
bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options)
@ -362,7 +352,7 @@ class Editor extends Model
# Public: Returns a {String} representing the contents of the line at the
# given buffer row.
#
# * row - A {Number} representing a zero-indexed buffer row.
# row - A {Number} representing a zero-indexed buffer row.
lineForBufferRow: (row) -> @buffer.lineForRow(row)
# Public: Returns a {Number} representing the line length for the given
@ -437,10 +427,8 @@ class Editor extends Model
# Public: Inserts text at the current cursor positions
#
# * text:
# A String representing the text to insert.
# * options:
# + A set of options equivalent to {Selection.insertText}
# text - A {String} representing the text to insert.
# options - A set of options equivalent to {Selection.insertText}.
insertText: (text, options={}) ->
options.autoIndentNewline ?= @shouldAutoIndent()
options.autoDecreaseIndent ?= @shouldAutoIndent()
@ -467,8 +455,7 @@ class Editor extends Model
# Public: Indents the current line.
#
# * options
# + A set of options equivalent to {Selection.indent}.
# options - A set of options equivalent to {Selection.indent}.
indent: (options={})->
options.autoIndent ?= @shouldAutoIndent()
@mutateSelectedText (selection) -> selection.indent(options)
@ -519,7 +506,7 @@ class Editor extends Model
#
# If the language doesn't have comments, nothing happens.
#
# Returns an {Array} of the commented {Ranges}.
# Returns an {Array} of the commented {Range}s.
toggleLineCommentsInSelection: ->
@mutateSelectedText (selection) -> selection.toggleLineComments()
@ -556,8 +543,7 @@ class Editor extends Model
# Public: Pastes the text in the clipboard.
#
# * options:
# + A set of options equivalent to {Selection.insertText}.
# options - A set of options equivalent to {Selection.insertText}.
pasteText: (options={}) ->
{text, metadata} = atom.clipboard.readWithMetadata()
@ -737,11 +723,9 @@ class Editor extends Model
@setCursorScreenPosition(@getCursorScreenPosition().translate([1]))
@foldCurrentRow() if cursorRowFolded
# Private:
mutateSelectedText: (fn) ->
@transact => fn(selection) for selection in @getSelections()
# Private:
replaceSelectedText: (options={}, fn) ->
{selectWordIfEmpty} = options
@mutateSelectedText (selection) ->
@ -785,7 +769,9 @@ class Editor extends Model
destroyMarker: (args...) ->
@displayBuffer.destroyMarker(args...)
# Public: {Delegates to: DisplayBuffer.getMarkerCount}
# Public: Get the number of markers in this editor's buffer.
#
# Returns a {Number}.
getMarkerCount: ->
@buffer.getMarkerCount()
@ -824,10 +810,8 @@ class Editor extends Model
# Public: Creates a new selection at the given marker.
#
# * marker:
# The {DisplayBufferMarker} to highlight
# * options:
# + A hash of options that pertain to the {Selection} constructor.
# marker - The {DisplayBufferMarker} to highlight
# options - An {Object} that pertains to the {Selection} constructor.
#
# Returns the new {Selection}.
addSelection: (marker, options={}) ->
@ -848,10 +832,8 @@ class Editor extends Model
# Public: Given a buffer range, this adds a new selection for it.
#
# * bufferRange:
# A {Range} in the buffer
# * options:
# + A hash of options for {.markBufferRange}
# bufferRange - A {Range} in the buffer.
# options - An options {Object} for {.markBufferRange}.
#
# Returns the new {Selection}.
addSelectionForBufferRange: (bufferRange, options={}) ->
@ -861,20 +843,16 @@ class Editor extends Model
# Public: Given a buffer range, this removes all previous selections and
# creates a new selection for it.
#
# * bufferRange:
# A {Range} in the buffer
# * options:
# + A hash of options for {.setSelectedBufferRanges}
# bufferRange - A {Range} in the buffer.
# options - An options {Object} for {.setSelectedBufferRanges}.
setSelectedBufferRange: (bufferRange, options) ->
@setSelectedBufferRanges([bufferRange], options)
# Public: Given an array of buffer ranges, this removes all previous
# selections and creates new selections for them.
#
# * bufferRange:
# A {Range} in the buffer
# * options:
# + A hash of options for {.setSelectedBufferRanges}
# bufferRange - A {Range} in the buffer.
# options - An options {Object} for {.setSelectedBufferRanges}.
setSelectedBufferRanges: (bufferRanges, options={}) ->
throw new Error("Passed an empty array to setSelectedBufferRanges") unless bufferRanges.length
@ -891,7 +869,7 @@ class Editor extends Model
# Public: Unselects a given selection.
#
# * selection - The {Selection} to remove.
# selection - The {Selection} to remove.
removeSelection: (selection) ->
_.remove(@selections, selection)
@ -902,9 +880,7 @@ class Editor extends Model
@consolidateSelections()
@getSelection().clear()
# Public:
#
# Removes all but one cursor (if there are multiple cursors)
# Removes all but one cursor (if there are multiple cursors).
consolidateSelections: ->
selections = @getSelections()
if selections.length > 1
@ -941,8 +917,7 @@ class Editor extends Model
# Public: Determines if a given buffer range is included in a {Selection}.
#
# * bufferRange:
# The {Range} you're checking against
# bufferRange - The {Range} you're checking against.
#
# Returns a {Boolean}.
selectionIntersectsBufferRange: (bufferRange) ->
@ -951,10 +926,8 @@ class Editor extends Model
# Public: Moves every local cursor to a given screen position.
#
# * position:
# An {Array} of two numbers: the screen row, and the screen column.
# * options:
# An object with properties based on {Cursor.setScreenPosition}
# position - An {Array} of two numbers: the screen row, and the screen column.
# options - An {Object} with properties based on {Cursor.setScreenPosition}.
setCursorScreenPosition: (position, options) ->
@moveCursors (cursor) -> cursor.setScreenPosition(position, options)
@ -973,10 +946,8 @@ class Editor extends Model
# Public: Moves every cursor to a given buffer position.
#
# * position:
# An {Array} of two numbers: the buffer row, and the buffer column.
# * options:
# + An object with properties based on {Cursor.setBufferPosition}
# position - An {Array} of two numbers: the buffer row, and the buffer column.
# options - An object with properties based on {Cursor.setBufferPosition}.
setCursorBufferPosition: (position, options) ->
@moveCursors (cursor) -> cursor.setBufferPosition(position, options)
@ -1019,9 +990,8 @@ class Editor extends Model
# Public: Returns the word under the most recently added local {Cursor}.
#
# * options:
# + An object with properties based on
# {Cursor.getBeginningOfCurrentWordBufferPosition}.
# options - An object with properties based on
# {Cursor.getBeginningOfCurrentWordBufferPosition}.
getWordUnderCursor: (options) ->
@getTextInBufferRange(@getCursor().getCurrentWordBufferRange(options))
@ -1089,7 +1059,6 @@ class Editor extends Model
moveCursorToNextWordBoundary: ->
@moveCursors (cursor) -> cursor.moveToNextWordBoundary()
# Internal: Executes given function on all local cursors.
moveCursors: (fn) ->
fn(cursor) for cursor in @getCursors()
@mergeCursors()
@ -1097,8 +1066,7 @@ class Editor extends Model
# Public: Selects the text from the current cursor position to a given screen
# position.
#
# * position:
# An instance of {Point}, with a given `row` and `column`.
# position - An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
lastSelection = @getLastSelection()
lastSelection.selectToScreenPosition(position)
@ -1257,8 +1225,6 @@ class Editor extends Model
@setSelectedBufferRange(range)
range
# Public:
#
# FIXME: Not sure how to describe what this does.
mergeCursors: ->
positions = []
@ -1269,27 +1235,21 @@ class Editor extends Model
else
positions.push(position)
# Public:
#
# FIXME: Not sure how to describe what this does.
expandSelectionsForward: (fn) ->
@mergeIntersectingSelections =>
fn(selection) for selection in @getSelections()
# Public:
#
# FIXME: Not sure how to describe what this does.
expandSelectionsBackward: (fn) ->
@mergeIntersectingSelections isReversed: true, =>
fn(selection) for selection in @getSelections()
# Public:
#
# FIXME: No idea what this does.
finalizeSelections: ->
selection.finalize() for selection in @getSelections()
# Private: Merges intersecting selections. If passed a function, it executes
# Merges intersecting selections. If passed a function, it executes
# the function with merging suppressed, then merges intersecting selections
# afterward.
mergeIntersectingSelections: (args...) ->
@ -1313,7 +1273,6 @@ class Editor extends Model
_.reduce(@getSelections(), reducer, [])
# Private:
preserveCursorPositionOnBufferReload: ->
cursorPosition = null
@subscribe @buffer, "will-reload", =>
@ -1334,7 +1293,6 @@ class Editor extends Model
reloadGrammar: ->
@displayBuffer.reloadGrammar()
# Private:
shouldAutoIndent: ->
atom.config.get("editor.autoIndent")
@ -1345,32 +1303,24 @@ class Editor extends Model
# undo stack remains relevant.
transact: (fn) -> @buffer.transact(fn)
# Private:
beginTransaction: -> @buffer.beginTransaction()
# Private:
commitTransaction: -> @buffer.commitTransaction()
# Private:
abortTransaction: -> @buffer.abortTransaction()
# Private:
inspect: ->
"<Editor #{@id}>"
# Private:
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
# Private:
handleGrammarChange: ->
@unfoldAll()
@emit 'grammar-changed'
# Private:
handleMarkerCreated: (marker) =>
if marker.matchesAttributes(@getSelectionMarkerAttributes())
@addSelection(marker)
# Private:
getSelectionMarkerAttributes: ->
type: 'selection', editorId: @id, invalidate: 'never'

View File

@ -25,16 +25,14 @@ class File
# Public: Creates a new file.
#
# * path:
# A String containing the absolute path to the file
# * symlink:
# A Boolean indicating if the path is a symlink (default: false)
# path - A {String} containing the absolute path to the file
# symlink - A {Boolean} indicating if the path is a symlink (default: false).
constructor: (@path, @symlink=false) ->
throw new Error("#{@path} is a directory") if fs.isDirectorySync(@path)
@handleEventSubscriptions()
# Private: Subscribes to file system notifications when necessary.
# Subscribes to file system notifications when necessary.
handleEventSubscriptions: ->
eventNames = ['contents-changed', 'moved', 'removed']
@ -49,13 +47,13 @@ class File
subscriptionsEmpty = _.every eventNames, (eventName) => @getSubscriptionCount(eventName) is 0
@unsubscribeFromNativeChangeEvents() if subscriptionsEmpty
# Private: Sets the path for the file.
# Sets the path for the file.
setPath: (@path) ->
# Public: Returns the path for the file.
# Public: Returns the {String} path for the file.
getPath: -> @path
# Public: Return the filename without any directory information.
# Public: Return the {String} filename without any directory information.
getBaseName: ->
path.basename(@path)
@ -66,7 +64,7 @@ class File
fs.writeFileSync(@getPath(), text)
@subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
# Private: Deprecated
# Deprecated
readSync: (flushCache) ->
if not @exists()
@cachedContents = null
@ -80,9 +78,8 @@ class File
# Public: Reads the contents of the file.
#
# * flushCache:
# A Boolean indicating whether to require a direct read or if a cached
# copy is acceptable.
# flushCache - A {Boolean} indicating whether to require a direct read or if
# a cached copy is acceptable.
#
# Returns a promise that resovles to a String.
read: (flushCache) ->
@ -118,7 +115,6 @@ class File
exists: ->
fs.existsSync(@getPath())
# Private:
setDigest: (contents) ->
@digest = crypto.createHash('sha1').update(contents ? '').digest('hex')
@ -126,7 +122,6 @@ class File
getDigest: ->
@digest ? @setDigest(@readSync())
# Private:
handleNativeChangeEvent: (eventType, path) ->
if eventType is "delete"
@unsubscribeFromNativeChangeEvents()
@ -139,11 +134,9 @@ class File
@read(true).done (newContents) =>
@emit 'contents-changed' unless oldContents == newContents
# Private:
detectResurrectionAfterDelay: ->
_.delay (=> @detectResurrection()), 50
# Private:
detectResurrection: ->
if @exists()
@subscribeToNativeChangeEvents()
@ -152,13 +145,11 @@ class File
@cachedContents = null
@emit "removed"
# Private:
subscribeToNativeChangeEvents: ->
unless @watchSubscription?
@watchSubscription = pathWatcher.watch @path, (eventType, path) =>
@handleNativeChangeEvent(eventType, path)
# Private:
unsubscribeFromNativeChangeEvents: ->
if @watchSubscription?
@watchSubscription.close()

View File

@ -1,6 +1,6 @@
{Point, Range} = require 'text-buffer'
# Private: Represents a fold that collapses multiple buffer lines into a single
# Represents a fold that collapses multiple buffer lines into a single
# line on the screen.
#
# Their creation is managed by the {DisplayBuffer}.
@ -10,8 +10,6 @@ class Fold
displayBuffer: null
marker: null
### Internal ###
constructor: (@displayBuffer, @marker) ->
@id = @marker.id
@displayBuffer.foldsByMarkerId[@marker.id] = this

View File

@ -27,17 +27,19 @@ class Git
Emitter.includeInto(this)
Subscriber.includeInto(this)
# Private: Creates a new `Git` instance.
# Public: Creates a new Git instance.
#
# * path: The path to the git repository to open
# * options:
# + refreshOnWindowFocus:
# A Boolean that identifies if the windows should refresh
# path - The path to the Git repository to open.
# options - An object with the following keys (default: {}):
# :refreshOnWindowFocus - `true` to refresh the index and statuses when the
# window is focused.
#
# Returns a Git instance or null if the repository could not be opened.
@open: (path, options) ->
return null unless path
try
new Git(path, options)
catch e
catch
null
@exists: (path) ->
@ -47,20 +49,6 @@ class Git
else
false
path: null
statuses: null
upstream: null
branch: null
statusTask: null
# Private: Creates a new `Git` object.
#
# * path: The {String} representing the path to your git working directory
# * options:
# + refreshOnWindowFocus: If `true`, {#refreshIndex} and {#refreshStatus}
# are called on focus
# + project: A project that supplies buffers that will be monitored for
# save and reload events to trigger status refreshes.
constructor: (path, options={}) ->
@repo = GitUtils.open(path)
unless @repo?
@ -80,7 +68,7 @@ class Git
if @project?
@subscribe @project.eachBuffer (buffer) => @subscribeToBuffer(buffer)
# Private: Subscribes to buffer events.
# Subscribes to buffer events.
subscribeToBuffer: (buffer) ->
@subscribe buffer, 'saved reloaded path-changed', =>
if path = buffer.getPath()
@ -100,29 +88,29 @@ class Git
@unsubscribe()
# Private: Returns the corresponding {Repository}
# Returns the corresponding {Repository}
getRepo: ->
unless @repo?
throw new Error("Repository has been destroyed")
@repo
# Public: Reread the index to update any values that have changed since the
# Reread the index to update any values that have changed since the
# last time the index was read.
refreshIndex: -> @getRepo().refreshIndex()
# Public: Returns the path of the repository.
# Public: Returns the {String} path of the repository.
getPath: ->
@path ?= fs.absolute(@getRepo().getPath())
# Public: Returns the working directory of the repository.
# Public: Returns the {String} working directory path of the repository.
getWorkingDirectory: -> @getRepo().getWorkingDirectory()
# Public: Returns the status of a single path in the repository.
# Public: Get the status of a single path in the repository.
#
# * path:
# A String defining a relative path
# path - A {String} repository-relative path.
#
# Returns a {Number}, FIXME representing what?
# Returns a {Number} representing the status. This value can be passed to
# {.isStatusModified} or {.isStatusNew} to get more information.
getPathStatus: (path) ->
currentPathStatus = @statuses[path] ? 0
pathStatus = @getRepo().getStatus(@relativize(path)) ? 0
@ -134,7 +122,9 @@ class Git
@emit 'status-changed', path, pathStatus
pathStatus
# Public: Returns true if the given path is ignored.
# Public: Is the given path ignored?
#
# Returns a {Boolean}.
isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path))
# Public: Returns true if the given status indicates modification.
@ -163,22 +153,22 @@ class Git
# `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7
# characters.
#
# Returns a String.
# Returns a {String}.
getShortHead: -> @getRepo().getShortHead()
# Public: Restore the contents of a path in the working directory and index
# to the version at `HEAD`.
#
# This is essentially the same as running:
#
# ```
# git reset HEAD -- <path>
# git checkout HEAD -- <path>
# ```
#
# * path:
# The String path to checkout
# path - The {String} path to checkout.
#
# Returns a Boolean that's true if the method was successful.
# Returns a {Boolean} that's true if the method was successful.
checkoutHead: (path) ->
headCheckedOut = @getRepo().checkoutHead(@relativize(path))
@getPathStatus(path) if headCheckedOut
@ -186,10 +176,9 @@ class Git
# Public: Checks out a branch in your repository.
#
# * reference:
# The String reference to checkout
# * create:
# A Boolean value which, if true creates the new reference if it doesn't exist.
# reference - The String reference to checkout
# create - A Boolean value which, if true creates the new reference if it
# doesn't exist.
#
# Returns a Boolean that's true if the method was successful.
checkoutReference: (reference, create) ->
@ -200,27 +189,26 @@ class Git
# This compares the working directory contents of the path to the `HEAD`
# version.
#
# * path:
# The String path to check
# path - The {String} path to check.
#
# Returns an object with two keys, `added` and `deleted`. These will always
# be greater than 0.
# Returns an {Object} with the following keys:
# :added - The {Number} of added lines.
# :deleted - The {Number} of deleted lines.
getDiffStats: (path) -> @getRepo().getDiffStats(@relativize(path))
# Public: Identifies if a path is a submodule.
# Public: Is the given path a submodule in the repository?
#
# * path:
# The String path to check
# path - The {String} path to check.
#
# Returns a Boolean.
# Returns a {Boolean}.
isSubmodule: (path) -> @getRepo().isSubmodule(@relativize(path))
# Public: Retrieves the status of a directory.
# Public: Get the status of a directory in the repository's working directory.
#
# * path:
# The String path to check
# path - The {String} path to check.
#
# Returns a Number representing the status.
# Returns a {Number} representing the status. This value can be passed to
# {.isStatusModified} or {.isStatusNew} to get more information.
getDirectoryStatus: (directoryPath) ->
{sep} = require 'path'
directoryPath = "#{directoryPath}#{sep}"
@ -232,16 +220,14 @@ class Git
# Public: Retrieves the line diffs comparing the `HEAD` version of the given
# path and the given text.
#
# This is similar to the commit numbers reported by `git status` when a
# remote tracking branch exists.
# path - The {String} path relative to the repository.
# text - The {String} to compare against the `HEAD` contents
#
# * path:
# The String path (relative to the repository)
# * text:
# The String to compare against the `HEAD` contents
#
# Returns an object with two keys, `ahead` and `behind`. These will always be
# greater than zero.
# Returns an {Array} of hunk {Object}s with the following keys:
# :oldStart - The line {Number} of the old hunk.
# :newStart - The line {Number} of the new hunk.
# :oldLines - The {Number} of lines in the old hunk.
# :newLines - The {Number} of lines in the new hunk
getLineDiffs: (path, text) ->
# Ignore eol of line differences on windows so that files checked in as
# LF don't report every line modified when the text contains CRLF endings.
@ -257,7 +243,7 @@ class Git
# Public: Returns the upstream branch for the current HEAD, or null if there
# is no upstream branch for the current HEAD.
#
# Returns a String branch name such as `refs/remotes/origin/master`
# Returns a {String} branch name such as `refs/remotes/origin/master`.
getUpstreamBranch: -> @getRepo().getUpstreamBranch()
# Public: Returns the current SHA for the given reference.
@ -265,19 +251,21 @@ class Git
# Public: Gets all the local and remote references.
#
# Returns an object with three keys: `heads`, `remotes`, and `tags`. Each key
# can be an array of strings containing the reference names.
# Returns an {Object} with the following keys:
# :heads - An {Array} of head reference names.
# :remotes - An {Array} of remote reference names.
# :tags - An {Array} of tag reference names.
getReferences: -> @getRepo().getReferences()
# Public: Returns the number of commits behind the current branch is from the
# default remote branch.
# its upstream remote branch.
getAheadBehindCount: (reference) -> @getRepo().getAheadBehindCount(reference)
# Public: Returns true if the given branch exists.
hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")?
# Private: Refreshes the current git status in an outside process and
# asynchronously updates the relevant properties.
# Refreshes the current git status in an outside process and asynchronously
# updates the relevant properties.
refreshStatus: ->
@statusTask = Task.once require.resolve('./repository-status-handler'), @getPath(), ({statuses, upstream, branch}) =>
statusesUnchanged = _.isEqual(statuses, @statuses) and _.isEqual(upstream, @upstream) and _.isEqual(branch, @branch)

View File

@ -2,14 +2,11 @@
{Range} = require 'text-buffer'
_ = require 'underscore-plus'
# Private: Represents the portion of the {EditorView} containing row numbers.
# Represents the portion of the {EditorView} containing row numbers.
#
# The gutter also indicates if rows are folded.
module.exports =
class GutterView extends View
### Internal ###
@content: ->
@div class: 'gutter', =>
@div outlet: 'lineNumbers', class: 'line-numbers'
@ -51,8 +48,6 @@ class GutterView extends View
$(document).on "mousemove.gutter-#{editorView.id}", moveHandler
$(document).one "mouseup.gutter-#{editorView.id}", => $(document).off 'mousemove', moveHandler
### Public ###
# Retrieves the containing {EditorView}.
#
# Returns an {EditorView}.
@ -138,8 +133,6 @@ class GutterView extends View
el.classList.remove(klass) if hasClass
classesRemoved
### Internal ###
updateLineNumbers: (changes, startScreenRow, endScreenRow) ->
# Check if we have something already rendered that overlaps the requested range
updateAllLines = not (startScreenRow? and endScreenRow?)
@ -223,7 +216,7 @@ class GutterView extends View
html
# Private: Called to update the 'foldable' class of line numbers when there's
# Called to update the 'foldable' class of line numbers when there's
# a change to the display buffer that doesn't regenerate all the line numbers
# anyway.
updateFoldableClasses: (changes) ->

View File

@ -2,8 +2,6 @@ _ = require 'underscore-plus'
fs = require 'fs-plus'
{specificity} = require 'clear-cut'
### Internal ###
module.exports =
class KeyBinding
@parser: null

View File

@ -9,19 +9,23 @@ File = require './file'
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'cmd']
# Internal: Associates keymaps with actions.
# Public: Associates keybindings with commands.
#
# Keymaps are defined in a CSON format. A typical keymap looks something like this:
# An instance of this class is always available as the `atom.keymap` global.
#
# Keymaps are defined in a CSON/JSON format. A typical keymap looks something
# like this:
#
# ```cson
# 'body':
# 'ctrl-l': 'package:do-something'
#'.someClass':
# 'enter': 'package:confirm'
# 'ctrl-l': 'package:do-something'
# '.someClass':
# 'enter': 'package:confirm'
# ```
#
# As a key, you define the DOM element you want to work on, using CSS notation. For that
# key, you define one or more key:value pairs, associating keystrokes with a command to execute.
# As a key, you define the DOM element you want to work on, using CSS notation.
# For that key, you define one or more key:value pairs, associating keystrokes
# with a command to execute.
module.exports =
class Keymap
Emitter.includeInto(this)
@ -39,10 +43,8 @@ class Keymap
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
# that match a keystroke and element.
#
# * keystroke:
# The string representing the keys pressed (e.g. ctrl-P).
# * element:
# The DOM node that will match a {KeyBinding}'s selector.
# keystroke - The {String} representing the keys pressed (e.g. ctrl-P).
# element - The DOM node that will match a {KeyBinding}'s selector.
keyBindingsForKeystrokeMatchingElement: (keystroke, element) ->
keyBindings = @keyBindingsForKeystroke(keystroke)
@keyBindingsMatchingElement(element, keyBindings)
@ -50,41 +52,37 @@ class Keymap
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
# that match a command.
#
# * command:
# The string representing the command (tree-view:toggle)
# * element:
# The DOM node that will match a {KeyBinding}'s selector.
# command - The {String} representing the command (tree-view:toggle).
# element - The DOM node that will match a {KeyBinding}'s selector.
keyBindingsForCommandMatchingElement: (command, element) ->
keyBindings = @keyBindingsForCommand(command)
@keyBindingsMatchingElement(element, keyBindings)
# Public: Returns an array of {KeyBinding}s that match a keystroke
# * keystroke:
# The string representing the keys pressed (e.g. ctrl-P)
#
# keystroke: The {String} representing the keys pressed (e.g. ctrl-P)
keyBindingsForKeystroke: (keystroke) ->
keystroke = KeyBinding.normalizeKeystroke(keystroke)
@keyBindings.filter (keyBinding) -> keyBinding.matches(keystroke)
# Public: Returns an array of {KeyBinding}s that match a command
# * keystroke:
# The string representing the keys pressed (e.g. ctrl-P)
#
# keystroke - The {String} representing the keys pressed (e.g. ctrl-P)
keyBindingsForCommand: (command) ->
@keyBindings.filter (keyBinding) -> keyBinding.command == command
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
# whos selector matches the element.
#
# * element:
# The DOM node that will match a {KeyBinding}'s selector.
# element - The DOM node that will match a {KeyBinding}'s selector.
keyBindingsMatchingElement: (element, keyBindings=@keyBindings) ->
keyBindings = keyBindings.filter ({selector}) -> $(element).closest(selector).length > 0
keyBindings.sort (a, b) -> a.compare(b)
# Public: Returns a keystroke string derived from an event.
# * event:
# A DOM or jQuery event
# * previousKeystroke:
# An optional string used for multiKeystrokes
#
# event - A DOM or jQuery event.
# previousKeystroke - An optional string used for multiKeystrokes.
keystrokeStringForEvent: (event, previousKeystroke) ->
if event.originalEvent.keyIdentifier.indexOf('U+') == 0
hexCharCode = event.originalEvent.keyIdentifier[2..]

View File

@ -3,8 +3,6 @@ _ = require 'underscore-plus'
{OnigRegExp} = require 'oniguruma'
{Emitter, Subscriber} = require 'emissary'
### Internal ###
module.exports =
class LanguageMode
Emitter.includeInto(this)
@ -15,13 +13,9 @@ class LanguageMode
editor: null
currentGrammarScore: null
### Internal ###
destroy: ->
@unsubscribe()
### Public ###
# Sets up a `LanguageMode` for the given {Editor}.
#
# editor - The {Editor} to associate with
@ -187,7 +181,7 @@ class LanguageMode
isFoldableAtBufferRow: (bufferRow) ->
@isFoldableCodeAtBufferRow(bufferRow) or @isFoldableCommentAtBufferRow(bufferRow)
# Private: Returns a {Boolean} indicating whether the given buffer row starts
# Returns a {Boolean} indicating whether the given buffer row starts
# a a foldable row range due to the code's indentation patterns.
isFoldableCodeAtBufferRow: (bufferRow) ->
return false if @editor.isBufferRowBlank(bufferRow) or @isLineCommentedAtBufferRow(bufferRow)
@ -195,14 +189,14 @@ class LanguageMode
return false unless nextNonEmptyRow?
@editor.indentationForBufferRow(nextNonEmptyRow) > @editor.indentationForBufferRow(bufferRow)
# Private: Returns a {Boolean} indicating whether the given buffer row starts
# Returns a {Boolean} indicating whether the given buffer row starts
# a foldable row range due to being the start of a multi-line comment.
isFoldableCommentAtBufferRow: (bufferRow) ->
@isLineCommentedAtBufferRow(bufferRow) and
@isLineCommentedAtBufferRow(bufferRow + 1) and
not @isLineCommentedAtBufferRow(bufferRow - 1)
# Private: Returns a {Boolean} indicating whether the line at the given buffer
# Returns a {Boolean} indicating whether the line at the given buffer
# row is a comment.
isLineCommentedAtBufferRow: (bufferRow) ->
return false unless 0 <= bufferRow <= @editor.getLastBufferRow()

View File

@ -5,7 +5,7 @@ LessCache = require 'less-cache'
tmpDir = if process.platform is 'win32' then os.tmpdir() else '/tmp'
# Private: {LessCache} wrapper used by {ThemeManager} to read stylesheets.
# {LessCache} wrapper used by {ThemeManager} to read stylesheets.
module.exports =
class LessCompileCache
Subscriber.includeInto(this)

View File

@ -8,31 +8,41 @@ fs = require 'fs-plus'
# Public: Provides a registry for menu items that you'd like to appear in the
# application menu.
#
# Should be accessed via `atom.menu`.
# An instance of this class is always available as the `atom.menu` global.
module.exports =
class MenuManager
pendingUpdateOperation: null
# Private:
constructor: ({@resourcePath}) ->
@template = []
atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems()
# Public: Adds the given item definition to the existing template.
#
# * item:
# An object which describes a menu item as defined by
# https://github.com/atom/atom-shell/blob/master/docs/api/browser/menu.md
# ## Example
# ```coffee
# atom.menu.add [
# {
# label: 'Hello'
# submenu : [{label: 'World!', command: 'hello:world'}]
# }
# ]
# ```
#
# items - An {Array} of menu item {Object}s containing the keys:
# :label - The {String} menu label.
# :submenu - An optional {Array} of sub menu items.
# :command - An option {String} command to trigger when the item is clicked.
#
# Returns nothing.
add: (items) ->
@merge(@template, item) for item in items
@update()
# Private: Should the binding for the given selector be included in the menu
# Should the binding for the given selector be included in the menu
# commands.
#
# * selector: A String selector to check.
# selector - A {String} selector to check.
#
# Returns true to include the selector, false otherwise.
includeSelector: (selector) ->
@ -59,14 +69,13 @@ class MenuManager
keystrokesByCommand[binding.command].push binding.keystroke
@sendToBrowserProcess(@template, keystrokesByCommand)
# Private:
loadPlatformItems: ->
menusDirPath = path.join(@resourcePath, 'menus')
platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json'])
{menu} = CSON.readFileSync(platformMenuPath)
@add(menu)
# Private: Merges an item in a submenu aware way such that new items are always
# Merges an item in a submenu aware way such that new items are always
# appended to the bottom of existing menus where possible.
merge: (menu, item) ->
item = _.deepClone(item)
@ -76,7 +85,7 @@ class MenuManager
else
menu.push(item) unless _.find(menu, (i) => @normalizeLabel(i.label) == @normalizeLabel(item.label))
# Private: OSX can't handle displaying accelerators for multiple keystrokes.
# OSX can't handle displaying accelerators for multiple keystrokes.
# If they are sent across, it will stop processing accelerators for the rest
# of the menu items.
filterMultipleKeystroke: (keystrokesByCommand) ->
@ -89,12 +98,10 @@ class MenuManager
filtered[key].push(binding)
filtered
# Private:
sendToBrowserProcess: (template, keystrokesByCommand) ->
keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand)
ipc.sendChannel 'update-application-menu', template, keystrokesByCommand
# Private:
normalizeLabel: (label) ->
return undefined unless label?

View File

@ -1,11 +1,14 @@
{Emitter} = require 'emissary'
fs = require 'fs-plus'
_ = require 'underscore-plus'
Q = require 'q'
Package = require './package'
path = require 'path'
# Public: Package manager for coordinating the lifecycle of Atom packages.
#
# An instance of this class is always available as the `atom.packages` global.
#
# Packages can be loaded, activated, and deactivated, and unloaded:
# * Loading a package reads and parses the package's metadata and resources
# such as keymaps, menus, stylesheets, etc.
@ -17,13 +20,10 @@ path = require 'path'
#
# Packages can also be enabled/disabled via the `core.disabledPackages` config
# settings and also by calling `enablePackage()/disablePackage()`.
#
# An instance of this class is globally available via `atom.packages`.
module.exports =
class PackageManager
Emitter.includeInto(this)
# Private:
constructor: ({configDirPath, devMode, @resourcePath}) ->
@packageDirPaths = [path.join(configDirPath, "packages")]
if devMode
@ -32,7 +32,6 @@ class PackageManager
@loadedPackages = {}
@activePackages = {}
@packageStates = {}
@observingDisabledPackages = false
@packageActivators = []
@registerPackageActivator(this, ['atom', 'textmate'])
@ -47,11 +46,9 @@ class PackageManager
getPackageDirPaths: ->
_.clone(@packageDirPaths)
# Private:
getPackageState: (name) ->
@packageStates[name]
# Private:
setPackageState: (name, state) ->
@packageStates[name] = state
@ -67,44 +64,55 @@ class PackageManager
pack?.disable()
pack
# Private: Activate all the packages that should be activated.
# Activate all the packages that should be activated.
activate: ->
for [activator, types] in @packageActivators
packages = @getLoadedPackagesForTypes(types)
activator.activatePackages(packages)
@emit 'activated'
# Private: another type of package manager can handle other package types.
# another type of package manager can handle other package types.
# See ThemeManager
registerPackageActivator: (activator, types) ->
@packageActivators.push([activator, types])
# Private:
activatePackages: (packages) ->
@activatePackage(pack.name) for pack in packages
@observeDisabledPackages()
# Private: Activate a single package by name
activatePackage: (name, options) ->
# Activate a single package by name
activatePackage: (name, options={}) ->
if options.sync? or options.immediate?
return @activatePackageSync(name, options)
if pack = @getActivePackage(name)
Q(pack)
else
pack = @loadPackage(name)
pack.activate(options).then =>
@activePackages[pack.name] = pack
pack
# Deprecated
activatePackageSync: (name, options) ->
return pack if pack = @getActivePackage(name)
if pack = @loadPackage(name, options)
if pack = @loadPackage(name)
@activePackages[pack.name] = pack
pack.activate(options)
pack.activateSync(options)
pack
# Private: Deactivate all packages
# Deactivate all packages
deactivatePackages: ->
@deactivatePackage(pack.name) for pack in @getActivePackages()
@deactivatePackage(pack.name) for pack in @getLoadedPackages()
@unobserveDisabledPackages()
# Private: Deactivate the package with the given name
# Deactivate the package with the given name
deactivatePackage: (name) ->
if pack = @getActivePackage(name)
pack = @getLoadedPackage(name)
if @isPackageActive(name)
@setPackageState(pack.name, state) if state = pack.serialize?()
pack.deactivate()
delete @activePackages[pack.name]
else
throw new Error("No active package for name '#{name}'")
pack.deactivate()
delete @activePackages[pack.name]
# Public: Get an array of all the active packages
getActivePackages: ->
@ -118,17 +126,12 @@ class PackageManager
isPackageActive: (name) ->
@getActivePackage(name)?
# Private:
unobserveDisabledPackages: ->
return unless @observingDisabledPackages
atom.config.unobserve('core.disabledPackages')
@observingDisabledPackages = false
@disabledPackagesSubscription?.off()
@disabledPackagesSubscription = null
# Private:
observeDisabledPackages: ->
return if @observingDisabledPackages
atom.config.observe 'core.disabledPackages', callNow: false, (disabledPackages, {previous}) =>
@disabledPackagesSubscription ?= atom.config.observe 'core.disabledPackages', callNow: false, (disabledPackages, {previous}) =>
packagesToEnable = _.difference(previous, disabledPackages)
packagesToDisable = _.difference(disabledPackages, previous)
@ -136,10 +139,7 @@ class PackageManager
@activatePackage(packageName) for packageName in packagesToEnable
null
@observingDisabledPackages = true
# Private:
loadPackages: (options) ->
loadPackages: ->
# Ensure atom exports is already in the require cache so the load time
# of the first package isn't skewed by being the first to require atom
require '../exports/atom'
@ -147,27 +147,24 @@ class PackageManager
packagePaths = @getAvailablePackagePaths()
packagePaths = packagePaths.filter (packagePath) => not @isPackageDisabled(path.basename(packagePath))
packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath)
@loadPackage(packagePath, options) for packagePath in packagePaths
@loadPackage(packagePath) for packagePath in packagePaths
@emit 'loaded'
# Private:
loadPackage: (nameOrPath, options) ->
loadPackage: (nameOrPath) ->
if packagePath = @resolvePackagePath(nameOrPath)
name = path.basename(nameOrPath)
return pack if pack = @getLoadedPackage(name)
pack = Package.load(packagePath, options)
pack = Package.load(packagePath)
@loadedPackages[pack.name] = pack if pack?
pack
else
throw new Error("Could not resolve '#{nameOrPath}' to a package path")
# Private:
unloadPackages: ->
@unloadPackage(name) for name in _.keys(@loadedPackages)
null
# Private:
unloadPackage: (name) ->
if @isPackageActive(name)
throw new Error("Tried to unload active package '#{name}'")
@ -189,9 +186,9 @@ class PackageManager
getLoadedPackages: ->
_.values(@loadedPackages)
# Private: Get packages for a certain package type
# Get packages for a certain package type
#
# * types: an {Array} of {String}s like ['atom', 'textmate']
# types - an {Array} of {String}s like ['atom', 'textmate'].
getLoadedPackagesForTypes: (types) ->
pack for pack in @getLoadedPackages() when pack.getType() in types
@ -209,7 +206,6 @@ class PackageManager
isPackageDisabled: (name) ->
_.include(atom.config.get('core.disabledPackages') ? [], name)
# Private:
hasAtomEngine: (packagePath) ->
metadata = Package.loadMetadata(packagePath, true)
metadata?.engines?.atom?
@ -218,7 +214,6 @@ class PackageManager
isBundledPackage: (name) ->
@getPackageDependencies().hasOwnProperty(name)
# Private:
getPackageDependencies: ->
unless @packageDependencies?
try

View File

@ -1,7 +1,6 @@
CSON = require 'season'
{basename, join} = require 'path'
### Internal ###
module.exports =
class Package
@build: (path) ->
@ -23,9 +22,9 @@ class Package
pack
@load: (path, options) ->
@load: (path) ->
pack = @build(path)
pack?.load(options)
pack?.load()
pack
@loadMetadata: (path, ignoreErrors=false) ->
@ -44,9 +43,6 @@ class Package
constructor: (@path) ->
@name = basename(@path)
isActive: ->
atom.packages.isPackageActive(@name)
enable: ->
atom.config.removeAtKeyPath('core.disabledPackages', @metadata.name)
@ -56,7 +52,6 @@ class Package
isTheme: ->
@metadata?.theme?
# Private:
measure: (key, fn) ->
startTime = Date.now()
value = fn()

View File

@ -1,7 +1,6 @@
{View} = require './space-pen-extensions'
PaneView = null
### Internal ###
module.exports =
class PaneAxisView extends View
initialize: (@model) ->

View File

@ -2,7 +2,6 @@
_ = require 'underscore-plus'
PaneAxisView = require './pane-axis-view'
# Internal:
module.exports =
class PaneColumnView extends PaneAxisView

View File

@ -3,7 +3,7 @@ Delegator = require 'delegato'
PaneView = require './pane-view'
PaneContainer = require './pane-container'
# Private: Manages the list of panes within a {WorkspaceView}
# Manages the list of panes within a {WorkspaceView}
module.exports =
class PaneContainerView extends View
Delegator.includeInto(this)
@ -27,8 +27,6 @@ class PaneContainerView extends View
viewClass = model.getViewClass()
model._view ?= new viewClass(model)
### Public ###
getRoot: ->
@children().first().view()
@ -98,3 +96,50 @@ class PaneContainerView extends View
focusPreviousPane: ->
@model.activatePreviousPane()
focusPaneAbove: ->
@nearestPaneInDirection('above')?.focus()
focusPaneBelow: ->
@nearestPaneInDirection('below')?.focus()
focusPaneOnLeft: ->
@nearestPaneInDirection('left')?.focus()
focusPaneOnRight: ->
@nearestPaneInDirection('right')?.focus()
nearestPaneInDirection: (direction) ->
distance = (pointA, pointB) ->
x = pointB.x - pointA.x
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
pane = @getActivePane()
box = @boundingBoxForPane(pane)
panes = @getPanes()
.filter (otherPane) =>
otherBox = @boundingBoxForPane(otherPane)
switch direction
when 'left' then otherBox.right.x <= box.left.x
when 'right' then otherBox.left.x >= box.right.x
when 'above' then otherBox.bottom.y <= box.top.y
when 'below' then otherBox.top.y >= box.bottom.y
.sort (paneA, paneB) =>
boxA = @boundingBoxForPane(paneA)
boxB = @boundingBoxForPane(paneB)
switch direction
when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right)
when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left)
when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom)
when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top)
panes[0]
boundingBoxForPane: (pane) ->
boundingBox = pane[0].getBoundingClientRect()
left: {x: boundingBox.left, y: boundingBox.top}
right: {x: boundingBox.right, y: boundingBox.top}
top: {x: boundingBox.left, y: boundingBox.top}
bottom: {x: boundingBox.left, y: boundingBox.bottom}

View File

@ -86,6 +86,6 @@ class PaneContainer extends Model
itemDestroyed: (item) ->
@emit 'item-destroyed', item
# Private: Called by Model superclass when destroyed
# Called by Model superclass when destroyed
destroyed: ->
pane.destroy() for pane in @getPanes()

View File

@ -2,8 +2,6 @@
_ = require 'underscore-plus'
PaneAxisView = require './pane-axis-view'
### Internal ###
module.exports =
class PaneRowView extends PaneAxisView
@content: ->

View File

@ -31,7 +31,6 @@ class PaneView extends View
previousActiveItem: null
# Private:
initialize: (args...) ->
if args[0] instanceof Pane
@model = args[0]
@ -97,7 +96,6 @@ class PaneView extends View
# Deprecated: Use ::activatePreviousItem
showPreviousItem: -> @activatePreviousItem()
# Private:
afterAttach: (onDom) ->
@focus() if @model.focused and onDom
@ -167,11 +165,9 @@ class PaneView extends View
@unsubscribe(item) if typeof item.off is 'function'
@trigger 'pane:before-item-destroyed', [item]
# Private:
activeItemTitleChanged: =>
@trigger 'pane:active-item-title-changed'
# Private:
viewForItem: (item) ->
return unless item?
if item instanceof $
@ -184,7 +180,6 @@ class PaneView extends View
@viewsByItem.set(item, view)
view
# Private:
@::accessor 'activeView', -> @viewForItem(@activeItem)
splitLeft: (items...) -> @model.splitLeft({items})._view
@ -195,14 +190,15 @@ class PaneView extends View
splitDown: (items...) -> @model.splitDown({items})._view
# Public:
# Public: Get the container view housing this pane.
#
# Returns a {View}.
getContainer: ->
@closest('.panes').view()
beforeRemove: ->
@model.destroy() unless @model.isDestroyed()
# Private:
remove: (selector, keepData) ->
return super if keepData
@unsubscribe()

View File

@ -27,7 +27,6 @@ class Pane extends Model
.map((activePane) => activePane is this)
.distinctUntilChanged()
# Private:
constructor: (params) ->
super
@ -43,31 +42,31 @@ class Pane extends Model
@activate() if params?.active
# Private: Called by the Serializable mixin during serialization.
# Called by the Serializable mixin during serialization.
serializeParams: ->
items: compact(@items.map((item) -> item.serialize?()))
activeItemUri: @activeItem?.getUri?()
focused: @focused
active: @active
# Private: Called by the Serializable mixin during deserialization.
# Called by the Serializable mixin during deserialization.
deserializeParams: (params) ->
{items, activeItemUri} = params
params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState))
params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri
params
# Private: Called by the view layer to construct a view for this model.
# Called by the view layer to construct a view for this model.
getViewClass: -> PaneView ?= require './pane-view'
isActive: -> @active
# Private: Called by the view layer to indicate that the pane has gained focus.
# Called by the view layer to indicate that the pane has gained focus.
focus: ->
@focused = true
@activate() unless @isActive()
# Private: Called by the view layer to indicate that the pane has lost focus.
# Called by the view layer to indicate that the pane has lost focus.
blur: ->
@focused = false
true # if this is called from an event handler, don't cancel it
@ -78,10 +77,11 @@ class Pane extends Model
@container?.activePane = this
@emit 'activated'
# Private:
getPanes: -> [this]
# Public:
# Public: Get the items in this pane.
#
# Returns an {Array} of items.
getItems: ->
@items.slice()
@ -121,11 +121,9 @@ class Pane extends Model
# Public: Adds the item to the pane.
#
# * item:
# The item to add. It can be a model with an associated view or a view.
# * index:
# An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# item - The item to add. It can be a model with an associated view or a view.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
#
# Returns the added item
addItem: (item, index=@getActiveItemIndex() + 1) ->
@ -138,12 +136,11 @@ class Pane extends Model
# Public: Adds the given items to the pane.
#
# * items:
# An {Array} of items to add. Items can be models with associated views
# or views. Any items that are already present in items will not be added.
# * index:
# An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# items - An {Array} of items to add. Items can be models with associated
# views or views. Any items that are already present in items will
# not be added.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
#
# Returns an {Array} of the added items
addItems: (items, index=@getActiveItemIndex() + 1) ->
@ -151,7 +148,6 @@ class Pane extends Model
@addItem(item, index + i) for item, i in items
items
# Private:
removeItem: (item, destroying) ->
index = @items.indexOf(item)
return if index is -1
@ -207,7 +203,7 @@ class Pane extends Model
destroy: ->
super unless @container?.isAlive() and @container?.getPanes().length is 1
# Private: Called by model superclass.
# Called by model superclass.
destroyed: ->
@container.activateNextPane() if @isActive()
item.destroy?() for item in @items.slice()
@ -238,8 +234,9 @@ class Pane extends Model
# Public: Saves the specified item.
#
# * item: The item to save.
# * nextAction: An optional function which will be called after the item is saved.
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
saveItem: (item, nextAction) ->
if item?.getUri?()
item.save?()
@ -249,8 +246,9 @@ class Pane extends Model
# Public: Saves the given item at a prompted-for location.
#
# * item: The item to save.
# * nextAction: An optional function which will be called after the item is saved.
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
saveItemAs: (item, nextAction) ->
return unless item?.saveAs?
@ -279,15 +277,14 @@ class Pane extends Model
else
false
# Private:
copyActiveItem: ->
if @activeItem?
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
# Public: Creates a new pane to the left of the receiver.
#
# * params:
# + items: An optional array of items with which to construct the new pane.
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitLeft: (params) ->
@ -295,8 +292,8 @@ class Pane extends Model
# Public: Creates a new pane to the right of the receiver.
#
# * params:
# + items: An optional array of items with which to construct the new pane.
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitRight: (params) ->
@ -304,8 +301,8 @@ class Pane extends Model
# Public: Creates a new pane above the receiver.
#
# * params:
# + items: An optional array of items with which to construct the new pane.
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitUp: (params) ->
@ -313,14 +310,13 @@ class Pane extends Model
# Public: Creates a new pane below the receiver.
#
# * params:
# + items: An optional array of items with which to construct the new pane.
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitDown: (params) ->
@split('vertical', 'after', params)
# Private:
split: (orientation, side, params) ->
if @parent.orientation isnt orientation
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
@ -333,7 +329,7 @@ class Pane extends Model
newPane.activate()
newPane
# Private: If the parent is a horizontal axis, returns its first child;
# If the parent is a horizontal axis, returns its first child;
# otherwise this pane.
findLeftmostSibling: ->
if @parent.orientation is 'horizontal'
@ -341,7 +337,7 @@ class Pane extends Model
else
this
# Private: If the parent is a horizontal axis, returns its last child;
# If the parent is a horizontal axis, returns its last child;
# otherwise returns a new pane created by splitting this pane rightward.
findOrCreateRightmostSibling: ->
if @parent.orientation is 'horizontal'

View File

@ -16,7 +16,7 @@ Git = require './git'
# Public: Represents a project that's opened in Atom.
#
# There is always a project available under the `atom.project` global.
# An instance of this class is always available as the `atom.project` global.
module.exports =
class Project extends Model
atom.deserializers.add(this)
@ -50,32 +50,29 @@ class Project extends Model
#
# An {Editor} will be used if no openers return a value.
#
# ## Example:
# ## Example
# ```coffeescript
# atom.project.registerOpener (filePath) ->
# if path.extname(filePath) is '.toml'
# return new TomlEditor(filePath)
# ```
#
# * opener: A function to be called when a path is being opened.
# opener - A {Function} to be called when a path is being opened.
registerOpener: (opener) -> @openers.push(opener)
# Public: Remove a previously registered opener.
unregisterOpener: (opener) -> _.remove(@openers, opener)
# Private:
destroyed: ->
editor.destroy() for editor in @getEditors()
buffer.destroy() for buffer in @getBuffers()
@destroyRepo()
# Private:
destroyRepo: ->
if @repo?
@repo.destroy()
@repo = null
# Private:
destroyUnretainedBuffers: ->
buffer.destroy() for buffer in @getBuffers() when not buffer.isRetained()
@ -111,8 +108,7 @@ class Project extends Model
# the path is already absolute or if it is prefixed with a scheme, it is
# returned unchanged.
#
# * uri:
# The String name of the path to convert
# uri - The {String} name of the path to convert.
#
# Returns a String.
resolve: (uri) ->
@ -136,10 +132,8 @@ class Project extends Model
# Public: Given a path to a file, this constructs and associates a new
# {Editor}, showing the file.
#
# * filePath:
# The {String} path of the file to associate with
# * options:
# Options that you can pass to the {Editor} constructor
# filePath - The {String} path of the file to associate with.
# options - Options that you can pass to the {Editor} constructor.
#
# Returns a promise that resolves to an {Editor}.
open: (filePath, options={}) ->
@ -152,7 +146,7 @@ class Project extends Model
@bufferForPath(filePath).then (buffer) =>
@buildEditorForBuffer(buffer, options)
# Private: Only be used in specs
# Only be used in specs
openSync: (filePath, options={}) ->
filePath = @resolve(filePath) ? ''
resource = opener(filePath, options) for opener in @openers when !resource
@ -174,28 +168,27 @@ class Project extends Model
removeEditor: (editor) ->
_.remove(@editors, editor)
# Private: Retrieves all the {TextBuffer}s in the project; that is, the
# Retrieves all the {TextBuffer}s in the project; that is, the
# buffers for all open files.
#
# Returns an {Array} of {TextBuffer}s.
getBuffers: ->
@buffers.slice()
# Private: Is the buffer for the given path modified?
# Is the buffer for the given path modified?
isPathModified: (filePath) ->
@findBufferForPath(@resolve(filePath))?.isModified()
# Private:
findBufferForPath: (filePath) ->
_.find @buffers, (buffer) -> buffer.getPath() == filePath
# Private: Only to be used in specs
# Only to be used in specs
bufferForPathSync: (filePath) ->
absoluteFilePath = @resolve(filePath)
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
existingBuffer ? @buildBufferSync(absoluteFilePath)
# Private: Given a file path, this retrieves or creates a new {TextBuffer}.
# Given a file path, this retrieves or creates a new {TextBuffer}.
#
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
# `text` is used as the contents of the new buffer.
@ -208,21 +201,20 @@ class Project extends Model
existingBuffer = @findBufferForPath(absoluteFilePath) if absoluteFilePath
Q(existingBuffer ? @buildBuffer(absoluteFilePath))
# Private:
bufferForId: (id) ->
_.find @buffers, (buffer) -> buffer.id is id
# Private: DEPRECATED
# DEPRECATED
buildBufferSync: (absoluteFilePath) ->
buffer = new TextBuffer({filePath: absoluteFilePath})
@addBuffer(buffer)
buffer.loadSync()
buffer
# Private: Given a file path, this sets its {TextBuffer}.
# Given a file path, this sets its {TextBuffer}.
#
# absoluteFilePath - A {String} representing a path
# text - The {String} text to use as a buffer
# absoluteFilePath - A {String} representing a path.
# text - The {String} text to use as a buffer.
#
# Returns a promise that resolves to the {TextBuffer}.
buildBuffer: (absoluteFilePath) ->
@ -232,38 +224,33 @@ class Project extends Model
.then((buffer) -> buffer)
.catch(=> @removeBuffer(buffer))
# Private:
addBuffer: (buffer, options={}) ->
@addBufferAtIndex(buffer, @buffers.length, options)
buffer.once 'destroyed', => @removeBuffer(buffer)
# Private:
addBufferAtIndex: (buffer, index, options={}) ->
@buffers.splice(index, 0, buffer)
buffer.once 'destroyed', => @removeBuffer(buffer)
@emit 'buffer-created', buffer
buffer
# Private: Removes a {TextBuffer} association from the project.
# Removes a {TextBuffer} association from the project.
#
# Returns the removed {TextBuffer}.
removeBuffer: (buffer) ->
index = @buffers.indexOf(buffer)
@removeBufferAtIndex(index) unless index is -1
# Private:
removeBufferAtIndex: (index, options={}) ->
[buffer] = @buffers.splice(index, 1)
buffer?.destroy()
# Public: Performs a search across all the files in the project.
#
# * regex:
# A RegExp to search with
# * options:
# - paths: an {Array} of glob patterns to search within
# * iterator:
# A Function callback on each file found
# regex - A {RegExp} to search with.
# options - An optional options {Object} (default: {}):
# :paths - An {Array} of glob patterns to search within
# iterator - A {Function} callback on each file found
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
@ -302,10 +289,11 @@ class Project extends Model
# Public: Performs a replace across all the specified files in the project.
#
# * regex: A RegExp to search with
# * replacementText: Text to replace all matches of regex with
# * filePaths: List of file path strings to run the replace on.
# * iterator: A Function callback on each file with replacements. `({filePath, replacements}) ->`
# regex - A {RegExp} to search with.
# replacementText - Text to replace all matches of regex with
# filePaths - List of file path strings to run the replace on.
# iterator - A {Function} callback on each file with replacements:
# `({filePath, replacements}) ->`.
replace: (regex, replacementText, filePaths, iterator) ->
deferred = Q.defer()
@ -337,18 +325,15 @@ class Project extends Model
deferred.promise
# Private:
buildEditorForBuffer: (buffer, editorOptions) ->
editor = new Editor(_.extend({buffer}, editorOptions))
@addEditor(editor)
editor
# Private:
eachEditor: (callback) ->
callback(editor) for editor in @getEditors()
@on 'editor-created', (editor) -> callback(editor)
# Private:
eachBuffer: (args...) ->
subscriber = args.shift() if args.length > 1
callback = args.shift()

View File

@ -1,4 +1,4 @@
# Private: Maintains the canonical map between screen and buffer positions.
# Maintains the canonical map between screen and buffer positions.
#
# Facilitates the mapping of screen rows to buffer rows and vice versa. All row
# ranges dealt with by this class are end-row exclusive. For example, a fold of

View File

@ -2,8 +2,14 @@
# Public: Represents a view that scrolls.
#
# This `View` subclass listens to events such as `page-up`, `page-down`,
# `move-to-top`, and `move-to-bottom`.
# Subclasses must call `super` if overriding the `initialize` method or else
# the following events won't be handled by the ScrollView.
#
# ## Events
# * `core:page-up`
# * `core:page-down`
# * `core:move-to-top`
# * `core:move-to-bottom`
#
# ## Requiring in packages
#
@ -12,8 +18,6 @@
# ```
module.exports =
class ScrollView extends View
# Internal: The constructor.
initialize: ->
@on 'core:page-up', => @pageUp()
@on 'core:page-down', => @pageDown()

View File

@ -12,8 +12,6 @@ fuzzyFilter = require('fuzzaldrin').filter
# ```
module.exports =
class SelectListView extends View
# Private:
@content: ->
@div class: @viewClass(), =>
@subview 'miniEditor', new EditorView(mini: true)
@ -23,7 +21,6 @@ class SelectListView extends View
@span class: 'badge', outlet: 'loadingBadge'
@ol class: 'list-group', outlet: 'list'
# Private:
@viewClass: -> 'select-list'
maxItems: Infinity
@ -59,7 +56,6 @@ class SelectListView extends View
@confirmSelection() if $(e.target).closest('li').hasClass('selected')
e.preventDefault()
# Private:
schedulePopulateList: ->
clearTimeout(@scheduleTimeout)
populateCallback = =>
@ -68,14 +64,14 @@ class SelectListView extends View
# Public: Set the array of items to display in the list.
#
# * array: The array of model elements to display in the list.
# array - The {Array} of model elements to display in the list.
setArray: (@array=[]) ->
@populateList()
@setLoading()
# Public: Set the error message to display.
#
# * message: The error message.
# message - The {String} error message (default: '').
setError: (message='') ->
if message.length is 0
@error.text('').hide()
@ -85,7 +81,7 @@ class SelectListView extends View
# Public: Set the loading message to display.
#
# * message: The loading message.
# message - The {String} loading message (default: '').
setLoading: (message='') ->
if message.length is 0
@loading.text("")
@ -135,30 +131,26 @@ class SelectListView extends View
#
# Subclasses may override this method to customize the message.
#
# * itemCount: The number of items in the array specified to {.setArray}
# * filteredItemCount: The number of items that pass the fuzzy filter test.
# itemCount - The {Number} of items in the array specified to {.setArray}
# filteredItemCount - The {Number} of items that pass the fuzzy filter test.
getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found'
# Private:
selectPreviousItem: ->
item = @getSelectedItem().prev()
item = @list.find('li:last') unless item.length
@selectItem(item)
# Private:
selectNextItem: ->
item = @getSelectedItem().next()
item = @list.find('li:first') unless item.length
@selectItem(item)
# Private:
selectItem: (item) ->
return unless item.length
@list.find('.selected').removeClass('selected')
item.addClass 'selected'
@scrollToItem(item)
# Private:
scrollToItem: (item) ->
scrollTop = @list.scrollTop()
desiredTop = item.position().top + scrollTop
@ -181,7 +173,6 @@ class SelectListView extends View
getSelectedElement: ->
@getSelectedItem().data('select-list-element')
# Private:
confirmSelection: ->
element = @getSelectedElement()
if element?
@ -193,25 +184,21 @@ class SelectListView extends View
#
# This method should be overridden by subclasses.
#
# * element: The selected model element.
# element - The selected model element.
confirmed: (element) ->
# Private:
attach: ->
@storeFocusedElement()
# Private:
storeFocusedElement: ->
@previouslyFocusedElement = $(':focus')
# Private:
restoreFocus: ->
if @previouslyFocusedElement?.isOnDom()
@previouslyFocusedElement.focus()
else
atom.workspaceView.focus()
# Private:
cancelled: ->
@miniEditor.getEditor().setText('')
@miniEditor.updateDisplay()

View File

@ -1,7 +1,6 @@
{Point, Range} = require 'text-buffer'
{View, $$} = require './space-pen-extensions'
# Internal:
module.exports =
class SelectionView extends View

View File

@ -14,7 +14,6 @@ class Selection
wordwise: false
needsAutoscroll: null
# Private:
constructor: ({@cursor, @marker, @editor}) ->
@cursor.selection = this
@marker.on 'changed', => @screenRangeChanged()
@ -23,18 +22,15 @@ class Selection
@editor.removeSelection(this)
@emit 'destroyed' unless @editor.isDestroyed()
# Private:
destroy: ->
@marker.destroy()
# Private:
finalize: ->
@initialScreenRange = null unless @initialScreenRange?.isEqual(@getScreenRange())
if @isEmpty()
@wordwise = false
@linewise = false
# Private:
clearAutoscroll: ->
@needsAutoscroll = null
@ -59,10 +55,8 @@ class Selection
# Public: Modifies the screen range for the selection.
#
# * screenRange:
# The new {Range} to use
# * options:
# + A hash of options matching those found in {.setBufferRange}
# screenRange - The new {Range} to use.
# options - A hash of options matching those found in {.setBufferRange}.
setScreenRange: (screenRange, options) ->
@setBufferRange(@editor.bufferRangeForScreenRange(screenRange), options)
@ -72,13 +66,11 @@ class Selection
# Public: Modifies the buffer {Range} for the selection.
#
# * screenRange:
# The new {Range} to select
# * options
# + preserveFolds:
# if `true`, the fold settings are preserved after the selection moves
# + autoscroll:
# if `true`, the {Editor} scrolls to the new selection
# screenRange - The new {Range} to select.
# options - An {Object} with the keys:
# :preserveFolds - if `true`, the fold settings are preserved after the
# selection moves.
# :autoscroll - if `true`, the {Editor} scrolls to the new selection.
setBufferRange: (bufferRange, options={}) ->
bufferRange = Range.fromObject(bufferRange)
@needsAutoscroll = options.autoscroll
@ -128,8 +120,7 @@ class Selection
# Public: Selects an entire line in the buffer.
#
# * row:
# The line Number to select (default: the row of the cursor)
# row - The line {Number} to select (default: the row of the cursor).
selectLine: (row=@cursor.getBufferPosition().row) ->
range = @editor.bufferRangeForBufferRow(row, includeNewline: true)
@setBufferRange(@getBufferRange().union(range))
@ -148,8 +139,7 @@ class Selection
# Public: Selects the text from the current cursor position to a given screen
# position.
#
# * position:
# An instance of {Point}, with a given `row` and `column`.
# position - An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
@modifySelection =>
if @initialScreenRange
@ -168,8 +158,7 @@ class Selection
# Public: Selects the text from the current cursor position to a given buffer
# position.
#
# * position:
# An instance of {Point}, with a given `row` and `column`.
# position - An instance of {Point}, with a given `row` and `column`.
selectToBufferPosition: (position) ->
@modifySelection => @cursor.setBufferPosition(position)
@ -259,8 +248,6 @@ class Selection
@editor.addSelectionForBufferRange(range, goalBufferRange: range)
break
# Public:
#
# FIXME: I have no idea what this does.
getGoalBufferRange: ->
@marker.getAttributes().goalBufferRange
@ -285,20 +272,14 @@ class Selection
# Public: Replaces text at the current selection.
#
# * text:
# A {String} representing the text to add
# * options
# + select:
# if `true`, selects the newly added text
# + autoIndent:
# if `true`, indents all inserted text appropriately
# + autoIndentNewline:
# if `true`, indent newline appropriately
# + autoDecreaseIndent:
# if `true`, decreases indent level appropriately (for example, when a
# closing bracket is inserted)
# + undo:
# if `skip`, skips the undo stack for this operation.
# text - A {String} representing the text to add
# options - An {Object} with keys:
# :select - if `true`, selects the newly added text.
# :autoIndent - if `true`, indents all inserted text appropriately.
# :autoIndentNewline - if `true`, indent newline appropriately.
# :autoDecreaseIndent - if `true`, decreases indent level appropriately
# (for example, when a closing bracket is inserted).
# :undo - if `skip`, skips the undo stack for this operation.
insertText: (text, options={}) ->
oldBufferRange = @getBufferRange()
@editor.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
@ -326,10 +307,8 @@ class Selection
# Public: Indents the given text to the suggested level based on the grammar.
#
# * text:
# The string to indent within the selection.
# * indentBasis:
# The beginning indent level.
# text - The {String} to indent within the selection.
# indentBasis - The beginning indent level.
normalizeIndents: (text, indentBasis) ->
textPrecedingCursor = @cursor.getCurrentBufferLine()[0...@cursor.getBufferColumn()]
isCursorInsideExistingLine = /\S/.test(textPrecedingCursor)
@ -357,10 +336,9 @@ class Selection
# Public: Indents the selection.
#
# * options - A hash with one key,
# + autoIndent:
# If `true`, the indentation is performed appropriately. Otherwise,
# {Editor.getTabText} is used
# options - A {Object} with the keys:
# :autoIndent - If `true`, the indentation is performed appropriately.
# Otherwise, {Editor.getTabText} is used.
indent: ({ autoIndent }={})->
{ row, column } = @cursor.getBufferPosition()
@ -505,25 +483,16 @@ class Selection
@editor.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
# Public: Cuts the selection until the end of the line.
#
# * maintainClipboard:
# ?
cutToEndOfLine: (maintainClipboard) ->
@selectToEndOfLine() if @isEmpty()
@cut(maintainClipboard)
# Public: Copies the selection to the clipboard and then deletes it.
#
# * maintainClipboard:
# ?
cut: (maintainClipboard=false) ->
@copy(maintainClipboard)
@delete()
# Public: Copies the current selection to the clipboard.
#
# * maintainClipboard:
# ?
copy: (maintainClipboard=false) ->
return if @isEmpty()
text = @editor.buffer.getTextInRange(@getBufferRange())
@ -540,14 +509,13 @@ class Selection
@editor.createFold(range.start.row, range.end.row)
@cursor.setBufferPosition([range.end.row + 1, 0])
# Public: ?
modifySelection: (fn) ->
@retainSelection = true
@plantTail()
fn()
@retainSelection = false
# Private: Sets the marker's tail to the same position as the marker's head.
# Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
@ -557,8 +525,7 @@ class Selection
# Public: Identifies if a selection intersects with a given buffer range.
#
# * bufferRange:
# A {Range} to check against
# bufferRange - A {Range} to check against.
#
# Returns a Boolean.
intersectsBufferRange: (bufferRange) ->
@ -566,8 +533,7 @@ class Selection
# Public: Identifies if a selection intersects with another selection.
#
# * otherSelection:
# A {Selection} to check against
# otherSelection - A {Selection} to check against.
#
# Returns a Boolean.
intersectsWith: (otherSelection) ->
@ -576,10 +542,8 @@ class Selection
# Public: Combines the given selection into this selection and then destroys
# the given selection.
#
# * otherSelection:
# A {Selection} to merge with
# * options
# + A hash of options matching those found in {.setBufferRange}
# otherSelection - A {Selection} to merge with.
# options - A hash of options matching those found in {.setBufferRange}.
merge: (otherSelection, options) ->
myGoalBufferRange = @getGoalBufferRange()
otherGoalBufferRange = otherSelection.getGoalBufferRange()
@ -595,12 +559,10 @@ class Selection
#
# See {Range.compare} for more details.
#
# * otherSelection:
# A {Selection} to compare with.
# otherSelection - A {Selection} to compare against.
compare: (otherSelection) ->
@getBufferRange().compare(otherSelection.getBufferRange())
# Private:
screenRangeChanged: ->
screenRange = @getScreenRange()
@emit 'screen-range-changed', screenRange

View File

@ -1,18 +1,13 @@
_ = require 'underscore-plus'
spacePen = require 'space-pen'
{Subscriber} = require 'emissary'
ConfigObserver = require './config-observer'
ConfigObserver.includeInto(spacePen.View)
Subscriber.includeInto(spacePen.View)
jQuery = spacePen.jQuery
originalCleanData = jQuery.cleanData
jQuery.cleanData = (elements) ->
for element in elements
if view = jQuery(element).view()
view.unobserveConfig()
view.unsubscribe()
jQuery(element).view()?.unsubscribe() for element in elements
originalCleanData(elements)
tooltipDefaults =

View File

@ -8,14 +8,13 @@ Token = require './token'
# Public: Syntax class holding the grammars used for tokenizing.
#
# An instance of this class is always available as the `atom.syntax` global.
#
# 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)
atom.deserializers.add(this)
@deserialize: ({grammarOverridesByPath}) ->
@ -62,8 +61,8 @@ class Syntax extends GrammarRegistry
# console.log(comment) # '# '
# ```
#
# * scope: An {Array} of {String} scopes.
# * keyPath: A {String} key path.
# scope - An {Array} of {String} scopes.
# keyPath - A {String} key path.
#
# Returns a {String} property value or undefined.
getProperty: (scope, keyPath) ->

View File

@ -24,18 +24,16 @@ class Task
# Public: A helper method to easily launch and run a task once.
#
# * taskPath:
# The path to the Coffeescript/Javascript file which exports a single
# function to execute.
# * args:
# The Array of arguments to pass to the exported function.
# taskPath - The {String} path to the CoffeeScript/JavaScript file which
# exports a single {Function} to execute.
# args - The arguments to pass to the exported function.
@once: (taskPath, args...) ->
task = new Task(taskPath)
task.once 'task:completed', -> task.terminate()
task.start(args...)
task
# Public: Called upon task completion.
# Called upon task completion.
#
# It receives the same arguments that were passed to the task.
#
@ -45,9 +43,8 @@ class Task
# Public: Creates a task.
#
# * taskPath:
# The path to the Coffeescript/Javascript file that exports a single
# function to execute.
# taskPath - The {String} path to the CoffeeScript/JavaScript file that
# exports a single {Function} to execute.
constructor: (taskPath) ->
coffeeCacheRequire = "require('#{require.resolve('./coffee-cache')}').register();"
coffeeScriptRequire = "require('#{require.resolve('coffee-script')}').register();"
@ -73,7 +70,7 @@ class Task
@handleEvents()
# Private: Routes messages from the child to the appropriate event.
# Routes messages from the child to the appropriate event.
handleEvents: ->
@childProcess.removeAllListeners()
@childProcess.on 'message', ({event, args}) =>
@ -81,21 +78,21 @@ class Task
# Public: Starts the task.
#
# * args:
# The Array of arguments to pass to the function exported by the script. If
# the last argument is a function, its removed from the array and called
# upon completion (and replaces the complete function on the task instance).
start: (args...) ->
# args - The arguments to pass to the function exported by this task's script.
# callback - An optional {Function} to call when the task completes.
start: (args..., callback) ->
throw new Error("Cannot start terminated process") unless @childProcess?
@handleEvents()
@callback = args.pop() if _.isFunction(args[args.length - 1])
if _.isFunction(callback)
@callback = callback
else
args = arguments
@send({event: 'start', args})
# Public: Send message to the task.
#
# * message:
# The message to send
# message - The message to send to the task.
send: (message) ->
throw new Error("Cannot send message to terminated process") unless @childProcess?
@childProcess.send(message)

View File

@ -8,7 +8,7 @@ TextBufferCore = require 'text-buffer'
File = require './file'
# Private: Represents the contents of a file.
# Represents the contents of a file.
#
# The `TextBuffer` is often associated with a {File}. However, this is not always
# the case, as a `TextBuffer` could be an unsaved chunk of text.
@ -40,7 +40,6 @@ class TextBuffer extends TextBufferCore
@load() if loadWhenAttached
# Private:
serializeParams: ->
params = super
_.extend params,
@ -48,7 +47,6 @@ class TextBuffer extends TextBufferCore
modifiedWhenLastPersisted: @isModified()
digestWhenLastPersisted: @file?.getDigest()
# Private:
deserializeParams: (params) ->
params = super(params)
params.loadWhenAttached = true
@ -71,8 +69,6 @@ class TextBuffer extends TextBufferCore
@clearUndoStack()
this
### Internal ###
handleTextChange: (event) =>
@conflict = false if @conflict and !@isModified()
@scheduleModifiedEvents()
@ -127,8 +123,6 @@ class TextBuffer extends TextBufferCore
@file.on "moved", =>
@emit "path-changed", this
### Public ###
# Identifies if the buffer belongs to multiple editors.
#
# For example, if the {EditorView} was split.
@ -145,11 +139,11 @@ class TextBuffer extends TextBufferCore
@emitModifiedStatusChanged(false)
@emit 'reloaded'
# Private: Rereads the contents of the file, and stores them in the cache.
# Rereads the contents of the file, and stores them in the cache.
updateCachedDiskContentsSync: ->
@cachedDiskContents = @file?.readSync() ? ""
# Private: Rereads the contents of the file, and stores them in the cache.
# Rereads the contents of the file, and stores them in the cache.
updateCachedDiskContents: ->
Q(@file?.read() ? "").then (contents) =>
@cachedDiskContents = contents
@ -390,8 +384,6 @@ class TextBuffer extends TextBufferCore
return match[0][0] != '\t'
undefined
### Internal ###
change: (oldRange, newText, options={}) ->
@setTextInRange(oldRange, newText, options.normalizeLineEndings)

View File

@ -2,9 +2,7 @@ Package = require './package'
path = require 'path'
_ = require 'underscore-plus'
fs = require 'fs-plus'
async = require 'async'
### Internal ###
Q = require 'q'
module.exports =
class TextMatePackage extends Package
@ -12,13 +10,12 @@ class TextMatePackage extends Package
packageName = path.basename(packageName)
/(^language-.+)|((\.|_|-)tmbundle$)/.test(packageName)
@getLoadQueue: ->
return @loadQueue if @loadQueue
@loadQueue = async.queue (pack, done) ->
pack.loadGrammars ->
pack.loadScopedProperties(done)
@loadQueue
@addToActivationPromise = (pack) ->
@activationPromise ?= Q()
@activationPromise = @activationPromise.then =>
pack.loadGrammars()
.then -> pack.loadScopedProperties()
.fail (error) -> console.log pack.name, error
constructor: ->
super
@ -28,21 +25,16 @@ class TextMatePackage extends Package
getType: -> 'textmate'
load: ({sync}={}) ->
load: ->
@measure 'loadTime', =>
@metadata = Package.loadMetadata(@path, true)
if sync
@loadGrammarsSync()
@loadScopedPropertiesSync()
else
TextMatePackage.getLoadQueue().push(this)
activate: ({sync, immediate}={})->
TextMatePackage.addToActivationPromise(this)
activate: ->
@measure 'activateTime', =>
grammar.activate() for grammar in @grammars
for { selector, properties } in @scopedProperties
atom.syntax.addProperties(@path, selector, properties)
activateSync: ->
@loadGrammarsSync()
@loadScopedPropertiesSync()
activateConfig: -> # noop
@ -52,33 +44,35 @@ class TextMatePackage extends Package
legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage', 'json', 'cson']
loadGrammars: (done) ->
loadGrammars: ->
deferred = Q.defer()
fs.isDirectory @getSyntaxesPath(), (isDirectory) =>
if isDirectory
fs.list @getSyntaxesPath(), @legalGrammarExtensions, (error, paths) =>
if error?
console.log("Error loading grammars of TextMate package '#{@path}':", error.stack, error)
done()
else
async.eachSeries(paths, @loadGrammarAtPath, done)
else
done()
return deferred.resolve() unless isDirectory
loadGrammarAtPath: (grammarPath, done) =>
fs.list @getSyntaxesPath(), @legalGrammarExtensions, (error, paths) =>
if error?
console.log("Error loading grammars of TextMate package '#{@path}':", error.stack, error)
deferred.resolve()
else
promises = paths.map (path) => @loadGrammarAtPath(path)
Q.all(promises).then -> deferred.resolve()
deferred.promise
loadGrammarAtPath: (grammarPath) ->
deferred = Q.defer()
atom.syntax.readGrammar grammarPath, (error, grammar) =>
if error?
console.log("Error loading grammar at path '#{grammarPath}':", error.stack ? error)
else
@addGrammar(grammar)
done?()
deferred.resolve()
loadGrammarsSync: ->
for grammarPath in fs.listSync(@getSyntaxesPath(), @legalGrammarExtensions)
@addGrammar(atom.syntax.readGrammarSync(grammarPath))
deferred.promise
addGrammar: (grammar) ->
@grammars.push(grammar)
grammar.activate() if @isActive()
grammar.activate()
getGrammars: -> @grammars
@ -96,23 +90,7 @@ class TextMatePackage extends Package
else
path.join(@path, "Preferences")
loadScopedPropertiesSync: ->
for grammar in @getGrammars()
if properties = @propertiesFromTextMateSettings(grammar)
selector = atom.syntax.cssSelectorFromScopeSelector(grammar.scopeName)
@scopedProperties.push({selector, properties})
for preferencePath in fs.listSync(@getPreferencesPath())
{scope, settings} = fs.readObjectSync(preferencePath)
if properties = @propertiesFromTextMateSettings(settings)
selector = atom.syntax.cssSelectorFromScopeSelector(scope) if scope?
@scopedProperties.push({selector, properties})
if @isActive()
for {selector, properties} in @scopedProperties
atom.syntax.addProperties(@path, selector, properties)
loadScopedProperties: (callback) ->
loadScopedProperties: ->
scopedProperties = []
for grammar in @getGrammars()
@ -120,38 +98,37 @@ class TextMatePackage extends Package
selector = atom.syntax.cssSelectorFromScopeSelector(grammar.scopeName)
scopedProperties.push({selector, properties})
preferenceObjects = []
done = =>
@loadTextMatePreferenceObjects().then (preferenceObjects=[]) =>
for {scope, settings} in preferenceObjects
if properties = @propertiesFromTextMateSettings(settings)
selector = atom.syntax.cssSelectorFromScopeSelector(scope) if scope?
scopedProperties.push({selector, properties})
@scopedProperties = scopedProperties
if @isActive()
for {selector, properties} in @scopedProperties
atom.syntax.addProperties(@path, selector, properties)
callback?()
@loadTextMatePreferenceObjects(preferenceObjects, done)
for {selector, properties} in @scopedProperties
atom.syntax.addProperties(@path, selector, properties)
loadTextMatePreferenceObjects: (preferenceObjects, done) ->
loadTextMatePreferenceObjects: ->
deferred = Q.defer()
fs.isDirectory @getPreferencesPath(), (isDirectory) =>
return done() unless isDirectory
return deferred.resolve() unless isDirectory
fs.list @getPreferencesPath(), (error, paths) =>
if error?
console.log("Error loading preferences of TextMate package '#{@path}':", error.stack, error)
done()
return
deferred.resolve()
else
promises = paths.map (path) => @loadPreferencesAtPath(path)
Q.all(promises).then (preferenceObjects) -> deferred.resolve(preferenceObjects)
loadPreferencesAtPath = (preferencePath, done) ->
fs.readObject preferencePath, (error, preferences) =>
if error?
console.warn("Failed to parse preference at path '#{preferencePath}'", error.stack, error)
else
preferenceObjects.push(preferences)
done()
async.eachSeries paths, loadPreferencesAtPath, done
deferred.promise
loadPreferencesAtPath: (preferencePath) ->
deferred = Q.defer()
fs.readObject preferencePath, (error, preference) ->
if error?
console.warn("Failed to parse preference at path '#{preferencePath}'", error.stack, error)
deferred.resolve(preference)
deferred.promise
propertiesFromTextMateSettings: (textMateSettings) ->
if textMateSettings.shellVariables
@ -169,3 +146,24 @@ class TextMatePackage extends Package
completions: textMateSettings.completions
)
{ editor: editorProperties } if _.size(editorProperties) > 0
# Deprecated
loadGrammarsSync: ->
for grammarPath in fs.listSync(@getSyntaxesPath(), @legalGrammarExtensions)
@addGrammar(atom.syntax.readGrammarSync(grammarPath))
# Deprecated
loadScopedPropertiesSync: ->
for grammar in @getGrammars()
if properties = @propertiesFromTextMateSettings(grammar)
selector = atom.syntax.cssSelectorFromScopeSelector(grammar.scopeName)
@scopedProperties.push({selector, properties})
for preferencePath in fs.listSync(@getPreferencesPath())
{scope, settings} = fs.readObjectSync(preferencePath)
if properties = @propertiesFromTextMateSettings(settings)
selector = atom.syntax.cssSelectorFromScopeSelector(scope) if scope?
@scopedProperties.push({selector, properties})
for {selector, properties} in @scopedProperties
atom.syntax.addProperties(@path, selector, properties)

View File

@ -3,6 +3,7 @@ path = require 'path'
_ = require 'underscore-plus'
{Emitter} = require 'emissary'
fs = require 'fs-plus'
Q = require 'q'
{$} = require './space-pen-extensions'
AtomPackage = require './atom-package'
@ -10,7 +11,7 @@ File = require './file'
# Public: Handles loading and activating available themes.
#
# A ThemeManager instance is always available under the `atom.themes` global.
# An instance of this class is always available as the `atom.themes` global.
module.exports =
class ThemeManager
Emitter.includeInto(this)
@ -41,7 +42,7 @@ class ThemeManager
activatePackages: (themePackages) -> @activateThemes()
# Private: Get the enabled theme names from the config.
# Get the enabled theme names from the config.
#
# Returns an array of theme names in the order that they should be activated.
getEnabledThemeNames: ->
@ -53,19 +54,22 @@ class ThemeManager
themeNames.reverse()
activateThemes: ->
deferred = Q.defer()
# atom.config.observe runs the callback once, then on subsequent changes.
atom.config.observe 'core.themes', =>
@deactivateThemes()
@refreshLessCache() # Update cache for packages in core.themes config
for themeName in @getEnabledThemeNames()
@packageManager.activatePackage(themeName)
promises = @getEnabledThemeNames().map (themeName) => @packageManager.activatePackage(themeName)
Q.all(promises).then =>
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@emit('reloaded')
deferred.resolve()
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@emit('reloaded')
deferred.promise
deactivateThemes: ->
@unwatchUserStylesheet()
@ -77,7 +81,7 @@ class ThemeManager
# Public: Set the list of enabled themes.
#
# * enabledThemeNames: An {Array} of {String} theme names.
# enabledThemeNames - An {Array} of {String} theme names.
setEnabledThemes: (enabledThemeNames) ->
atom.config.set('core.themes', enabledThemeNames)
@ -91,7 +95,7 @@ class ThemeManager
if themePath = @packageManager.resolvePackagePath(themeName)
themePaths.push(path.join(themePath, AtomPackage.stylesheetsDir))
themePath for themePath in themePaths when fs.isDirectorySync(themePath)
themePaths.filter (themePath) -> fs.isDirectorySync(themePath)
# Public: Returns the {String} path to the user's stylesheet under ~/.atom
getUserStylesheetPath: ->
@ -140,8 +144,9 @@ class ThemeManager
#
# This supports both CSS and LESS stylsheets.
#
# * 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.
# 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) ->

View File

@ -1,11 +1,8 @@
Q = require 'q'
AtomPackage = require './atom-package'
Package = require './package'
### Internal: Loads and resolves packages. ###
module.exports =
class ThemePackage extends AtomPackage
getType: -> 'theme'
getStylesheetType: -> 'theme'
@ -25,6 +22,11 @@ class ThemePackage extends AtomPackage
this
activate: ->
return @activationDeferred.promise if @activationDeferred?
@activationDeferred = Q.defer()
@measure 'activateTime', =>
@loadStylesheets()
@activateNow()
@activationDeferred.promise

View File

@ -12,7 +12,7 @@ WhitespaceRegex = /\S/
MaxTokenLength = 20000
# Private: Represents a single unit of text as selected by a grammar.
# Represents a single unit of text as selected by a grammar.
module.exports =
class Token
value: null
@ -21,15 +21,11 @@ class Token
isAtomic: null
isHardTab: null
### Internal ###
constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @isHardTab}) ->
@screenDelta = @value.length
@bufferDelta ?= @screenDelta
@hasSurrogatePair = textUtils.hasSurrogatePair(@value)
### Public ###
isEqual: (other) ->
@value == other.value and _.isEqual(@scopes, other.scopes) and !!@isAtomic == !!other.isAtomic

View File

@ -5,8 +5,6 @@ Serializable = require 'serializable'
TokenizedLine = require './tokenized-line'
Token = require './token'
### Internal ###
module.exports =
class TokenizedBuffer extends Model
Serializable.includeInto(this)

View File

@ -1,7 +1,5 @@
_ = require 'underscore-plus'
### Internal ###
module.exports =
class TokenizedLine
constructor: ({tokens, @lineEnding, @ruleStack, @startBufferColumn, @fold, tabLength}) ->

View File

@ -5,7 +5,7 @@ shell = require 'shell'
{Subscriber} = require 'emissary'
fs = require 'fs-plus'
# Private: Handles low-level events related to the window.
# Handles low-level events related to the window.
module.exports =
class WindowEventHandler
Subscriber.includeInto(this)
@ -75,7 +75,7 @@ class WindowEventHandler
@handleNativeKeybindings()
# Private: Wire commands that should be handled by the native menu
# Wire commands that should be handled by the native menu
# for elements with the `.native-key-bindings` class.
handleNativeKeybindings: ->
menu = null

View File

@ -16,6 +16,9 @@ Editor = require './editor'
# Public: The container for the entire Atom application.
#
# An instance of this class is always available as the `atom.workspaceView`
# global.
#
# ## Commands
#
# * `application:about` - Opens the about dialog.
@ -63,14 +66,12 @@ class WorkspaceView extends View
audioBeep: true
destroyEmptyPanes: false
# Private:
@content: ->
@div class: 'workspace', tabindex: -1, =>
@div class: 'horizontal', outlet: 'horizontal', =>
@div class: 'vertical', outlet: 'vertical', =>
@div class: 'panes', outlet: 'panes'
# Private:
initialize: (@model) ->
@model ?= new Workspace
@ -106,6 +107,7 @@ class WorkspaceView extends View
@command 'application:zoom', -> ipc.sendChannel('command', 'application:zoom')
@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-init-script', -> ipc.sendChannel('command', 'application:open-your-init-script')
@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')
@ -118,6 +120,10 @@ class WorkspaceView extends View
@command 'window:focus-next-pane', => @focusNextPane()
@command 'window:focus-previous-pane', => @focusPreviousPane()
@command 'window:focus-pane-above', => @focusPaneAbove()
@command 'window:focus-pane-below', => @focusPaneBelow()
@command 'window:focus-pane-on-left', => @focusPaneOnLeft()
@command 'window:focus-pane-on-right', => @focusPaneOnRight()
@command 'window:save-all', => @saveAll()
@command 'window:toggle-invisibles', =>
atom.config.toggle("editor.showInvisibles")
@ -151,7 +157,6 @@ class WorkspaceView extends View
message: "Commands installed."
detailedMessage: "The shell commands `atom` and `apm` are installed."
# Private:
handleFocus: (e) ->
if @getActivePane()
@getActivePane().focus()
@ -166,7 +171,6 @@ class WorkspaceView extends View
$(document.body).focus()
true
# Private:
afterAttach: (onDom) ->
@focus() if onDom
@ -188,7 +192,7 @@ class WorkspaceView extends View
setTitle: (title) ->
document.title = title
# Private: Returns an Array of all of the application's {EditorView}s.
# Returns an Array of all of the application's {EditorView}s.
getEditorViews: ->
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
@ -246,6 +250,18 @@ class WorkspaceView extends View
# Public: Focuses the next pane by id.
focusNextPane: -> @model.activateNextPane()
# Public: Focuses the pane directly above the active pane.
focusPaneAbove: -> @panes.focusPaneAbove()
# Public: Focuses the pane directly below the active pane.
focusPaneBelow: -> @panes.focusPaneBelow()
# Public: Focuses the pane directly to the left of the active pane.
focusPaneOnLeft: -> @panes.focusPaneOnLeft()
# Public: Focuses the pane directly to the right of the active pane.
focusPaneOnRight: -> @panes.focusPaneOnRight()
# Public:
#
# FIXME: Difference between active and focused pane?
@ -270,11 +286,11 @@ class WorkspaceView extends View
@on('editor:attached', attachedCallback)
off: => @off('editor:attached', attachedCallback)
# Private: Called by SpacePen
# Called by SpacePen
beforeRemove: ->
@model.destroy()
# Private: Destroys everything.
# Destroys everything.
remove: ->
editorView.remove() for editorView in @getEditorViews()
super

View File

@ -7,8 +7,9 @@ PaneContainer = require './pane-container'
Pane = require './pane'
# Public: Represents the view state of the entire window, including the panes at
# the center and panels around the periphery. You can access the singleton
# instance via `atom.workspace`.
# the center and panels around the periphery.
#
# An instance of this class is always available as the `atom.workspace` global.
module.exports =
class Workspace extends Model
atom.deserializers.add(this)
@ -23,7 +24,6 @@ class Workspace extends Model
fullScreen: false
destroyedItemUris: -> []
# Private:
constructor: ->
super
@subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed
@ -35,28 +35,30 @@ class Workspace extends Model
@open(atom.keymap.getUserKeymapPath())
when 'atom://.atom/config'
@open(atom.config.getUserConfigPath())
when 'atom://.atom/init-script'
@open(atom.getUserInitScriptPath())
# Private: Called by the Serializable mixin during deserialization
# Called by the Serializable mixin during deserialization
deserializeParams: (params) ->
params.paneContainer = PaneContainer.deserialize(params.paneContainer)
params
# Private: Called by the Serializable mixin during serialization.
# Called by the Serializable mixin during serialization.
serializeParams: ->
paneContainer: @paneContainer.serialize()
fullScreen: atom.isFullScreen()
# Public: Asynchronously opens a given a filepath in Atom.
#
# * filePath: A file path
# * options
# + initialLine: The buffer line number to open to.
# + split: Takes 'left' or 'right'. Opens in existing right or left pane if
# one exists, otherwise creates a new pane.
# + changeFocus: Boolean that allows a filePath to be opened without
# changing focus.
# + searchAllPanes: Boolean that will open existing editors from any pane
# if the filePath is already open (defaults to false)
# filePath - A {String} file path.
# options - An options {Object} (default: {}).
# :initialLine - A {Number} indicating which line number to open to.
# :split - A {String} ('left' or 'right') that opens the filePath in a new
# pane or an existing one if it exists.
# :changeFocus - A {Boolean} that allows the filePath to be opened without
# changing focus.
# :searchAllPanes - A {Boolean} that will open existing editors from any pane
# if the filePath is already open (default: false)
#
# Returns a promise that resolves to the {Editor} for the file URI.
open: (filePath, options={}) ->
@ -92,7 +94,7 @@ class Workspace extends Model
.catch (error) ->
console.error(error.stack ? error)
# Private: Only used in specs
# Only used in specs
openSync: (uri, options={}) ->
{initialLine} = options
# TODO: Remove deprecated changeFocus option
@ -160,16 +162,16 @@ class Workspace extends Model
fontSize = atom.config.get("editor.fontSize")
atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1
# Private: Removes the item's uri from the list of potential items to reopen.
# Removes the item's uri from the list of potential items to reopen.
itemOpened: (item) ->
if uri = item.getUri?()
remove(@destroyedItemUris, uri)
# Private: Adds the destroyed item's uri to the list of items to reopen.
# Adds the destroyed item's uri to the list of items to reopen.
onPaneItemDestroyed: (item) =>
if uri = item.getUri?()
@destroyedItemUris.push(uri)
# Private: Called by Model superclass when destroyed
# Called by Model superclass when destroyed
destroyed: ->
@paneContainer.destroy()

2
vendor/apm vendored

@ -1 +1 @@
Subproject commit aaba79564fbb58237cbec788f4499f6742d61a97
Subproject commit ce140e66283c07f5837dd999e75580f3757a249a