mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Merge branch 'master' into ns-fix-softwrap
Conflicts: src/row-map.coffee
This commit is contained in:
@@ -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) ->
|
||||
```
|
||||
|
||||
@@ -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",
|
||||
@@ -25,15 +25,13 @@
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
"harmony-collections": "~0.3.8",
|
||||
"js-yaml": "~2.1.0",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"rcedit": "~0.1.2",
|
||||
"request": "~2.27.0",
|
||||
"rimraf": "~2.2.2",
|
||||
"runas": "~0.3.0",
|
||||
"runas": "0.5.x",
|
||||
"underscore-plus": "1.x",
|
||||
"unzip": "~0.1.9",
|
||||
"vm-compatibility-layer": "~0.1.0",
|
||||
"walkdir": "0.0.7"
|
||||
"vm-compatibility-layer": "~0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{ScopeSelector} = require 'first-mate'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'convert-theme', 'Convert a TextMate theme to an Atom theme', ->
|
||||
if textMateThemePath = grunt.option('path')
|
||||
textMateThemePath = path.resolve(textMateThemePath)
|
||||
if grunt.file.isFile(textMateThemePath)
|
||||
textMateTheme = new TextMateTheme(textMateThemePath)
|
||||
themeName = path.basename(textMateThemePath, path.extname(textMateThemePath))
|
||||
atomThemePath = path.join(path.dirname(textMateThemePath), "#{themeName.toLowerCase()}-syntax.css")
|
||||
grunt.file.write(atomThemePath, textMateTheme.getStylesheet())
|
||||
grunt.log.ok("Atom theme written to: #{atomThemePath}")
|
||||
else
|
||||
grunt.log.error("No theme file found at: #{textMateThemePath}")
|
||||
false
|
||||
else
|
||||
grunt.log.error('Must specify --path=<path to TextMate theme>')
|
||||
false
|
||||
|
||||
class TextMateTheme
|
||||
constructor: (@path) ->
|
||||
@rulesets = []
|
||||
@buildRulesets()
|
||||
|
||||
buildRulesets: ->
|
||||
{settings} = fs.readPlistSync(@path)
|
||||
@buildGlobalSettingsRulesets(settings[0])
|
||||
@buildScopeSelectorRulesets(settings[1..])
|
||||
|
||||
getStylesheet: ->
|
||||
lines = []
|
||||
for {selector, properties} in @getRulesets()
|
||||
lines.push("#{selector} {")
|
||||
lines.push " #{name}: #{value};" for name, value of properties
|
||||
lines.push("}\n")
|
||||
lines.join('\n')
|
||||
|
||||
getRulesets: -> @rulesets
|
||||
|
||||
buildGlobalSettingsRulesets: ({settings}) ->
|
||||
{ background, foreground, caret, selection, lineHighlight } = settings
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor, .editor .gutter'
|
||||
properties:
|
||||
'background-color': @translateColor(background)
|
||||
'color': @translateColor(foreground)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .cursor'
|
||||
properties:
|
||||
'border-color': @translateColor(caret)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .selection .region'
|
||||
properties:
|
||||
'background-color': @translateColor(selection)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .line-number.cursor-line-no-selection, .editor.is-focused .line.cursor-line'
|
||||
properties:
|
||||
'background-color': @translateColor(lineHighlight)
|
||||
|
||||
buildScopeSelectorRulesets: (scopeSelectorSettings) ->
|
||||
for { name, scope, settings } in scopeSelectorSettings
|
||||
continue unless scope
|
||||
@rulesets.push
|
||||
comment: name
|
||||
selector: @translateScopeSelector(scope)
|
||||
properties: @translateScopeSelectorSettings(settings)
|
||||
|
||||
translateScopeSelector: (textmateScopeSelector) ->
|
||||
new ScopeSelector(textmateScopeSelector).toCssSelector()
|
||||
|
||||
translateScopeSelectorSettings: ({ foreground, background, fontStyle }) ->
|
||||
properties = {}
|
||||
|
||||
if fontStyle
|
||||
fontStyles = fontStyle.split(/\s+/)
|
||||
properties['font-weight'] = 'bold' if _.contains(fontStyles, 'bold')
|
||||
properties['font-style'] = 'italic' if _.contains(fontStyles, 'italic')
|
||||
properties['text-decoration'] = 'underline' if _.contains(fontStyles, 'underline')
|
||||
|
||||
properties['color'] = @translateColor(foreground) if foreground
|
||||
properties['background-color'] = @translateColor(background) if background
|
||||
properties
|
||||
|
||||
translateColor: (textmateColor) ->
|
||||
if textmateColor.length <= 7
|
||||
textmateColor
|
||||
else
|
||||
r = parseInt(textmateColor[1..2], 16)
|
||||
g = parseInt(textmateColor[3..4], 16)
|
||||
b = parseInt(textmateColor[5..6], 16)
|
||||
a = parseInt(textmateColor[7..8], 16)
|
||||
a = Math.round((a / 255.0) * 100) / 100
|
||||
|
||||
"rgba(#{r}, #{g}, #{b}, #{a})"
|
||||
@@ -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)
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
path = require 'path'
|
||||
runas = null
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, mkdir, rm, spawn} = require('./task-helpers')(grunt)
|
||||
{cp, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'install', 'Install the built application', ->
|
||||
installDir = grunt.config.get('atom.installDir')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
if process.platform is 'win32'
|
||||
done = @async()
|
||||
|
||||
runas = require 'runas'
|
||||
runas ?= require 'runas'
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
# cmd /c ""script" "source" "destination""
|
||||
arg = "/c \"\"#{copyFolder}\" \"#{shellAppDir}\" \"#{installDir}\"\""
|
||||
if runas('cmd', [arg], hide: true) isnt 0
|
||||
done("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: true) isnt 0
|
||||
grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
|
||||
createShortcut = path.resolve 'script', 'create-shortcut.cmd'
|
||||
args = ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom']
|
||||
spawn {cmd: 'cmd', args}, done
|
||||
runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom'])
|
||||
else
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
|
||||
@@ -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}.")
|
||||
|
||||
|
||||
22
build/tasks/update-octicons-task.coffee
Normal file
22
build/tasks/update-octicons-task.coffee
Normal file
@@ -0,0 +1,22 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'update-octicons', 'Update octicon font and LESS variables', ->
|
||||
pathToOcticons = path.resolve('..', 'octicons')
|
||||
if grunt.file.isDir(pathToOcticons)
|
||||
# Copy font-file
|
||||
fontSrc = path.join(pathToOcticons, 'octicons', 'octicons.woff')
|
||||
fontDest = path.resolve('static', 'octicons.woff')
|
||||
grunt.file.copy(fontSrc, fontDest)
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
output = []
|
||||
for {css, code} in grunt.file.readYAML(glyphsSrc)
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
||||
octiconUtfDest = path.resolve('static', 'variables', 'octicon-utf-codes.less')
|
||||
grunt.file.write(octiconUtfDest, "#{output.join('\n')}\n")
|
||||
else
|
||||
grunt.log.error("octicons repo must be cloned to #{pathToOcticons}")
|
||||
false
|
||||
@@ -71,27 +71,3 @@ will only attempt to call deserialize if the two versions match, and otherwise
|
||||
return undefined. We plan on implementing a migration system in the future, but
|
||||
this at least protects you from improperly deserializing old state. If you find
|
||||
yourself in dire need of the migration system, let us know.
|
||||
|
||||
### Deferred Package Deserializers
|
||||
|
||||
If your package defers loading on startup with an `activationEvents` property in
|
||||
its `package.cson`, your deserializers won't be loaded until your package is
|
||||
activated. If you want to deserialize an object from your package on startup,
|
||||
this could be a problem.
|
||||
|
||||
The solution is to also supply a `deferredDeserializers` array in your
|
||||
`package.cson` with the names of all your deserializers. When Atom attempts to
|
||||
deserialize some state whose `deserializer` matches one of these names, it will
|
||||
load your package first so it can register any necessary deserializers before
|
||||
proceeding.
|
||||
|
||||
For example, the markdown preview package doesn't fully load until a preview is
|
||||
triggered. But if you refresh a window with a preview pane, it loads the
|
||||
markdown package early so Atom can deserialize the view correctly.
|
||||
|
||||
```coffee-script
|
||||
# markdown-preview/package.cson
|
||||
'activationEvents': 'markdown-preview:toggle': '.editor'
|
||||
'deferredDeserializers': ['MarkdownPreviewView']
|
||||
...
|
||||
```
|
||||
|
||||
52
docs/converting-a-text-mate-bundle.md
Normal file
52
docs/converting-a-text-mate-bundle.md
Normal file
@@ -0,0 +1,52 @@
|
||||
## 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!
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [Publishing a Package](publish-a-package.html) for more information
|
||||
on publishing the package you just created 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
|
||||
68
docs/converting-a-text-mate-theme.md
Normal file
68
docs/converting-a-text-mate-theme.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## 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!
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [Publishing a Package](publish-a-package.html) for more information
|
||||
on publishing the theme you just created 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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
97
docs/publishing-a-package.md
Normal file
97
docs/publishing-a-package.md
Normal file
@@ -0,0 +1,97 @@
|
||||
## Publishing a Package
|
||||
|
||||
This guide will show you how to publish a package or theme to the
|
||||
[atom.io][atomio] package registry.
|
||||
|
||||
Publishing a package allows other people to install it and use it in Atom. It
|
||||
is a great way to share what you've made and get feedback and contributions from
|
||||
others.
|
||||
|
||||
This guide assumes your package's name is `my-package` and but you should pick a
|
||||
better name.
|
||||
|
||||
### Install apm
|
||||
|
||||
The `apm` command line utility that ships with Atom supports publishing packages
|
||||
to the atom.io registry.
|
||||
|
||||
Check that you have `apm` installed by running the following command in your
|
||||
terminal:
|
||||
|
||||
```sh
|
||||
apm help publish
|
||||
```
|
||||
|
||||
You should see a message print out with details about the `apm publish` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
### Prepare Your Package
|
||||
|
||||
If you've followed the steps in the [your first package][your-first-package]
|
||||
doc then you should be ready to publish and you can skip to the next step.
|
||||
|
||||
If not, there are a few things you should check before publishing:
|
||||
|
||||
* Your *package.json* file has `name`, `description`, and `repository` fields.
|
||||
* Your *package.json* file has a `version` field with a value of `"0.0.0"`.
|
||||
* Your *package.json* file has an `engines` field that contains an entry
|
||||
for Atom such as: `"engines": {"atom": ">=0.50.0"}`.
|
||||
* Your package has a `README.md` file at the root.
|
||||
* Your package is in a Git repository that has been pushed to
|
||||
[GitHub][github]. Follow [this guide][repo-guide] if your package isn't
|
||||
already on GitHub.
|
||||
|
||||
### Publish Your Package
|
||||
|
||||
Before you publish a package it is a good idea to check ahead of time if
|
||||
a package with the same name has already been published to atom.io. You can do
|
||||
that by visiting `http://atom.io/packages/my-package` to see if the package
|
||||
already exists. If it does, update your package's name to something that is
|
||||
available before proceeding.
|
||||
|
||||
Now let's review what the `apm publish` command does:
|
||||
|
||||
1. Registers the package name on atom.io if it is being published for the
|
||||
first time.
|
||||
2. Updates the `version` field in the *package.json* file and commits it.
|
||||
3. Creates a new [Git tag][git-tag] for the version being published.
|
||||
4. Pushes the tag and current branch up to GitHub.
|
||||
5. Updates atom.io with the new version being published.
|
||||
|
||||
Now run the following commands to publish your package:
|
||||
|
||||
```sh
|
||||
cd ~/github/my-package
|
||||
apm publish minor
|
||||
```
|
||||
|
||||
If this is the first package you are publishing, the `apm publish` command may
|
||||
prompt you for your GitHub username and password. This is required to publish
|
||||
and you only need to enter this information the first time you publish. The
|
||||
credentials are stored securely in your [keychain][keychain] once you login.
|
||||
|
||||
:tada: Your package is now published and available on atom.io. Head on over to
|
||||
`http://atom.io/packages/my-package` to see your package's page.
|
||||
|
||||
The `minor` option to the publish command tells apm to increment the second
|
||||
digit of the version before publishing so the published version will be `0.1.0`
|
||||
and the Git tag created will be `v0.1.0`.
|
||||
|
||||
In the future you can run `apm publish major` to publish the `1.0.0` version but
|
||||
since this was the first version being published it is a good idead to start
|
||||
with a minor release.
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [semantic versioning][semver] to learn more about versioning your
|
||||
package releases.
|
||||
|
||||
[atomio]: https://atom.io
|
||||
[github]: https://github.com
|
||||
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
|
||||
[keychain]: http://en.wikipedia.org/wiki/Keychain_(Apple)
|
||||
[repo-guide]: http://guides.github.com/overviews/desktop
|
||||
[semver]: http://semver.org
|
||||
[your-first-package]: your-first-package.html
|
||||
@@ -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,17 +26,17 @@ 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 =
|
||||
activate: ->
|
||||
module.exports =
|
||||
activate: ->
|
||||
```
|
||||
|
||||
## Create a Command
|
||||
@@ -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,25 +107,25 @@ 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:
|
||||
|
||||
```json
|
||||
"dependencies": {
|
||||
"figlet": "1.0.8"
|
||||
}
|
||||
"dependencies": {
|
||||
"figlet": "1.0.8"
|
||||
}
|
||||
```
|
||||
|
||||
After saving the file run the command 'update-package-dependencies:update' from
|
||||
the Command Palette. This will install the packages node module dependencies,
|
||||
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,7 +141,15 @@ 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
|
||||
Package](creating-a-package.html)
|
||||
* [Getting your project on GitHub guide](http://guides.github.com/overviews/desktop)
|
||||
|
||||
* [Creating a package guide](creating-a-package.html) for more information
|
||||
on the mechanics of packages
|
||||
|
||||
* [Publishing a package guide](publish-a-package.html) for more information
|
||||
on publishing your package to [atom.io](https://atom.io)
|
||||
|
||||
14
dot-atom/init.coffee
Normal file
14
dot-atom/init.coffee
Normal 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)
|
||||
@@ -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'
|
||||
#
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# For more on how to configure atom open `~/github/atom/docs/configuring-and-extending.md`
|
||||
@@ -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'
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
'cmd-_': 'window:decrease-font-size'
|
||||
'cmd-0': 'window:reset-font-size'
|
||||
|
||||
'cmd-k up': 'pane:split-up' # Atom Specific
|
||||
'cmd-k down': 'pane:split-down' # Atom Specific
|
||||
@@ -75,8 +77,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'
|
||||
@@ -118,7 +124,6 @@
|
||||
'alt-cmd-z': 'editor:checkout-head-revision'
|
||||
'cmd-<': 'editor:scroll-to-cursor'
|
||||
'alt-cmd-ctrl-f': 'editor:fold-selection'
|
||||
'cmd-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-enter': 'editor:newline-below'
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
'ctrl-=': 'window:increase-font-size'
|
||||
'ctrl-+': 'window:increase-font-size'
|
||||
'ctrl--': 'window:decrease-font-size'
|
||||
'ctrl-_': 'window:decrease-font-size'
|
||||
'ctrl-0': 'window:reset-font-size'
|
||||
|
||||
'ctrl-k up': 'pane:split-up' # Atom Specific
|
||||
'ctrl-k down': 'pane:split-down' # Atom Specific
|
||||
@@ -47,8 +49,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
|
||||
@@ -65,7 +71,6 @@
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
'ctrl-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-enter': 'editor:newline-below'
|
||||
|
||||
@@ -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', visible: false}
|
||||
{ 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' }
|
||||
|
||||
165
package.json
165
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.49.0",
|
||||
"version": "0.51.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,7 +16,7 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.8.7",
|
||||
"atomShellVersion": "0.9.2",
|
||||
"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,102 +42,103 @@
|
||||
"pathwatcher": "0.14.2",
|
||||
"pegjs": "0.8.0",
|
||||
"property-accessors": "1.x",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.13.0",
|
||||
"q": "1.0.x",
|
||||
"runas": "0.5.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"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.12.0",
|
||||
"atom-dark-ui": "0.21.0",
|
||||
"atom-light-syntax": "0.12.0",
|
||||
"atom-light-ui": "0.20.0",
|
||||
"base16-tomorrow-dark-theme": "0.10.0",
|
||||
"solarized-dark-syntax": "0.8.0",
|
||||
"solarized-light-syntax": "0.4.0",
|
||||
"archive-view": "0.21.0",
|
||||
"autocomplete": "0.21.0",
|
||||
"autoflow": "0.12.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",
|
||||
"atom-dark-syntax": "0.13.0",
|
||||
"atom-dark-ui": "0.22.0",
|
||||
"atom-light-syntax": "0.13.0",
|
||||
"atom-light-ui": "0.21.0",
|
||||
"base16-tomorrow-dark-theme": "0.11.0",
|
||||
"solarized-dark-syntax": "0.9.0",
|
||||
"solarized-light-syntax": "0.5.0",
|
||||
"archive-view": "0.22.0",
|
||||
"autocomplete": "0.22.0",
|
||||
"autoflow": "0.14.0",
|
||||
"autosave": "0.11.0",
|
||||
"background-tips": "0.7.0",
|
||||
"bookmarks": "0.19.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",
|
||||
"find-and-replace": "0.81.0",
|
||||
"fuzzy-finder": "0.32.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.23.0",
|
||||
"github-sign-in": "0.18.0",
|
||||
"feedback": "0.23.0",
|
||||
"find-and-replace": "0.83.0",
|
||||
"fuzzy-finder": "0.34.0",
|
||||
"gists": "0.17.0",
|
||||
"git-diff": "0.24.0",
|
||||
"github-sign-in": "0.19.0",
|
||||
"go-to-line": "0.16.0",
|
||||
"grammar-selector": "0.18.0",
|
||||
"image-view": "0.17.0",
|
||||
"keybinding-resolver": "0.9.0",
|
||||
"link": "0.15.0",
|
||||
"markdown-preview": "0.25.1",
|
||||
"metrics": "0.24.0",
|
||||
"package-generator": "0.25.0",
|
||||
"release-notes": "0.17.0",
|
||||
"settings-view": "0.63.0",
|
||||
"snippets": "0.24.0",
|
||||
"spell-check": "0.21.0",
|
||||
"grammar-selector": "0.19.0",
|
||||
"image-view": "0.23.0",
|
||||
"keybinding-resolver": "0.10.0",
|
||||
"link": "0.17.0",
|
||||
"markdown-preview": "0.29.0",
|
||||
"metrics": "0.26.0",
|
||||
"package-generator": "0.26.0",
|
||||
"release-notes": "0.20.0",
|
||||
"settings-view": "0.72.0",
|
||||
"snippets": "0.27.0",
|
||||
"spell-check": "0.24.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.18.0",
|
||||
"tree-view": "0.65.0",
|
||||
"update-package-dependencies": "0.2.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"to-the-hubs": "0.19.0",
|
||||
"tree-view": "0.69.0",
|
||||
"update-package-dependencies": "0.3.0",
|
||||
"visual-bell": "0.7.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"whitespace": "0.12.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.16.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-toml": "0.7.0",
|
||||
"language-xml": "0.2.0",
|
||||
"language-yaml": "0.1.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.8.0",
|
||||
"language-xml": "0.3.0",
|
||||
"language-yaml": "0.2.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 361 KiB |
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
usage = """
|
||||
Usage:
|
||||
update-octicons PATH-TO-OCTICONS
|
||||
"""
|
||||
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
YAML = require 'js-yaml'
|
||||
|
||||
scriptPath = process.argv[1]
|
||||
pathToOcticons = process.argv[2] ? path.join(process.env.HOME, 'github', 'octicons')
|
||||
atomDir = path.resolve(scriptPath, "../../..")
|
||||
|
||||
unless fs.existsSync(pathToOcticons)
|
||||
console.error(usage)
|
||||
process.exit(1)
|
||||
|
||||
# Copy font-file
|
||||
fontSrc = path.join(pathToOcticons, 'octicons', 'octicons.woff')
|
||||
fontDest = path.join(atomDir, 'static', 'octicons.woff')
|
||||
fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
octiconUtfDest = path.join atomDir, 'static', 'variables', 'octicon-utf-codes.less'
|
||||
output = []
|
||||
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
||||
fs.writeFileSync octiconUtfDest, "#{output.join('\n')}\n"
|
||||
@@ -1,35 +1,49 @@
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{convertStackTrace} = require 'coffeestack'
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
sourceMaps = {}
|
||||
formatStackTrace = (stackTrace) ->
|
||||
formatStackTrace = (message='', 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*$/
|
||||
firstJasmineLinePattern = /^\s*at \/.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
|
||||
convertedLines = []
|
||||
for line in stackTrace.split('\n')
|
||||
convertedLines.push(line) unless jasminePattern.test(line)
|
||||
break if firstJasmineLinePattern.test(line)
|
||||
|
||||
convertStackTrace(convertedLines.join('\n'), sourceMaps)
|
||||
stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps)
|
||||
lines = stackTrace.split('\n')
|
||||
|
||||
# Remove first line of stack when it is the same as the error message
|
||||
errorMatch = lines[0]?.match(/^Error: (.*)/)
|
||||
lines.shift() if message.trim() is errorMatch?[1]?.trim()
|
||||
|
||||
# Remove prefix of lines matching: at [object Object].<anonymous> (path:1:2)
|
||||
for line, index in lines
|
||||
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^\)]+)\)/)
|
||||
lines[index] = "at #{prefixMatch[1]}" if prefixMatch
|
||||
|
||||
lines = lines.map (line) -> line.trim()
|
||||
lines.join('\n')
|
||||
|
||||
module.exports =
|
||||
class AtomReporter extends View
|
||||
@content: ->
|
||||
@div id: 'HTMLReporter', class: 'jasmine_reporter', =>
|
||||
@div outlet: 'specPopup', class: "spec-popup"
|
||||
@div class: 'spec-reporter', =>
|
||||
@div outlet: "suites"
|
||||
@div outlet: 'coreArea', =>
|
||||
@div outlet: 'coreHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'bundledArea', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'bundledSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'userArea', =>
|
||||
@div outlet: 'userHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'userSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: "status", class: 'status', =>
|
||||
@div outlet: 'coreArea', class: 'symbol-area', =>
|
||||
@div outlet: 'coreHeader', class: 'symbol-header'
|
||||
@ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'bundledArea', class: 'symbol-area', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbol-header'
|
||||
@ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'userArea', class: 'symbol-area', =>
|
||||
@div outlet: 'userHeader', class: 'symbol-header'
|
||||
@ul outlet: 'userSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: "status", class: 'status alert alert-info', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@div outlet: "message", class: 'message'
|
||||
@@ -46,7 +60,7 @@ class AtomReporter extends View
|
||||
|
||||
reportRunnerStarting: (runner) ->
|
||||
@handleEvents()
|
||||
@startedAt = new Date()
|
||||
@startedAt = Date.now()
|
||||
specs = runner.specs()
|
||||
@totalSpecCount = specs.length
|
||||
@addSpecs(specs)
|
||||
@@ -54,57 +68,29 @@ class AtomReporter extends View
|
||||
|
||||
reportRunnerResults: (runner) ->
|
||||
@updateSpecCounts()
|
||||
if @failedCount == 0
|
||||
@message.text "Success!"
|
||||
@status.addClass('alert-success').removeClass('alert-info') if @failedCount is 0
|
||||
if @failedCount is 1
|
||||
@message.text "#{@failedCount} failure"
|
||||
else
|
||||
@message.text "Game Over"
|
||||
@message.text "#{@failedCount} failures"
|
||||
|
||||
reportSuiteResults: (suite) ->
|
||||
|
||||
reportSpecResults: (spec) ->
|
||||
@completeSpecCount++
|
||||
spec.endedAt = new Date().getTime()
|
||||
spec.endedAt = Date.now()
|
||||
@specComplete(spec)
|
||||
@updateStatusView(spec)
|
||||
|
||||
reportSpecStarting: (spec) ->
|
||||
@specStarted(spec)
|
||||
|
||||
specFilter: (spec) ->
|
||||
globalFocusPriority = jasmine.getEnv().focusPriority
|
||||
parent = spec.parentSuite ? spec.suite
|
||||
|
||||
if !globalFocusPriority
|
||||
true
|
||||
else if spec.focusPriority >= globalFocusPriority
|
||||
true
|
||||
else if not parent
|
||||
false
|
||||
else
|
||||
@specFilter(parent)
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "mouseover", ".spec-summary", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
description = element.data("description")
|
||||
return unless description
|
||||
|
||||
clearTimeout @timeoutId if @timeoutId?
|
||||
@specPopup.show()
|
||||
spec = _.find(window.timedSpecs, ({fullName}) -> description is fullName)
|
||||
description = "#{description} #{spec.time}ms" if spec
|
||||
@specPopup.text description
|
||||
{left, top} = element.offset()
|
||||
left += 20
|
||||
top += 20
|
||||
@specPopup.offset({left, top})
|
||||
@timeoutId = setTimeout((=> @specPopup.hide()), 3000)
|
||||
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
specFailures = element.parent().find('.spec-failures')
|
||||
specFailures.toggle()
|
||||
if specFailures.is(":visible") then element.text "\uf03d" else element.html "\uf03f"
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
|
||||
updateSpecCounts: ->
|
||||
@@ -116,7 +102,7 @@ class AtomReporter extends View
|
||||
|
||||
updateStatusView: (spec) ->
|
||||
if @failedCount > 0
|
||||
@status.addClass('failed') unless @status.hasClass('failed')
|
||||
@status.addClass('alert-danger').removeClass('alert-info')
|
||||
|
||||
@updateSpecCounts()
|
||||
|
||||
@@ -124,7 +110,7 @@ class AtomReporter extends View
|
||||
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
|
||||
@message.text rootSuite.description
|
||||
|
||||
time = "#{Math.round((spec.endedAt - @startedAt.getTime()) / 10)}"
|
||||
time = "#{Math.round((spec.endedAt - @startedAt) / 10)}"
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
@@ -146,15 +132,22 @@ class AtomReporter extends View
|
||||
@userSummary.append symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs}):")
|
||||
@coreHeader.text("Core Specs (#{coreSpecs})")
|
||||
else
|
||||
@coreArea.hide()
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs}):")
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})")
|
||||
else
|
||||
@bundledArea.hide()
|
||||
if userPackageSpecs > 0
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs}):")
|
||||
if coreSpecs is 0 and bundledPackageSpecs is 0
|
||||
# Package specs being run, show a more descriptive label
|
||||
{specDirectory} = specs[0]
|
||||
packageFolderName = path.basename(path.dirname(specDirectory))
|
||||
packageName = _.undasherize(_.uncamelcase(packageFolderName))
|
||||
@userHeader.text("#{packageName} Specs")
|
||||
else
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs})")
|
||||
else
|
||||
@userArea.hide()
|
||||
|
||||
@@ -164,7 +157,7 @@ class AtomReporter extends View
|
||||
specComplete: (spec) ->
|
||||
specSummaryElement = $("#spec-summary-#{spec.id}")
|
||||
specSummaryElement.removeClass('pending')
|
||||
specSummaryElement.data("description", spec.getFullName())
|
||||
specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter')
|
||||
|
||||
results = spec.results()
|
||||
if results.skipped
|
||||
@@ -185,11 +178,9 @@ class SuiteResultView extends View
|
||||
@div class: 'suite', =>
|
||||
@div outlet: 'description', class: 'description'
|
||||
|
||||
suite: null
|
||||
|
||||
initialize: (@suite) ->
|
||||
@attr('id', "suite-view-#{@suite.id}")
|
||||
@description.html @suite.description
|
||||
@description.text(@suite.description)
|
||||
|
||||
attach: ->
|
||||
(@parentSuiteView() or $('.results')).append this
|
||||
@@ -206,20 +197,22 @@ class SuiteResultView extends View
|
||||
class SpecResultView extends View
|
||||
@content: ->
|
||||
@div class: 'spec', =>
|
||||
@div "\uf03d", class: 'spec-toggle'
|
||||
@div class: 'spec-toggle'
|
||||
@div outlet: 'description', class: 'description'
|
||||
@div outlet: 'specFailures', class: 'spec-failures'
|
||||
spec: null
|
||||
|
||||
initialize: (@spec) ->
|
||||
@addClass("spec-view-#{@spec.id}")
|
||||
@description.html @spec.description
|
||||
|
||||
description = @spec.description
|
||||
description = "it #{description}" if description.indexOf('it ') isnt 0
|
||||
@description.text(description)
|
||||
|
||||
for result in @spec.results().getItems() when not result.passed()
|
||||
stackTrace = formatStackTrace(result.trace.stack)
|
||||
stackTrace = formatStackTrace(result.message, result.trace.stack)
|
||||
@specFailures.append $$ ->
|
||||
@div result.message, class: 'resultMessage fail'
|
||||
@div stackTrace, class: 'stackTrace' if stackTrace
|
||||
@div result.message, class: 'result-message fail'
|
||||
@pre stackTrace, class: 'stack-trace padded' if stackTrace
|
||||
|
||||
attach: ->
|
||||
@parentSuiteView().append this
|
||||
|
||||
@@ -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", ->
|
||||
@@ -9,35 +10,28 @@ describe "the `atom` global", ->
|
||||
|
||||
describe "package lifecycle methods", ->
|
||||
describe ".loadPackage(name)", ->
|
||||
describe "when the package has deferred deserializers", ->
|
||||
it "requires the package's main module if one of its deferred deserializers is referenced", ->
|
||||
pack = atom.packages.loadPackage('package-with-activation-events')
|
||||
spyOn(pack, 'activateStylesheets').andCallThrough()
|
||||
expect(pack.mainModule).toBeNull()
|
||||
object = atom.deserializers.deserialize({deserializer: 'Foo', data: 5})
|
||||
expect(pack.mainModule).toBeDefined()
|
||||
expect(object.constructor.name).toBe 'Foo'
|
||||
expect(object.data).toBe 5
|
||||
expect(pack.activateStylesheets).toHaveBeenCalled()
|
||||
it "continues if the package has an invalid package.json", ->
|
||||
spyOn(console, 'warn')
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
|
||||
it "continues if the package has an invalid package.json", ->
|
||||
spyOn(console, 'warn')
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
|
||||
it "continues if the package has an invalid keymap", ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
it "continues if the package has an invalid keymap", ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
|
||||
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", ->
|
||||
@@ -54,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()
|
||||
@@ -78,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()
|
||||
@@ -116,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", [])
|
||||
@@ -245,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$'
|
||||
@@ -275,80 +304,118 @@ 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')
|
||||
atom.packages.activatePackage('package-with-serialize-error', immediate: true)
|
||||
atom.packages.activatePackage('package-with-serialization', immediate: true)
|
||||
atom.packages.deactivatePackages()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialize-error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialization')
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackages()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
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", ->
|
||||
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"
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby')
|
||||
|
||||
runs ->
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
|
||||
it "removes the package's scoped properties", ->
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby')
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
|
||||
describe ".activate()", ->
|
||||
packageActivator = null
|
||||
@@ -382,7 +449,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'
|
||||
@@ -391,28 +458,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()
|
||||
@@ -426,18 +501,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", ->
|
||||
|
||||
12
spec/clipboard-spec.coffee
Normal file
12
spec/clipboard-spec.coffee
Normal file
@@ -0,0 +1,12 @@
|
||||
describe "Clipboard", ->
|
||||
describe "write(text, metadata) and read()", ->
|
||||
it "writes and reads text to/from the native clipboard", ->
|
||||
expect(atom.clipboard.read()).toBe 'initial clipboard content'
|
||||
atom.clipboard.write('next')
|
||||
expect(atom.clipboard.read()).toBe 'next'
|
||||
|
||||
it "returns metadata if the item on the native clipboard matches the last written item", ->
|
||||
atom.clipboard.write('next', {meta: 'data'})
|
||||
expect(atom.clipboard.read()).toBe 'next'
|
||||
expect(atom.clipboard.readWithMetadata().text).toBe 'next'
|
||||
expect(atom.clipboard.readWithMetadata().metadata).toEqual {meta: 'data'}
|
||||
@@ -20,7 +20,7 @@ describe "install(commandPath, callback)", ->
|
||||
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.install commandFilePath, (error) ->
|
||||
installer.install commandFilePath, false, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
|
||||
|
||||
@@ -63,6 +63,19 @@ describe "Config", ->
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
describe ".restoreDefault(keyPath)", ->
|
||||
it "sets the value of the key path to its default", ->
|
||||
atom.config.setDefaults('a', b: 3)
|
||||
atom.config.set('a.b', 4)
|
||||
expect(atom.config.get('a.b')).toBe 4
|
||||
atom.config.restoreDefault('a.b')
|
||||
expect(atom.config.get('a.b')).toBe 3
|
||||
|
||||
atom.config.set('a.c', 5)
|
||||
expect(atom.config.get('a.c')).toBe 5
|
||||
atom.config.restoreDefault('a.c')
|
||||
expect(atom.config.get('a.c')).toBeUndefined()
|
||||
|
||||
describe ".pushAtKeyPath(keyPath, value)", ->
|
||||
it "pushes the given value to the array at the key path and updates observers", ->
|
||||
atom.config.set("foo.bar.baz", ["a"])
|
||||
@@ -220,6 +233,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 ->
|
||||
|
||||
@@ -5,12 +5,15 @@ describe "DisplayBuffer", ->
|
||||
[displayBuffer, buffer, changeHandler, tabLength] = []
|
||||
beforeEach ->
|
||||
tabLength = 2
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
afterEach ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
|
||||
@@ -16,11 +16,13 @@ describe "Editor", ->
|
||||
|
||||
describe "with default options", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
buffer = editor.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
describe "when the editor is deserialized", ->
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
@@ -1856,12 +1858,12 @@ describe "Editor", ->
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, editor.getTabLength() * 2]
|
||||
|
||||
describe "pasteboard operations", ->
|
||||
describe "clipboard operations", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
|
||||
|
||||
describe ".cutSelectedText()", ->
|
||||
it "removes the selected text from the buffer and places it on the pasteboard", ->
|
||||
it "removes the selected text from the buffer and places it on the clipboard", ->
|
||||
editor.cutSelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe "var = function () {"
|
||||
expect(buffer.lineForRow(1)).toBe " var = function(items) {"
|
||||
@@ -1885,7 +1887,7 @@ describe "Editor", ->
|
||||
editor.cutToEndOfLine()
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.pasteboard.read()[0]).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the line", ->
|
||||
@@ -1895,7 +1897,7 @@ describe "Editor", ->
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.pasteboard.read()[0]).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
@@ -1906,7 +1908,7 @@ describe "Editor", ->
|
||||
|
||||
describe ".pasteText()", ->
|
||||
it "pastes text into the buffer", ->
|
||||
atom.pasteboard.write('first')
|
||||
atom.clipboard.write('first')
|
||||
editor.pasteText()
|
||||
expect(editor.buffer.lineForRow(0)).toBe "var first = function () {"
|
||||
expect(buffer.lineForRow(1)).toBe " var first = function(items) {"
|
||||
@@ -2733,19 +2735,26 @@ describe "Editor", ->
|
||||
|
||||
describe "when the editor's grammar has an injection selector", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-hyperlink')
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
runs ->
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
@@ -2756,11 +2765,13 @@ describe "Editor", ->
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-hyperlink')
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
runs ->
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
@@ -2771,14 +2782,17 @@ describe "Editor", ->
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('package-with-injection-selector', sync: true)
|
||||
|
||||
atom.packages.activatePackage('package-with-injection-selector')
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-sql')
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
runs ->
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
@@ -10,8 +10,6 @@ describe "EditorView", ->
|
||||
[buffer, editorView, editor, cachedLineHeight, cachedCharWidth] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js')
|
||||
buffer = editor.buffer
|
||||
editorView = new EditorView(editor)
|
||||
@@ -26,6 +24,12 @@ describe "EditorView", ->
|
||||
@width(getCharWidth() * widthInChars) if widthInChars
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
|
||||
getLineHeight = ->
|
||||
return cachedLineHeight if cachedLineHeight?
|
||||
calcDimensions()
|
||||
@@ -2515,9 +2519,9 @@ describe "EditorView", ->
|
||||
expect(edited).toBe false
|
||||
|
||||
describe "when editor:copy-path is triggered", ->
|
||||
it "copies the absolute path to the editor view's file to the pasteboard", ->
|
||||
it "copies the absolute path to the editor view's file to the clipboard", ->
|
||||
editorView.trigger 'editor:copy-path'
|
||||
expect(atom.pasteboard.read()[0]).toBe editor.getPath()
|
||||
expect(atom.clipboard.read()).toBe editor.getPath()
|
||||
|
||||
describe "when editor:move-line-up is triggered", ->
|
||||
describe "when there is no selection", ->
|
||||
@@ -2532,6 +2536,52 @@ describe "EditorView", ->
|
||||
editorView.trigger 'editor:move-line-up'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0,2]
|
||||
|
||||
describe "when the line above is folded", ->
|
||||
it "moves the line around the fold", ->
|
||||
editor.foldBufferRow(1)
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editorView.trigger 'editor:move-line-up'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||
expect(buffer.lineForRow(1)).toBe ''
|
||||
expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {'
|
||||
expect(editor.isFoldedAtBufferRow(1)).toBe false
|
||||
expect(editor.isFoldedAtBufferRow(2)).toBe true
|
||||
|
||||
describe "when the line being moved is folded", ->
|
||||
it "moves the fold around the fold above it", ->
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.insertText """
|
||||
var a = function() {
|
||||
b = 3;
|
||||
};
|
||||
|
||||
"""
|
||||
editor.foldBufferRow(0)
|
||||
editor.foldBufferRow(3)
|
||||
editor.setCursorBufferPosition([3, 0])
|
||||
editorView.trigger 'editor:move-line-up'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(buffer.lineForRow(13)).toBe 'var a = function() {'
|
||||
editor.logScreenLines()
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe true
|
||||
expect(editor.isFoldedAtBufferRow(13)).toBe true
|
||||
|
||||
describe "when the line above is empty and the line above that is folded", ->
|
||||
it "moves the line to the empty line", ->
|
||||
editor.foldBufferRow(2)
|
||||
editor.setCursorBufferPosition([11, 0])
|
||||
editorView.trigger 'editor:move-line-up'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
expect(buffer.lineForRow(9)).toBe ' };'
|
||||
expect(buffer.lineForRow(10)).toBe ' return sort(Array.apply(this, arguments));'
|
||||
expect(buffer.lineForRow(11)).toBe ''
|
||||
expect(editor.isFoldedAtBufferRow(2)).toBe true
|
||||
expect(editor.isFoldedAtBufferRow(10)).toBe false
|
||||
|
||||
describe "where there is a selection", ->
|
||||
describe "when the selection falls inside the line", ->
|
||||
it "maintains the selection", ->
|
||||
@@ -2631,6 +2681,54 @@ describe "EditorView", ->
|
||||
editorView.trigger 'editor:move-line-down'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 2]
|
||||
|
||||
describe "when the line below is folded", ->
|
||||
it "moves the line around the fold", ->
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.foldBufferRow(1)
|
||||
editorView.trigger 'editor:move-line-down'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [9, 0]
|
||||
expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {'
|
||||
expect(buffer.lineForRow(9)).toBe 'var quicksort = function () {'
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe true
|
||||
expect(editor.isFoldedAtBufferRow(9)).toBe false
|
||||
|
||||
describe "when the line being moved is folded", ->
|
||||
it "moves the fold around the fold below it", ->
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.insertText """
|
||||
var a = function() {
|
||||
b = 3;
|
||||
};
|
||||
|
||||
"""
|
||||
editor.foldBufferRow(0)
|
||||
editor.foldBufferRow(3)
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editorView.trigger 'editor:move-line-down'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [13, 0]
|
||||
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(buffer.lineForRow(13)).toBe 'var a = function() {'
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe true
|
||||
expect(editor.isFoldedAtBufferRow(13)).toBe true
|
||||
|
||||
describe "when the line below is empty and the line below that is folded", ->
|
||||
it "moves the line to the empty line", ->
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
editor.insertText('\n')
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.foldBufferRow(2)
|
||||
editorView.trigger 'editor:move-line-down'
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||
expect(buffer.lineForRow(0)).toBe ''
|
||||
expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {'
|
||||
expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {'
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe false
|
||||
expect(editor.isFoldedAtBufferRow(1)).toBe false
|
||||
expect(editor.isFoldedAtBufferRow(2)).toBe true
|
||||
|
||||
describe "when the cursor is on the last line", ->
|
||||
it "does not move the line", ->
|
||||
editor.moveCursorToBottom()
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
'activationEvents': ['activation-event']
|
||||
'deferredDeserializers': ['Foo']
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
# This package loads async, otherwise it would log errors when it
|
||||
# is automatically serialized when workspaceView is deactivatated
|
||||
|
||||
'main': 'index.coffee'
|
||||
'activationEvents': ['activation-event']
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"theme": true
|
||||
"theme": "ui"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"theme": true
|
||||
"theme": "ui"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"theme": true
|
||||
"theme": "ui"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"theme": true,
|
||||
"theme": "ui",
|
||||
"stylesheets": ["first.css", "second.less", "last.css"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"theme": true,
|
||||
"theme": "ui",
|
||||
"stylesheets": ["editor.less"]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,10 +6,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
|
||||
it "returns the minimum indent level for the given row range", ->
|
||||
expect(languageMode.minIndentLevelForRowRange(4, 7)).toBe 2
|
||||
@@ -100,10 +102,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
editor = atom.project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 6)
|
||||
@@ -147,10 +151,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-css')
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
@@ -188,11 +194,15 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-less', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-less')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-css')
|
||||
|
||||
describe "when commenting lines", ->
|
||||
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
@@ -200,10 +210,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editor.setCursorBufferPosition([5,5])
|
||||
languageMode.foldAll()
|
||||
@@ -298,10 +310,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editor.getScreenLineCount()
|
||||
@@ -362,10 +376,12 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-source', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('css.css', autoIndent: true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-source')
|
||||
atom.packages.activatePackage('language-css')
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "does not return negative values (regression)", ->
|
||||
editor.setText('.test {\npadding: 0;\n}')
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
describe "Pasteboard", ->
|
||||
describe "write(text, metadata) and read()", ->
|
||||
it "writes and reads text to/from the native pasteboard", ->
|
||||
expect(atom.pasteboard.read()).toEqual ['initial pasteboard content']
|
||||
atom.pasteboard.write('next')
|
||||
expect(atom.pasteboard.read()[0]).toBe 'next'
|
||||
|
||||
it "returns metadata if the item on the native pasteboard matches the last written item", ->
|
||||
atom.pasteboard.write('next', {meta: 'data'})
|
||||
expect(atom.pasteboard.read()).toEqual ['next', {meta: 'data'}]
|
||||
@@ -72,7 +72,7 @@ describe "Project", ->
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
[absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
@@ -80,129 +80,92 @@ describe "Project", ->
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
describe "when given an absolute path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
afterEach ->
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe ".open(path)", ->
|
||||
[absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
describe "when given an absolute path that isn't currently open", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
expect(atom.project.openSync(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(atom.project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
|
||||
afterEach ->
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that isn't currently open", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
waitsForPromise ->
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
atom.project.open(pathToOpen, hey: "there").then (item) ->
|
||||
expect(item).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = atom.project.resolve 'a'
|
||||
totalBytes = 0
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ beforeEach ->
|
||||
TokenizedBuffer.prototype.chunkSize = Infinity
|
||||
spyOn(TokenizedBuffer.prototype, "tokenizeInBackground").andCallFake -> @tokenizeNextChunk()
|
||||
|
||||
pasteboardContent = 'initial pasteboard content'
|
||||
spyOn(clipboard, 'writeText').andCallFake (text) -> pasteboardContent = text
|
||||
spyOn(clipboard, 'readText').andCallFake -> pasteboardContent
|
||||
clipboardContent = 'initial clipboard content'
|
||||
spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text
|
||||
spyOn(clipboard, 'readText').andCallFake -> clipboardContent
|
||||
|
||||
addCustomMatchers(this)
|
||||
|
||||
@@ -159,6 +159,16 @@ addCustomMatchers = (spec) ->
|
||||
@message = -> return "Expected path '" + @actual + "'" + notText + " to exist."
|
||||
fs.existsSync(@actual)
|
||||
|
||||
toHaveFocus: ->
|
||||
notText = this.isNot and " not" or ""
|
||||
if not document.hasFocus()
|
||||
console.error "Specs will fail because the Dev Tools have focus. To fix this close the Dev Tools or click the spec runner."
|
||||
|
||||
@message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus."
|
||||
element = @actual
|
||||
element = element.get(0) if element.jquery
|
||||
element.webkitMatchesSelector(":focus") or element.querySelector(":focus")
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
|
||||
@@ -4,10 +4,18 @@ temp = require 'temp'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby')
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers grammar overrides by path", ->
|
||||
@@ -20,29 +28,33 @@ describe "the `syntax` global", ->
|
||||
|
||||
describe ".selectGrammar(filePath)", ->
|
||||
it "can use the filePath to load the correct grammar based on the grammar's filetype", ->
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-git')
|
||||
|
||||
expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
runs ->
|
||||
expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
|
||||
it "uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
expect(atom.syntax.selectGrammar(filePath).name).toBe "Ruby"
|
||||
|
||||
it "uses the number of newlines in the first line regex to determine the number of lines to test against", ->
|
||||
atom.packages.activatePackage('language-property-list', sync: true)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-property-list')
|
||||
|
||||
fileContent = "first-line\n<html>"
|
||||
expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
runs ->
|
||||
fileContent = "first-line\n<html>"
|
||||
expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
|
||||
fileContent = '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
fileContent = '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
|
||||
fileContent += '\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
fileContent += '\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
|
||||
it "doesn't read the file when the file contents are specified", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
|
||||
@@ -571,6 +571,23 @@ describe 'TextBuffer', ->
|
||||
saveBuffer.reload()
|
||||
expect(events).toEqual ['will-reload', 'reloaded']
|
||||
|
||||
it "no longer reports being in conflict", ->
|
||||
saveBuffer.setText('a')
|
||||
saveBuffer.save()
|
||||
saveBuffer.setText('ab')
|
||||
|
||||
fs.writeFileSync(saveBuffer.getPath(), 'c')
|
||||
conflictHandler = jasmine.createSpy('conflictHandler')
|
||||
saveBuffer.on 'contents-conflicted', conflictHandler
|
||||
|
||||
waitsFor ->
|
||||
conflictHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(saveBuffer.isInConflict()).toBe true
|
||||
saveBuffer.save()
|
||||
expect(saveBuffer.isInConflict()).toBe false
|
||||
|
||||
describe "when the buffer has no path", ->
|
||||
it "throws an exception", ->
|
||||
saveBuffer = atom.project.bufferForPathSync(null)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,11 +5,13 @@ describe "TokenizedBuffer", ->
|
||||
[tokenizedBuffer, buffer, changeHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
# enable async tokenization
|
||||
TokenizedBuffer.prototype.chunkSize = 5
|
||||
jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
startTokenizing = (tokenizedBuffer) ->
|
||||
tokenizedBuffer.setVisible(true)
|
||||
|
||||
@@ -311,10 +313,13 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains hard-tabs", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
@@ -341,14 +346,17 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains surrogate pairs", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync 'sample-with-pairs.js'
|
||||
buffer.setText """
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync 'sample-with-pairs.js'
|
||||
buffer.setText """
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
@@ -379,22 +387,30 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the grammar is updated because a grammar it includes is activated", ->
|
||||
it "retokenizes the buffer", ->
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby-on-rails')
|
||||
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby')
|
||||
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-html')
|
||||
|
||||
runs ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
|
||||
describe ".tokenForPosition(position)", ->
|
||||
afterEach ->
|
||||
|
||||
@@ -7,49 +7,141 @@ describe "Workspace", ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
workspace = new Workspace
|
||||
|
||||
describe "::open(uri)", ->
|
||||
describe "::open(uri, options)", ->
|
||||
beforeEach ->
|
||||
spyOn(workspace.activePane, 'activate')
|
||||
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
spyOn(workspace.activePane, 'activate').andCallThrough()
|
||||
|
||||
describe "when the 'searchAllPanes' option is false (default)", ->
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
workspace.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.getUri()).toBe 'a'
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.getUri()).toBe 'a'
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'searchAllPanes' option is true", ->
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
it "activates the existing editor on the inactive pane, then activates that pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
pane1 = workspace.activePane
|
||||
pane2 = workspace.activePane.splitRight()
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true)
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when no editor for the given uri is open in any pane", ->
|
||||
it "opens an editor for the given uri in the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
|
||||
describe "when the 'split' option is set", ->
|
||||
describe "when the 'split' option is 'left'", ->
|
||||
it "opens the editor in the leftmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
expect(workspace.activePane).toBe pane2
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
# Focus right pane and reopen the file on the left
|
||||
waitsForPromise ->
|
||||
pane2.focus()
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
it "opens the editor in the rightmost pane of the current pane axis", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane2 = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane2 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
# Focus right pane and reopen the file on the right
|
||||
waitsForPromise ->
|
||||
pane1.focus()
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
workspace.registerOpener(fooOpener)
|
||||
workspace.registerOpener(barOpener)
|
||||
|
||||
waitsForPromise ->
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
workspace.open(pathToOpen, hey: "there").then (item) ->
|
||||
expect(item).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe "::openSync(uri, options)", ->
|
||||
[activePane, initialItemCount] = []
|
||||
|
||||
@@ -92,61 +184,6 @@ describe "Workspace", ->
|
||||
workspace.openSync('b', activatePane: false)
|
||||
expect(activePane.activate).not.toHaveBeenCalled()
|
||||
|
||||
describe "::openSingletonSync(uri, options)", ->
|
||||
describe "when an editor for the given uri is already open on the active pane", ->
|
||||
it "activates the existing editor", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
workspace.openSingletonSync('a')
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
it "activates the existing editor on the inactive pane, then activates that pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
pane1 = workspace.activePane
|
||||
pane2 = workspace.activePane.splitRight()
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
workspace.openSingletonSync('a')
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when no editor for the given uri is open in any pane", ->
|
||||
it "opens an editor for the given uri in the active pane", ->
|
||||
editor1 = workspace.openSingletonSync('a')
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when the 'split' option is 'left'", ->
|
||||
it "opens the editor in the leftmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
expect(workspace.activePane).toBe pane2
|
||||
editor1 = workspace.openSingletonSync('a', split: 'left')
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor1]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
describe "when the active pane is in a horizontal pane axis", ->
|
||||
it "activates the editor on the rightmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
pane1.activate()
|
||||
editor1 = workspace.openSingletonSync('a', split: 'right')
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane1.items).toEqual []
|
||||
|
||||
describe "when the active pane is not in a horizontal pane axis", ->
|
||||
it "splits the current pane to the right, then activates the editor on the right pane", ->
|
||||
pane1 = workspace.activePane
|
||||
editor1 = workspace.openSingletonSync('a', split: 'right')
|
||||
pane2 = workspace.activePane
|
||||
expect(workspace.paneContainer.root.children).toEqual [pane1, pane2]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane1.items).toEqual []
|
||||
|
||||
describe "::reopenItemSync()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.activePane
|
||||
|
||||
@@ -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)
|
||||
@@ -42,11 +43,7 @@ class AtomPackage extends Package
|
||||
@loadStylesheets()
|
||||
@loadGrammars()
|
||||
@loadScopedProperties()
|
||||
|
||||
if @metadata.activationEvents?
|
||||
@registerDeferredDeserializers()
|
||||
else
|
||||
@requireMainModule()
|
||||
@requireMainModule() unless @metadata.activationEvents?
|
||||
|
||||
catch e
|
||||
console.warn "Failed to load package named '#{@name}'", e.stack ? e
|
||||
@@ -59,14 +56,19 @@ 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
|
||||
|
||||
activateNow: ->
|
||||
try
|
||||
@activateConfig()
|
||||
@@ -77,6 +79,8 @@ class AtomPackage extends Package
|
||||
catch e
|
||||
console.warn "Failed to activate package named '#{@name}'", e.stack
|
||||
|
||||
@activationDeferred.resolve()
|
||||
|
||||
activateConfig: ->
|
||||
return if @configActivated
|
||||
|
||||
@@ -162,6 +166,8 @@ class AtomPackage extends Package
|
||||
console.error "Error serializing package '#{@name}'", e.stack
|
||||
|
||||
deactivate: ->
|
||||
@activationDeferred?.reject()
|
||||
@activationDeferred = null
|
||||
@unsubscribeFromActivationEvents()
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
@@ -203,12 +209,6 @@ class AtomPackage extends Package
|
||||
path.join(@path, 'index')
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
|
||||
registerDeferredDeserializers: ->
|
||||
for deserializerName in @metadata.deferredDeserializers ? []
|
||||
atom.deserializers.addDeferred deserializerName, =>
|
||||
@activateStylesheets()
|
||||
@requireMainModule()
|
||||
|
||||
subscribeToActivationEvents: ->
|
||||
return unless @metadata.activationEvents?
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
@@ -226,6 +226,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)
|
||||
|
||||
173
src/atom.coffee
173
src/atom.coffee
@@ -6,8 +6,6 @@ path = require 'path'
|
||||
remote = require 'remote'
|
||||
screen = require 'screen'
|
||||
shell = require 'shell'
|
||||
dialog = remote.require 'dialog'
|
||||
app = remote.require 'app'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Model} = require 'theorist'
|
||||
@@ -22,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.pasteboard` - A {Pasteboard} 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.
|
||||
@@ -43,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)
|
||||
@@ -65,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
|
||||
@@ -82,41 +82,47 @@ 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: ->
|
||||
_.deepClone(@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings))
|
||||
@loadSettings ?= JSON.parse(decodeURIComponent(location.search.substr(14)))
|
||||
cloned = _.deepClone(@loadSettings)
|
||||
# The loadSettings.windowState could be large, request it only when needed.
|
||||
cloned.__defineGetter__ 'windowState', =>
|
||||
@getCurrentWindow().loadSettings.windowState
|
||||
cloned.__defineSetter__ 'windowState', (value) =>
|
||||
@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 ?= app.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
|
||||
@@ -134,7 +140,7 @@ class Atom extends Model
|
||||
Config = require './config'
|
||||
Keymap = require './keymap'
|
||||
PackageManager = require './package-manager'
|
||||
Pasteboard = require './pasteboard'
|
||||
Clipboard = require './clipboard'
|
||||
Syntax = require './syntax'
|
||||
ThemeManager = require './theme-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
@@ -148,7 +154,8 @@ class Atom extends Model
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@pasteboard = new Pasteboard()
|
||||
@clipboard = new Clipboard()
|
||||
|
||||
@syntax = @deserializers.deserialize(@state.syntax) ? new Syntax()
|
||||
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
@@ -167,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}")
|
||||
|
||||
@@ -196,15 +202,13 @@ class Atom extends Model
|
||||
# + width: The new width.
|
||||
# + height: The new height.
|
||||
setWindowDimensions: ({x, y, width, height}) ->
|
||||
browserWindow = @getCurrentWindow()
|
||||
if width? and height?
|
||||
browserWindow.setSize(width, height)
|
||||
@setSize(width, height)
|
||||
if x? and y?
|
||||
browserWindow.setPosition(x, y)
|
||||
@setPosition(x, y)
|
||||
else
|
||||
browserWindow.center()
|
||||
@center()
|
||||
|
||||
# Private:
|
||||
restoreWindowDimensions: ->
|
||||
workAreaSize = screen.getPrimaryDisplay().workAreaSize
|
||||
windowDimensions = @state.windowDimensions ? {}
|
||||
@@ -213,7 +217,6 @@ class Atom extends Model
|
||||
windowDimensions.width ?= initialSize?.width ? Math.min(workAreaSize.width, 1024)
|
||||
@setWindowDimensions(windowDimensions)
|
||||
|
||||
# Private:
|
||||
storeWindowDimensions: ->
|
||||
@state.windowDimensions = @getWindowDimensions()
|
||||
|
||||
@@ -223,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'
|
||||
@@ -236,24 +237,22 @@ 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
|
||||
CommandInstaller.installAtomCommand resourcePath, (error) ->
|
||||
CommandInstaller.installAtomCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
CommandInstaller.installApmCommand resourcePath, (error) ->
|
||||
CommandInstaller.installApmCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
@restoreWindowDimensions()
|
||||
@@ -276,7 +275,6 @@ class Atom extends Model
|
||||
|
||||
@displayWindow()
|
||||
|
||||
# Private:
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@@ -292,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
|
||||
@@ -309,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)
|
||||
@@ -341,6 +338,7 @@ class Atom extends Model
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
dialog = remote.require('dialog')
|
||||
chosen = dialog.showMessageBox @getCurrentWindow(),
|
||||
type: 'info'
|
||||
message: message
|
||||
@@ -353,42 +351,59 @@ class Atom extends Model
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
# Private:
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
|
||||
# Private:
|
||||
showSaveDialogSync: (defaultPath) ->
|
||||
defaultPath ?= @project?.getPath()
|
||||
currentWindow = @getCurrentWindow()
|
||||
dialog = remote.require('dialog')
|
||||
dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
@getCurrentWindow().openDevTools()
|
||||
ipc.sendChannel('call-window-method', 'openDevTools')
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
@getCurrentWindow().toggleDevTools()
|
||||
ipc.sendChannel('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
@getCurrentWindow().restart()
|
||||
ipc.sendChannel('call-window-method', 'restart')
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
@getCurrentWindow().focus()
|
||||
ipc.sendChannel('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
@getCurrentWindow().show()
|
||||
ipc.sendChannel('call-window-method', 'show')
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
@getCurrentWindow().hide()
|
||||
ipc.sendChannel('call-window-method', 'hide')
|
||||
|
||||
# Private: Schedule the window to be shown and focused on the next tick.
|
||||
# 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)
|
||||
|
||||
# Public: Move current window to the center of the screen.
|
||||
center: ->
|
||||
ipc.sendChannel('call-window-method', 'center')
|
||||
|
||||
# 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.
|
||||
@@ -402,8 +417,9 @@ class Atom extends Model
|
||||
close: ->
|
||||
@getCurrentWindow().close()
|
||||
|
||||
# Private:
|
||||
exit: (status) -> app.exit(status)
|
||||
exit: (status) ->
|
||||
app = remote.require('app')
|
||||
app.exit(status)
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@@ -419,7 +435,7 @@ class Atom extends Model
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
@getCurrentWindow().setFullScreen(fullScreen)
|
||||
ipc.sendChannel('call-window-method', 'setFullScreen', fullScreen)
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@@ -450,7 +466,6 @@ class Atom extends Model
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
# Private:
|
||||
saveSync: ->
|
||||
stateString = JSON.stringify(@state)
|
||||
if statePath = @constructor.getStatePath(@mode)
|
||||
@@ -468,11 +483,9 @@ class Atom extends Model
|
||||
getWindowLoadTime: ->
|
||||
@loadTime
|
||||
|
||||
# Private:
|
||||
crashMainProcess: ->
|
||||
remote.process.crash()
|
||||
|
||||
# Private:
|
||||
crashRenderProcess: ->
|
||||
process.crash()
|
||||
|
||||
@@ -481,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
|
||||
@@ -493,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
AtomWindow = require './atom-window'
|
||||
ApplicationMenu = require './application-menu'
|
||||
AtomProtocolHandler = require './atom-protocol-handler'
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
autoUpdater = require 'auto-updater'
|
||||
app = require 'app'
|
||||
@@ -21,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.
|
||||
@@ -72,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})
|
||||
@@ -97,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
|
||||
@@ -112,23 +113,60 @@ 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)
|
||||
|
||||
# Check for update after Atom has fully started and the menus are created
|
||||
setTimeout((-> autoUpdater.checkForUpdates()), 5000)
|
||||
|
||||
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)
|
||||
@@ -147,9 +185,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')
|
||||
@@ -170,12 +211,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?
|
||||
@@ -195,6 +230,14 @@ class AtomApplication
|
||||
ipc.on 'command', (processId, routingId, command) =>
|
||||
@emit(command)
|
||||
|
||||
ipc.on 'window-command', (processId, routingId, command, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
win.emit(command, args...)
|
||||
|
||||
ipc.on 'call-window-method', (processId, routingId, method, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
win[method](args...)
|
||||
|
||||
# Public: Executes the given command.
|
||||
#
|
||||
# If it isn't handled globally, delegate to the currently focused window.
|
||||
@@ -221,7 +264,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)
|
||||
@@ -309,7 +352,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
|
||||
@@ -341,7 +384,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:
|
||||
@@ -372,7 +415,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.
|
||||
#
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
ContextMenu = require './context-menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
url = require 'url'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
# Private:
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
@iconPath: path.resolve(__dirname, '..', '..', 'resources', 'atom.png')
|
||||
@@ -31,6 +32,7 @@ class AtomWindow
|
||||
|
||||
loadSettings = _.extend({}, settings)
|
||||
loadSettings.windowState ?= '{}'
|
||||
loadSettings.appVersion = app.getVersion()
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
if @constructor.includeShellLoadTime and not @isSpec
|
||||
@@ -43,7 +45,7 @@ class AtomWindow
|
||||
|
||||
@browserWindow.loadSettings = loadSettings
|
||||
@browserWindow.once 'window:loaded', => @loaded = true
|
||||
@browserWindow.loadUrl "file://#{@resourcePath}/static/index.html"
|
||||
@browserWindow.loadUrl @getUrl(loadSettings)
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
|
||||
@openPath(pathToOpen, initialLine)
|
||||
@@ -51,6 +53,18 @@ class AtomWindow
|
||||
setupNodePath: (resourcePath) ->
|
||||
process.env['NODE_PATH'] = path.resolve(resourcePath, 'exports')
|
||||
|
||||
getUrl: (loadSettingsObj) ->
|
||||
# Ignore the windowState when passing loadSettings via URL, since it could
|
||||
# be quite large.
|
||||
loadSettings = _.clone(loadSettingsObj)
|
||||
delete loadSettings['windowState']
|
||||
|
||||
url.format
|
||||
protocol: 'file'
|
||||
pathname: "#{@resourcePath}/static/index.html"
|
||||
slashes: true
|
||||
query: {loadSettings: JSON.stringify(loadSettings)}
|
||||
|
||||
getInitialPath: ->
|
||||
@browserWindow.loadSettings.initialPath
|
||||
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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..])
|
||||
|
||||
@@ -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()
|
||||
|
||||
49
src/clipboard.coffee
Normal file
49
src/clipboard.coffee
Normal file
@@ -0,0 +1,49 @@
|
||||
clipboard = require 'clipboard'
|
||||
crypto = require 'crypto'
|
||||
|
||||
# Public: Represents the clipboard used for copying and pasting in Atom.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.clipboard` global.
|
||||
module.exports =
|
||||
class Clipboard
|
||||
metadata: null
|
||||
signatureForMetadata: null
|
||||
|
||||
# Creates an `md5` hash of some text.
|
||||
#
|
||||
# text - A {String} to hash.
|
||||
#
|
||||
# Returns a hashed {String}.
|
||||
md5: (text) ->
|
||||
crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
|
||||
# Public: Write the given text to the clipboard.
|
||||
#
|
||||
# The metadata associated with the text is available by calling
|
||||
# {.readWithMetadata}.
|
||||
#
|
||||
# text - The {String} to store.
|
||||
# metadata - The additional info to associate with the text.
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
clipboard.writeText(text)
|
||||
|
||||
# Public: Read the text from the clipboard.
|
||||
#
|
||||
# Returns a {String}.
|
||||
read: ->
|
||||
clipboard.readText()
|
||||
|
||||
# Public: Read the text from the clipboard and return both the text and the
|
||||
# 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)
|
||||
{text, @metadata}
|
||||
else
|
||||
{text}
|
||||
@@ -3,52 +3,55 @@ _ = require 'underscore-plus'
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
mkdirp = require 'mkdirp'
|
||||
runas = require 'runas'
|
||||
|
||||
symlinkCommand = (sourcePath, destinationPath, callback) ->
|
||||
mkdirp path.dirname(destinationPath), (error) ->
|
||||
if error?
|
||||
fs.unlink destinationPath, (error) ->
|
||||
if error? and error?.code != 'ENOENT'
|
||||
callback(error)
|
||||
else
|
||||
fs.symlink sourcePath, destinationPath, (error) ->
|
||||
mkdirp path.dirname(destinationPath), (error) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
fs.chmod(destinationPath, 0o755, callback)
|
||||
fs.symlink sourcePath, destinationPath, (error) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
fs.chmod(destinationPath, '755', callback)
|
||||
|
||||
unlinkCommand = (destinationPath, callback) ->
|
||||
fs.unlink destinationPath, (error) ->
|
||||
if error? and error.code isnt 'ENOENT'
|
||||
callback(error)
|
||||
else
|
||||
callback()
|
||||
symlinkCommandWithPrivilegeSync = (sourcePath, destinationPath) ->
|
||||
if runas('/bin/rm', ['-f', destinationPath], admin: true) != 0
|
||||
throw new Error("Failed to remove '#{destinationPath}'")
|
||||
|
||||
if runas('/bin/mkdir', ['-p', path.dirname(destinationPath)], admin: true) != 0
|
||||
throw new Error("Failed to create directory '#{destinationPath}'")
|
||||
|
||||
if runas('/bin/ln', ['-s', sourcePath, destinationPath], admin: true) != 0
|
||||
throw new Error("Failed to symlink '#{sourcePath}' to '#{destinationPath}'")
|
||||
|
||||
module.exports =
|
||||
getInstallDirectory: ->
|
||||
"/usr/local/bin"
|
||||
|
||||
install: (commandPath, callback) ->
|
||||
install: (commandPath, askForPrivilege, callback) ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
commandName = path.basename(commandPath, path.extname(commandPath))
|
||||
directory = @getInstallDirectory()
|
||||
if fs.existsSync(directory)
|
||||
destinationPath = path.join(directory, commandName)
|
||||
unlinkCommand destinationPath, (error) =>
|
||||
if error?
|
||||
error = new Error "Could not remove file at #{destinationPath}." if error
|
||||
callback?(error)
|
||||
else
|
||||
symlinkCommand commandPath, destinationPath, (error) =>
|
||||
error = new Error "Failed to symlink #{commandPath} to #{destinationPath}." if error
|
||||
callback?(error)
|
||||
else
|
||||
error = new Error "Directory '#{directory} doesn't exist."
|
||||
destinationPath = path.join(@getInstallDirectory(), commandName)
|
||||
symlinkCommand commandPath, destinationPath, (error) =>
|
||||
if askForPrivilege and error?.code is 'EACCES'
|
||||
try
|
||||
error = null
|
||||
symlinkCommandWithPrivilegeSync(commandPath, destinationPath)
|
||||
catch error
|
||||
|
||||
callback?(error)
|
||||
|
||||
installAtomCommand: (resourcePath, callback) ->
|
||||
installAtomCommand: (resourcePath, askForPrivilege, callback) ->
|
||||
commandPath = path.join(resourcePath, 'atom.sh')
|
||||
@install commandPath, callback
|
||||
@install commandPath, askForPrivilege, callback
|
||||
|
||||
installApmCommand: (resourcePath, callback) ->
|
||||
installApmCommand: (resourcePath, askForPrivilege, callback) ->
|
||||
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
|
||||
@install commandPath, callback
|
||||
@install commandPath, askForPrivilege, callback
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -160,6 +150,14 @@ class Config
|
||||
toggle: (keyPath) ->
|
||||
@set(keyPath, !@get(keyPath))
|
||||
|
||||
# Public: Restore the key path to its default value.
|
||||
#
|
||||
# keyPath - The {String} name of the key.
|
||||
#
|
||||
# Returns the new value.
|
||||
restoreDefault: (keyPath) ->
|
||||
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
|
||||
|
||||
# Public: Push the value to the array at the key path.
|
||||
#
|
||||
# keyPath - The {String} key path.
|
||||
@@ -205,6 +203,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 +231,10 @@ class Config
|
||||
unobserve: (keyPath) ->
|
||||
@off("updated.#{keyPath.replace(/\./, '-')}")
|
||||
|
||||
# Private:
|
||||
update: ->
|
||||
return if @configFileHasErrors
|
||||
@save()
|
||||
@emit 'updated'
|
||||
|
||||
# Private:
|
||||
save: ->
|
||||
CSON.writeFileSync(@configFilePath, @settings)
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -1,38 +1,49 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class CursorView extends View
|
||||
@content: ->
|
||||
@div class: 'cursor idle', => @raw ' '
|
||||
|
||||
blinkPeriod: 800
|
||||
editorView: null
|
||||
visible: true
|
||||
@blinkPeriod: 800
|
||||
|
||||
@blinkCursors: ->
|
||||
element.classList.toggle('blink-off') for [element] in @cursorViews
|
||||
|
||||
@startBlinking: (cursorView) ->
|
||||
@cursorViews ?= []
|
||||
@cursorViews.push(cursorView)
|
||||
if @cursorViews.length is 1
|
||||
@blinkInterval = setInterval(@blinkCursors.bind(this), @blinkPeriod / 2)
|
||||
|
||||
@stopBlinking: (cursorView) ->
|
||||
cursorView[0].classList.remove('blink-off')
|
||||
_.remove(@cursorViews, cursorView)
|
||||
clearInterval(@blinkInterval) if @cursorViews.length is 0
|
||||
|
||||
blinking: false
|
||||
visible: true
|
||||
needsUpdate: true
|
||||
needsRemoval: false
|
||||
shouldPauseBlinking: false
|
||||
|
||||
initialize: (@cursor, @editorView) ->
|
||||
@cursor.on 'moved.cursor-view', =>
|
||||
@subscribe @cursor, 'moved', =>
|
||||
@needsUpdate = true
|
||||
@shouldPauseBlinking = true
|
||||
|
||||
@cursor.on 'visibility-changed.cursor-view', (visible) =>
|
||||
@subscribe @cursor, 'visibility-changed', =>
|
||||
@needsUpdate = true
|
||||
|
||||
@cursor.on 'autoscrolled.cursor-view', =>
|
||||
@subscribe @cursor, 'autoscrolled', =>
|
||||
@editorView.requestDisplayUpdate()
|
||||
|
||||
@cursor.on 'destroyed.cursor-view', =>
|
||||
@subscribe @cursor, 'destroyed', =>
|
||||
@needsRemoval = true
|
||||
|
||||
beforeRemove: ->
|
||||
@editorView.removeCursorView(this)
|
||||
@cursor.off('.cursor-view')
|
||||
@stopBlinking()
|
||||
|
||||
updateDisplay: ->
|
||||
@@ -53,11 +64,7 @@ class CursorView extends View
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
style = this[0].style
|
||||
if style.display == 'none' or not @isOnDom()
|
||||
true
|
||||
else
|
||||
false
|
||||
this[0].style.display is 'none' or not @isOnDom()
|
||||
|
||||
needsAutoscroll: ->
|
||||
@cursor.needsAutoscroll
|
||||
@@ -69,19 +76,17 @@ class CursorView extends View
|
||||
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
|
||||
setVisible: (visible) ->
|
||||
unless @visible == visible
|
||||
unless @visible is visible
|
||||
@visible = visible
|
||||
@toggle(@visible)
|
||||
|
||||
stopBlinking: ->
|
||||
clearInterval(@blinkInterval) if @blinkInterval
|
||||
@blinkInterval = null
|
||||
this[0].classList.remove('blink-off')
|
||||
@constructor.stopBlinking(this) if @blinking
|
||||
@blinking = false
|
||||
|
||||
startBlinking: ->
|
||||
return if @blinkInterval?
|
||||
blink = => @toggleClass('blink-off')
|
||||
@blinkInterval = setInterval(blink, @blinkPeriod / 2)
|
||||
@constructor.startBlinking(this) unless @blinking
|
||||
@blinking = true
|
||||
|
||||
resetBlinking: ->
|
||||
@stopBlinking()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -1,25 +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 = {}
|
||||
@deferredDeserializers = {}
|
||||
|
||||
# Public: Register the given class(es) as deserializers.
|
||||
add: (klasses...) ->
|
||||
@deserializers[klass.name] = klass for klass in klasses
|
||||
|
||||
# Public: Add a deferred deserializer for the given class name.
|
||||
addDeferred: (name, fn) ->
|
||||
@deferredDeserializers[name] = fn
|
||||
#
|
||||
# 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?
|
||||
|
||||
@@ -30,13 +44,11 @@ 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?
|
||||
|
||||
name = state.get?('deserializer') ? state.deserializer
|
||||
if @deferredDeserializers[name]
|
||||
@deferredDeserializers[name]()
|
||||
delete @deferredDeserializers[name]
|
||||
|
||||
@deserializers[name]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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, @bufferRowForScreenRow(row), line, line.length
|
||||
|
||||
### Internal ###
|
||||
|
||||
handleTokenizedBufferChange: (tokenizedBufferChange) =>
|
||||
{start, end, delta, bufferChange} = tokenizedBufferChange
|
||||
@updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?)
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports =
|
||||
class EditorView extends View
|
||||
@characterWidthCache: {}
|
||||
@configDefaults:
|
||||
fontFamily: ''
|
||||
fontSize: 20
|
||||
showInvisibles: false
|
||||
showIndentGuide: false
|
||||
@@ -41,8 +42,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 +78,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 +117,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: ->
|
||||
@@ -209,7 +206,7 @@ class EditorView extends View
|
||||
'editor:toggle-line-comments': => @toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': => @logCursorScope()
|
||||
'editor:checkout-head-revision': => @checkoutHead()
|
||||
'editor:copy-path': => @copyPathToPasteboard()
|
||||
'editor:copy-path': => @copyPathToClipboard()
|
||||
'editor:move-line-up': => @editor.moveLineUp()
|
||||
'editor:move-line-down': => @editor.moveLineDown()
|
||||
'editor:duplicate-line': => @editor.duplicateLine()
|
||||
@@ -223,6 +220,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 +238,6 @@ class EditorView extends View
|
||||
insertText: (text, options) ->
|
||||
@editor.insertText(text, options)
|
||||
|
||||
# Private:
|
||||
setHeightInLines: (heightInLines)->
|
||||
heightInLines ?= @calculateHeightInLines()
|
||||
@heightInLines = heightInLines if heightInLines
|
||||
@@ -248,39 +247,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 +290,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 +317,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 +493,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 +594,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 +613,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 +621,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 +652,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 +682,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,15 +695,15 @@ 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`,
|
||||
# fontFamily - A {String} identifying the CSS `font-family`.
|
||||
setFontFamily: (fontFamily='') ->
|
||||
@css('font-family', fontFamily)
|
||||
|
||||
@@ -708,12 +711,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`,
|
||||
# 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 +726,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 +757,6 @@ class EditorView extends View
|
||||
super
|
||||
atom.workspaceView?.focus()
|
||||
|
||||
# Private:
|
||||
beforeRemove: ->
|
||||
@trigger 'editor:will-be-removed'
|
||||
@removed = true
|
||||
@@ -797,8 +803,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 +839,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 +1139,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 +1148,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 +1158,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 +1166,6 @@ class EditorView extends View
|
||||
isScreenRowVisible: (row) ->
|
||||
@getFirstVisibleScreenRow() <= row <= @getLastVisibleScreenRow()
|
||||
|
||||
### Internal ###
|
||||
|
||||
handleScreenLinesChange: (change) ->
|
||||
@pendingChanges.push(change)
|
||||
@requestDisplayUpdate()
|
||||
@@ -1246,21 +1248,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 +1297,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 +1342,6 @@ class EditorView extends View
|
||||
|
||||
returnLeft ? left
|
||||
|
||||
# Private:
|
||||
getCharacterWidthCache: (scopes, char) ->
|
||||
scopes ?= NoScope
|
||||
obj = @constructor.characterWidthCache
|
||||
@@ -1352,7 +1350,6 @@ class EditorView extends View
|
||||
return null unless obj?
|
||||
obj[char]
|
||||
|
||||
# Private:
|
||||
setCharacterWidthCache: (scopes, char, val) ->
|
||||
scopes ?= NoScope
|
||||
obj = @constructor.characterWidthCache
|
||||
@@ -1361,7 +1358,6 @@ class EditorView extends View
|
||||
obj = obj[scope]
|
||||
obj[char] = val
|
||||
|
||||
# Private:
|
||||
clearCharacterWidthCache: ->
|
||||
@constructor.characterWidthCache = {}
|
||||
|
||||
@@ -1411,11 +1407,9 @@ class EditorView extends View
|
||||
@highlightedLine = null
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToPasteboard: ->
|
||||
copyPathToClipboard: ->
|
||||
path = @editor.getPath()
|
||||
atom.pasteboard.write(path) if path?
|
||||
|
||||
### Internal ###
|
||||
atom.clipboard.write(path) if path?
|
||||
|
||||
@buildLineHtml: ({tokens, text, lineEnding, fold, isSoftWrapped, invisibles, eolInvisibles, htmlEolInvisibles, attributes, showIndentGuide, indentation, editor, mini}) ->
|
||||
scopeStack = []
|
||||
@@ -1426,7 +1420,7 @@ class EditorView extends View
|
||||
line.push("<div #{attributePairs}>")
|
||||
|
||||
if text == ''
|
||||
html = EditorView.buildEmptyLineHtml(showIndentGuide, eolInvisibles, htmlEolInvisibles, indentation, editor, mini)
|
||||
html = @buildEmptyLineHtml(showIndentGuide, eolInvisibles, htmlEolInvisibles, indentation, editor, mini)
|
||||
line.push(html) if html
|
||||
else
|
||||
firstNonWhitespacePosition = text.search(/\S/)
|
||||
|
||||
@@ -78,7 +78,7 @@ class Editor extends Model
|
||||
position = [0, 0]
|
||||
@addCursorAtBufferPosition(position)
|
||||
|
||||
@languageMode = new LanguageMode(this, @buffer.getExtension())
|
||||
@languageMode = new LanguageMode(this)
|
||||
|
||||
@subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop
|
||||
@subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
|
||||
@@ -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()
|
||||
@@ -238,15 +234,14 @@ class Editor extends Model
|
||||
|
||||
# Public: Given a position, this clips it to a real position.
|
||||
#
|
||||
# For example, if `position`'s row exceeds the row count of the buffer,
|
||||
# For example, if `bufferPosition`'s row exceeds the row count of the buffer,
|
||||
# or if its column goes beyond a line's length, this "sanitizes" the value
|
||||
# to a real position.
|
||||
#
|
||||
# * position:
|
||||
# The {Point} to clip
|
||||
# bufferPosition - The {Point} to clip.
|
||||
#
|
||||
# Returns the new, clipped {Point}. Note that this could be the same as
|
||||
# `position` if no clipping was performed.
|
||||
# `bufferPosition` if no clipping was performed.
|
||||
clipBufferPosition: (bufferPosition) -> @buffer.clipPosition(bufferPosition)
|
||||
|
||||
# Public: Given a range, this clips it to a real range.
|
||||
@@ -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())
|
||||
@@ -308,9 +298,6 @@ class Editor extends Model
|
||||
# {Delegates to: TextBuffer.saveAs}
|
||||
saveAs: (path) -> @buffer.saveAs(path)
|
||||
|
||||
# {Delegates to: TextBuffer.getExtension}
|
||||
getFileExtension: -> @buffer.getExtension()
|
||||
|
||||
# {Delegates to: TextBuffer.getPath}
|
||||
getPath: -> @buffer.getPath()
|
||||
|
||||
@@ -326,7 +313,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 +340,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 +349,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
|
||||
@@ -439,10 +426,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()
|
||||
@@ -469,8 +454,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)
|
||||
@@ -521,7 +505,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()
|
||||
|
||||
@@ -537,31 +521,30 @@ class Editor extends Model
|
||||
# Public: Copies and removes all characters from cursor to the end of the
|
||||
# line.
|
||||
cutToEndOfLine: ->
|
||||
maintainPasteboard = false
|
||||
maintainClipboard = false
|
||||
@mutateSelectedText (selection) ->
|
||||
selection.cutToEndOfLine(maintainPasteboard)
|
||||
maintainPasteboard = true
|
||||
selection.cutToEndOfLine(maintainClipboard)
|
||||
maintainClipboard = true
|
||||
|
||||
# Public: Cuts the selected text.
|
||||
cutSelectedText: ->
|
||||
maintainPasteboard = false
|
||||
maintainClipboard = false
|
||||
@mutateSelectedText (selection) ->
|
||||
selection.cut(maintainPasteboard)
|
||||
maintainPasteboard = true
|
||||
selection.cut(maintainClipboard)
|
||||
maintainClipboard = true
|
||||
|
||||
# Public: Copies the selected text.
|
||||
copySelectedText: ->
|
||||
maintainPasteboard = false
|
||||
maintainClipboard = false
|
||||
for selection in @getSelections()
|
||||
selection.copy(maintainPasteboard)
|
||||
maintainPasteboard = true
|
||||
selection.copy(maintainClipboard)
|
||||
maintainClipboard = true
|
||||
|
||||
# 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.pasteboard.read()
|
||||
{text, metadata} = atom.clipboard.readWithMetadata()
|
||||
|
||||
containsNewlines = text.indexOf('\n') isnt -1
|
||||
|
||||
@@ -640,7 +623,7 @@ class Editor extends Model
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@displayBuffer.largestFoldStartingAtScreenRow(screenRow)
|
||||
|
||||
# Public: Moves the selected line up one row.
|
||||
# Public: Moves the selected lines up one screen row.
|
||||
moveLineUp: ->
|
||||
selection = @getSelectedBufferRange()
|
||||
return if selection.start.row is 0
|
||||
@@ -652,29 +635,47 @@ class Editor extends Model
|
||||
rows = [selection.start.row..selection.end.row]
|
||||
if selection.start.row isnt selection.end.row and selection.end.column is 0
|
||||
rows.pop() unless @isFoldedAtBufferRow(selection.end.row)
|
||||
|
||||
# Move line around the fold that is directly above the selection
|
||||
precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1])
|
||||
precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row
|
||||
if fold = @largestFoldContainingBufferRow(precedingBufferRow)
|
||||
insertDelta = fold.getBufferRange().getRowCount()
|
||||
else
|
||||
insertDelta = 1
|
||||
|
||||
for row in rows
|
||||
screenRow = @screenPositionForBufferPosition([row]).row
|
||||
if @isFoldedAtScreenRow(screenRow)
|
||||
bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]])
|
||||
if fold = @displayBuffer.largestFoldStartingAtBufferRow(row)
|
||||
bufferRange = fold.getBufferRange()
|
||||
startRow = bufferRange.start.row
|
||||
endRow = bufferRange.end.row - 1
|
||||
foldedRows.push(endRow - 1)
|
||||
endRow = bufferRange.end.row
|
||||
foldedRows.push(startRow - insertDelta)
|
||||
else
|
||||
startRow = row
|
||||
endRow = row
|
||||
|
||||
insertPosition = Point.fromObject([startRow - insertDelta])
|
||||
endPosition = Point.min([endRow + 1], @buffer.getEofPosition())
|
||||
lines = @buffer.getTextInRange([[startRow], endPosition])
|
||||
if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row)
|
||||
lines = "#{lines}\n"
|
||||
|
||||
@buffer.deleteRows(startRow, endRow)
|
||||
@buffer.insert([startRow - 1], lines)
|
||||
|
||||
@foldBufferRow(foldedRow) for foldedRow in foldedRows
|
||||
# Make sure the inserted text doesn't go into an existing fold
|
||||
if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row)
|
||||
@destroyFoldsContainingBufferRow(insertPosition.row)
|
||||
foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount())
|
||||
|
||||
@setSelectedBufferRange(selection.translate([-1]), preserveFolds: true)
|
||||
@buffer.insert(insertPosition, lines)
|
||||
|
||||
# Public: Moves the selected line down one row.
|
||||
# Restore folds that existed before the lines were moved
|
||||
for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow()
|
||||
@foldBufferRow(foldedRow)
|
||||
|
||||
@setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true)
|
||||
|
||||
# Public: Moves the selected lines down one screen row.
|
||||
moveLineDown: ->
|
||||
selection = @getSelectedBufferRange()
|
||||
lastRow = @buffer.getLastRow()
|
||||
@@ -686,13 +687,21 @@ class Editor extends Model
|
||||
rows = [selection.end.row..selection.start.row]
|
||||
if selection.start.row isnt selection.end.row and selection.end.column is 0
|
||||
rows.shift() unless @isFoldedAtBufferRow(selection.end.row)
|
||||
|
||||
# Move line around the fold that is directly below the selection
|
||||
followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1])
|
||||
followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row
|
||||
if fold = @largestFoldContainingBufferRow(followingBufferRow)
|
||||
insertDelta = fold.getBufferRange().getRowCount()
|
||||
else
|
||||
insertDelta = 1
|
||||
|
||||
for row in rows
|
||||
screenRow = @screenPositionForBufferPosition([row]).row
|
||||
if @isFoldedAtScreenRow(screenRow)
|
||||
bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]])
|
||||
if fold = @displayBuffer.largestFoldStartingAtBufferRow(row)
|
||||
bufferRange = fold.getBufferRange()
|
||||
startRow = bufferRange.start.row
|
||||
endRow = bufferRange.end.row - 1
|
||||
foldedRows.push(endRow + 1)
|
||||
endRow = bufferRange.end.row
|
||||
foldedRows.push(endRow + insertDelta)
|
||||
else
|
||||
startRow = row
|
||||
endRow = row
|
||||
@@ -703,14 +712,23 @@ class Editor extends Model
|
||||
endPosition = [endRow + 1]
|
||||
lines = @buffer.getTextInRange([[startRow], endPosition])
|
||||
@buffer.deleteRows(startRow, endRow)
|
||||
insertPosition = Point.min([startRow + 1], @buffer.getEofPosition())
|
||||
|
||||
insertPosition = Point.min([startRow + insertDelta], @buffer.getEofPosition())
|
||||
if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0
|
||||
lines = "\n#{lines}"
|
||||
|
||||
# Make sure the inserted text doesn't go into an existing fold
|
||||
if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row)
|
||||
@destroyFoldsContainingBufferRow(insertPosition.row)
|
||||
foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount())
|
||||
|
||||
@buffer.insert(insertPosition, lines)
|
||||
|
||||
@foldBufferRow(foldedRow) for foldedRow in foldedRows
|
||||
# Restore folds that existed before the lines were moved
|
||||
for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow()
|
||||
@foldBufferRow(foldedRow)
|
||||
|
||||
@setSelectedBufferRange(selection.translate([1]), preserveFolds: true)
|
||||
@setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true)
|
||||
|
||||
# Public: Duplicates the current line.
|
||||
#
|
||||
@@ -739,11 +757,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) ->
|
||||
@@ -787,7 +803,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()
|
||||
|
||||
@@ -826,10 +844,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={}) ->
|
||||
@@ -850,10 +866,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={}) ->
|
||||
@@ -863,20 +877,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
|
||||
|
||||
@@ -893,7 +903,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)
|
||||
|
||||
@@ -904,9 +914,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
|
||||
@@ -943,8 +951,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) ->
|
||||
@@ -953,10 +960,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)
|
||||
|
||||
@@ -975,10 +980,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)
|
||||
|
||||
@@ -1021,9 +1024,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))
|
||||
|
||||
@@ -1091,7 +1093,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()
|
||||
@@ -1099,8 +1100,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)
|
||||
@@ -1259,8 +1259,6 @@ class Editor extends Model
|
||||
@setSelectedBufferRange(range)
|
||||
range
|
||||
|
||||
# Public:
|
||||
#
|
||||
# FIXME: Not sure how to describe what this does.
|
||||
mergeCursors: ->
|
||||
positions = []
|
||||
@@ -1271,27 +1269,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...) ->
|
||||
@@ -1315,7 +1307,6 @@ class Editor extends Model
|
||||
|
||||
_.reduce(@getSelections(), reducer, [])
|
||||
|
||||
# Private:
|
||||
preserveCursorPositionOnBufferReload: ->
|
||||
cursorPosition = null
|
||||
@subscribe @buffer, "will-reload", =>
|
||||
@@ -1336,7 +1327,6 @@ class Editor extends Model
|
||||
reloadGrammar: ->
|
||||
@displayBuffer.reloadGrammar()
|
||||
|
||||
# Private:
|
||||
shouldAutoIndent: ->
|
||||
atom.config.get("editor.autoIndent")
|
||||
|
||||
@@ -1347,32 +1337,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'
|
||||
|
||||
@@ -5,6 +5,7 @@ Q = require 'q'
|
||||
{Emitter} = require 'emissary'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
runas = require 'runas'
|
||||
|
||||
# Public: Represents an individual file.
|
||||
#
|
||||
@@ -25,16 +26,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,24 +48,24 @@ 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)
|
||||
|
||||
# Public: Overwrites the file with the given String.
|
||||
write: (text) ->
|
||||
previouslyExisted = @exists()
|
||||
@writeFileWithPrivilegeEscalationSync(@getPath(), text)
|
||||
@cachedContents = text
|
||||
fs.writeFileSync(@getPath(), text)
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
|
||||
|
||||
# Private: Deprecated
|
||||
# Deprecated
|
||||
readSync: (flushCache) ->
|
||||
if not @exists()
|
||||
@cachedContents = null
|
||||
@@ -80,9 +79,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 +116,6 @@ class File
|
||||
exists: ->
|
||||
fs.existsSync(@getPath())
|
||||
|
||||
# Private:
|
||||
setDigest: (contents) ->
|
||||
@digest = crypto.createHash('sha1').update(contents ? '').digest('hex')
|
||||
|
||||
@@ -126,7 +123,21 @@ class File
|
||||
getDigest: ->
|
||||
@digest ? @setDigest(@readSync())
|
||||
|
||||
# Private:
|
||||
# Writes the text to specified path.
|
||||
#
|
||||
# Privilege escalation would be asked when current user doesn't have
|
||||
# permission to the path.
|
||||
writeFileWithPrivilegeEscalationSync: (path, text) ->
|
||||
try
|
||||
fs.writeFileSync(path, text)
|
||||
catch error
|
||||
if error.code is 'EACCES' and process.platform is 'darwin'
|
||||
authopen = '/usr/libexec/authopen' # man 1 authopen
|
||||
unless runas(authopen, ['-w', '-c', path], stdin: text) is 0
|
||||
throw error
|
||||
else
|
||||
throw error
|
||||
|
||||
handleNativeChangeEvent: (eventType, path) ->
|
||||
if eventType is "delete"
|
||||
@unsubscribeFromNativeChangeEvents()
|
||||
@@ -139,11 +150,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 +161,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()
|
||||
|
||||
@@ -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
|
||||
|
||||
118
src/git.coffee
118
src/git.coffee
@@ -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)
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -2,8 +2,6 @@ _ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{specificity} = require 'clear-cut'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class KeyBinding
|
||||
@parser: null
|
||||
|
||||
@@ -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..]
|
||||
|
||||
@@ -3,30 +3,19 @@ _ = require 'underscore-plus'
|
||||
{OnigRegExp} = require 'oniguruma'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class LanguageMode
|
||||
Emitter.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
buffer: null
|
||||
grammar: null
|
||||
editor: null
|
||||
currentGrammarScore: null
|
||||
|
||||
### Internal ###
|
||||
|
||||
destroy: ->
|
||||
@unsubscribe()
|
||||
|
||||
### Public ###
|
||||
|
||||
# Sets up a `LanguageMode` for the given {Editor}.
|
||||
#
|
||||
# editor - The {Editor} to associate with
|
||||
constructor: (@editor) ->
|
||||
@buffer = @editor.buffer
|
||||
{@buffer} = @editor
|
||||
|
||||
destroy: ->
|
||||
@unsubscribe()
|
||||
|
||||
toggleLineCommentForBufferRow: (row) ->
|
||||
@toggleLineCommentsForBufferRows(row, row)
|
||||
@@ -187,7 +176,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 +184,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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,61 +8,81 @@ 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
|
||||
# Private:
|
||||
constructor: ({@resourcePath}) ->
|
||||
@pendingUpdateOperation = null
|
||||
@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 optional {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) ->
|
||||
return true if document.body.webkitMatchesSelector(selector)
|
||||
|
||||
# Simulate an .editor element attached to a body element that has the same
|
||||
# classes as the current body element.
|
||||
# Simulate an .editor element attached to a .workspace element attached to
|
||||
# a body element that has the same classes as the current body element.
|
||||
unless @testEditor?
|
||||
testBody = document.createElement('body')
|
||||
testBody.classList.add(@classesForElement(document.body)...)
|
||||
|
||||
testWorkspace = document.createElement('body')
|
||||
workspaceClasses = @classesForElement(document.body.querySelector('.workspace')) ? ['.workspace']
|
||||
testWorkspace.classList.add(workspaceClasses...)
|
||||
|
||||
testBody.appendChild(testWorkspace)
|
||||
|
||||
@testEditor = document.createElement('div')
|
||||
@testEditor.classList.add('editor')
|
||||
testBody = document.createElement('body')
|
||||
testBody.classList.add(document.body.classList.toString().split(' ')...)
|
||||
testBody.appendChild(@testEditor)
|
||||
testWorkspace.appendChild(@testEditor)
|
||||
|
||||
@testEditor.webkitMatchesSelector(selector)
|
||||
|
||||
# Public: Refreshes the currently visible menu.
|
||||
update: ->
|
||||
keystrokesByCommand = {}
|
||||
for binding in atom.keymap.getKeyBindings() when @includeSelector(binding.selector)
|
||||
keystrokesByCommand[binding.command] ?= []
|
||||
keystrokesByCommand[binding.command].push binding.keystroke
|
||||
@sendToBrowserProcess(@template, keystrokesByCommand)
|
||||
clearImmediate(@pendingUpdateOperation) if @pendingUpdateOperation?
|
||||
@pendingUpdateOperation = setImmediate =>
|
||||
keystrokesByCommand = {}
|
||||
for binding in atom.keymap.getKeyBindings() when @includeSelector(binding.selector)
|
||||
keystrokesByCommand[binding.command] ?= []
|
||||
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)
|
||||
@@ -72,7 +92,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) ->
|
||||
@@ -85,12 +105,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?
|
||||
|
||||
@@ -98,3 +116,7 @@ class MenuManager
|
||||
label.replace(/\&/g, '')
|
||||
else
|
||||
label
|
||||
|
||||
# Get an {Array} of {String} classes for the given element.
|
||||
classesForElement: (element) ->
|
||||
element?.classList.toString().split(' ') ? []
|
||||
|
||||
@@ -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,44 @@ 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) ->
|
||||
return pack if pack = @getActivePackage(name)
|
||||
if pack = @loadPackage(name, options)
|
||||
@activePackages[pack.name] = pack
|
||||
pack.activate(options)
|
||||
pack
|
||||
# Activate a single package by name
|
||||
activatePackage: (name) ->
|
||||
if pack = @getActivePackage(name)
|
||||
Q(pack)
|
||||
else
|
||||
pack = @loadPackage(name)
|
||||
pack.activate().then =>
|
||||
@activePackages[pack.name] = pack
|
||||
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 +115,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 +128,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 +136,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 +175,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 +195,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 +203,6 @@ class PackageManager
|
||||
isBundledPackage: (name) ->
|
||||
@getPackageDependencies().hasOwnProperty(name)
|
||||
|
||||
# Private:
|
||||
getPackageDependencies: ->
|
||||
unless @packageDependencies?
|
||||
try
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -54,9 +50,8 @@ class Package
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
|
||||
isTheme: ->
|
||||
!!@metadata?.theme
|
||||
@metadata?.theme?
|
||||
|
||||
# Private:
|
||||
measure: (key, fn) ->
|
||||
startTime = Date.now()
|
||||
value = fn()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
PaneView = null
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class PaneAxisView extends View
|
||||
initialize: (@model) ->
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
_ = require 'underscore-plus'
|
||||
PaneAxisView = require './pane-axis-view'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class PaneColumnView extends PaneAxisView
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
_ = require 'underscore-plus'
|
||||
PaneAxisView = require './pane-axis-view'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class PaneRowView extends PaneAxisView
|
||||
@content: ->
|
||||
|
||||
@@ -27,11 +27,10 @@ class PaneView extends View
|
||||
'destroyItem', 'destroyItems', 'destroyActiveItem', 'destroyInactiveItems',
|
||||
'saveActiveItem', 'saveActiveItemAs', 'saveItem', 'saveItemAs', 'saveItems',
|
||||
'itemForUri', 'activateItemForUri', 'promptToSaveItem', 'copyActiveItem', 'isActive',
|
||||
'activate', toProperty: 'model'
|
||||
'activate', 'getActiveItem', toProperty: 'model'
|
||||
|
||||
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()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{Model, Sequence} = require 'theorist'
|
||||
Serializable = require 'serializable'
|
||||
PaneAxis = require './pane-axis'
|
||||
Editor = require './editor'
|
||||
PaneView = null
|
||||
|
||||
# Public: A container for multiple items, one of which is *active* at a given
|
||||
@@ -27,7 +28,6 @@ class Pane extends Model
|
||||
.map((activePane) => activePane is this)
|
||||
.distinctUntilChanged()
|
||||
|
||||
# Private:
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
@@ -43,31 +43,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,13 +78,25 @@ 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()
|
||||
|
||||
# Public: Get the active pane item in this pane.
|
||||
#
|
||||
# Returns a pane item.
|
||||
getActiveItem: ->
|
||||
@activeItem
|
||||
|
||||
# Public: Returns an {Editor} if the pane item is an {Editor}, or null
|
||||
# otherwise.
|
||||
getActiveEditor: ->
|
||||
@activeItem if @activeItem instanceof Editor
|
||||
|
||||
# Public: Returns the item at the specified index.
|
||||
itemAtIndex: (index) ->
|
||||
@items[index]
|
||||
@@ -105,15 +117,15 @@ class Pane extends Model
|
||||
else
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
# Public: Returns the index of the current active item.
|
||||
# Returns the index of the current active item.
|
||||
getActiveItemIndex: ->
|
||||
@items.indexOf(@activeItem)
|
||||
|
||||
# Public: Makes the item at the given index active.
|
||||
# Makes the item at the given index active.
|
||||
activateItemAtIndex: (index) ->
|
||||
@activateItem(@itemAtIndex(index))
|
||||
|
||||
# Public: Makes the given item active, adding the item if necessary.
|
||||
# Makes the given item active, adding the item if necessary.
|
||||
activateItem: (item) ->
|
||||
if item?
|
||||
@addItem(item)
|
||||
@@ -121,11 +133,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 +148,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 +160,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 +215,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 +246,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 +258,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 +289,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 +304,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 +313,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 +322,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 +341,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 +349,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'
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
clipboard = require 'clipboard'
|
||||
crypto = require 'crypto'
|
||||
|
||||
# Public: Represents the clipboard used for copying and pasting in Atom.
|
||||
#
|
||||
# A pasteboard instance is always available under the `atom.pasteboard` global.
|
||||
module.exports =
|
||||
class Pasteboard
|
||||
signatureForMetadata: null
|
||||
|
||||
# Creates an `md5` hash of some text.
|
||||
#
|
||||
# text - A {String} to encrypt.
|
||||
#
|
||||
# Returns an encrypted {String}.
|
||||
md5: (text) ->
|
||||
crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
|
||||
# Public: Write the given text to the clipboard.
|
||||
#
|
||||
# text - A {String} to store.
|
||||
# metadata - An {Object} of additional info to associate with the text.
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
clipboard.writeText(text)
|
||||
|
||||
# Public: Read the text from the clipboard.
|
||||
#
|
||||
# Returns an {Array}. The first element is the saved text and the second is
|
||||
# any metadata associated with the text.
|
||||
read: ->
|
||||
text = clipboard.readText()
|
||||
value = [text]
|
||||
value.push(@metadata) if @signatureForMetadata == @md5(text)
|
||||
value
|
||||
@@ -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)
|
||||
@@ -30,11 +30,12 @@ class Project extends Model
|
||||
|
||||
constructor: ({path, @buffers}={}) ->
|
||||
@buffers ?= []
|
||||
@openers = []
|
||||
|
||||
for buffer in @buffers
|
||||
do (buffer) =>
|
||||
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||
|
||||
@openers = []
|
||||
@editors = []
|
||||
@setPath(path)
|
||||
|
||||
@@ -46,36 +47,16 @@ class Project extends Model
|
||||
params.buffers = params.buffers.map (bufferState) -> atom.deserializers.deserialize(bufferState)
|
||||
params
|
||||
|
||||
# Public: Register an opener for project files.
|
||||
#
|
||||
# An {Editor} will be used if no openers return a value.
|
||||
#
|
||||
# ## 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.
|
||||
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 +92,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) ->
|
||||
@@ -133,71 +113,53 @@ class Project extends Model
|
||||
contains: (pathToCheck) ->
|
||||
@rootDirectory?.contains(pathToCheck) ? false
|
||||
|
||||
# Public: Given a path to a file, this constructs and associates a new
|
||||
# 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={}) ->
|
||||
filePath = @resolve(filePath)
|
||||
resource = null
|
||||
_.find @openers, (opener) -> resource = opener(filePath, options)
|
||||
@bufferForPath(filePath).then (buffer) =>
|
||||
@buildEditorForBuffer(buffer, options)
|
||||
|
||||
if resource
|
||||
Q(resource)
|
||||
else
|
||||
@bufferForPath(filePath).then (buffer) =>
|
||||
@buildEditorForBuffer(buffer, options)
|
||||
|
||||
# Private: Only be used in specs
|
||||
# Deprecated
|
||||
openSync: (filePath, options={}) ->
|
||||
filePath = @resolve(filePath)
|
||||
for opener in @openers
|
||||
return resource if resource = opener(filePath, options)
|
||||
|
||||
@buildEditorForBuffer(@bufferForPathSync(filePath), options)
|
||||
|
||||
# Public: Retrieves all {Editor}s for all open files.
|
||||
#
|
||||
# Returns an {Array} of {Editor}s.
|
||||
getEditors: ->
|
||||
new Array(@editors...)
|
||||
|
||||
# Public: Add the given {Editor}.
|
||||
# Add the given {Editor}.
|
||||
addEditor: (editor) ->
|
||||
@editors.push editor
|
||||
@emit 'editor-created', editor
|
||||
|
||||
# Public: Return and removes the given {Editor}.
|
||||
# Return and removes the given {Editor}.
|
||||
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.
|
||||
@@ -210,21 +172,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) ->
|
||||
@@ -234,38 +195,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
|
||||
@@ -304,10 +260,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()
|
||||
|
||||
@@ -339,18 +296,11 @@ 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()
|
||||
@@ -360,3 +310,20 @@ class Project extends Model
|
||||
subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer)
|
||||
else
|
||||
@on 'buffer-created', (buffer) -> callback(buffer)
|
||||
|
||||
# Deprecated: delegate
|
||||
registerOpener: (opener) ->
|
||||
@openers.push(opener)
|
||||
|
||||
# Deprecated: delegate
|
||||
unregisterOpener: (opener) ->
|
||||
_.remove(@openers, opener)
|
||||
|
||||
# Deprecated: delegate
|
||||
eachEditor: (callback) ->
|
||||
callback(editor) for editor in @getEditors()
|
||||
@on 'editor-created', (editor) -> callback(editor)
|
||||
|
||||
# Deprecated: delegate
|
||||
getEditors: ->
|
||||
new Array(@editors...)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{View, $$} = require './space-pen-extensions'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class SelectionView extends View
|
||||
|
||||
|
||||
@@ -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,35 +483,25 @@ class Selection
|
||||
@editor.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
|
||||
|
||||
# Public: Cuts the selection until the end of the line.
|
||||
#
|
||||
# * maintainPasteboard:
|
||||
# ?
|
||||
cutToEndOfLine: (maintainPasteboard) ->
|
||||
cutToEndOfLine: (maintainClipboard) ->
|
||||
@selectToEndOfLine() if @isEmpty()
|
||||
@cut(maintainPasteboard)
|
||||
@cut(maintainClipboard)
|
||||
|
||||
# Public: Copies the selection to the pasteboard and then deletes it.
|
||||
#
|
||||
# * maintainPasteboard:
|
||||
# ?
|
||||
cut: (maintainPasteboard=false) ->
|
||||
@copy(maintainPasteboard)
|
||||
# Public: Copies the selection to the clipboard and then deletes it.
|
||||
cut: (maintainClipboard=false) ->
|
||||
@copy(maintainClipboard)
|
||||
@delete()
|
||||
|
||||
# Public: Copies the current selection to the pasteboard.
|
||||
#
|
||||
# * maintainPasteboard:
|
||||
# ?
|
||||
copy: (maintainPasteboard=false) ->
|
||||
# Public: Copies the current selection to the clipboard.
|
||||
copy: (maintainClipboard=false) ->
|
||||
return if @isEmpty()
|
||||
text = @editor.buffer.getTextInRange(@getBufferRange())
|
||||
if maintainPasteboard
|
||||
[currentText, metadata] = atom.pasteboard.read()
|
||||
text = currentText + '\n' + text
|
||||
if maintainClipboard
|
||||
text = "#{atom.clipboard.read()}\n#{text}"
|
||||
else
|
||||
metadata = { indentBasis: @editor.indentationForBufferRow(@getBufferRange().start.row) }
|
||||
|
||||
atom.pasteboard.write(text, metadata)
|
||||
atom.clipboard.write(text, metadata)
|
||||
|
||||
# Public: Creates a fold containing the current selection.
|
||||
fold: ->
|
||||
@@ -541,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.
|
||||
#
|
||||
@@ -558,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) ->
|
||||
@@ -567,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) ->
|
||||
@@ -577,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()
|
||||
@@ -596,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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user