Comparar commits
103 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ff70ae633d | |||
| d1f372e439 | |||
| 041f52aaaa | |||
| 270d17814e | |||
| 16095c8086 | |||
| 94e2dbbc2c | |||
| 12580bce83 | |||
| 753b11cf15 | |||
| 0fee962faa | |||
| ed1f51b987 | |||
| f1bdcaedc1 | |||
| 2efbe9ce4e | |||
| 3366c4d6a5 | |||
| acb69a4221 | |||
| b3b501ef07 | |||
| e34d8e4c42 | |||
| adebae6c47 | |||
| 4ae0d8af9f | |||
| 651bbaa3ce | |||
| 0448ba5ceb | |||
| 6d70bf9106 | |||
| 857629e07e | |||
| 18c5c788d7 | |||
| a8e7a9a091 | |||
| 59097ddf96 | |||
| 433beac9aa | |||
| c7f6e87132 | |||
| 64e2fee975 | |||
| df35cb0756 | |||
| 0e6a3c52ad | |||
| b786bcc774 | |||
| 6cb13e843c | |||
| 7e76721712 | |||
| 23df564189 | |||
| 0c17ed9c33 | |||
| cde9ae02fe | |||
| 77017c2cd3 | |||
| 7615d54337 | |||
| 6b591b121d | |||
| 268edf16d8 | |||
| 01f220fb1b | |||
| 53a92751e6 | |||
| b66431e3a6 | |||
| 5a9e45f9d8 | |||
| 8561bb34a8 | |||
| d55c09c9d3 | |||
| f4300b8cab | |||
| f3f9c2c921 | |||
| 33dca9315c | |||
| c596514d11 | |||
| 390b03647e | |||
| 5b00cf1701 | |||
| 95e69ad858 | |||
| ef0363e8ff | |||
| a62cd0f16a | |||
| f340828506 | |||
| 8b0a68dd4a | |||
| 1049a677bf | |||
| d2bc415042 | |||
| b439e6239d | |||
| b999c71ad6 | |||
| 9f31f9a76b | |||
| 7fe356b649 | |||
| b5aa0b900c | |||
| cd4622fbeb | |||
| 89192c2ee2 | |||
| b5b408d134 | |||
| b08f320ed5 | |||
| 4c7025ae81 | |||
| 554af1becb | |||
| 6d49e0fd57 | |||
| 31d872fbfe | |||
| b842a727ee | |||
| 0851b4d011 | |||
| 8a7c57994d | |||
| 6480e956af | |||
| 9c95a4751b | |||
| d283834978 | |||
| 66b490b5da | |||
| 0d252ab9fd | |||
| 3e8de2d22d | |||
| 52aee1d050 | |||
| ff9dc2c5af | |||
| 3b8cdb58e2 | |||
| bd14aeee17 | |||
| e434f7c0a9 | |||
| 837e3000d3 | |||
| e1c2aa0da1 | |||
| e7aac2ef6e | |||
| f9545acde6 | |||
| 45ce8e1d63 | |||
| bdbec54f24 | |||
| 1df48d1725 | |||
| 6c0f4c09d9 | |||
| f45ce3a7ee | |||
| dc6e624eda | |||
| e67676b01c | |||
| f37ae7e4e7 | |||
| f913f6a8d5 | |||
| 20991381bd | |||
| 6930fe0eff | |||
| f3b50eed3b | |||
| a4217909c0 |
@@ -4,3 +4,6 @@
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/github/apm.git
|
||||
[submodule "vendor/telepath"]
|
||||
path = vendor/telepath
|
||||
url = https://github.com/github/telepath.git
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
* Fixed: cmd-n now works when no windows are open
|
||||
|
||||
* Fixed: Error selecting a grammar for an untitled editor
|
||||
|
||||
* Added: j/k now can be used to navigate the tree view and archive editor
|
||||
|
||||
* Fixed: Atom can now be launched when ~/.atom/config.cson doesn't exist
|
||||
* Added: Initial collaboration sessions
|
||||
* Fixed: Empty lines being deleted via uppercase/downcase command
|
||||
* Fixed: Keybindings not working when using non-English keyboard language
|
||||
* Fixed: cmd-shift-p and cmd-alt-w not doing anything when pressed
|
||||
|
||||
* Improved: Use grunt (instead of rake) for build system
|
||||
* Fixed: Java files not syntax highlighting correctly.
|
||||
* Fixed: LESS/CSS now indents properly after hitting enter.
|
||||
|
||||
+13
-143
@@ -1,29 +1,28 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
walkdir = require 'walkdir'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
APP_NAME = "Atom.app"
|
||||
BUILD_DIR = grunt.option('build-dir') ? "/tmp/atom-build/"
|
||||
SHELL_APP_DIR = path.join(BUILD_DIR, APP_NAME)
|
||||
CONTENTS_DIR = path.join(SHELL_APP_DIR, 'Contents')
|
||||
APP_DIR = path.join(CONTENTS_DIR, "Resources", "app")
|
||||
INSTALL_DIR = path.join('/Applications', APP_NAME)
|
||||
appName = 'Atom.app'
|
||||
buildDir = grunt.option('build-dir') ? '/tmp/atom-build'
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
installDir = path.join('/Applications', appName)
|
||||
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
|
||||
atom: {appDir, appName, buildDir, contentsDir, installDir, shellAppDir}
|
||||
|
||||
coffee:
|
||||
options:
|
||||
sourceMap: true
|
||||
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.coffee'
|
||||
'static/**/*.coffee'
|
||||
]
|
||||
dest: APP_DIR
|
||||
dest: appDir
|
||||
ext: '.js'
|
||||
|
||||
less:
|
||||
@@ -39,7 +38,7 @@ module.exports = (grunt) ->
|
||||
'static/**/*.less'
|
||||
'themes/**/*.less'
|
||||
]
|
||||
dest: APP_DIR
|
||||
dest: appDir
|
||||
ext: '.css'
|
||||
|
||||
cson:
|
||||
@@ -52,7 +51,7 @@ module.exports = (grunt) ->
|
||||
'static/**/*.cson'
|
||||
'themes/**/*.cson'
|
||||
]
|
||||
dest: APP_DIR
|
||||
dest: appDir
|
||||
ext: '.json'
|
||||
|
||||
coffeelint:
|
||||
@@ -61,7 +60,6 @@ module.exports = (grunt) ->
|
||||
level: 'error'
|
||||
max_line_length:
|
||||
level: 'ignore'
|
||||
|
||||
src: [
|
||||
'dot-atom/**/*.coffee'
|
||||
'src/**/*.coffee'
|
||||
@@ -110,138 +108,10 @@ module.exports = (grunt) ->
|
||||
grunt.loadNpmTasks('grunt-contrib-csslint')
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
|
||||
grunt.registerTask 'partial-clean', 'Delete some of the build files', ->
|
||||
rm BUILD_DIR
|
||||
rm '/tmp/atom-coffee-cache'
|
||||
rm '/tmp/atom-cached-atom-shells'
|
||||
rm 'node'
|
||||
|
||||
grunt.registerTask 'clean', 'Delete all the build files', ->
|
||||
rm 'node_modules'
|
||||
grunt.task.run('partial-clean')
|
||||
|
||||
grunt.registerTask 'build', 'Build the application', ->
|
||||
rm SHELL_APP_DIR
|
||||
mkdir path.dirname(BUILD_DIR)
|
||||
cp 'atom-shell/Atom.app', SHELL_APP_DIR
|
||||
|
||||
mkdir APP_DIR
|
||||
|
||||
cp 'atom.sh', path.join(APP_DIR, 'atom.sh')
|
||||
cp 'package.json', path.join(APP_DIR, 'package.json')
|
||||
|
||||
directories = [
|
||||
'benchmark'
|
||||
'dot-atom'
|
||||
'spec'
|
||||
'vendor'
|
||||
]
|
||||
|
||||
{devDependencies, dependencies} = grunt.file.readJSON('package.json')
|
||||
for child in fs.readdirSync('node_modules')
|
||||
directory = path.join('node_modules', child)
|
||||
try
|
||||
{name} = grunt.file.readJSON(path.join(directory, 'package.json'))
|
||||
if not devDependencies[name]? or dependencies[name]?
|
||||
directories.push(directory)
|
||||
catch e
|
||||
directories.push(directory)
|
||||
|
||||
ignoredPaths = [
|
||||
path.join('git-utils', 'deps')
|
||||
path.join('oniguruma', 'deps')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('vendor', 'bootstrap', 'docs')
|
||||
]
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
for directory in directories
|
||||
cp directory, path.join(APP_DIR, directory), filter: nodeModulesFilter
|
||||
|
||||
cp 'src', path.join(APP_DIR, 'src'), filter: /.+\.(cson|coffee|less)$/
|
||||
cp 'static', path.join(APP_DIR, 'static'), filter: /.+\.less$/
|
||||
cp 'themes', path.join(APP_DIR, 'themes'), filter: /.+\.(cson|less)$/
|
||||
|
||||
grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
|
||||
unless /.+\.plist/.test(sourcePath)
|
||||
grunt.file.copy(sourcePath, path.resolve(APP_DIR, '..', subDirectory, filename))
|
||||
|
||||
grunt.task.run('compile', 'copy-info-plist')
|
||||
|
||||
grunt.registerTask 'copy-info-plist', 'Copy plist', ->
|
||||
plistPath = path.join(CONTENTS_DIR, 'Info.plist')
|
||||
helperPlistPath = path.join(CONTENTS_DIR, 'Frameworks/Atom Helper.app/Contents/Info.plist')
|
||||
|
||||
# Copy custom plist files
|
||||
cp 'resources/mac/atom-Info.plist', plistPath
|
||||
cp 'resources/mac/helper-Info.plist', helperPlistPath
|
||||
|
||||
grunt.registerTask 'set-development-version', "Sets version to current sha", ->
|
||||
done = @async()
|
||||
spawn cmd: 'script/set-version', args: [BUILD_DIR], (error, result, code) ->
|
||||
done(error)
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
done = @async()
|
||||
args = ["-f", "-v", "-s", "Developer ID Application: GitHub", SHELL_APP_DIR]
|
||||
spawn cmd: "codesign", args: args, (error) -> done(error)
|
||||
|
||||
grunt.registerTask 'install', 'Install the built application', ->
|
||||
rm INSTALL_DIR
|
||||
mkdir path.dirname(INSTALL_DIR)
|
||||
cp SHELL_APP_DIR, INSTALL_DIR
|
||||
|
||||
grunt.registerTask 'update-atom-shell', 'Update atom-shell', ->
|
||||
done = @async()
|
||||
spawn cmd: 'script/update-atom-shell', (error) -> done(error)
|
||||
|
||||
grunt.registerTask 'test', 'Run the specs', ->
|
||||
done = @async()
|
||||
commands = []
|
||||
commands.push (callback) ->
|
||||
spawn cmd: 'pkill', args: ['Atom'], -> callback()
|
||||
commands.push (callback) ->
|
||||
atomBinary = path.join(CONTENTS_DIR, 'MacOS', 'Atom')
|
||||
spawn cmd: atomBinary, args: ['--test', "--resource-path=#{__dirname}"], (error, result) ->
|
||||
process.stderr.write(result.stderr)
|
||||
process.stdout.write(result.stdout)
|
||||
callback(error)
|
||||
grunt.util.async.waterfall commands, (error) -> done(error)
|
||||
|
||||
grunt.registerTask 'nof', 'Un-focus all specs', ->
|
||||
nof = require.resolve('.bin/nof')
|
||||
spawn({cmd: nof, args: ['spec', 'src']}, @async())
|
||||
grunt.loadTasks('tasks')
|
||||
|
||||
grunt.registerTask('compile', ['coffee', 'less', 'cson'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('ci', ['lint', 'partial-clean', 'update-atom-shell', 'build', 'test'])
|
||||
grunt.registerTask('deploy', ['update-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'update-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('default', ['update-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
|
||||
spawn = (options, callback) ->
|
||||
grunt.util.spawn options, (error, results, code) ->
|
||||
grunt.log.errorlns results.stderr if results.stderr
|
||||
callback(error, results, code)
|
||||
|
||||
cp = (source, destination, {filter}={}) ->
|
||||
walkdir.sync source, (sourcePath, stats) ->
|
||||
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)
|
||||
|
||||
if grunt.file.exists(destinationPath)
|
||||
fs.chmodSync(destinationPath, fs.statSync(sourcePath).mode)
|
||||
|
||||
grunt.log.writeln("Copied #{source.cyan} to #{destination.cyan}.")
|
||||
|
||||
mkdir = (args...) ->
|
||||
grunt.file.mkdir(args...)
|
||||
|
||||
rm = (args...) ->
|
||||
grunt.file.delete(args..., force: true) if grunt.file.exists(args...)
|
||||
|
||||
@@ -29,7 +29,7 @@ on creating your first package.
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software)), Atom packages
|
||||
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software\)), Atom packages
|
||||
can contain a _package.json_ file in their top-level directory. This file contains metadata
|
||||
about the package, such as the path to its "main" module, library dependencies,
|
||||
and manifests specifying the order in which its resources should be loaded.
|
||||
|
||||
+13
-10
@@ -8,19 +8,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"coffee-script": "1.6.2",
|
||||
"ctags": "0.3.0",
|
||||
"ctags": "0.5.0",
|
||||
"oniguruma": "0.16.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"git-utils": "0.17.0",
|
||||
"git-utils": "0.19.0",
|
||||
"underscore": "1.4.4",
|
||||
"d3": "3.0.8",
|
||||
"coffee-cache": "0.1.0",
|
||||
"pegjs": "0.7.0",
|
||||
"async": "0.2.6",
|
||||
"nak": "0.2.16",
|
||||
"spellchecker": "0.3.0",
|
||||
"pathwatcher": "0.3.0",
|
||||
"keytar": "0.4.0",
|
||||
"spellchecker": "0.6.0",
|
||||
"pathwatcher": "0.5.0",
|
||||
"keytar": "0.9.0",
|
||||
"ls-archive": "0.9.0",
|
||||
"temp": "0.5.0",
|
||||
"rimraf": "2.1.4",
|
||||
@@ -30,9 +30,12 @@
|
||||
"roaster": "0.0.5",
|
||||
"jqueryui-browser": "1.10.2-1",
|
||||
"optimist": "0.4.0",
|
||||
"season": "0.7.0",
|
||||
"season": "0.10.0",
|
||||
"humanize-plus": "1.1.0",
|
||||
"semver": "1.1.4",
|
||||
"guid": "0.0.10",
|
||||
"tantamount": "0.3.0",
|
||||
"coffeestack": "0.4.0",
|
||||
"c-tmbundle": "1.0.0",
|
||||
"coffee-script-tmbundle": "2.0.0",
|
||||
"css-tmbundle": "1.0.0",
|
||||
@@ -67,16 +70,16 @@
|
||||
"nslog": "0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.11",
|
||||
"biscotto": "0.0.12",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "0.0.6",
|
||||
"grunt-lesslint": "0.7.0",
|
||||
"grunt-cson": "0.3.0",
|
||||
"grunt-lesslint": "0.8.0",
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.5.2",
|
||||
"jasmine-focused": "~0.6.0",
|
||||
"jasmine-focused": "~0.7.0",
|
||||
"walkdir": "0.0.7"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
@@ -36,6 +36,17 @@
|
||||
<string>speakeasy.pem</string>
|
||||
<key>SUScheduledCheckInterval</key>
|
||||
<string>3600</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>atom</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Atom Shared Session Protocol</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -7,4 +7,5 @@ cd "$(dirname "$0")/.."
|
||||
rm -rf ~/.atom
|
||||
git clean -dff
|
||||
|
||||
rm -fr node_modules
|
||||
./script/test
|
||||
|
||||
@@ -6,6 +6,7 @@ set -ex
|
||||
# xcode
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
rm -fr node_modules
|
||||
./script/bootstrap
|
||||
./node_modules/.bin/grunt --build-dir="$BUILT_PRODUCTS_DIR" deploy
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ RootView = require 'root-view'
|
||||
{$$} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
Exec = require('child_process').exec
|
||||
path = require('path')
|
||||
path = require 'path'
|
||||
|
||||
describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
@@ -293,28 +293,3 @@ describe "the `atom` global", ->
|
||||
describe ".getVersion", ->
|
||||
it "returns the current version number", ->
|
||||
expect(typeof atom.getVersion()).toBe 'string'
|
||||
|
||||
describe "API documentation", ->
|
||||
it "meets a minimum threshold for /app (with no errors)", ->
|
||||
docRunner = jasmine.createSpy("docRunner")
|
||||
Exec "./node_modules/.bin/coffee ./node_modules/.bin/biscotto -- --statsOnly src/app/", cwd: project.resolve('../..'), docRunner
|
||||
waitsFor ->
|
||||
docRunner.callCount > 0
|
||||
|
||||
runs ->
|
||||
# error
|
||||
expect(docRunner.argsForCall[0][0]).toBeNull()
|
||||
|
||||
results = docRunner.argsForCall[0][1].split("\n")
|
||||
results.pop()
|
||||
|
||||
errors = parseInt results.pop().match(/\d+/)
|
||||
if errors > 0
|
||||
console.error results.join('\n')
|
||||
throw new Error("There were errors compiling documentation. See console for details.")
|
||||
|
||||
coverage = parseFloat results.pop().match(/.+?%/)
|
||||
expect(coverage).toBeGreaterThan 75
|
||||
|
||||
# stderr
|
||||
expect(docRunner.argsForCall[0][2]).toBe ''
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
fs = require 'fs'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
CSON = require 'season'
|
||||
|
||||
describe "Config", ->
|
||||
describe ".get(keyPath)", ->
|
||||
@@ -210,6 +211,13 @@ describe "Config", ->
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the config file does not exist", ->
|
||||
it "creates it with an empty object", ->
|
||||
fsUtils.makeTree(config.configDirPath)
|
||||
config.loadUserConfig()
|
||||
expect(fsUtils.exists(config.configFilePath)).toBe true
|
||||
expect(CSON.readFileSync(config.configFilePath)).toEqual {}
|
||||
|
||||
describe ".observeUserConfig()", ->
|
||||
updatedHandler = null
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{createSite} = require 'telepath'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "EditSession replication", ->
|
||||
[editSession1, editSession2] = []
|
||||
beforeEach ->
|
||||
editSession1 = project.open('sample.js')
|
||||
doc1 = editSession1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc1.connect(doc2)
|
||||
editSession2 = deserialize(doc2)
|
||||
|
||||
it "replicates the scroll position", ->
|
||||
editor1 = new Editor(editSession1)
|
||||
editor2 = new Editor(editSession2)
|
||||
|
||||
editor1.attachToDom().width(50).height(50)
|
||||
editor2.attachToDom().width(50).height(50)
|
||||
|
||||
editor1.scrollTop(10)
|
||||
expect(editor1.scrollTop()).toBe 10
|
||||
expect(editor2.scrollTop()).toBe 10
|
||||
|
||||
editor2.scrollLeft(20)
|
||||
expect(editor2.scrollLeft()).toBe 20
|
||||
expect(editor1.scrollLeft()).toBe 20
|
||||
@@ -2117,6 +2117,26 @@ describe "EditSession", ->
|
||||
expect(editSession.isFoldedAtScreenRow(4)).toBeTruthy()
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".replaceSelectedText(options, fn)", ->
|
||||
describe "when no text is selected", ->
|
||||
it "inserts the text returned from the function at the cursor position", ->
|
||||
editSession.replaceSelectedText {}, -> '123'
|
||||
expect(buffer.lineForRow(0)).toBe '123var quicksort = function () {'
|
||||
|
||||
editSession.replaceSelectedText {selectWordIfEmpty: true}, -> 'var'
|
||||
editSession.setCursorBufferPosition([0])
|
||||
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
|
||||
|
||||
editSession.setCursorBufferPosition([10])
|
||||
editSession.replaceSelectedText null, -> ''
|
||||
expect(buffer.lineForRow(10)).toBe ''
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "replaces the selected text with the text returned from the function", ->
|
||||
editSession.setSelectedBufferRange([[0, 1], [0, 3]])
|
||||
editSession.replaceSelectedText {}, -> 'ia'
|
||||
expect(buffer.lineForRow(0)).toBe 'via quicksort = function () {'
|
||||
|
||||
describe ".transpose()", ->
|
||||
it "swaps two characters", ->
|
||||
editSession.buffer.setText("abc")
|
||||
|
||||
@@ -124,7 +124,7 @@ describe "Editor", ->
|
||||
previousScrollTop = editor.scrollTop()
|
||||
previousScrollLeft = editor.scrollLeft()
|
||||
|
||||
newEditSession.scrollTop = 120
|
||||
newEditSession.setScrollTop(120)
|
||||
newEditSession.setSelectedBufferRange([[40, 0], [43, 1]])
|
||||
|
||||
editor.edit(newEditSession)
|
||||
@@ -214,18 +214,18 @@ describe "Editor", ->
|
||||
expect(editor.scrollTop()).toBe 50
|
||||
|
||||
it "sets the new scroll top position on the active edit session", ->
|
||||
expect(editor.activeEditSession.scrollTop).toBe 0
|
||||
expect(editor.activeEditSession.getScrollTop()).toBe 0
|
||||
editor.scrollTop(123)
|
||||
expect(editor.activeEditSession.scrollTop).toBe 123
|
||||
expect(editor.activeEditSession.getScrollTop()).toBe 123
|
||||
|
||||
describe ".scrollHorizontally(pixelPosition)", ->
|
||||
it "sets the new scroll left position on the active edit session", ->
|
||||
editor.attachToDom(heightInLines: 5)
|
||||
setEditorWidthInChars(editor, 5)
|
||||
expect(editor.activeEditSession.scrollLeft).toBe 0
|
||||
expect(editor.activeEditSession.getScrollLeft()).toBe 0
|
||||
editor.scrollHorizontally(left: 50)
|
||||
expect(editor.activeEditSession.scrollLeft).toBeGreaterThan 0
|
||||
expect(editor.activeEditSession.scrollLeft).toBe editor.scrollLeft()
|
||||
expect(editor.activeEditSession.getScrollLeft()).toBeGreaterThan 0
|
||||
expect(editor.activeEditSession.getScrollLeft()).toBe editor.scrollLeft()
|
||||
|
||||
describe "editor:attached event", ->
|
||||
it 'only triggers an editor:attached event when it is first added to the DOM', ->
|
||||
|
||||
@@ -35,10 +35,22 @@ describe "Keymap", ->
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'alt-meta-x'
|
||||
|
||||
event = keydownEvent(',', metaKey: true)
|
||||
event.which = 188
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-,'
|
||||
|
||||
describe "when no binding matches the event's keystroke", ->
|
||||
it "does not return false so the event continues to propagate", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).not.toBe false
|
||||
|
||||
describe "when a non-English keyboard language is used", ->
|
||||
it "uses the physical character pressed instead of the character it maps to in the current language", ->
|
||||
event = keydownEvent('U+03B6', metaKey: true) # This is the 'z' key using the Greek keyboard layout
|
||||
event.which = 122
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-z'
|
||||
|
||||
describe "when at least one binding fully matches the event's keystroke", ->
|
||||
describe "when the event's target node matches a selector with a matching binding", ->
|
||||
it "triggers the command event associated with that binding on the target node and returns false", ->
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
{createSite} = require 'telepath'
|
||||
{View} = require 'space-pen'
|
||||
PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
|
||||
describe "PaneContainer replication", ->
|
||||
[container1, container2, pane1a, pane1b, pane1c] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getState: -> @serialize()
|
||||
getUri: -> "/tmp/#{@name}"
|
||||
isEqual: (other) -> @name is other.name
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
container1 = new PaneContainer
|
||||
pane1a = new Pane(new TestView('A'))
|
||||
container1.setRoot(pane1a)
|
||||
pane1b = pane1a.splitRight(new TestView('B'))
|
||||
pane1c = pane1b.splitDown(new TestView('C'))
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc1.connect(doc2)
|
||||
container2 = deserialize(doc2)
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
|
||||
it "replicates the inital state of a pane container with splits", ->
|
||||
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container1.find('.row > :eq(1)')).toHaveClass 'column'
|
||||
expect(container1.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
|
||||
expect(container1.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('.row > :eq(1)')).toHaveClass 'column'
|
||||
expect(container2.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
|
||||
expect(container2.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
|
||||
|
||||
it "replicates the splitting of panes", ->
|
||||
container1.attachToDom().width(400).height(200)
|
||||
container2.attachToDom().width(400).height(200)
|
||||
|
||||
pane1d = pane1a.splitRight(new TestView('D'))
|
||||
|
||||
expect(container1.find('.row > :eq(1):contains(D)')).toExist()
|
||||
expect(container2.find('.row > :eq(1):contains(D)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(1):contains(D)').outerWidth()).toBe container1.find('.row > :eq(1):contains(D)').outerWidth()
|
||||
|
||||
pane1d.splitDown(new TestView('E'))
|
||||
|
||||
expect(container1.find('.row > :eq(1)')).toHaveClass('column')
|
||||
expect(container1.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
|
||||
expect(container1.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(1)')).toHaveClass('column')
|
||||
expect(container2.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
|
||||
expect(container2.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
|
||||
|
||||
it "replicates removal of panes", ->
|
||||
pane1c.remove()
|
||||
|
||||
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container1.find('.row > :eq(1):contains(B)')).toExist()
|
||||
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('.row > :eq(1):contains(B)')).toExist()
|
||||
|
||||
pane1b.remove()
|
||||
|
||||
expect(container1.find('> :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('> :eq(0):contains(A)')).toExist()
|
||||
|
||||
pane1a.remove()
|
||||
|
||||
expect(container1.children()).not.toExist()
|
||||
expect(container2.children()).not.toExist()
|
||||
@@ -20,7 +20,7 @@ describe "PaneContainer", ->
|
||||
|
||||
container = new PaneContainer
|
||||
pane1 = new Pane(new TestView('1'))
|
||||
container.append(pane1)
|
||||
container.setRoot(pane1)
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
@@ -151,41 +151,27 @@ describe "PaneContainer", ->
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "resolves the returned promise after modified files are saved", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, f, g, noSaveFn) -> noSaveFn()
|
||||
spyOn(atom, "confirmSync").andReturn(0)
|
||||
|
||||
promiseHandler = jasmine.createSpy("promiseHandler")
|
||||
failedPromiseHandler = jasmine.createSpy("failedPromiseHandler")
|
||||
promise = container.confirmClose()
|
||||
promise.done promiseHandler
|
||||
promise.fail failedPromiseHandler
|
||||
|
||||
waitsFor ->
|
||||
promiseHandler.wasCalled
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(failedPromiseHandler).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
|
||||
it "rejects the returned promise if the user cancels saving", ->
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, cancelFn, f, g) -> cancelFn()
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
|
||||
promiseHandler = jasmine.createSpy("promiseHandler")
|
||||
failedPromiseHandler = jasmine.createSpy("failedPromiseHandler")
|
||||
promise = container.confirmClose()
|
||||
promise.done promiseHandler
|
||||
promise.fail failedPromiseHandler
|
||||
|
||||
waitsFor ->
|
||||
failedPromiseHandler.wasCalled
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(promiseHandler).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
@@ -198,7 +184,7 @@ describe "PaneContainer", ->
|
||||
expect(newContainer.find('.row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.row > .column > :contains(2)').height()).toBe 100
|
||||
|
||||
it "removes empty panes on deserialization", ->
|
||||
xit "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = deserialize(container.serialize())
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
{createSite} = require 'telepath'
|
||||
|
||||
describe "Pane replication", ->
|
||||
[editSession1a, editSession1b, container1, pane1, doc1] = []
|
||||
[editSession2a, editSession2b, container2, pane2, doc2] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession1a = project.open('sample.js')
|
||||
editSession1b = project.open('sample.txt')
|
||||
container1 = new PaneContainer
|
||||
pane1 = new Pane(editSession1a, editSession1b)
|
||||
container1.setRoot(pane1)
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc1.connect(doc2)
|
||||
|
||||
container2 = deserialize(doc2)
|
||||
pane2 = container2.getRoot()
|
||||
|
||||
it "replicates the initial state of the panes", ->
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates addition and removal of pane items", ->
|
||||
pane1.addItem(project.open('css.css'), 1)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
pane1.removeItemAtIndex(2)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates the movement of pane items", ->
|
||||
pane1.moveItem(editSession1a, 1)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates which pane item is active", ->
|
||||
pane1.showNextItem()
|
||||
expect(pane2.activeItem).toEqual pane1.activeItem
|
||||
pane1.showNextItem()
|
||||
expect(pane2.activeItem).toEqual pane1.activeItem
|
||||
+83
-48
@@ -1,19 +1,31 @@
|
||||
PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
{$$} = require 'space-pen'
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
|
||||
describe "Pane", ->
|
||||
[container, view1, view2, editSession1, editSession2, pane] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
serialize: -> { deserializer: 'TestView', @id, @text }
|
||||
getUri: -> @id
|
||||
isEqual: (other) -> @id == other.id and @text == other.text
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
container = new PaneContainer
|
||||
view1 = $$ -> @div id: 'view-1', tabindex: -1, 'View 1'
|
||||
view2 = $$ -> @div id: 'view-2', tabindex: -1, 'View 2'
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editSession1 = project.open('sample.js')
|
||||
editSession2 = project.open('sample.txt')
|
||||
pane = new Pane(view1, editSession1, view2, editSession2)
|
||||
container.append(pane)
|
||||
container.setRoot(pane)
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
|
||||
describe ".initialize(items...)", ->
|
||||
it "displays the first item in the pane", ->
|
||||
@@ -56,7 +68,7 @@ describe "Pane", ->
|
||||
describe "when the given item isn't yet in the items list on the pane", ->
|
||||
view3 = null
|
||||
beforeEach ->
|
||||
view3 = $$ -> @div id: 'view-3', "View 3"
|
||||
view3 = new TestView(id: 'view-3', text: "View 3")
|
||||
pane.showItem(editSession1)
|
||||
expect(pane.getActiveItemIndex()).toBe 1
|
||||
|
||||
@@ -82,13 +94,43 @@ describe "Pane", ->
|
||||
expect(editor.activeEditSession).toBe editSession1
|
||||
|
||||
describe "when a valid view has already been appended for another item", ->
|
||||
it "recycles the existing view by assigning the selected item to it", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 1
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).toBe ''
|
||||
expect(editor.activeEditSession).toBe editSession2
|
||||
describe "when the view has a setModel method", ->
|
||||
it "recycles the existing view by assigning the selected item to it", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 1
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).toBe ''
|
||||
expect(editor.activeEditSession).toBe editSession2
|
||||
|
||||
describe "when the view does not have a setModel method", ->
|
||||
it "creates a new view with the item", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
pane.showItem(model1)
|
||||
pane.showItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
pane.showPreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
pane.removeItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
pane.removeItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
describe "when showing a view item", ->
|
||||
it "appends it to the itemViews div if it hasn't already been appended and shows it", ->
|
||||
@@ -106,28 +148,17 @@ describe "Pane", ->
|
||||
|
||||
describe "if the item is modified", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'confirm')
|
||||
spyOn(atom, 'showSaveDialog')
|
||||
spyOn(editSession2, 'save')
|
||||
spyOn(editSession2, 'saveAs')
|
||||
|
||||
atom.confirm.selectOption = (buttonText) ->
|
||||
for arg, i in @argsForCall[0] when arg is buttonText
|
||||
@argsForCall[0][i + 1]?()
|
||||
|
||||
editSession2.insertText('a')
|
||||
expect(editSession2.isModified()).toBeTruthy()
|
||||
pane.destroyItem(editSession2)
|
||||
|
||||
it "presents a dialog with the option to save the item first", ->
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).not.toBe -1
|
||||
expect(editSession2.destroyed).toBeFalsy()
|
||||
|
||||
describe "if the [Save] option is selected", ->
|
||||
describe "when the item has a uri", ->
|
||||
it "saves the item before removing and destroying it", ->
|
||||
atom.confirm.selectOption('Save')
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
|
||||
expect(editSession2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
@@ -137,11 +168,11 @@ describe "Pane", ->
|
||||
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
|
||||
editSession2.buffer.setPath(undefined)
|
||||
|
||||
atom.confirm.selectOption('Save')
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
|
||||
expect(atom.showSaveDialog).toHaveBeenCalled()
|
||||
|
||||
atom.showSaveDialog.argsForCall[0][0]("/selected/path")
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
@@ -149,7 +180,8 @@ describe "Pane", ->
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
it "removes and destroys the item without saving it", ->
|
||||
atom.confirm.selectOption("Don't Save")
|
||||
spyOn(atom, 'confirmSync').andReturn(2)
|
||||
pane.destroyItem(editSession2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
@@ -157,7 +189,8 @@ describe "Pane", ->
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
it "does not save, remove, or destroy the item", ->
|
||||
atom.confirm.selectOption("Cancel")
|
||||
spyOn(atom, 'confirmSync').andReturn(1)
|
||||
pane.destroyItem(editSession2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).not.toBe -1
|
||||
@@ -188,8 +221,9 @@ describe "Pane", ->
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
it "shifts focus to the next pane", ->
|
||||
expect(container.getRoot()).toBe pane
|
||||
container.attachToDom()
|
||||
pane2 = pane.splitRight($$ -> @div class: 'view-3', tabindex: -1, 'View 3')
|
||||
pane2 = pane.splitRight(new TestView(id: 'view-3', text: 'View 3'))
|
||||
pane.focus()
|
||||
expect(pane).toMatchSelector(':has(:focus)')
|
||||
pane.removeItem(item) for item in pane.getItems()
|
||||
@@ -236,7 +270,7 @@ describe "Pane", ->
|
||||
[pane2, view3] = []
|
||||
|
||||
beforeEach ->
|
||||
view3 = $$ -> @div id: 'view-3', "View 3"
|
||||
view3 = new TestView(id: 'view-3', text: "View 3")
|
||||
pane2 = pane.splitRight(view3)
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
@@ -309,7 +343,7 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has no uri", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'showSaveDialog')
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
@@ -319,19 +353,18 @@ describe "Pane", ->
|
||||
|
||||
pane.trigger 'core:save'
|
||||
|
||||
expect(atom.showSaveDialog).toHaveBeenCalled()
|
||||
atom.showSaveDialog.argsForCall[0][0]('/selected/path')
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.trigger 'core:save'
|
||||
expect(atom.showSaveDialog).not.toHaveBeenCalled()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "core:save-as", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'showSaveDialog')
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens the save dialog and calls saveAs on the item with the selected path", ->
|
||||
@@ -340,15 +373,14 @@ describe "Pane", ->
|
||||
|
||||
pane.trigger 'core:save-as'
|
||||
|
||||
expect(atom.showSaveDialog).toHaveBeenCalled()
|
||||
atom.showSaveDialog.argsForCall[0][0]('/selected/path')
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.trigger 'core:save-as'
|
||||
expect(atom.showSaveDialog).not.toHaveBeenCalled()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "pane:show-next-item and pane:show-previous-item", ->
|
||||
it "advances forward/backward through the pane's items, looping around at either end", ->
|
||||
@@ -489,8 +521,8 @@ describe "Pane", ->
|
||||
beforeEach ->
|
||||
pane1 = pane
|
||||
pane.showItem(editSession1)
|
||||
view3 = $$ -> @div id: 'view-3', 'View 3'
|
||||
view4 = $$ -> @div id: 'view-4', 'View 4'
|
||||
view3 = new TestView(id: 'view-3', text: 'View 3')
|
||||
view4 = new TestView(id: 'view-4', text: 'View 4')
|
||||
|
||||
describe "splitRight(items...)", ->
|
||||
it "builds a row if needed, then appends a new pane after itself", ->
|
||||
@@ -677,18 +709,21 @@ describe "Pane", ->
|
||||
expect(pane.itemForUri(editSession2.getUri())).toBe editSession2
|
||||
|
||||
describe "serialization", ->
|
||||
it "can serialize and deserialize the pane and all its serializable items", ->
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual [editSession1, editSession2]
|
||||
expect(newPane.getItems()).toEqual [view1, editSession1, view2, editSession2]
|
||||
|
||||
it "restores the active item on deserialization if it serializable", ->
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.showItem(editSession2)
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession2
|
||||
|
||||
it "defaults to the first item on deserialization if the active item was not serializable", ->
|
||||
xit "defaults to the first item on deserialization if the active item was not serializable", ->
|
||||
expect(view2.serialize?()).toBeFalsy()
|
||||
pane.showItem(view2)
|
||||
|
||||
console.log pane.serialize().toObject()
|
||||
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession1
|
||||
|
||||
@@ -699,7 +734,7 @@ describe "Pane", ->
|
||||
state = pane.serialize()
|
||||
pane.remove()
|
||||
newPane = deserialize(state)
|
||||
container.append(newPane)
|
||||
container.setRoot(newPane)
|
||||
expect(newPane).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
|
||||
@@ -39,64 +39,63 @@ describe "RootView", ->
|
||||
expect(rootView.getActiveView().getText()).toBe buffer.getText()
|
||||
expect(rootView.title).toBe "untitled - #{project.getPath()}"
|
||||
|
||||
describe "when the serialized RootView has a project", ->
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
pane1 = rootView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(project.open('b'))
|
||||
pane3.showItem(project.open('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(project.open('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
pane1 = rootView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(project.open('b'))
|
||||
pane3.showItem(project.open('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(project.open('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
rootView.attachToDom()
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
rootView.attachToDom()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
expect(rootView.getEditors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe project.resolve('a')
|
||||
expect(editor2.getPath()).toBe project.resolve('b')
|
||||
expect(editor3.getPath()).toBe project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
expect(editor1.getPath()).toBe project.resolve('a')
|
||||
expect(editor2.getPath()).toBe project.resolve('b')
|
||||
expect(editor3.getPath()).toBe project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
expect(rootView.title).toBe "#{path.basename(editor2.getPath())} - #{project.getPath()}"
|
||||
expect(rootView.title).toBe "#{path.basename(editor2.getPath())} - #{project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
|
||||
rootView.attachToDom()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
rootView.attachToDom()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
describe "when there is an active view", ->
|
||||
|
||||
@@ -75,6 +75,12 @@ describe "the `syntax` global", ->
|
||||
|
||||
expect(syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
|
||||
describe "when there is no file path", ->
|
||||
it "does not throw an exception (regression)", ->
|
||||
expect(-> syntax.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, '')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, null)).not.toThrow()
|
||||
|
||||
describe ".removeGrammar(grammar)", ->
|
||||
it "removes the grammar, so it won't be returned by selectGrammar", ->
|
||||
grammar = syntax.selectGrammar('foo.js')
|
||||
|
||||
@@ -38,30 +38,43 @@ describe "Window", ->
|
||||
expect($("body")).not.toHaveClass("is-blurred")
|
||||
|
||||
describe "window:close event", ->
|
||||
describe "when no pane items are modified", ->
|
||||
it "calls window.closeWithoutConfirm", ->
|
||||
spyOn window, 'closeWithoutConfirm'
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.closeWithoutConfirm).toHaveBeenCalled()
|
||||
it "closes the window", ->
|
||||
spyOn(window, 'close')
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
|
||||
it "emits the beforeunload event", ->
|
||||
$(window).off 'beforeunload'
|
||||
beforeunload = jasmine.createSpy('beforeunload').andReturn(false)
|
||||
$(window).on 'beforeunload', beforeunload
|
||||
|
||||
$(window).trigger 'window:close'
|
||||
expect(beforeunload).toHaveBeenCalled()
|
||||
|
||||
describe "beforeunload event", ->
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls window.closeWithoutConfirm", ->
|
||||
spyOn(window, 'closeWithoutConfirm')
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, f, g, noSave) -> noSave()
|
||||
it "prompts user to save and and calls rootView.confirmClose", ->
|
||||
spyOn(rootView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.open("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.closeWithoutConfirm).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
$(window).trigger 'beforeunload'
|
||||
expect(rootView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and aborts if dialog is canceled", ->
|
||||
spyOn(window, 'closeWithoutConfirm')
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, cancel) -> cancel()
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.open("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.closeWithoutConfirm).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(window.onbeforeunload(new Event('beforeunload'))).toBeTruthy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
editSession = rootView.open("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
expect(window.onbeforeunload(new Event('beforeunload'))).toBeFalsy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
@@ -125,18 +138,14 @@ describe "Window", ->
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
windowState = {}
|
||||
jasmine.unspy(atom, 'setWindowState')
|
||||
spyOn(atom, 'setWindowState').andCallFake (key, value) -> windowState[key] = value
|
||||
|
||||
# JSON.stringify removes keys with undefined values
|
||||
rootViewState = rootView.serialize()
|
||||
syntaxState = syntax.serialize()
|
||||
|
||||
window.unloadEditorWindow()
|
||||
|
||||
expect(windowState.rootView).toEqual rootViewState
|
||||
expect(windowState.syntax).toEqual syntaxState
|
||||
expect(atom.getWindowState().getObject('rootView')).toEqual rootViewState.toObject()
|
||||
expect(atom.getWindowState().getObject('syntax')).toEqual syntaxState
|
||||
expect(atom.saveWindowState).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
rootView.open('sample.js')
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
$ = require 'jquery'
|
||||
{View, $$} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
{convertStackTrace} = require 'coffeestack'
|
||||
|
||||
sourceMaps = {}
|
||||
formatStackTrace = (stackTrace) ->
|
||||
return stackTrace unless stackTrace
|
||||
|
||||
jasminePath = require.resolve('jasmine')
|
||||
jasminePattern = new RegExp("\\(#{_.escapeRegExp(jasminePath)}:\\d+:\\d+\\)\\s*$")
|
||||
convertedLines = []
|
||||
for line in stackTrace.split('\n')
|
||||
convertedLines.push(line) unless jasminePattern.test(line)
|
||||
|
||||
convertStackTrace(convertedLines.join('\n'), sourceMaps)
|
||||
|
||||
module.exports =
|
||||
class AtomReporter extends View
|
||||
@@ -42,6 +55,7 @@ class AtomReporter extends View
|
||||
|
||||
reportSpecResults: (spec) ->
|
||||
@completeSpecCount++
|
||||
spec.endedAt = new Date().getTime()
|
||||
@specComplete(spec)
|
||||
@updateStatusView(spec)
|
||||
|
||||
@@ -99,7 +113,7 @@ class AtomReporter extends View
|
||||
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
|
||||
@message.text rootSuite.description
|
||||
|
||||
time = "#{Math.round((new Date().getTime() - @startedAt.getTime()) / 10)}"
|
||||
time = "#{Math.round((spec.endedAt - @startedAt.getTime()) / 10)}"
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time.text "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
@@ -166,9 +180,10 @@ class SpecResultView extends View
|
||||
@description.html @spec.description
|
||||
|
||||
for result in @spec.results().getItems() when not result.passed()
|
||||
stackTrace = formatStackTrace(result.trace.stack)
|
||||
@specFailures.append $$ ->
|
||||
@div result.message, class: 'resultMessage fail'
|
||||
@div result.trace.stack, class: 'stackTrace' if result.trace.stack
|
||||
@div stackTrace, class: 'stackTrace' if stackTrace
|
||||
|
||||
attach: ->
|
||||
@parentSuiteView().append this
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
setGlobalFocusPriority = (priority) ->
|
||||
env = jasmine.getEnv()
|
||||
env.focusPriority = 1 unless env.focusPriority
|
||||
env.focusPriority = priority if priority > env.focusPriority
|
||||
|
||||
window.fdescribe = (description, specDefinitions, priority) ->
|
||||
priority = 1 unless priority
|
||||
setGlobalFocusPriority(priority)
|
||||
suite = describe(description, specDefinitions)
|
||||
suite.focusPriority = priority
|
||||
suite
|
||||
|
||||
window.ffdescribe = (description, specDefinitions) ->
|
||||
fdescribe(description, specDefinitions, 2)
|
||||
|
||||
window.fffdescribe = (description, specDefinitions) ->
|
||||
fdescribe(description, specDefinitions, 3)
|
||||
|
||||
window.fit = (description, definition, priority) ->
|
||||
priority = 1 unless priority
|
||||
setGlobalFocusPriority(priority)
|
||||
spec = it(description, definition)
|
||||
spec.focusPriority = priority
|
||||
spec
|
||||
|
||||
window.ffit = (description, specDefinitions) ->
|
||||
fit(description, specDefinitions, 2)
|
||||
|
||||
window.fffit = (description, specDefinitions) ->
|
||||
fit(description, specDefinitions, 3)
|
||||
@@ -1,5 +1,5 @@
|
||||
require 'window'
|
||||
window.setUpEnvironment()
|
||||
window.setUpEnvironment('spec')
|
||||
window.restoreDimensions()
|
||||
|
||||
nakedLoad 'jasmine-jquery'
|
||||
@@ -25,6 +25,9 @@ keymap.loadBundledKeymaps()
|
||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
atom.windowMode = 'spec'
|
||||
atom.saveWindowState()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
|
||||
@@ -41,7 +44,7 @@ beforeEach ->
|
||||
window.resetTimeouts()
|
||||
atom.windowMode = 'editor'
|
||||
atom.packageStates = {}
|
||||
spyOn(atom, 'setWindowState')
|
||||
spyOn(atom, 'saveWindowState')
|
||||
syntax.clearGrammarOverrides()
|
||||
syntax.clearProperties()
|
||||
|
||||
@@ -89,8 +92,9 @@ afterEach ->
|
||||
if git?
|
||||
git.destroy()
|
||||
window.git = null
|
||||
$('#jasmine-content').empty()
|
||||
jasmine.unspy(atom, 'setWindowState')
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
ensureNoPathSubscriptions()
|
||||
syntax.off()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
@@ -230,7 +234,7 @@ $.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) => window.keymap.handleKeyEvent(e)
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
$('#jasmine-content').append(this)
|
||||
@appendTo($('#jasmine-content'))
|
||||
|
||||
$.fn.simulateDomAttachment = ->
|
||||
$('<html>').append(this)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
describe "fsUtils", ->
|
||||
@@ -100,7 +101,7 @@ describe "fsUtils", ->
|
||||
paths = fsUtils.listSync(project.getPath(), ['.css', 'coffee'])
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(path).toMatch /(css|coffee)$/ for path in paths
|
||||
expect(listedPath).toMatch /(css|coffee)$/ for listedPath in paths
|
||||
|
||||
describe ".list(path, [extensions,] callback)", ->
|
||||
paths = null
|
||||
@@ -123,4 +124,10 @@ describe "fsUtils", ->
|
||||
runs ->
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(path).toMatch /(css|coffee)$/ for path in paths
|
||||
expect(listedPath).toMatch /(css|coffee)$/ for listedPath in paths
|
||||
|
||||
describe ".absolute(relativePath)", ->
|
||||
it "converts a leading ~ segment to the HOME directory", ->
|
||||
expect(fsUtils.absolute('~')).toBe fs.realpathSync(process.env.HOME)
|
||||
expect(fsUtils.absolute(path.join('~', 'does', 'not', 'exist'))).toBe path.join(process.env.HOME, 'does', 'not', 'exist')
|
||||
expect(fsUtils.absolute('~test')).toBe '~test'
|
||||
|
||||
+32
-20
@@ -7,6 +7,8 @@ ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
crypto = require 'crypto'
|
||||
path = require 'path'
|
||||
dialog = remote.require 'dialog'
|
||||
telepath = require 'telepath'
|
||||
|
||||
window.atom =
|
||||
loadedThemes: []
|
||||
@@ -173,6 +175,9 @@ window.atom =
|
||||
openConfig: ->
|
||||
ipc.sendChannel('open-config')
|
||||
|
||||
openWindow: (windowSettings) ->
|
||||
ipc.sendChannel('open-window', windowSettings)
|
||||
|
||||
confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
|
||||
buttons = []
|
||||
callbacks = []
|
||||
@@ -181,18 +186,22 @@ window.atom =
|
||||
buttons.push buttonLabelsAndCallbacks.shift()
|
||||
callbacks.push buttonLabelsAndCallbacks.shift()
|
||||
|
||||
chosen = remote.require('dialog').showMessageBox
|
||||
chosen = @confirmSync(message, detailedMessage, buttons)
|
||||
callbacks[chosen]?()
|
||||
|
||||
confirmSync: (message, detailedMessage, buttons, browserWindow = null) ->
|
||||
dialog.showMessageBox browserWindow,
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttons
|
||||
|
||||
callbacks[chosen]?()
|
||||
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
|
||||
showSaveDialogSync: ->
|
||||
currentWindow = remote.getCurrentWindow()
|
||||
result = remote.require('dialog').showSaveDialog currentWindow, title: 'Save File'
|
||||
callback(result)
|
||||
dialog.showSaveDialog currentWindow, title: 'Save File'
|
||||
|
||||
openDevTools: ->
|
||||
remote.getCurrentWindow().openDevTools()
|
||||
@@ -227,8 +236,8 @@ window.atom =
|
||||
|
||||
getWindowStatePath: ->
|
||||
switch @windowMode
|
||||
when 'config'
|
||||
filename = 'config'
|
||||
when 'config', 'spec'
|
||||
filename = @windowMode
|
||||
when 'editor'
|
||||
{initialPath} = @getLoadSettings()
|
||||
if initialPath
|
||||
@@ -240,20 +249,12 @@ window.atom =
|
||||
else
|
||||
null
|
||||
|
||||
saveWindowState: (windowState) ->
|
||||
windowStateJson = JSON.stringify(windowState)
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
fsUtils.writeSync(windowStatePath, windowStateJson)
|
||||
else
|
||||
@getLoadSettings().windowState = windowStateJson
|
||||
|
||||
setWindowState: (keyPath, value) ->
|
||||
windowState = @getWindowState()
|
||||
_.setValueForKeyPath(windowState, keyPath, value)
|
||||
@saveWindowState(windowState)
|
||||
windowState.set(keyPath, value)
|
||||
windowState
|
||||
|
||||
getWindowState: (keyPath) ->
|
||||
loadWindowState: ->
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
if fsUtils.exists(windowStatePath)
|
||||
try
|
||||
@@ -269,10 +270,21 @@ window.atom =
|
||||
console.warn "Error parsing window state: #{windowStatePath}", error.stack, error
|
||||
|
||||
windowState ?= {}
|
||||
if keyPath
|
||||
_.valueForKeyPath(windowState, keyPath)
|
||||
telepath.Document.create(windowState, site: telepath.createSite(1))
|
||||
|
||||
saveWindowState: ->
|
||||
windowStateJson = JSON.stringify(@getWindowState().toObject())
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
fsUtils.writeSync(windowStatePath, windowStateJson)
|
||||
else
|
||||
windowState
|
||||
@getLoadSettings().windowState = windowStateJson
|
||||
|
||||
getWindowState: (keyPath) ->
|
||||
@windowState ?= @loadWindowState()
|
||||
if keyPath
|
||||
@windowState.get(keyPath)
|
||||
else
|
||||
@windowState
|
||||
|
||||
update: ->
|
||||
ipc.sendChannel 'install-update'
|
||||
|
||||
+13
-10
@@ -66,16 +66,19 @@ class Config
|
||||
@observeUserConfig()
|
||||
|
||||
loadUserConfig: ->
|
||||
if fsUtils.exists(@configFilePath)
|
||||
try
|
||||
userConfig = CSON.readFileSync(@configFilePath)
|
||||
_.extend(@settings, userConfig)
|
||||
@configFileHasErrors = false
|
||||
@trigger 'updated'
|
||||
catch e
|
||||
@configFileHasErrors = true
|
||||
console.error "Failed to load user config '#{@configFilePath}'", e.message
|
||||
console.error e.stack
|
||||
if !fsUtils.exists(@configFilePath)
|
||||
fsUtils.makeTree(path.dirname(@configFilePath))
|
||||
CSON.writeFileSync(@configFilePath, {})
|
||||
|
||||
try
|
||||
userConfig = CSON.readFileSync(@configFilePath)
|
||||
_.extend(@settings, userConfig)
|
||||
@configFileHasErrors = false
|
||||
@trigger 'updated'
|
||||
catch e
|
||||
@configFileHasErrors = true
|
||||
console.error "Failed to load user config '#{@configFilePath}'", e.message
|
||||
console.error e.stack
|
||||
|
||||
observeUserConfig: ->
|
||||
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
_ = require 'underscore'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
telepath = require 'telepath'
|
||||
Point = require 'point'
|
||||
Buffer = require 'text-buffer'
|
||||
LanguageMode = require 'language-mode'
|
||||
@@ -7,14 +11,13 @@ Selection = require 'selection'
|
||||
EventEmitter = require 'event-emitter'
|
||||
Subscriber = require 'subscriber'
|
||||
Range = require 'range'
|
||||
_ = require 'underscore'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
TextMateScopeSelector = require 'text-mate-scope-selector'
|
||||
|
||||
# An `EditSession` manages the states between {Editor}s, {Buffer}s, and the project as a whole.
|
||||
module.exports =
|
||||
class EditSession
|
||||
@acceptsDocuments: true
|
||||
|
||||
registerDeserializer(this)
|
||||
|
||||
### Internal ###
|
||||
@@ -22,17 +25,8 @@ class EditSession
|
||||
@version: 1
|
||||
|
||||
@deserialize: (state) ->
|
||||
session = project.buildEditSessionForBuffer(Buffer.deserialize(state.buffer))
|
||||
if !session?
|
||||
console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists" if state.buffer
|
||||
session = project.open(null)
|
||||
session.setScrollTop(state.scrollTop)
|
||||
session.setScrollLeft(state.scrollLeft)
|
||||
session.setCursorScreenPosition(state.cursorScreenPosition)
|
||||
session
|
||||
new EditSession(state)
|
||||
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
languageMode: null
|
||||
displayBuffer: null
|
||||
cursors: null
|
||||
@@ -40,17 +34,34 @@ class EditSession
|
||||
softTabs: true
|
||||
softWrap: false
|
||||
|
||||
constructor: ({@project, @buffer, tabLength, softTabs, @softWrap}) ->
|
||||
constructor: (optionsOrState) ->
|
||||
if optionsOrState instanceof telepath.Document
|
||||
project.editSessions.push(this)
|
||||
@state = optionsOrState
|
||||
{tabLength, softTabs, @softWrap} = @state.toObject()
|
||||
@buffer = deserialize(@state.get('buffer'))
|
||||
@setScrollTop(@state.get('scrollTop'))
|
||||
@setScrollLeft(@state.get('scrollLeft'))
|
||||
cursorScreenPosition = @state.getObject('cursorScreenPosition')
|
||||
else
|
||||
{@buffer, tabLength, softTabs, @softWrap} = optionsOrState
|
||||
@state = telepath.Document.create
|
||||
deserializer: 'EditSession'
|
||||
version: @constructor.version
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
cursorScreenPosition = [0, 0]
|
||||
|
||||
@softTabs = @buffer.usesSoftTabs() ? softTabs ? true
|
||||
@languageMode = new LanguageMode(this, @buffer.getExtension())
|
||||
@displayBuffer = new DisplayBuffer(@buffer, { @languageMode, tabLength })
|
||||
@cursors = []
|
||||
@selections = []
|
||||
@addCursorAtScreenPosition([0, 0])
|
||||
@addCursorAtScreenPosition(cursorScreenPosition)
|
||||
|
||||
@buffer.retain()
|
||||
@subscribe @buffer, "path-changed", =>
|
||||
@project.setPath(path.dirname(@getPath())) unless @project.getPath()?
|
||||
project.setPath(path.dirname(@getPath())) unless project.getPath()?
|
||||
@trigger "title-changed"
|
||||
@trigger "path-changed"
|
||||
@subscribe @buffer, "contents-conflicted", => @trigger "contents-conflicted"
|
||||
@@ -64,6 +75,13 @@ class EditSession
|
||||
|
||||
@displayBuffer.on 'grammar-changed', => @handleGrammarChange()
|
||||
|
||||
@state.observe ({key, newValue}) =>
|
||||
switch key
|
||||
when 'scrollTop'
|
||||
@trigger 'scroll-top-changed', newValue
|
||||
when 'scrollLeft'
|
||||
@trigger 'scroll-left-changed', newValue
|
||||
|
||||
getViewClass: ->
|
||||
require 'editor'
|
||||
|
||||
@@ -75,21 +93,26 @@ class EditSession
|
||||
selection.destroy() for selection in @getSelections()
|
||||
@displayBuffer.destroy()
|
||||
@languageMode.destroy()
|
||||
@project?.removeEditSession(this)
|
||||
project?.removeEditSession(this)
|
||||
@trigger 'destroyed'
|
||||
@off()
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'EditSession'
|
||||
version: @constructor.version
|
||||
buffer: @buffer.serialize()
|
||||
scrollTop: @getScrollTop()
|
||||
scrollLeft: @getScrollLeft()
|
||||
cursorScreenPosition: @getCursorScreenPosition().serialize()
|
||||
@state.set
|
||||
buffer: @buffer.serialize()
|
||||
scrollTop: @getScrollTop()
|
||||
scrollLeft: @getScrollLeft()
|
||||
tabLength: @getTabLength()
|
||||
softTabs: @softTabs
|
||||
softWrap: @softWrap
|
||||
cursorScreenPosition: @getCursorScreenPosition().serialize()
|
||||
@state
|
||||
|
||||
getState: -> @serialize()
|
||||
|
||||
# Creates a copy of the current {EditSession}.Returns an identical `EditSession`.
|
||||
copy: ->
|
||||
EditSession.deserialize(@serialize(), @project)
|
||||
EditSession.deserialize(@serialize())
|
||||
|
||||
### Public ###
|
||||
|
||||
@@ -129,8 +152,8 @@ class EditSession
|
||||
isEqual: (other) ->
|
||||
return false unless other instanceof EditSession
|
||||
@buffer == other.buffer and
|
||||
@scrollTop == other.getScrollTop() and
|
||||
@scrollLeft == other.getScrollLeft() and
|
||||
@getScrollTop() == other.getScrollTop() and
|
||||
@getScrollLeft() == other.getScrollLeft() and
|
||||
@getCursorScreenPosition().isEqual(other.getCursorScreenPosition())
|
||||
|
||||
setVisible: (visible) -> @displayBuffer.setVisible(visible)
|
||||
@@ -138,22 +161,23 @@ class EditSession
|
||||
# Defines the value of the `EditSession`'s `scrollTop` property.
|
||||
#
|
||||
# scrollTop - A {Number} defining the `scrollTop`, in pixels.
|
||||
setScrollTop: (@scrollTop) ->
|
||||
setScrollTop: (scrollTop) -> @state.set('scrollTop', scrollTop)
|
||||
|
||||
# Gets the value of the `EditSession`'s `scrollTop` property.
|
||||
#
|
||||
# Returns a {Number} defining the `scrollTop`, in pixels.
|
||||
getScrollTop: -> @scrollTop
|
||||
getScrollTop: ->
|
||||
@state.get('scrollTop') ? 0
|
||||
|
||||
# Defines the value of the `EditSession`'s `scrollLeft` property.
|
||||
#
|
||||
# scrollLeft - A {Number} defining the `scrollLeft`, in pixels.
|
||||
setScrollLeft: (@scrollLeft) ->
|
||||
setScrollLeft: (scrollLeft) -> @state.set('scrollLeft', scrollLeft)
|
||||
|
||||
# Gets the value of the `EditSession`'s `scrollLeft` property.
|
||||
#
|
||||
# Returns a {Number} defining the `scrollLeft`, in pixels.
|
||||
getScrollLeft: -> @scrollLeft
|
||||
getScrollLeft: -> @state.get('scrollLeft')
|
||||
|
||||
# Defines the limit at which the buffer begins to soft wrap text.
|
||||
#
|
||||
@@ -729,12 +753,12 @@ class EditSession
|
||||
|
||||
replaceSelectedText: (options={}, fn) ->
|
||||
{selectWordIfEmpty} = options
|
||||
@mutateSelectedText (selection) =>
|
||||
@mutateSelectedText (selection) ->
|
||||
range = selection.getBufferRange()
|
||||
if selectWordIfEmpty and selection.isEmpty()
|
||||
selection.selectWord()
|
||||
text = selection.getText()
|
||||
selection.delete()
|
||||
selection.deleteSelectedText()
|
||||
selection.insertText(fn(text))
|
||||
selection.setBufferRange(range)
|
||||
|
||||
@@ -1274,7 +1298,7 @@ class EditSession
|
||||
abort: -> @buffer.abort()
|
||||
|
||||
inspect: ->
|
||||
JSON.stringify @serialize()
|
||||
JSON.stringify @state.toObject()
|
||||
|
||||
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
|
||||
|
||||
|
||||
@@ -728,6 +728,12 @@ class Editor extends View
|
||||
@activeEditSession.on 'screen-lines-changed.editor', (e) =>
|
||||
@handleScreenLinesChange(e)
|
||||
|
||||
@activeEditSession.on 'scroll-top-changed.editor', (scrollTop) =>
|
||||
@scrollTop(scrollTop)
|
||||
|
||||
@activeEditSession.on 'scroll-left-changed.editor', (scrollLeft) =>
|
||||
@scrollLeft(scrollLeft)
|
||||
|
||||
@trigger 'editor:path-changed'
|
||||
@resetDisplay()
|
||||
|
||||
@@ -1089,8 +1095,8 @@ class Editor extends View
|
||||
|
||||
@clearRenderedLines()
|
||||
@removeAllCursorAndSelectionViews()
|
||||
editSessionScrollTop = @activeEditSession.scrollTop ? 0
|
||||
editSessionScrollLeft = @activeEditSession.scrollLeft ? 0
|
||||
editSessionScrollTop = @activeEditSession.getScrollTop() ? 0
|
||||
editSessionScrollLeft = @activeEditSession.getScrollLeft() ? 0
|
||||
@updateLayerDimensions()
|
||||
@scrollTop(editSessionScrollTop)
|
||||
@scrollLeft(editSessionScrollLeft)
|
||||
|
||||
@@ -150,6 +150,7 @@ class Keymap
|
||||
if event.originalEvent.keyIdentifier.indexOf('U+') == 0
|
||||
hexCharCode = event.originalEvent.keyIdentifier[2..]
|
||||
charCode = parseInt(hexCharCode, 16)
|
||||
charCode = event.which if !@isAscii(charCode) and @isAscii(event.which)
|
||||
key = @keyFromCharCode(charCode)
|
||||
else
|
||||
key = event.originalEvent.keyIdentifier.toLowerCase()
|
||||
@@ -170,6 +171,9 @@ class Keymap
|
||||
|
||||
[modifiers..., key].join('-')
|
||||
|
||||
isAscii: (charCode) ->
|
||||
0 <= charCode <= 127
|
||||
|
||||
keyFromCharCode: (charCode) ->
|
||||
switch charCode
|
||||
when 8 then 'backspace'
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
'meta-8': 'pane:show-item-8'
|
||||
'meta-9': 'pane:show-item-9'
|
||||
'meta-T': 'pane:reopen-closed-item'
|
||||
'alt-meta-w': 'pane:close-other-items'
|
||||
'meta-P': 'pane:close'
|
||||
|
||||
'meta-n': 'new-window'
|
||||
'meta-N': 'new-editor'
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
'meta-alt-p': 'editor:log-cursor-scope'
|
||||
'meta-u': 'editor:upper-case'
|
||||
'meta-U': 'editor:lower-case'
|
||||
'alt-meta-w': 'editor:close-other-edit-sessions'
|
||||
'meta-P': 'editor:close-all-edit-sessions'
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
'ctrl-meta-up': 'editor:move-line-up'
|
||||
'ctrl-meta-down': 'editor:move-line-down'
|
||||
|
||||
+79
-10
@@ -1,23 +1,92 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
telepath = require 'telepath'
|
||||
|
||||
# Internal:
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class PaneAxis extends View
|
||||
@acceptsDocuments: true
|
||||
|
||||
@deserialize: ({children}) ->
|
||||
childViews = children.map (child) -> deserialize(child)
|
||||
new this(childViews)
|
||||
@deserialize: (state) ->
|
||||
new this(state)
|
||||
|
||||
initialize: (children=[]) ->
|
||||
@append(children...)
|
||||
initialize: (args...) ->
|
||||
if args[0] instanceof telepath.Document
|
||||
@state = args[0]
|
||||
@state.get('children').each (child, index) => @addChild(deserialize(child), index, updateState: false)
|
||||
else
|
||||
@state = telepath.Document.create(deserializer: @className(), children: [])
|
||||
@addChild(child) for child in args
|
||||
|
||||
@state.get('children').observe ({index, inserted, removed, site}) =>
|
||||
return if site is @state.site.id
|
||||
for childState in removed
|
||||
@removeChild(@children(":eq(#{index})").view(), updateState: false)
|
||||
for childState, i in inserted
|
||||
@addChild(deserialize(childState), index + i, updateState: false)
|
||||
|
||||
addChild: (child, index=@children().length, options={}) ->
|
||||
@insertAt(index, child)
|
||||
@state.get('children').insert(index, child.serialize()) if options.updateState ? true
|
||||
@getContainer()?.adjustPaneDimensions()
|
||||
|
||||
removeChild: (child, options={}) ->
|
||||
options.updateState ?= true
|
||||
|
||||
parent = @parent().view()
|
||||
container = @getContainer()
|
||||
|
||||
primitiveRemove = (child) =>
|
||||
node = child[0]
|
||||
$.cleanData(node.getElementsByTagName('*'))
|
||||
$.cleanData([node])
|
||||
this[0].removeChild(node)
|
||||
|
||||
# use primitive .removeChild() dom method instead of .remove() to avoid recursive loop
|
||||
if @children().length == 2
|
||||
primitiveRemove(child)
|
||||
sibling = @children().view()
|
||||
siblingFocused = sibling.is(':has(:focus)')
|
||||
sibling.detach()
|
||||
if parent.setRoot?
|
||||
parent.setRoot(sibling, options)
|
||||
else
|
||||
parent.insertChildBefore(this, sibling, options)
|
||||
parent.removeChild(this, options)
|
||||
sibling.focus() if siblingFocused
|
||||
else
|
||||
@state.get('children').remove(@indexOf(child)) if options.updateState
|
||||
primitiveRemove(child)
|
||||
|
||||
container.adjustPaneDimensions()
|
||||
Pane = require 'pane'
|
||||
container.trigger 'pane:removed', [child] if child instanceof Pane
|
||||
|
||||
detachChild: (child) ->
|
||||
@state.get('children').remove(@indexOf(child))
|
||||
child.detach()
|
||||
|
||||
getContainer: ->
|
||||
@closest('#panes').view()
|
||||
|
||||
insertChildBefore: (child, newChild, options={}) ->
|
||||
newChild.insertBefore(child)
|
||||
if options.updateState ? true
|
||||
children = @state.get('children')
|
||||
childIndex = children.indexOf(child.getState())
|
||||
children.insert(childIndex, newChild.getState())
|
||||
|
||||
insertChildAfter: (child, newChild) ->
|
||||
newChild.insertAfter(child)
|
||||
children = @state.get('children')
|
||||
childIndex = children.indexOf(child.getState())
|
||||
children.insert(childIndex + 1, newChild.getState())
|
||||
|
||||
serialize: ->
|
||||
deserializer: @className()
|
||||
children: @childViewStates()
|
||||
child.serialize() for child in @children().views()
|
||||
@state
|
||||
|
||||
childViewStates: ->
|
||||
$(child).view().serialize() for child in @children()
|
||||
getState: -> @state
|
||||
|
||||
horizontalChildUnits: ->
|
||||
$(child).view().horizontalGridUnits() for child in @children()
|
||||
|
||||
@@ -1,29 +1,42 @@
|
||||
{View} = require 'space-pen'
|
||||
Pane = require 'pane'
|
||||
$ = require 'jquery'
|
||||
telepath = require 'telepath'
|
||||
|
||||
module.exports =
|
||||
class PaneContainer extends View
|
||||
registerDeserializer(this)
|
||||
|
||||
### Internal ###
|
||||
@acceptsDocuments: true
|
||||
|
||||
@deserialize: ({root}) ->
|
||||
container = new PaneContainer
|
||||
container.append(deserialize(root)) if root
|
||||
@deserialize: (state) ->
|
||||
container = new PaneContainer(state)
|
||||
container.removeEmptyPanes()
|
||||
container
|
||||
|
||||
@content: ->
|
||||
@div id: 'panes'
|
||||
|
||||
initialize: ->
|
||||
initialize: (@state) ->
|
||||
if @state?
|
||||
@setRoot(deserialize(@state.get('root')), updateState: false)
|
||||
else
|
||||
@state = telepath.Document.create(deserializer: 'PaneContainer')
|
||||
|
||||
@state.observe ({key, newValue, site}) =>
|
||||
return if site is @state.site.id
|
||||
if key is 'root'
|
||||
@setRoot(deserialize(newValue), updateState: false)
|
||||
|
||||
@destroyedItemStates = []
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'PaneContainer'
|
||||
root: @getRoot()?.serialize()
|
||||
|
||||
@getRoot()?.serialize()
|
||||
@state
|
||||
|
||||
getState: -> @state
|
||||
|
||||
### Public ###
|
||||
|
||||
focusNextPane: ->
|
||||
@@ -60,7 +73,7 @@ class PaneContainer extends View
|
||||
true
|
||||
else
|
||||
newPane = new Pane(deserialize(lastItemState))
|
||||
@append(newPane)
|
||||
@setRoot(newPane)
|
||||
newPane.focus()
|
||||
|
||||
itemDestroyed: (item) ->
|
||||
@@ -76,25 +89,27 @@ class PaneContainer extends View
|
||||
getRoot: ->
|
||||
@children().first().view()
|
||||
|
||||
setRoot: (root, options={}) ->
|
||||
@empty()
|
||||
@append(root) if root?
|
||||
@state.set(root: root?.getState()) if options.updateState ? true
|
||||
|
||||
removeChild: (child) ->
|
||||
throw new Error("Removing non-existant child") unless @getRoot() is child
|
||||
@setRoot(null)
|
||||
@trigger 'pane:removed', [child] if child instanceof Pane
|
||||
|
||||
saveAll: ->
|
||||
pane.saveItems() for pane in @getPanes()
|
||||
|
||||
confirmClose: ->
|
||||
deferred = $.Deferred()
|
||||
modifiedItems = []
|
||||
saved = true
|
||||
for pane in @getPanes()
|
||||
modifiedItems.push(item) for item in pane.getItems() when item.isModified?()
|
||||
|
||||
cancel = => deferred.reject()
|
||||
saveNextModifiedItem = =>
|
||||
if modifiedItems.length == 0
|
||||
deferred.resolve()
|
||||
else
|
||||
item = modifiedItems.pop()
|
||||
@paneAtIndex(0).promptToSaveItem item, saveNextModifiedItem, cancel
|
||||
|
||||
saveNextModifiedItem()
|
||||
deferred.promise()
|
||||
for item in pane.getItems() when item.isModified?()
|
||||
if not @paneAtIndex(0).promptToSaveItem item
|
||||
saved = false
|
||||
break
|
||||
saved
|
||||
|
||||
getPanes: ->
|
||||
@find('.pane').views()
|
||||
|
||||
+109
-62
@@ -1,6 +1,7 @@
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
telepath = require 'telepath'
|
||||
PaneRow = require 'pane-row'
|
||||
PaneColumn = require 'pane-column'
|
||||
|
||||
@@ -8,24 +9,49 @@ module.exports =
|
||||
class Pane extends View
|
||||
|
||||
### Internal ###
|
||||
@acceptsDocuments: true
|
||||
|
||||
@content: (wrappedView) ->
|
||||
@div class: 'pane', =>
|
||||
@div class: 'item-views', outlet: 'itemViews'
|
||||
|
||||
@deserialize: ({items, focused, activeItemUri}) ->
|
||||
deserializedItems = _.compact(items.map((item) -> deserialize(item)))
|
||||
pane = new Pane(deserializedItems...)
|
||||
pane.showItemForUri(activeItemUri) if activeItemUri
|
||||
pane.focusOnAttach = true if focused
|
||||
@deserialize: (state) ->
|
||||
pane = new Pane(state)
|
||||
pane.focusOnAttach = true if state.get('focused')
|
||||
pane
|
||||
|
||||
activeItem: null
|
||||
items: null
|
||||
viewsByClassName: null # Views with a setModel() method are stored here
|
||||
viewsByItem: null # Views without a setModel() method are stored here
|
||||
|
||||
initialize: (args...) ->
|
||||
if args[0] instanceof telepath.Document
|
||||
@state = args[0]
|
||||
@items = @state.get('items').map (item) -> deserialize(item)
|
||||
else
|
||||
@items = args
|
||||
@state = telepath.Document.create
|
||||
deserializer: 'Pane'
|
||||
items: @items.map (item) -> item.getState?() ? item.serialize()
|
||||
|
||||
@state.get('items').observe ({index, removed, inserted, site}) =>
|
||||
return if site is @state.site.id
|
||||
for itemState in removed
|
||||
@removeItemAtIndex(index, updateState: false)
|
||||
for itemState, i in inserted
|
||||
@addItem(deserialize(itemState), index + i, updateState: false)
|
||||
|
||||
@state.observe ({key, newValue, site}) =>
|
||||
return if site is @state.site.id
|
||||
@showItemForUri(newValue) if key is 'activeItemUri'
|
||||
|
||||
initialize: (@items...) ->
|
||||
@viewsByClassName = {}
|
||||
@showItem(@items[0]) if @items.length > 0
|
||||
@viewsByItem = new WeakMap()
|
||||
if activeItemUri = @state.get('activeItemUri')
|
||||
@showItemForUri(activeItemUri)
|
||||
else
|
||||
@showItem(@items[0]) if @items.length > 0
|
||||
|
||||
@command 'core:close', @destroyActiveItem
|
||||
@command 'core:save', @saveActiveItem
|
||||
@@ -129,12 +155,15 @@ class Pane extends View
|
||||
@activeView = view
|
||||
@trigger 'pane:active-item-changed', [item]
|
||||
|
||||
@state.set('activeItemUri', item.getUri?())
|
||||
|
||||
activeItemTitleChanged: =>
|
||||
@trigger 'pane:active-item-title-changed'
|
||||
|
||||
addItem: (item) ->
|
||||
addItem: (item, index=@getActiveItemIndex()+1, options={}) ->
|
||||
return if _.include(@items, item)
|
||||
index = @getActiveItemIndex() + 1
|
||||
|
||||
@state.get('items').splice(index, 0, item.getState?() ? item.serialize()) if options.updateState ? true
|
||||
@items.splice(index, 0, item)
|
||||
@getContainer().itemAdded(item)
|
||||
@trigger 'pane:item-added', [item, index]
|
||||
@@ -154,7 +183,7 @@ class Pane extends View
|
||||
@autosaveItem(item)
|
||||
|
||||
if item.shouldPromptToSave?()
|
||||
@promptToSaveItem(item, reallyDestroyItem)
|
||||
reallyDestroyItem() if @promptToSaveItem(item)
|
||||
else
|
||||
reallyDestroyItem()
|
||||
|
||||
@@ -164,15 +193,19 @@ class Pane extends View
|
||||
destroyInactiveItems: ->
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
promptToSaveItem: (item, nextAction, cancelAction) ->
|
||||
promptToSaveItem: (item) ->
|
||||
uri = item.getUri()
|
||||
atom.confirm(
|
||||
currentWindow = require('remote').getCurrentWindow()
|
||||
chosen = atom.confirmSync(
|
||||
"'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
"Your changes will be lost if you close this item without saving."
|
||||
"Save", => @saveItem(item, nextAction)
|
||||
"Cancel", cancelAction
|
||||
"Don't Save", nextAction
|
||||
["Save", "Cancel", "Don't Save"]
|
||||
currentWindow
|
||||
)
|
||||
switch chosen
|
||||
when 0 then @saveItem(item, -> true)
|
||||
when 1 then false
|
||||
when 2 then true
|
||||
|
||||
saveActiveItem: =>
|
||||
@saveItem(@activeItem)
|
||||
@@ -189,10 +222,10 @@ class Pane extends View
|
||||
|
||||
saveItemAs: (item, nextAction) ->
|
||||
return unless item.saveAs?
|
||||
atom.showSaveDialog (path) =>
|
||||
if path
|
||||
item.saveAs(path)
|
||||
nextAction?()
|
||||
path = atom.showSaveDialogSync()
|
||||
if path
|
||||
item.saveAs(path)
|
||||
nextAction?()
|
||||
|
||||
saveItems: =>
|
||||
@saveItem(item) for item in @getItems()
|
||||
@@ -205,10 +238,13 @@ class Pane extends View
|
||||
|
||||
removeItem: (item) ->
|
||||
index = @items.indexOf(item)
|
||||
return if index == -1
|
||||
@removeItemAtIndex(index) if index >= 0
|
||||
|
||||
removeItemAtIndex: (index, options={}) ->
|
||||
item = @items[index]
|
||||
@showNextItem() if item is @activeItem and @items.length > 1
|
||||
_.remove(@items, item)
|
||||
@state.get('items').remove(index) if options.updateState ? true
|
||||
@cleanupItemView(item)
|
||||
@trigger 'pane:item-removed', [item, index]
|
||||
|
||||
@@ -216,6 +252,8 @@ class Pane extends View
|
||||
oldIndex = @items.indexOf(item)
|
||||
@items.splice(oldIndex, 1)
|
||||
@items.splice(newIndex, 0, item)
|
||||
@state.get('items').splice(oldIndex, 1)
|
||||
@state.get('items').splice(newIndex, 0, item.getState?() ? item.serialize())
|
||||
@trigger 'pane:item-moved', [item, newIndex]
|
||||
|
||||
moveItemToPane: (item, pane, index) ->
|
||||
@@ -234,12 +272,15 @@ class Pane extends View
|
||||
if item instanceof $
|
||||
viewToRemove = item
|
||||
else
|
||||
viewClass = item.getViewClass()
|
||||
otherItemsForView = @items.filter (i) -> i.getViewClass?() is viewClass
|
||||
unless otherItemsForView.length
|
||||
viewToRemove = @viewsByClassName[viewClass.name]
|
||||
viewToRemove?.setModel(null)
|
||||
delete @viewsByClassName[viewClass.name]
|
||||
if viewToRemove = @viewsByItem.get(item)
|
||||
@viewsByItem.delete(item)
|
||||
else
|
||||
viewClass = item.getViewClass()
|
||||
otherItemsForView = @items.filter (i) -> i.getViewClass?() is viewClass
|
||||
unless otherItemsForView.length
|
||||
viewToRemove = @viewsByClassName[viewClass.name]
|
||||
viewToRemove?.setModel(null)
|
||||
delete @viewsByClassName[viewClass.name]
|
||||
|
||||
if @items.length > 0
|
||||
if @isMovingItem and item is viewToRemove
|
||||
@@ -248,27 +289,34 @@ class Pane extends View
|
||||
viewToRemove?.remove()
|
||||
else
|
||||
viewToRemove?.detach() if @isMovingItem and item is viewToRemove
|
||||
@remove()
|
||||
@parent().view().removeChild(this, updateState: false)
|
||||
|
||||
viewForItem: (item) ->
|
||||
if item instanceof $
|
||||
item
|
||||
else if view = @viewsByItem.get(item)
|
||||
view
|
||||
else
|
||||
viewClass = item.getViewClass()
|
||||
if view = @viewsByClassName[viewClass.name]
|
||||
view.setModel(item)
|
||||
else
|
||||
view = @viewsByClassName[viewClass.name] = new viewClass(item)
|
||||
view = new viewClass(item)
|
||||
if _.isFunction(view.setModel)
|
||||
@viewsByClassName[viewClass.name] = view
|
||||
else
|
||||
@viewsByItem.set(item, view)
|
||||
view
|
||||
|
||||
viewForActiveItem: ->
|
||||
@viewForItem(@activeItem)
|
||||
|
||||
serialize: ->
|
||||
deserializer: "Pane"
|
||||
focused: @is(':has(:focus)')
|
||||
activeItemUri: @activeItem.getUri?() if typeof @activeItem.serialize is 'function'
|
||||
items: _.compact(@getItems().map (item) -> item.serialize?())
|
||||
@state.get('items').set(index, item.serialize()) for item, index in @items
|
||||
@state.set focused: @is(':has(:focus)')
|
||||
@state
|
||||
|
||||
getState: -> @state
|
||||
|
||||
adjustDimensions: -> # do nothing
|
||||
|
||||
@@ -289,22 +337,35 @@ class Pane extends View
|
||||
@split(items, 'row', 'after')
|
||||
|
||||
split: (items, axis, side) ->
|
||||
unless @parent().hasClass(axis)
|
||||
@buildPaneAxis(axis)
|
||||
.insertBefore(this)
|
||||
.append(@detach())
|
||||
PaneContainer = require 'pane-container'
|
||||
|
||||
parent = @parent().view()
|
||||
unless parent.hasClass(axis)
|
||||
axis = @buildPaneAxis(axis)
|
||||
if parent instanceof PaneContainer
|
||||
@detach()
|
||||
parent.setRoot(axis)
|
||||
else
|
||||
parent.insertChildBefore(this, axis)
|
||||
parent.detachChild(this)
|
||||
|
||||
axis.addChild(this)
|
||||
parent = axis
|
||||
|
||||
items = [@copyActiveItem()] unless items.length
|
||||
pane = new Pane(items...)
|
||||
this[side](pane)
|
||||
newPane = new Pane(items...)
|
||||
|
||||
switch side
|
||||
when 'before' then parent.insertChildBefore(this, newPane)
|
||||
when 'after' then parent.insertChildAfter(this, newPane)
|
||||
@getContainer().adjustPaneDimensions()
|
||||
pane.focus()
|
||||
pane
|
||||
newPane.focus()
|
||||
newPane
|
||||
|
||||
buildPaneAxis: (axis) ->
|
||||
switch axis
|
||||
when 'row' then new PaneRow
|
||||
when 'column' then new PaneColumn
|
||||
when 'row' then new PaneRow()
|
||||
when 'column' then new PaneColumn()
|
||||
|
||||
getContainer: ->
|
||||
@closest('#panes').view()
|
||||
@@ -314,26 +375,12 @@ class Pane extends View
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
return super if keepData
|
||||
|
||||
# find parent elements before removing from dom
|
||||
container = @getContainer()
|
||||
parentAxis = @parent('.row, .column')
|
||||
|
||||
if @is(':has(:focus)')
|
||||
container.focusNextPane() or rootView?.focus()
|
||||
else if @isActive()
|
||||
container.makeNextPaneActive()
|
||||
|
||||
super
|
||||
|
||||
if parentAxis.children().length == 1
|
||||
sibling = parentAxis.children()
|
||||
siblingFocused = sibling.is(':has(:focus)')
|
||||
sibling.detach()
|
||||
parentAxis.replaceWith(sibling)
|
||||
sibling.focus() if siblingFocused
|
||||
container.adjustPaneDimensions()
|
||||
container.trigger 'pane:removed', [this]
|
||||
@parent().view().removeChild(this)
|
||||
|
||||
beforeRemove: ->
|
||||
if @is(':has(:focus)')
|
||||
@getContainer().focusNextPane() or rootView?.focus()
|
||||
else if @isActive()
|
||||
@getContainer().makeNextPaneActive()
|
||||
|
||||
item.destroy?() for item in @getItems()
|
||||
|
||||
@@ -15,6 +15,10 @@ BufferedProcess = require 'buffered-process'
|
||||
# of directories and files that you can operate on.
|
||||
module.exports =
|
||||
class Project
|
||||
registerDeserializer(this)
|
||||
|
||||
@deserialize: (state) -> new Project(state.path)
|
||||
|
||||
@openers: []
|
||||
|
||||
@registerOpener: (opener) ->
|
||||
@@ -34,6 +38,7 @@ class Project
|
||||
|
||||
destroy: ->
|
||||
editSession.destroy() for editSession in @getEditSessions()
|
||||
buffer.release() for buffer in @getBuffers()
|
||||
|
||||
### Public ###
|
||||
|
||||
@@ -45,6 +50,10 @@ class Project
|
||||
@editSessions = []
|
||||
@buffers = []
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'Project'
|
||||
path: @getPath()
|
||||
|
||||
# Retrieves the project path.
|
||||
#
|
||||
# Returns a {String}.
|
||||
@@ -182,10 +191,7 @@ class Project
|
||||
#
|
||||
# Returns an {Array} of {Buffer}s.
|
||||
getBuffers: ->
|
||||
buffers = []
|
||||
for editSession in @editSessions when not _.include(buffers, editSession.buffer)
|
||||
buffers.push editSession.buffer
|
||||
buffers
|
||||
new Array(@buffers...)
|
||||
|
||||
# Given a file path, this retrieves or creates a new {Buffer}.
|
||||
#
|
||||
@@ -289,7 +295,6 @@ class Project
|
||||
|
||||
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
|
||||
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
|
||||
options.project = this
|
||||
options.buffer = buffer
|
||||
editSession = new EditSession(options)
|
||||
@editSessions.push editSession
|
||||
|
||||
+17
-13
@@ -2,7 +2,7 @@ $ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
_ = require 'underscore'
|
||||
|
||||
telepath = require 'telepath'
|
||||
{View} = require 'space-pen'
|
||||
Buffer = require 'text-buffer'
|
||||
Editor = require 'editor'
|
||||
@@ -26,18 +26,23 @@ class RootView extends View
|
||||
themes: ['atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
### Internal ###
|
||||
@acceptsDocuments: true
|
||||
|
||||
@content: ({panes}={}) ->
|
||||
@content: (state) ->
|
||||
@div id: 'root-view', =>
|
||||
@div id: 'horizontal', outlet: 'horizontal', =>
|
||||
@div id: 'vertical', outlet: 'vertical', =>
|
||||
@subview 'panes', panes ? new PaneContainer
|
||||
@subview 'panes', deserialize(state?.get?('panes')) ? new PaneContainer
|
||||
|
||||
@deserialize: ({panes, fullScreen}) ->
|
||||
panes = deserialize(panes) if panes?.deserializer is 'PaneContainer'
|
||||
new RootView({panes, fullScreen})
|
||||
@deserialize: (state) ->
|
||||
new RootView(state)
|
||||
|
||||
initialize: (state={}) ->
|
||||
if state instanceof telepath.Document
|
||||
@state = state
|
||||
else
|
||||
@state = telepath.Document.create(_.extend({version: RootView.version, deserializer: 'RootView', panes: @panes.serialize()}, state))
|
||||
|
||||
initialize: ({fullScreen}={})->
|
||||
@on 'focus', (e) => @handleFocus(e)
|
||||
@subscribe $(window), 'focus', (e) =>
|
||||
@handleFocus(e) if document.activeElement is document.body
|
||||
@@ -75,13 +80,12 @@ class RootView extends View
|
||||
@command 'new-editor', =>
|
||||
@open()
|
||||
|
||||
_.nextTick -> atom.setFullScreen(fullScreen)
|
||||
_.nextTick => atom.setFullScreen(@state.get('fullScreen'))
|
||||
|
||||
serialize: ->
|
||||
version: RootView.version
|
||||
deserializer: 'RootView'
|
||||
panes: @panes.serialize()
|
||||
fullScreen: atom.isFullScreen()
|
||||
@panes.serialize()
|
||||
@state.set('fullScreen', atom.isFullScreen())
|
||||
@state
|
||||
|
||||
handleFocus: (e) ->
|
||||
if @getActivePane()
|
||||
@@ -117,7 +121,7 @@ class RootView extends View
|
||||
else
|
||||
editSession = project.open(path)
|
||||
activePane = new Pane(editSession)
|
||||
@panes.append(activePane)
|
||||
@panes.setRoot(activePane)
|
||||
|
||||
activePane.focus() if changeFocus
|
||||
editSession
|
||||
|
||||
@@ -13,7 +13,7 @@ BufferMarker = require 'buffer-marker'
|
||||
# The `Buffer` is often associated with a {File}. However, this is not always
|
||||
# the case, as a `Buffer` could be an unsaved chunk of text.
|
||||
module.exports =
|
||||
class Buffer
|
||||
class TextBuffer
|
||||
@idCounter = 1
|
||||
registerDeserializer(this)
|
||||
stoppedChangingDelay: 300
|
||||
@@ -695,4 +695,4 @@ class Buffer
|
||||
lines.push "#{row}: #{@lineForRow(row)}"
|
||||
lines.join('\n')
|
||||
|
||||
_.extend(Buffer.prototype, EventEmitter)
|
||||
_.extend(TextBuffer.prototype, EventEmitter)
|
||||
|
||||
@@ -74,11 +74,10 @@ class TextMateGrammar
|
||||
getScore: (filePath, contents) ->
|
||||
contents = fsUtils.read(filePath) if not contents? and fsUtils.isFileSync(filePath)
|
||||
|
||||
|
||||
if syntax.grammarOverrideForPath(filePath) is @scopeName
|
||||
2 + filePath.length
|
||||
2 + (filePath?.length ? 0)
|
||||
else if @matchesContents(contents)
|
||||
1 + filePath.length
|
||||
1 + (filePath?.length ? 0)
|
||||
else
|
||||
@getPathScore(filePath)
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ class WindowEventHandler
|
||||
@subscribe $(window), 'blur', -> $("body").addClass('is-blurred')
|
||||
@subscribe $(window), 'window:open-path', (event, pathToOpen) ->
|
||||
rootView?.open(pathToOpen) unless fsUtils.isDirectorySync(pathToOpen)
|
||||
@subscribe $(window), 'beforeunload', -> rootView?.confirmClose()
|
||||
|
||||
@subscribeToCommand $(window), 'window:toggle-full-screen', => atom.toggleFullScreen()
|
||||
@subscribeToCommand $(window), 'window:close', =>
|
||||
if rootView?
|
||||
rootView.confirmClose().done -> closeWithoutConfirm()
|
||||
else
|
||||
closeWithoutConfirm()
|
||||
@subscribeToCommand $(window), 'window:close', => window.close()
|
||||
@subscribeToCommand $(window), 'window:reload', => atom.reload()
|
||||
|
||||
@subscribeToCommand $(document), 'core:focus-next', @focusNext
|
||||
|
||||
+30
-22
@@ -1,5 +1,6 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
telepath = require 'telepath'
|
||||
$ = require 'jquery'
|
||||
less = require 'less'
|
||||
ipc = require 'ipc'
|
||||
@@ -18,7 +19,8 @@ defaultWindowDimensions = {width: 800, height: 600}
|
||||
windowEventHandler = null
|
||||
|
||||
# This method is called in any window needing a general environment, including specs
|
||||
window.setUpEnvironment = ->
|
||||
window.setUpEnvironment = (windowMode) ->
|
||||
atom.windowMode = windowMode
|
||||
window.resourcePath = remote.getCurrentWindow().loadSettings.resourcePath
|
||||
|
||||
Config = require 'config'
|
||||
@@ -44,7 +46,6 @@ window.startEditorWindow = ->
|
||||
installAtomCommand()
|
||||
installApmCommand()
|
||||
|
||||
atom.windowMode = 'editor'
|
||||
windowEventHandler = new WindowEventHandler
|
||||
restoreDimensions()
|
||||
config.load()
|
||||
@@ -60,7 +61,6 @@ window.startEditorWindow = ->
|
||||
atom.focus()
|
||||
|
||||
window.startConfigWindow = ->
|
||||
atom.windowMode = 'config'
|
||||
restoreDimensions()
|
||||
windowEventHandler = new WindowEventHandler
|
||||
config.load()
|
||||
@@ -76,10 +76,12 @@ window.startConfigWindow = ->
|
||||
|
||||
window.unloadEditorWindow = ->
|
||||
return if not project and not rootView
|
||||
atom.setWindowState('syntax', syntax.serialize())
|
||||
atom.setWindowState('rootView', rootView.serialize())
|
||||
windowState = atom.getWindowState()
|
||||
windowState.set('syntax', syntax.serialize())
|
||||
windowState.set('rootView', rootView.serialize())
|
||||
atom.deactivatePackages()
|
||||
atom.setWindowState('packageStates', atom.packageStates)
|
||||
windowState.set('packageStates', atom.packageStates)
|
||||
atom.saveWindowState()
|
||||
rootView.remove()
|
||||
project.destroy()
|
||||
git?.destroy()
|
||||
@@ -98,7 +100,7 @@ window.installApmCommand = (callback) ->
|
||||
|
||||
window.unloadConfigWindow = ->
|
||||
return if not configView
|
||||
atom.setWindowState('configView', configView.serialize())
|
||||
atom.getWindowState().set('configView', configView.serialize())
|
||||
configView.remove()
|
||||
windowEventHandler?.unsubscribe()
|
||||
window.configView = null
|
||||
@@ -114,13 +116,19 @@ window.deserializeEditorWindow = ->
|
||||
Project = require 'project'
|
||||
Git = require 'git'
|
||||
|
||||
{initialPath} = atom.getLoadSettings()
|
||||
|
||||
windowState = atom.getWindowState()
|
||||
|
||||
atom.packageStates = windowState.packageStates ? {}
|
||||
window.project = new Project(initialPath)
|
||||
window.rootView = deserialize(windowState.rootView) ? new RootView
|
||||
atom.packageStates = windowState.getObject('packageStates') ? {}
|
||||
|
||||
window.project = deserialize(windowState.get('project'))
|
||||
unless window.project?
|
||||
window.project = new Project(atom.getLoadSettings().initialPath)
|
||||
windowState.set('project', window.project.serialize())
|
||||
|
||||
window.rootView = deserialize(windowState.get('rootView'))
|
||||
unless window.rootView?
|
||||
window.rootView = new RootView()
|
||||
windowState.set('rootView', window.rootView.serialize())
|
||||
|
||||
$(rootViewParentSelector).append(rootView)
|
||||
|
||||
@@ -134,8 +142,7 @@ window.deserializeEditorWindow = ->
|
||||
|
||||
window.deserializeConfigWindow = ->
|
||||
ConfigView = require 'config-view'
|
||||
windowState = atom.getWindowState()
|
||||
window.configView = deserialize(windowState.configView) ? new ConfigView()
|
||||
window.configView = deserialize(atom.getWindowState('configView')) ? new ConfigView()
|
||||
$(rootViewParentSelector).append(configView)
|
||||
|
||||
window.stylesheetElementForId = (id) ->
|
||||
@@ -205,14 +212,10 @@ window.setDimensions = ({x, y, width, height}) ->
|
||||
browserWindow.center()
|
||||
|
||||
window.restoreDimensions = ->
|
||||
dimensions = atom.getWindowState('dimensions')
|
||||
dimensions = atom.getWindowState().getObject('dimensions')
|
||||
dimensions = defaultWindowDimensions unless dimensions?.width and dimensions?.height
|
||||
window.setDimensions(dimensions)
|
||||
$(window).on 'unload', -> atom.setWindowState('dimensions', window.getDimensions())
|
||||
|
||||
window.closeWithoutConfirm = ->
|
||||
atom.hide()
|
||||
ipc.sendChannel 'close-without-confirm'
|
||||
$(window).on 'unload', -> atom.getWindowState().set('dimensions', window.getDimensions())
|
||||
|
||||
window.onerror = ->
|
||||
atom.openDevTools()
|
||||
@@ -231,11 +234,16 @@ window.unregisterDeserializer = (klass) ->
|
||||
|
||||
window.deserialize = (state) ->
|
||||
if deserializer = getDeserializer(state)
|
||||
return if deserializer.version? and deserializer.version isnt state.version
|
||||
stateVersion = state.get?('version') ? state.version
|
||||
return if deserializer.version? and deserializer.version isnt stateVersion
|
||||
if (state instanceof telepath.Document) and not deserializer.acceptsDocuments
|
||||
state = state.toObject()
|
||||
deserializer.deserialize(state)
|
||||
|
||||
window.getDeserializer = (state) ->
|
||||
name = state?.deserializer
|
||||
return unless state?
|
||||
|
||||
name = state.get?('deserializer') ? state.deserializer
|
||||
if deferredDeserializers[name]
|
||||
deferredDeserializers[name]()
|
||||
delete deferredDeserializers[name]
|
||||
|
||||
@@ -50,9 +50,7 @@ class AtomApplication
|
||||
@buildApplicationMenu()
|
||||
@handleEvents()
|
||||
|
||||
# Don't check for updates if it's a custom build.
|
||||
if @version.indexOf('.') isnt -1
|
||||
@checkForUpdates()
|
||||
@checkForUpdates()
|
||||
|
||||
if test
|
||||
@runSpecs({exitWhenDone: true, @resourcePath})
|
||||
@@ -82,6 +80,8 @@ class AtomApplication
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony_collections'
|
||||
|
||||
checkForUpdates: ->
|
||||
return if /\w{7}/.test @version # Don't check for updates if version is a short sha
|
||||
|
||||
autoUpdater.setAutomaticallyChecksForUpdates true
|
||||
autoUpdater.checkForUpdatesInBackground()
|
||||
|
||||
@@ -124,6 +124,7 @@ class AtomApplication
|
||||
menus.push
|
||||
label: 'File'
|
||||
submenu: [
|
||||
{ label: 'New Window', accelerator: 'Command+N', click: => @openPath() }
|
||||
{ label: 'Open...', accelerator: 'Command+O', click: => @promptForPath() }
|
||||
{ label: 'Open In Dev Mode...', accelerator: 'Command+Shift+O', click: => @promptForPath(devMode: true) }
|
||||
]
|
||||
@@ -175,11 +176,6 @@ class AtomApplication
|
||||
@installUpdate = quitAndUpdate
|
||||
@buildApplicationMenu version, quitAndUpdate
|
||||
|
||||
ipc.on 'close-without-confirm', (processId, routingId) ->
|
||||
window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId
|
||||
window.removeAllListeners 'close'
|
||||
window.close()
|
||||
|
||||
ipc.on 'open-config', =>
|
||||
@openConfig()
|
||||
|
||||
@@ -189,6 +185,9 @@ class AtomApplication
|
||||
else
|
||||
@promptForPath()
|
||||
|
||||
ipc.on 'open-window', (processId, routingId, windowSettings) ->
|
||||
new AtomWindow(windowSettings)
|
||||
|
||||
ipc.on 'open-dev', (processId, routingId, pathsToOpen) =>
|
||||
if pathsToOpen?.length > 0
|
||||
@openPaths({pathsToOpen, devMode: true})
|
||||
|
||||
+12
-12
@@ -4,23 +4,28 @@ dialog = require 'dialog'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
browserWindow: null
|
||||
|
||||
constructor: ({bootstrapScript, resourcePath, pathToOpen, exitWhenDone, @isSpec}) ->
|
||||
constructor: (settings={}) ->
|
||||
{resourcePath, pathToOpen, isSpec} = settings
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(resourcePath)
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom'
|
||||
@handleEvents()
|
||||
@handleEvents(isSpec)
|
||||
|
||||
initialPath = pathToOpen
|
||||
loadSettings = _.extend({}, settings)
|
||||
loadSettings.windowState ?= ''
|
||||
loadSettings.initialPath = pathToOpen
|
||||
try
|
||||
initialPath = path.dirname(pathToOpen) if fs.statSync(pathToOpen).isFile()
|
||||
if fs.statSync(pathToOpen).isFile()
|
||||
loadSettings.initialPath = path.dirname(pathToOpen)
|
||||
|
||||
@browserWindow.loadSettings = {initialPath, bootstrapScript, resourcePath, exitWhenDone, windowState: ''}
|
||||
@browserWindow.loadSettings = loadSettings
|
||||
@browserWindow.once 'window:loaded', => @loaded = true
|
||||
@browserWindow.loadUrl "file://#{resourcePath}/static/index.html"
|
||||
|
||||
@@ -62,7 +67,7 @@ class AtomWindow
|
||||
else
|
||||
false
|
||||
|
||||
handleEvents: ->
|
||||
handleEvents: (isSpec)->
|
||||
@browserWindow.on 'destroyed', =>
|
||||
global.atomApplication.removeWindow(this)
|
||||
|
||||
@@ -85,15 +90,10 @@ class AtomWindow
|
||||
when 0 then setImmediate => @browserWindow.destroy()
|
||||
when 1 then @browserWindow.restart()
|
||||
|
||||
if @isSpec
|
||||
if isSpec
|
||||
# Spec window's web view should always have focus
|
||||
@browserWindow.on 'blur', =>
|
||||
@browserWindow.focusOnWebView()
|
||||
else
|
||||
@browserWindow.on 'close', (event) =>
|
||||
unless @browserWindow.isCrashed()
|
||||
event.preventDefault()
|
||||
@sendCommand 'window:close'
|
||||
|
||||
openPath: (pathToOpen) ->
|
||||
if @loaded
|
||||
|
||||
@@ -2,6 +2,6 @@ date = new Date().getTime()
|
||||
require 'atom'
|
||||
require 'window'
|
||||
|
||||
window.setUpEnvironment()
|
||||
window.setUpEnvironment('config')
|
||||
window.startConfigWindow()
|
||||
console.log "Load time: #{new Date().getTime() - date}"
|
||||
|
||||
@@ -7,6 +7,7 @@ fs = require 'fs'
|
||||
path = require 'path'
|
||||
optimist = require 'optimist'
|
||||
nslog = require 'nslog'
|
||||
dialog = require 'dialog'
|
||||
_ = require 'underscore'
|
||||
|
||||
console.log = (args...) ->
|
||||
@@ -21,6 +22,13 @@ delegate.browserMainParts.preMainMessageLoopRun = ->
|
||||
event.preventDefault()
|
||||
args.pathsToOpen.push(filePath)
|
||||
|
||||
app.on 'open-url', (event, url) =>
|
||||
event.preventDefault()
|
||||
dialog.showMessageBox
|
||||
message: 'Atom opened with URL'
|
||||
detail: url
|
||||
buttons: ['OK']
|
||||
|
||||
app.on 'open-file', addPathToOpen
|
||||
|
||||
app.on 'will-finish-launching', ->
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
'.archive-view':
|
||||
'k': 'core:move-up'
|
||||
'j': 'core:move-down'
|
||||
@@ -0,0 +1,4 @@
|
||||
'body':
|
||||
'meta-C': 'collaboration:copy-session-id'
|
||||
'meta-H': 'collaboration:start-session'
|
||||
'meta-K': 'collaboration:join-session'
|
||||
@@ -0,0 +1,27 @@
|
||||
require 'atom'
|
||||
require 'window'
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
{createPeer, connectDocument} = require './session-utils'
|
||||
{createSite, Document} = require 'telepath'
|
||||
|
||||
window.setDimensions(width: 350, height: 100)
|
||||
window.setUpEnvironment('editor')
|
||||
{sessionId} = atom.getLoadSettings()
|
||||
|
||||
loadingView = $$ ->
|
||||
@div style: 'margin: 10px; text-align: center', =>
|
||||
@div "Joining session #{sessionId}"
|
||||
$(window.rootViewParentSelector).append(loadingView)
|
||||
atom.show()
|
||||
|
||||
peer = createPeer()
|
||||
connection = peer.connect(sessionId, reliable: true)
|
||||
connection.on 'open', ->
|
||||
console.log 'connection opened'
|
||||
connection.once 'data', (data) ->
|
||||
loadingView.remove()
|
||||
console.log 'received document'
|
||||
atom.windowState = Document.deserialize(createSite(peer.id), data)
|
||||
connectDocument(atom.windowState, connection)
|
||||
window.startEditorWindow()
|
||||
@@ -0,0 +1,32 @@
|
||||
JoinPromptView = require './join-prompt-view'
|
||||
{createSite, Document} = require 'telepath'
|
||||
{createPeer, connectDocument} = require './session-utils'
|
||||
|
||||
startSession = ->
|
||||
peer = createPeer()
|
||||
peer.on 'connection', (connection) ->
|
||||
connection.on 'open', ->
|
||||
console.log 'sending document'
|
||||
windowState = atom.getWindowState()
|
||||
connection.send(windowState.serialize())
|
||||
connectDocument(windowState, connection)
|
||||
peer.id
|
||||
|
||||
module.exports =
|
||||
activate: ->
|
||||
sessionId = null
|
||||
|
||||
rootView.command 'collaboration:copy-session-id', ->
|
||||
pasteboard.write(sessionId) if sessionId
|
||||
|
||||
rootView.command 'collaboration:start-session', ->
|
||||
if sessionId = startSession()
|
||||
pasteboard.write(sessionId)
|
||||
|
||||
rootView.command 'collaboration:join-session', ->
|
||||
new JoinPromptView (id) ->
|
||||
windowSettings =
|
||||
bootstrapScript: require.resolve('collaboration/lib/bootstrap')
|
||||
resourcePath: window.resourcePath
|
||||
sessionId: id
|
||||
atom.openWindow(windowSettings)
|
||||
@@ -0,0 +1,39 @@
|
||||
{View} = require 'space-pen'
|
||||
Editor = require 'editor'
|
||||
$ = require 'jquery'
|
||||
Point = require 'point'
|
||||
_ = require 'underscore'
|
||||
Guid = require 'guid'
|
||||
|
||||
module.exports =
|
||||
class JoinPromptView extends View
|
||||
@activate: -> new Prompt
|
||||
|
||||
@content: ->
|
||||
@div class: 'overlay from-top', =>
|
||||
@subview 'miniEditor', new Editor(mini: true)
|
||||
@div class: 'message', outlet: 'message', 'Enter a session id to join'
|
||||
|
||||
initialize: (@confirmed) ->
|
||||
@miniEditor.on 'focusout', => @remove()
|
||||
@on 'core:confirm', => @confirm()
|
||||
@on 'core:cancel', => @remove()
|
||||
|
||||
clipboard = pasteboard.read()[0]
|
||||
if Guid.isGuid(clipboard)
|
||||
@miniEditor.setText(clipboard)
|
||||
|
||||
@attach()
|
||||
|
||||
beforeRemove: ->
|
||||
@previouslyFocusedElement?.focus()
|
||||
@miniEditor.setText('')
|
||||
|
||||
confirm: ->
|
||||
@confirmed(@miniEditor.getText())
|
||||
@remove()
|
||||
|
||||
attach: ->
|
||||
@previouslyFocusedElement = $(':focus')
|
||||
rootView.append(this)
|
||||
@miniEditor.focus()
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,45 @@
|
||||
Peer = require './peer'
|
||||
Guid = require 'guid'
|
||||
|
||||
module.exports =
|
||||
createPeer: ->
|
||||
id = Guid.create().toString()
|
||||
new Peer(id, host: 'ec2-54-218-51-127.us-west-2.compute.amazonaws.com', port: 8080)
|
||||
|
||||
connectDocument: (doc, connection) ->
|
||||
nextOutputEventId = 1
|
||||
outputListener = (event) ->
|
||||
event.id = nextOutputEventId++
|
||||
console.log 'sending event', event.id, event
|
||||
connection.send(event)
|
||||
doc.outputEvents.on('changed', outputListener)
|
||||
|
||||
queuedEvents = []
|
||||
nextInputEventId = 1
|
||||
handleInputEvent = (event) ->
|
||||
console.log 'received event', event.id, event
|
||||
doc.handleInputEvent(event)
|
||||
nextInputEventId = event.id + 1
|
||||
flushQueuedEvents = ->
|
||||
loop
|
||||
eventHandled = false
|
||||
for event, index in queuedEvents when event.id is nextInputEventId
|
||||
handleInputEvent(event)
|
||||
queuedEvents.splice(index, 1)
|
||||
eventHandled = true
|
||||
break
|
||||
break unless eventHandled
|
||||
|
||||
connection.on 'data', (event) ->
|
||||
if event.id is nextInputEventId
|
||||
handleInputEvent(event)
|
||||
flushQueuedEvents()
|
||||
else
|
||||
console.log 'enqueing event', event.id, event
|
||||
queuedEvents.push(event)
|
||||
|
||||
connection.on 'close', ->
|
||||
doc.outputEvents.removeListener('changed', outputListener)
|
||||
|
||||
connection.on 'error', (error) ->
|
||||
console.error 'connection error', error.stack ? error
|
||||
@@ -0,0 +1 @@
|
||||
'main': './lib/collaboration'
|
||||
@@ -12,6 +12,8 @@
|
||||
'a': 'tree-view:add'
|
||||
'delete': 'tree-view:remove'
|
||||
'backspace': 'tree-view:remove'
|
||||
'k': 'core:move-up'
|
||||
'j': 'core:move-down'
|
||||
|
||||
'.tree-view-dialog .mini.editor':
|
||||
'enter': 'core:confirm'
|
||||
|
||||
@@ -278,7 +278,7 @@ class TreeView extends ScrollView
|
||||
try
|
||||
if fsUtils.exists(pathToCreate)
|
||||
pathType = if fsUtils.isFileSync(pathToCreate) then "file" else "directory"
|
||||
dialog.showError("Error: A #{pathType} already exists at path '#{path}'. Try a different path.")
|
||||
dialog.showError("Error: A #{pathType} already exists at path '#{pathToCreate}'. Try a different path.")
|
||||
else if endsWithDirectorySeparator
|
||||
fsUtils.makeTree(pathToCreate)
|
||||
dialog.cancel()
|
||||
|
||||
@@ -12,12 +12,11 @@ module.exports =
|
||||
absolute: (relativePath) ->
|
||||
return null unless relativePath?
|
||||
|
||||
if relativePath.indexOf('~/') is 0
|
||||
if process.platform is 'win32'
|
||||
home = process.env.USERPROFILE
|
||||
else
|
||||
home = process.env.HOME
|
||||
relativePath = "#{home}#{relativePath.substring(1)}"
|
||||
if relativePath is '~'
|
||||
relativePath = process.env.HOME
|
||||
else if relativePath.indexOf('~/') is 0
|
||||
relativePath = "#{process.env.HOME}#{relativePath.substring(1)}"
|
||||
|
||||
try
|
||||
fs.realpathSync(relativePath)
|
||||
catch e
|
||||
|
||||
@@ -46,6 +46,19 @@ $.fn.enable = ->
|
||||
$.fn.disable = ->
|
||||
@attr('disabled', 'disabled')
|
||||
|
||||
$.fn.insertAt = (index, element) ->
|
||||
target = @children(":eq(#{index})")
|
||||
if target.length
|
||||
$(element).insertBefore(target)
|
||||
else
|
||||
@append(element)
|
||||
|
||||
$.fn.removeAt = (index) ->
|
||||
@children(":eq(#{index})").remove()
|
||||
|
||||
$.fn.indexOf = (child) ->
|
||||
@children().toArray().indexOf($(child)[0])
|
||||
|
||||
$.fn.containsElement = (element) ->
|
||||
(element[0].compareDocumentPosition(this[0]) & 8) == 8
|
||||
|
||||
|
||||
@@ -180,52 +180,4 @@ _.mixin
|
||||
newObject[key] = value if value?
|
||||
newObject
|
||||
|
||||
originalIsEqual = _.isEqual
|
||||
extendedIsEqual = (a, b, aStack=[], bStack=[]) ->
|
||||
return originalIsEqual(a, b) if a is b
|
||||
return originalIsEqual(a, b) if _.isFunction(a) or _.isFunction(b)
|
||||
return a.isEqual(b) if _.isFunction(a?.isEqual)
|
||||
return b.isEqual(a) if _.isFunction(b?.isEqual)
|
||||
|
||||
stackIndex = aStack.length
|
||||
while stackIndex--
|
||||
return bStack[stackIndex] is b if aStack[stackIndex] is a
|
||||
aStack.push(a)
|
||||
bStack.push(b)
|
||||
|
||||
equal = false
|
||||
if _.isArray(a) and _.isArray(b) and a.length is b.length
|
||||
equal = true
|
||||
for aElement, i in a
|
||||
unless extendedIsEqual(aElement, b[i], aStack, bStack)
|
||||
equal = false
|
||||
break
|
||||
else if _.isObject(a) and _.isObject(b)
|
||||
aCtor = a.constructor
|
||||
bCtor = b.constructor
|
||||
aCtorValid = _.isFunction(aCtor) and aCtor instanceof aCtor
|
||||
bCtorValid = _.isFunction(bCtor) and bCtor instanceof bCtor
|
||||
if aCtor isnt bCtor and not (aCtorValid and bCtorValid)
|
||||
equal = false
|
||||
else
|
||||
aKeyCount = 0
|
||||
equal = true
|
||||
for key, aValue of a
|
||||
continue unless _.has(a, key)
|
||||
aKeyCount++
|
||||
unless _.has(b, key) and extendedIsEqual(aValue, b[key], aStack, bStack)
|
||||
equal = false
|
||||
break
|
||||
if equal
|
||||
bKeyCount = 0
|
||||
for key, bValue of b
|
||||
bKeyCount++ if _.has(b, key)
|
||||
equal = aKeyCount is bKeyCount
|
||||
else
|
||||
equal = originalIsEqual(a, b)
|
||||
|
||||
aStack.pop()
|
||||
bStack.pop()
|
||||
equal
|
||||
|
||||
_.isEqual = (a, b) -> extendedIsEqual(a, b)
|
||||
_.isEqual = require 'tantamount'
|
||||
|
||||
@@ -3,6 +3,6 @@ date = new Date().getTime()
|
||||
require 'atom'
|
||||
require 'window'
|
||||
|
||||
window.setUpEnvironment()
|
||||
window.setUpEnvironment('editor')
|
||||
window.startEditorWindow()
|
||||
console.log "Load time: #{new Date().getTime() - date}"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
currentWindow.emit('window:loaded');
|
||||
}
|
||||
catch (error) {
|
||||
currentWindow.show();
|
||||
currentWindow.openDevTools();
|
||||
console.error(error.stack || error);
|
||||
}
|
||||
|
||||
@@ -158,4 +158,5 @@ body {
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'build', 'Build the application', ->
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
rm shellAppDir
|
||||
mkdir path.dirname(buildDir)
|
||||
cp 'atom-shell/Atom.app', shellAppDir
|
||||
|
||||
mkdir appDir
|
||||
|
||||
cp 'atom.sh', path.join(appDir, 'atom.sh')
|
||||
cp 'package.json', path.join(appDir, 'package.json')
|
||||
|
||||
directories = [
|
||||
'benchmark'
|
||||
'dot-atom'
|
||||
'spec'
|
||||
'vendor'
|
||||
]
|
||||
|
||||
{devDependencies, dependencies} = grunt.file.readJSON('package.json')
|
||||
for child in fs.readdirSync('node_modules')
|
||||
directory = path.join('node_modules', child)
|
||||
try
|
||||
{name} = grunt.file.readJSON(path.join(directory, 'package.json'))
|
||||
if not devDependencies[name]? or dependencies[name]?
|
||||
directories.push(directory)
|
||||
catch e
|
||||
directories.push(directory)
|
||||
|
||||
ignoredPaths = [
|
||||
path.join('git-utils', 'deps')
|
||||
path.join('oniguruma', 'deps')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('vendor', 'bootstrap', 'docs')
|
||||
]
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
for directory in directories
|
||||
cp directory, path.join(appDir, directory), filter: nodeModulesFilter
|
||||
|
||||
cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee|less)$/
|
||||
cp 'static', path.join(appDir, 'static'), filter: /.+\.less$/
|
||||
cp 'themes', path.join(appDir, 'themes'), filter: /.+\.(cson|less)$/
|
||||
|
||||
grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
|
||||
unless /.+\.plist/.test(sourcePath)
|
||||
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
|
||||
|
||||
grunt.task.run('compile', 'copy-info-plist')
|
||||
@@ -0,0 +1,16 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{rm} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'partial-clean', 'Delete some of the build files', ->
|
||||
rm grunt.config.get('atom.buildDir')
|
||||
rm '/tmp/atom-coffee-cache'
|
||||
rm '/tmp/atom-cached-atom-shells'
|
||||
rm 'node'
|
||||
rm 'atom-shell'
|
||||
|
||||
grunt.registerTask 'clean', 'Delete all the build files', ->
|
||||
rm 'node_modules'
|
||||
rm path.join(process.env.HOME, '.atom', '.node-gyp')
|
||||
grunt.task.run('partial-clean')
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
done = @async()
|
||||
cmd = 'codesign'
|
||||
args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> done(error)
|
||||
@@ -0,0 +1,13 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'copy-info-plist', 'Copy plist', ->
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
plistPath = path.join(contentsDir, 'Info.plist')
|
||||
helperPlistPath = path.join(contentsDir, 'Frameworks/Atom Helper.app/Contents/Info.plist')
|
||||
|
||||
# Copy custom plist files
|
||||
cp 'resources/mac/atom-Info.plist', plistPath
|
||||
cp 'resources/mac/helper-Info.plist', helperPlistPath
|
||||
@@ -0,0 +1,22 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
cmd = path.join('node_modules', '.bin', 'coffee')
|
||||
commonArgs = [path.join('node_modules', '.bin', 'biscotto'), '--']
|
||||
opts =
|
||||
stdio: 'inherit'
|
||||
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src/app', ->
|
||||
done = @async()
|
||||
args = [commonArgs..., '-o', 'docs/api', 'src/app/']
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
|
||||
done = @async()
|
||||
args = [commonArgs..., '--noOutput', 'src/app/']
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'missing-docs', 'Generate stats about the doc coverage', ->
|
||||
done = @async()
|
||||
args = [commonArgs..., '--noOutput', '--missing', 'src/app/']
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
@@ -0,0 +1,11 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (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')
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
cp shellAppDir, installDir
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'nof', 'Un-focus all specs', ->
|
||||
nof = require.resolve('.bin/nof')
|
||||
spawn({cmd: nof, args: ['spec', 'src']}, @async())
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'set-development-version', 'Sets version to current SHA-1', ->
|
||||
done = @async()
|
||||
cmd = 'script/set-version'
|
||||
args = [grunt.config.get('atom.buildDir')]
|
||||
spawn {cmd, args}, (error, result, code) -> done(error)
|
||||
@@ -0,0 +1,31 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
walkdir = require 'walkdir'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
cp: (source, destination, {filter}={}) ->
|
||||
walkdir.sync source, (sourcePath, stats) ->
|
||||
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)
|
||||
|
||||
if grunt.file.exists(destinationPath)
|
||||
fs.chmodSync(destinationPath, fs.statSync(sourcePath).mode)
|
||||
|
||||
grunt.log.writeln("Copied #{source.cyan} to #{destination.cyan}.")
|
||||
|
||||
mkdir: (args...) ->
|
||||
grunt.file.mkdir(args...)
|
||||
|
||||
rm: (args...) ->
|
||||
grunt.file.delete(args..., force: true) if grunt.file.exists(args...)
|
||||
|
||||
spawn: (options, callback) ->
|
||||
grunt.util.spawn options, (error, results, code) ->
|
||||
grunt.log.errorlns results.stderr if results.stderr
|
||||
callback(error, results, code)
|
||||
@@ -0,0 +1,15 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'test', 'Run the specs', ->
|
||||
done = @async()
|
||||
commands = []
|
||||
commands.push (callback) ->
|
||||
spawn cmd: 'pkill', args: ['Atom'], -> callback()
|
||||
commands.push (callback) ->
|
||||
atomBinary = path.join(grunt.config.get('atom.contentsDir'), 'MacOS', 'Atom')
|
||||
resourcePath = path.resolve(__dirname, '..')
|
||||
spawn cmd: atomBinary, args: ['--test', "--resource-path=#{resourcePath}"], (error) -> callback(error)
|
||||
grunt.util.async.waterfall commands, (error) -> done(error)
|
||||
@@ -0,0 +1,26 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getAtomShellVersion = ->
|
||||
versionPath = path.join('atom-shell', 'version')
|
||||
if grunt.file.isFile(versionPath)
|
||||
grunt.file.read(versionPath).trim()
|
||||
else
|
||||
null
|
||||
|
||||
grunt.registerTask 'update-atom-shell', 'Update atom-shell', ->
|
||||
done = @async()
|
||||
currentVersion = getAtomShellVersion()
|
||||
spawn cmd: 'script/update-atom-shell', (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
newVersion = getAtomShellVersion()
|
||||
if newVersion and currentVersion isnt newVersion
|
||||
grunt.log.writeln("Rebuilding native modules for new atom-shell version #{newVersion.cyan}.")
|
||||
cmd = path.join('node_modules', '.bin', 'apm')
|
||||
spawn {cmd, args: ['rebuild']}, (error) -> done(error)
|
||||
else
|
||||
done()
|
||||
@@ -1,5 +1,8 @@
|
||||
.tree-view {
|
||||
background: #dde3e8;
|
||||
}
|
||||
|
||||
.tree-view-resizer {
|
||||
border-right: 1px solid #989898;
|
||||
}
|
||||
|
||||
|
||||
externo
+1
-1
Submodule vendor/apm updated: 0945ca14da...ac0e54801b
+29
-1
@@ -1,3 +1,31 @@
|
||||
var _ = require('underscore');
|
||||
var convertStackTrace = require('coffeestack').convertStackTrace;
|
||||
|
||||
var sourceMaps = {};
|
||||
var formatStackTrace = function(stackTrace) {
|
||||
if (!stackTrace)
|
||||
return stackTrace;
|
||||
|
||||
// Remove all lines containing jasmine.js path
|
||||
var jasminePath = require.resolve('jasmine');
|
||||
var jasminePattern = new RegExp("\\(" + _.escapeRegExp(jasminePath) + ":\\d+:\\d+\\)\\s*$");
|
||||
var convertedLines = [];
|
||||
var lines = stackTrace.split('\n');
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (!jasminePattern.test(lines[i]))
|
||||
convertedLines.push(lines[i]);
|
||||
|
||||
//Remove last util.spawn.callDone line and all lines after it
|
||||
var gruntSpawnPattern = /^\s*at util\.spawn\.callDone\s*\(.*\/grunt\/util\.js:\d+:\d+\)\s*$/
|
||||
for (var i = convertedLines.length - 1; i > 0; i--)
|
||||
if (gruntSpawnPattern.test(convertedLines[i])) {
|
||||
convertedLines = convertedLines.slice(0, i);
|
||||
break;
|
||||
}
|
||||
|
||||
return convertStackTrace(convertedLines.join('\n'), sourceMaps);
|
||||
}
|
||||
|
||||
jasmine.ConsoleReporter = function(doc, logErrors) {
|
||||
this.logErrors = logErrors == false ? false : true
|
||||
};
|
||||
@@ -35,7 +63,7 @@ jasmine.ConsoleReporter.prototype.reportSpecResults = function(spec) {
|
||||
console.log("\n\n" + message)
|
||||
console.log((new Array(message.length + 1)).join('-'))
|
||||
if (result.trace.stack) {
|
||||
console.log(result.trace.stack)
|
||||
console.log(formatStackTrace(result.trace.stack));
|
||||
}
|
||||
else {
|
||||
console.log(result.message)
|
||||
|
||||
+1
Submodule vendor/telepath added at 3b465ef7e0
Referência em uma Nova Issue
Bloquear um usuário